diff --git a/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.ts b/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.ts index e5e66a769..43028ebfd 100644 --- a/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.ts +++ b/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes.ts @@ -103,7 +103,7 @@ export class SignInWithRecoveryCodes implements UseCaseInterface { payload: { payload: { ephemeral: false, - mergeLocal: false, + mergeLocal: dto.mergeLocal ?? true, awaitSync: true, checkIntegrity: false, }, diff --git a/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodesDTO.ts b/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodesDTO.ts index ed37d6600..0e469bff4 100644 --- a/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodesDTO.ts +++ b/packages/snjs/lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodesDTO.ts @@ -3,4 +3,5 @@ export interface SignInWithRecoveryCodesDTO { username: string password: string hvmToken?: string + mergeLocal?: boolean } diff --git a/packages/web/src/javascripts/Components/AccountMenu/ConfirmNoMergeDialog.tsx b/packages/web/src/javascripts/Components/AccountMenu/ConfirmNoMergeDialog.tsx new file mode 100644 index 000000000..7888be217 --- /dev/null +++ b/packages/web/src/javascripts/Components/AccountMenu/ConfirmNoMergeDialog.tsx @@ -0,0 +1,41 @@ +import AlertDialog from '@/Components/AlertDialog/AlertDialog' +import Button from '@/Components/Button/Button' +import Icon from '@/Components/Icon/Icon' +import { FunctionComponent } from 'react' + +type Props = { + onClose: () => void + onConfirm: () => void +} + +const ConfirmNoMergeDialog: FunctionComponent = ({ onClose, onConfirm }) => { + return ( + +
+ Delete local data? + +
+
+
+

+ You have chosen not to merge your local data. If you proceed, your local notes and tags will be permanently + deleted and replaced with data from your account. This action cannot be undone. +

+

+ Are you sure you want to continue without merging? +

+
+
+
+ + +
+
+ ) +} + +export default ConfirmNoMergeDialog diff --git a/packages/web/src/javascripts/Components/AccountMenu/ConfirmPassword.tsx b/packages/web/src/javascripts/Components/AccountMenu/ConfirmPassword.tsx index 3a20e8217..673abc0ab 100644 --- a/packages/web/src/javascripts/Components/AccountMenu/ConfirmPassword.tsx +++ b/packages/web/src/javascripts/Components/AccountMenu/ConfirmPassword.tsx @@ -18,6 +18,8 @@ import IconButton from '@/Components/Button/IconButton' import { useApplication } from '../ApplicationProvider' import { useCaptcha } from '@/Hooks/useCaptcha' import { isErrorResponse } from '@standardnotes/snjs' +import MergeLocalDataCheckbox from './MergeLocalDataCheckbox' +import ConfirmNoMergeDialog from './ConfirmNoMergeDialog' type Props = { setMenuPane: (pane: AccountMenuPane) => void @@ -37,6 +39,7 @@ const ConfirmPassword: FunctionComponent = ({ setMenuPane, email, passwor const [hvmToken, setHVMToken] = useState('') const [captchaURL, setCaptchaURL] = useState('') + const [showNoMergeConfirmation, setShowNoMergeConfirmation] = useState(false) const register = useCallback(() => { setIsRegistering(true) @@ -124,9 +127,14 @@ const ConfirmPassword: FunctionComponent = ({ setMenuPane, email, passwor return } + if (notesAndTagsCount > 0 && !shouldMergeLocal) { + setShowNoMergeConfirmation(true) + return + } + checkIfCaptchaRequiredAndRegister() }, - [checkIfCaptchaRequiredAndRegister, confirmPassword, password], + [checkIfCaptchaRequiredAndRegister, confirmPassword, password, notesAndTagsCount, shouldMergeLocal], ) const handleKeyDown: KeyboardEventHandler = useCallback( @@ -145,6 +153,15 @@ const ConfirmPassword: FunctionComponent = ({ setMenuPane, email, passwor setMenuPane(AccountMenuPane.Register) }, [setMenuPane]) + const closeNoMergeConfirmation = useCallback(() => { + setShowNoMergeConfirmation(false) + }, []) + + const confirmRegisterWithoutMerge = useCallback(() => { + setShowNoMergeConfirmation(false) + checkIfCaptchaRequiredAndRegister() + }, [checkIfCaptchaRequiredAndRegister]) + const confirmPasswordForm = ( <>
@@ -182,12 +199,11 @@ const ConfirmPassword: FunctionComponent = ({ setMenuPane, email, passwor disabled={isRegistering} /> {notesAndTagsCount > 0 ? ( - ) : null} @@ -208,6 +224,9 @@ const ConfirmPassword: FunctionComponent = ({ setMenuPane, email, passwor
{captchaURL ? 'Human verification' : 'Confirm password'}
{captchaURL ?
{captchaIframe}
: confirmPasswordForm} + {showNoMergeConfirmation && ( + + )} ) } diff --git a/packages/web/src/javascripts/Components/AccountMenu/MergeLocalDataCheckbox.tsx b/packages/web/src/javascripts/Components/AccountMenu/MergeLocalDataCheckbox.tsx new file mode 100644 index 000000000..cd2e10b2c --- /dev/null +++ b/packages/web/src/javascripts/Components/AccountMenu/MergeLocalDataCheckbox.tsx @@ -0,0 +1,38 @@ +import Icon from '@/Components/Icon/Icon' +import StyledTooltip from '@/Components/StyledTooltip/StyledTooltip' +import { ChangeEventHandler, FunctionComponent } from 'react' + +type Props = { + checked: boolean + onChange: ChangeEventHandler + disabled?: boolean + notesAndTagsCount: number +} + +const MergeLocalDataCheckbox: FunctionComponent = ({ checked, onChange, disabled, notesAndTagsCount }) => { + return ( + + ) +} + +export default MergeLocalDataCheckbox diff --git a/packages/web/src/javascripts/Components/AccountMenu/SignIn.tsx b/packages/web/src/javascripts/Components/AccountMenu/SignIn.tsx index 63d38d8cc..4ad8dc3c1 100644 --- a/packages/web/src/javascripts/Components/AccountMenu/SignIn.tsx +++ b/packages/web/src/javascripts/Components/AccountMenu/SignIn.tsx @@ -13,6 +13,8 @@ import HorizontalSeparator from '../Shared/HorizontalSeparator' import { getErrorFromErrorResponse, isErrorResponse, getCaptchaHeader } from '@standardnotes/snjs' import { useApplication } from '../ApplicationProvider' import { useCaptcha } from '@/Hooks/useCaptcha' +import MergeLocalDataCheckbox from './MergeLocalDataCheckbox' +import ConfirmNoMergeDialog from './ConfirmNoMergeDialog' type Props = { setMenuPane: (pane: AccountMenuPane) => void @@ -34,6 +36,7 @@ const SignInPane: FunctionComponent = ({ setMenuPane }) => { const [isPrivateUsername, setIsPrivateUsername] = useState(false) const [isRecoverySignIn, setIsRecoverySignIn] = useState(false) + const [showNoMergeConfirmation, setShowNoMergeConfirmation] = useState(false) const [captchaURL, setCaptchaURL] = useState('') const [showCaptcha, setShowCaptcha] = useState(false) @@ -139,6 +142,7 @@ const SignInPane: FunctionComponent = ({ setMenuPane }) => { username: email, password: password, hvmToken, + mergeLocal: shouldMergeLocal, }) .then((result) => { if (result.isFailed()) { @@ -166,7 +170,15 @@ const SignInPane: FunctionComponent = ({ setMenuPane }) => { .finally(() => { setIsSigningIn(false) }) - }, [application.accountMenuController, application.signInWithRecoveryCodes, email, hvmToken, password, recoveryCodes]) + }, [ + application.accountMenuController, + application.signInWithRecoveryCodes, + email, + hvmToken, + password, + recoveryCodes, + shouldMergeLocal, + ]) const onPrivateUsernameChange = useCallback( (newisPrivateUsername: boolean, privateUsernameIdentifier?: string) => { @@ -189,13 +201,18 @@ const SignInPane: FunctionComponent = ({ setMenuPane }) => { return } + if (notesAndTagsCount > 0 && !shouldMergeLocal) { + setShowNoMergeConfirmation(true) + return + } + if (isRecoverySignIn) { recoverySignIn() return } signIn() - }, [email, isRecoverySignIn, password, recoverySignIn, signIn]) + }, [email, isRecoverySignIn, password, recoverySignIn, signIn, notesAndTagsCount, shouldMergeLocal]) const handleSignInFormSubmit = useCallback( (e: React.SyntheticEvent) => { @@ -272,12 +289,11 @@ const SignInPane: FunctionComponent = ({ setMenuPane }) => { onChange={handleEphemeralChange} /> {notesAndTagsCount > 0 ? ( - ) : null} @@ -291,6 +307,19 @@ const SignInPane: FunctionComponent = ({ setMenuPane }) => { ) + const closeNoMergeConfirmation = useCallback(() => { + setShowNoMergeConfirmation(false) + }, []) + + const confirmSignInWithoutMerge = useCallback(() => { + setShowNoMergeConfirmation(false) + if (isRecoverySignIn) { + recoverySignIn() + } else { + signIn() + } + }, [signIn, isRecoverySignIn, recoverySignIn]) + return ( <>
@@ -305,6 +334,9 @@ const SignInPane: FunctionComponent = ({ setMenuPane }) => {
{showCaptcha ? 'Human verification' : 'Sign in'}
{showCaptcha ?
{captchaIframe}
: signInForm} + {showNoMergeConfirmation && ( + + )} ) }