feat: Show warning modal when Merge local data option is unchecked (#2953) [skip e2e]
* feat: Show warning modal when Merge local data option is unchecked * keep checkbox available when using recovery sign in
This commit is contained in:
committed by
GitHub
parent
11e36e1d4c
commit
531cc70667
@@ -103,7 +103,7 @@ export class SignInWithRecoveryCodes implements UseCaseInterface<void> {
|
||||
payload: {
|
||||
payload: {
|
||||
ephemeral: false,
|
||||
mergeLocal: false,
|
||||
mergeLocal: dto.mergeLocal ?? true,
|
||||
awaitSync: true,
|
||||
checkIntegrity: false,
|
||||
},
|
||||
|
||||
@@ -3,4 +3,5 @@ export interface SignInWithRecoveryCodesDTO {
|
||||
username: string
|
||||
password: string
|
||||
hvmToken?: string
|
||||
mergeLocal?: boolean
|
||||
}
|
||||
|
||||
@@ -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<Props> = ({ onClose, onConfirm }) => {
|
||||
return (
|
||||
<AlertDialog closeDialog={onClose}>
|
||||
<div className="flex items-center justify-between text-lg font-bold">
|
||||
Delete local data?
|
||||
<button className="rounded p-1 font-bold hover:bg-contrast" onClick={onClose}>
|
||||
<Icon type="close" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="sk-panel-row">
|
||||
<div>
|
||||
<p className="text-base text-foreground lg:text-sm">
|
||||
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.
|
||||
</p>
|
||||
<p className="mt-2 text-base font-semibold text-danger lg:text-sm">
|
||||
Are you sure you want to continue without merging?
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 flex justify-end gap-2">
|
||||
<Button onClick={onClose}>Cancel</Button>
|
||||
<Button primary colorStyle="danger" onClick={onConfirm}>
|
||||
Delete Local Data and Continue
|
||||
</Button>
|
||||
</div>
|
||||
</AlertDialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default ConfirmNoMergeDialog
|
||||
@@ -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<Props> = ({ 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<Props> = ({ 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<Props> = ({ setMenuPane, email, passwor
|
||||
setMenuPane(AccountMenuPane.Register)
|
||||
}, [setMenuPane])
|
||||
|
||||
const closeNoMergeConfirmation = useCallback(() => {
|
||||
setShowNoMergeConfirmation(false)
|
||||
}, [])
|
||||
|
||||
const confirmRegisterWithoutMerge = useCallback(() => {
|
||||
setShowNoMergeConfirmation(false)
|
||||
checkIfCaptchaRequiredAndRegister()
|
||||
}, [checkIfCaptchaRequiredAndRegister])
|
||||
|
||||
const confirmPasswordForm = (
|
||||
<>
|
||||
<div className="mb-3 px-3 text-sm">
|
||||
@@ -182,12 +199,11 @@ const ConfirmPassword: FunctionComponent<Props> = ({ setMenuPane, email, passwor
|
||||
disabled={isRegistering}
|
||||
/>
|
||||
{notesAndTagsCount > 0 ? (
|
||||
<Checkbox
|
||||
name="should-merge-local"
|
||||
label={`Merge local data (${notesAndTagsCount} notes and tags)`}
|
||||
<MergeLocalDataCheckbox
|
||||
checked={shouldMergeLocal}
|
||||
onChange={handleShouldMergeChange}
|
||||
disabled={isRegistering}
|
||||
notesAndTagsCount={notesAndTagsCount}
|
||||
/>
|
||||
) : null}
|
||||
</form>
|
||||
@@ -208,6 +224,9 @@ const ConfirmPassword: FunctionComponent<Props> = ({ setMenuPane, email, passwor
|
||||
<div className="text-base font-bold">{captchaURL ? 'Human verification' : 'Confirm password'}</div>
|
||||
</div>
|
||||
{captchaURL ? <div className="p-[10px]">{captchaIframe}</div> : confirmPasswordForm}
|
||||
{showNoMergeConfirmation && (
|
||||
<ConfirmNoMergeDialog onClose={closeNoMergeConfirmation} onConfirm={confirmRegisterWithoutMerge} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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<HTMLInputElement>
|
||||
disabled?: boolean
|
||||
notesAndTagsCount: number
|
||||
}
|
||||
|
||||
const MergeLocalDataCheckbox: FunctionComponent<Props> = ({ checked, onChange, disabled, notesAndTagsCount }) => {
|
||||
return (
|
||||
<label htmlFor="should-merge-local" className="fit-content mb-2 flex items-center text-sm">
|
||||
<input
|
||||
className="mr-2 accent-danger"
|
||||
type="checkbox"
|
||||
name="should-merge-local"
|
||||
id="should-merge-local"
|
||||
checked={checked}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<span className="text-danger">Merge local data ({notesAndTagsCount} notes and tags)</span>
|
||||
<StyledTooltip
|
||||
label="If unchecked, your local notes and tags will be permanently deleted and replaced with data from your account."
|
||||
showOnMobile
|
||||
className="!z-modal !max-w-[30ch] whitespace-normal"
|
||||
>
|
||||
<button type="button" className="ml-1 rounded-full p-0.5 hover:bg-contrast">
|
||||
<Icon type="info" className="text-danger" size="small" />
|
||||
</button>
|
||||
</StyledTooltip>
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
||||
export default MergeLocalDataCheckbox
|
||||
@@ -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<Props> = ({ 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<Props> = ({ setMenuPane }) => {
|
||||
username: email,
|
||||
password: password,
|
||||
hvmToken,
|
||||
mergeLocal: shouldMergeLocal,
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.isFailed()) {
|
||||
@@ -166,7 +170,15 @@ const SignInPane: FunctionComponent<Props> = ({ 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<Props> = ({ 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<Props> = ({ setMenuPane }) => {
|
||||
onChange={handleEphemeralChange}
|
||||
/>
|
||||
{notesAndTagsCount > 0 ? (
|
||||
<Checkbox
|
||||
name="should-merge-local"
|
||||
label={`Merge local data (${notesAndTagsCount} notes and tags)`}
|
||||
<MergeLocalDataCheckbox
|
||||
checked={shouldMergeLocal}
|
||||
disabled={isSigningIn}
|
||||
onChange={handleShouldMergeChange}
|
||||
disabled={isSigningIn}
|
||||
notesAndTagsCount={notesAndTagsCount}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -291,6 +307,19 @@ const SignInPane: FunctionComponent<Props> = ({ setMenuPane }) => {
|
||||
</>
|
||||
)
|
||||
|
||||
const closeNoMergeConfirmation = useCallback(() => {
|
||||
setShowNoMergeConfirmation(false)
|
||||
}, [])
|
||||
|
||||
const confirmSignInWithoutMerge = useCallback(() => {
|
||||
setShowNoMergeConfirmation(false)
|
||||
if (isRecoverySignIn) {
|
||||
recoverySignIn()
|
||||
} else {
|
||||
signIn()
|
||||
}
|
||||
}, [signIn, isRecoverySignIn, recoverySignIn])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-3 mt-1 flex items-center px-3">
|
||||
@@ -305,6 +334,9 @@ const SignInPane: FunctionComponent<Props> = ({ setMenuPane }) => {
|
||||
<div className="text-base font-bold">{showCaptcha ? 'Human verification' : 'Sign in'}</div>
|
||||
</div>
|
||||
{showCaptcha ? <div className="p-[10px]">{captchaIframe}</div> : signInForm}
|
||||
{showNoMergeConfirmation && (
|
||||
<ConfirmNoMergeDialog onClose={closeNoMergeConfirmation} onConfirm={confirmSignInWithoutMerge} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user