feat: added new hide recovery password modal

when we turn on the setting we prevent any recovery phrase ui from loading and return null
pull/3083/head
William Grant 11 months ago
parent 51534d4cc9
commit fb3a87a5f5

@ -426,6 +426,8 @@
"recoveryPasswordErrorMessageIncorrect": "Some of the words in your Recovery Password are incorrect. Please check and try again.", "recoveryPasswordErrorMessageIncorrect": "Some of the words in your Recovery Password are incorrect. Please check and try again.",
"recoveryPasswordErrorMessageShort": "The Recovery Password you entered is not long enough. Please check and try again.", "recoveryPasswordErrorMessageShort": "The Recovery Password you entered is not long enough. Please check and try again.",
"recoveryPasswordHidePermanently": "Hide Recovery Password Permanently", "recoveryPasswordHidePermanently": "Hide Recovery Password Permanently",
"recoveryPasswordHidePermanentlyDescription1": "Without your recovery password, you cannot load your account on new devices. <br /><br />We strongly recommend you save your recovery password in a safe and secure place before continuing.",
"recoveryPasswordHidePermanentlyDescription2": "Are you sure you want to permanently hide your recovery password on this device? This cannot be undone.",
"recoveryPasswordHideRecoveryPasswordDescription": "Permanently hide your recover password on this device.", "recoveryPasswordHideRecoveryPasswordDescription": "Permanently hide your recover password on this device.",
"recoveryPasswordWarningSendDescription": "This is your recovery password. If you send it to someone they'll have full access to your account.", "recoveryPasswordWarningSendDescription": "This is your recovery password. If you send it to someone they'll have full access to your account.",
"recoveryPhraseSavePromptMain": "Your recovery password is the master key to your Account ID — you can use it to restore your Account ID if you lose access to your device. Store your recovery password in a safe place, and don't give it to anyone.", "recoveryPhraseSavePromptMain": "Your recovery password is the master key to your Account ID — you can use it to restore your Account ID if you lose access to your device. Store your recovery password in a safe place, and don't give it to anyone.",
@ -585,6 +587,7 @@
"windowMenuClose": "Close Window", "windowMenuClose": "Close Window",
"windowMenuMinimize": "Minimize", "windowMenuMinimize": "Minimize",
"windowMenuZoom": "Zoom", "windowMenuZoom": "Zoom",
"yes": "Yes",
"yesterday": "Yesterday", "yesterday": "Yesterday",
"you": "You", "you": "You",
"youChangedTheTimer": "<b>You</b> have set messages to disappear <b>$time$</b> after they have been <b>$mode$</b>", "youChangedTheTimer": "<b>You</b> have set messages to disappear <b>$time$</b> after they have been <b>$mode$</b>",

@ -0,0 +1,109 @@
import { isEmpty } from 'lodash';
import { useDispatch } from 'react-redux';
import styled from 'styled-components';
import { SettingsKey } from '../../data/settings-key';
import { updateHideRecoveryPasswordModel } from '../../state/ducks/modalDialog';
import { showSettingsSection } from '../../state/ducks/section';
import { SessionWrapperModal } from '../SessionWrapperModal';
import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
import { SessionHtmlRenderer } from '../basic/SessionHTMLRenderer';
import { SpacerMD } from '../basic/Text';
import { ModalConfirmButtonContainer } from '../buttons/ModalConfirmButtonContainer';
const StyledDescriptionContainer = styled.div`
width: 280px;
line-height: 120%;
`;
export type HideRecoveryPasswordDialogProps = {
state: 'firstWarning' | 'secondWarning';
};
export function HideRecoveryPasswordDialog(props: HideRecoveryPasswordDialogProps) {
const { state } = props;
const dispatch = useDispatch();
const onClose = () => {
dispatch(updateHideRecoveryPasswordModel(null));
};
const onConfirmation = async () => {
await window.setSettingValue(SettingsKey.hideRecoveryPassword, true);
onClose();
dispatch(showSettingsSection('privacy'));
};
if (isEmpty(state)) {
return null;
}
const leftButtonProps =
state === 'firstWarning'
? {
text: window.i18n('continue'),
buttonColor: SessionButtonColor.Danger,
onClick: () => {
dispatch(updateHideRecoveryPasswordModel({ state: 'secondWarning' }));
},
dataTestId: 'session-confirm-ok-button',
}
: {
text: window.i18n('cancel'),
onClick: onClose,
dataTestId: 'session-confirm-cancel-button',
};
const rightButtonProps =
state === 'firstWarning'
? {
text: window.i18n('cancel'),
onClick: onClose,
dataTestId: 'session-confirm-cancel-button',
}
: {
text: window.i18n('yes'),
buttonColor: SessionButtonColor.Danger,
onClick: () => {
void onConfirmation();
},
dataTestId: 'session-confirm-ok-button',
};
return (
<SessionWrapperModal
title={window.i18n('recoveryPasswordHidePermanently')}
onClose={onClose}
showExitIcon={false}
showHeader={true}
>
<StyledDescriptionContainer>
<SessionHtmlRenderer
html={
state === 'firstWarning'
? window.i18n('recoveryPasswordHidePermanentlyDescription1')
: window.i18n('recoveryPasswordHidePermanentlyDescription2')
}
/>
</StyledDescriptionContainer>
<SpacerMD />
<ModalConfirmButtonContainer
container={true}
justifyContent="center"
alignItems="center"
width="100%"
>
<SessionButton
{...leftButtonProps}
buttonType={SessionButtonType.Ghost}
margin={'0 0 0 calc(var(--margins-lg) * -1)'}
/>
<SessionButton
{...rightButtonProps}
buttonType={SessionButtonType.Ghost}
margin={'0 calc(var(--margins-lg) * -1) 0 0'}
/>
</ModalConfirmButtonContainer>
</SessionWrapperModal>
);
}

@ -8,6 +8,7 @@ import {
getEditProfileDialog, getEditProfileDialog,
getEditProfilePictureModalState, getEditProfilePictureModalState,
getEnterPasswordModalState, getEnterPasswordModalState,
getHideRecoveryPasswordModalState,
getInviteContactModal, getInviteContactModal,
getOnionPathDialog, getOnionPathDialog,
getReactClearAllDialog, getReactClearAllDialog,
@ -24,6 +25,7 @@ import { DeleteAccountModal } from './DeleteAccountModal';
import { EditProfileDialog } from './EditProfileDialog'; import { EditProfileDialog } from './EditProfileDialog';
import { EditProfilePictureModal } from './EditProfilePictureModal'; import { EditProfilePictureModal } from './EditProfilePictureModal';
import { EnterPasswordModal } from './EnterPasswordModal'; import { EnterPasswordModal } from './EnterPasswordModal';
import { HideRecoveryPasswordDialog } from './HideRecoveryPasswordDialog';
import { InviteContactsDialog } from './InviteContactsDialog'; import { InviteContactsDialog } from './InviteContactsDialog';
import { AddModeratorsDialog } from './ModeratorsAddDialog'; import { AddModeratorsDialog } from './ModeratorsAddDialog';
import { RemoveModeratorsDialog } from './ModeratorsRemoveDialog'; import { RemoveModeratorsDialog } from './ModeratorsRemoveDialog';
@ -57,6 +59,7 @@ export const ModalContainer = () => {
const reactListModalState = useSelector(getReactListDialog); const reactListModalState = useSelector(getReactListDialog);
const reactClearAllModalState = useSelector(getReactClearAllDialog); const reactClearAllModalState = useSelector(getReactClearAllDialog);
const editProfilePictureModalState = useSelector(getEditProfilePictureModalState); const editProfilePictureModalState = useSelector(getEditProfilePictureModalState);
const hideRecoveryPasswordModalState = useSelector(getHideRecoveryPasswordModalState);
return ( return (
<> <>
@ -82,6 +85,9 @@ export const ModalContainer = () => {
{editProfilePictureModalState && ( {editProfilePictureModalState && (
<EditProfilePictureModal {...editProfilePictureModalState} /> <EditProfilePictureModal {...editProfilePictureModalState} />
)} )}
{hideRecoveryPasswordModalState && (
<HideRecoveryPasswordDialog {...hideRecoveryPasswordModalState} />
)}
</> </>
); );
}; };

@ -4,6 +4,7 @@ import { recoveryPhraseModal } from '../../state/ducks/modalDialog';
import { SectionType, setLeftOverlayMode } from '../../state/ducks/section'; import { SectionType, setLeftOverlayMode } from '../../state/ducks/section';
import { disableRecoveryPhrasePrompt } from '../../state/ducks/userConfig'; import { disableRecoveryPhrasePrompt } from '../../state/ducks/userConfig';
import { getFocusedSection, getLeftOverlayMode } from '../../state/selectors/section'; import { getFocusedSection, getLeftOverlayMode } from '../../state/selectors/section';
import { useHideRecoveryPasswordEnabled } from '../../state/selectors/settings';
import { getShowRecoveryPhrasePrompt } from '../../state/selectors/userConfig'; import { getShowRecoveryPhrasePrompt } from '../../state/selectors/userConfig';
import { isSignWithRecoveryPhrase } from '../../util/storage'; import { isSignWithRecoveryPhrase } from '../../util/storage';
import { Flex } from '../basic/Flex'; import { Flex } from '../basic/Flex';
@ -75,6 +76,7 @@ const StyledLeftPaneBanner = styled.div`
export const LeftPaneBanner = () => { export const LeftPaneBanner = () => {
const section = useSelector(getFocusedSection); const section = useSelector(getFocusedSection);
const isSignInWithRecoveryPhrase = isSignWithRecoveryPhrase(); const isSignInWithRecoveryPhrase = isSignWithRecoveryPhrase();
const hideRecoveryPassword = useHideRecoveryPasswordEnabled();
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -83,7 +85,7 @@ export const LeftPaneBanner = () => {
dispatch(recoveryPhraseModal({})); dispatch(recoveryPhraseModal({}));
}; };
if (section !== SectionType.Message || isSignInWithRecoveryPhrase) { if (section !== SectionType.Message || isSignInWithRecoveryPhrase || hideRecoveryPassword) {
return null; return null;
} }

@ -10,6 +10,7 @@ import {
showSettingsSection, showSettingsSection,
} from '../../state/ducks/section'; } from '../../state/ducks/section';
import { getFocusedSettingsSection } from '../../state/selectors/section'; import { getFocusedSettingsSection } from '../../state/selectors/section';
import { useHideRecoveryPasswordEnabled } from '../../state/selectors/settings';
import type { SessionSettingCategory } from '../../types/ReduxTypes'; import type { SessionSettingCategory } from '../../types/ReduxTypes';
import { Flex } from '../basic/Flex'; import { Flex } from '../basic/Flex';
import { SessionIcon, SessionIconSize, SessionIconType } from '../icon'; import { SessionIcon, SessionIconSize, SessionIconType } from '../icon';
@ -164,7 +165,12 @@ const LeftPaneSettingsCategoryRow = (props: { item: Categories }) => {
}; };
const LeftPaneSettingsCategories = () => { const LeftPaneSettingsCategories = () => {
const categories = getCategories(); let categories = getCategories();
const hideRecoveryPassword = useHideRecoveryPasswordEnabled();
if (hideRecoveryPassword) {
categories = categories.filter(category => category.id !== 'recoveryPassword');
}
return ( return (
<> <>

@ -6,7 +6,9 @@ import styled from 'styled-components';
import { usePasswordModal } from '../../../hooks/usePasswordModal'; import { usePasswordModal } from '../../../hooks/usePasswordModal';
import { mnDecode } from '../../../session/crypto/mnemonic'; import { mnDecode } from '../../../session/crypto/mnemonic';
import { ToastUtils } from '../../../session/utils'; import { ToastUtils } from '../../../session/utils';
import { updateHideRecoveryPasswordModel } from '../../../state/ducks/modalDialog';
import { showSettingsSection } from '../../../state/ducks/section'; import { showSettingsSection } from '../../../state/ducks/section';
import { useHideRecoveryPasswordEnabled } from '../../../state/selectors/settings';
import { getTheme } from '../../../state/selectors/theme'; import { getTheme } from '../../../state/selectors/theme';
import { THEME_GLOBALS, getThemeValue } from '../../../themes/globals'; import { THEME_GLOBALS, getThemeValue } from '../../../themes/globals';
import { getCurrentRecoveryPhrase } from '../../../util/storage'; import { getCurrentRecoveryPhrase } from '../../../util/storage';
@ -49,6 +51,8 @@ export const SettingsCategoryRecoveryPassword = () => {
const [hexEncodedSeed, setHexEncodedSeed] = useState(''); const [hexEncodedSeed, setHexEncodedSeed] = useState('');
const [isQRVisible, setIsQRVisible] = useState(false); const [isQRVisible, setIsQRVisible] = useState(false);
const hideRecoveryPassword = useHideRecoveryPasswordEnabled();
const dispatch = useDispatch(); const dispatch = useDispatch();
const { hasPassword, passwordValid } = usePasswordModal({ const { hasPassword, passwordValid } = usePasswordModal({
@ -79,7 +83,7 @@ export const SettingsCategoryRecoveryPassword = () => {
} }
}); });
if ((hasPassword && !passwordValid) || loadingSeed) { if ((hasPassword && !passwordValid) || loadingSeed || hideRecoveryPassword) {
return null; return null;
} }
@ -166,16 +170,18 @@ export const SettingsCategoryRecoveryPassword = () => {
{isQRVisible ? 'View as Password' : 'View QR'} {isQRVisible ? 'View as Password' : 'View QR'}
</SessionIconButton> </SessionIconButton>
</SessionSettingsItemWrapper> </SessionSettingsItemWrapper>
<SessionSettingButtonItem {!hideRecoveryPassword ? (
title={window.i18n('recoveryPasswordHidePermanently')} <SessionSettingButtonItem
description={window.i18n('recoveryPasswordHideRecoveryPasswordDescription')} title={window.i18n('recoveryPasswordHidePermanently')}
onClick={() => { description={window.i18n('recoveryPasswordHideRecoveryPasswordDescription')}
// TODO onClick={() => {
}} dispatch(updateHideRecoveryPasswordModel({ state: 'firstWarning' }));
buttonText={window.i18n('hide')} }}
buttonColor={SessionButtonColor.Danger} buttonText={window.i18n('hide')}
dataTestId={'hide-recovery-password-button'} buttonColor={SessionButtonColor.Danger}
/> dataTestId={'hide-recovery-password-button'}
/>
) : null}
</StyledSettingsItemContainer> </StyledSettingsItemContainer>
); );
}; };

@ -1,5 +1,6 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { EnterPasswordModalProps } from '../../components/dialog/EnterPasswordModal'; import { EnterPasswordModalProps } from '../../components/dialog/EnterPasswordModal';
import { HideRecoveryPasswordDialogProps } from '../../components/dialog/HideRecoveryPasswordDialog';
import { SessionConfirmDialogProps } from '../../components/dialog/SessionConfirm'; import { SessionConfirmDialogProps } from '../../components/dialog/SessionConfirm';
import type { EditProfilePictureModalProps, PasswordAction } from '../../types/ReduxTypes'; import type { EditProfilePictureModalProps, PasswordAction } from '../../types/ReduxTypes';
@ -38,6 +39,8 @@ export type ReactModalsState = {
export type EditProfilePictureModalState = EditProfilePictureModalProps | null; export type EditProfilePictureModalState = EditProfilePictureModalProps | null;
export type HideRecoveryPasswordModalState = HideRecoveryPasswordDialogProps | null;
export type ModalState = { export type ModalState = {
confirmModal: ConfirmModalState; confirmModal: ConfirmModalState;
inviteContactModal: InviteContactModalState; inviteContactModal: InviteContactModalState;
@ -57,6 +60,7 @@ export type ModalState = {
reactListModalState: ReactModalsState; reactListModalState: ReactModalsState;
reactClearAllModalState: ReactModalsState; reactClearAllModalState: ReactModalsState;
editProfilePictureModalState: EditProfilePictureModalState; editProfilePictureModalState: EditProfilePictureModalState;
hideRecoveryPasswordModalState: HideRecoveryPasswordModalState;
}; };
export const initialModalState: ModalState = { export const initialModalState: ModalState = {
@ -78,6 +82,7 @@ export const initialModalState: ModalState = {
reactListModalState: null, reactListModalState: null,
reactClearAllModalState: null, reactClearAllModalState: null,
editProfilePictureModalState: null, editProfilePictureModalState: null,
hideRecoveryPasswordModalState: null,
}; };
const ModalSlice = createSlice({ const ModalSlice = createSlice({
@ -138,6 +143,9 @@ const ModalSlice = createSlice({
updateEditProfilePictureModel(state, action: PayloadAction<EditProfilePictureModalState>) { updateEditProfilePictureModel(state, action: PayloadAction<EditProfilePictureModalState>) {
return { ...state, editProfilePictureModalState: action.payload }; return { ...state, editProfilePictureModalState: action.payload };
}, },
updateHideRecoveryPasswordModel(state, action: PayloadAction<HideRecoveryPasswordModalState>) {
return { ...state, hideRecoveryPasswordModalState: action.payload };
},
}, },
}); });
@ -161,5 +169,6 @@ export const {
updateReactListModal, updateReactListModal,
updateReactClearAllModal, updateReactClearAllModal,
updateEditProfilePictureModel, updateEditProfilePictureModel,
updateHideRecoveryPasswordModel,
} = actions; } = actions;
export const modalReducer = reducer; export const modalReducer = reducer;

@ -9,6 +9,7 @@ import {
EditProfileModalState, EditProfileModalState,
EditProfilePictureModalState, EditProfilePictureModalState,
EnterPasswordModalState, EnterPasswordModalState,
HideRecoveryPasswordModalState,
InviteContactModalState, InviteContactModalState,
ModalState, ModalState,
OnionPathModalState, OnionPathModalState,
@ -115,3 +116,8 @@ export const getEditProfilePictureModalState = createSelector(
getModal, getModal,
(state: ModalState): EditProfilePictureModalState => state.editProfilePictureModalState (state: ModalState): EditProfilePictureModalState => state.editProfilePictureModalState
); );
export const getHideRecoveryPasswordModalState = createSelector(
getModal,
(state: ModalState): HideRecoveryPasswordModalState => state.hideRecoveryPasswordModalState
);

@ -426,6 +426,8 @@ export type LocalizerKeys =
| 'recoveryPasswordErrorMessageIncorrect' | 'recoveryPasswordErrorMessageIncorrect'
| 'recoveryPasswordErrorMessageShort' | 'recoveryPasswordErrorMessageShort'
| 'recoveryPasswordHidePermanently' | 'recoveryPasswordHidePermanently'
| 'recoveryPasswordHidePermanentlyDescription1'
| 'recoveryPasswordHidePermanentlyDescription2'
| 'recoveryPasswordHideRecoveryPasswordDescription' | 'recoveryPasswordHideRecoveryPasswordDescription'
| 'recoveryPasswordWarningSendDescription' | 'recoveryPasswordWarningSendDescription'
| 'recoveryPhraseSavePromptMain' | 'recoveryPhraseSavePromptMain'
@ -585,6 +587,7 @@ export type LocalizerKeys =
| 'windowMenuClose' | 'windowMenuClose'
| 'windowMenuMinimize' | 'windowMenuMinimize'
| 'windowMenuZoom' | 'windowMenuZoom'
| 'yes'
| 'yesterday' | 'yesterday'
| 'you' | 'you'
| 'youChangedTheTimer' | 'youChangedTheTimer'

Loading…
Cancel
Save