feat: implement credentials information on Prefs -> Account pane (#632)
* feat: implement prefs -> credentials section UI (w/o backend integration) * feat: implement credentials information on Prefs -> Account pane - implement email changing UI (w/o backend integration) - implement password changing UI and reuse existing change password logic - replace 2FA dialog with shared one - implement React hook for preventing window refresh * fix: provide correct types * refactor: reuse styles from stylekit, rename components and create enum for input types * refactor: update default exports to named ones, correct texts * chore: remove unnecessary depenedency * chore: yarn.lock without unnecessary packages * Revert "chore: yarn.lock without unnecessary packages" This reverts commit 64aa75e8408b06884d6e7383180292a4a9a3e8ad.
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import { DecoratedInput } from '@/components/DecoratedInput';
|
||||
import { StateUpdater } from 'preact/hooks';
|
||||
import { FunctionalComponent } from 'preact';
|
||||
import { HtmlInputTypes } from '@/enums';
|
||||
|
||||
type Props = {
|
||||
setCurrentPassword: StateUpdater<string>
|
||||
setNewPassword: StateUpdater<string>
|
||||
setNewPasswordConfirmation: StateUpdater<string>
|
||||
}
|
||||
export const ChangePasswordForm: FunctionalComponent<Props> = ({
|
||||
setCurrentPassword,
|
||||
setNewPassword,
|
||||
setNewPasswordConfirmation
|
||||
}) => {
|
||||
return (
|
||||
(
|
||||
<>
|
||||
<div className={'mt-2 mb-3'}>
|
||||
<DecoratedInput
|
||||
type={HtmlInputTypes.Password}
|
||||
onChange={(currentPassword) => {
|
||||
setCurrentPassword(currentPassword);
|
||||
}}
|
||||
placeholder={'Current Password'}
|
||||
/>
|
||||
</div>
|
||||
<div className={'mt-2 mb-3'}>
|
||||
<DecoratedInput
|
||||
type={HtmlInputTypes.Password}
|
||||
placeholder={'New Password'}
|
||||
onChange={newPassword => setNewPassword(newPassword)}
|
||||
/>
|
||||
</div>
|
||||
<div className={'mt-2 mb-3'}>
|
||||
<DecoratedInput
|
||||
type={HtmlInputTypes.Password}
|
||||
placeholder={'Confirm New Password'}
|
||||
onChange={newPasswordConfirmation => setNewPasswordConfirmation(newPasswordConfirmation)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
import { FunctionalComponent } from 'preact';
|
||||
|
||||
export const ChangePasswordSuccess: FunctionalComponent = () => {
|
||||
return (
|
||||
<>
|
||||
<div className={'sk-label sk-bold info'}>Your password has been successfully changed.</div>
|
||||
<p className={'sk-p'}>
|
||||
Please ensure you are running the latest version of Standard Notes on all platforms to ensure maximum compatibility.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,191 @@
|
||||
import { useState } from '@node_modules/preact/hooks';
|
||||
import {
|
||||
ModalDialog,
|
||||
ModalDialogButtons,
|
||||
ModalDialogDescription,
|
||||
ModalDialogLabel
|
||||
} from '@/components/shared/ModalDialog';
|
||||
import { Button } from '@/components/Button';
|
||||
import { FunctionalComponent } from 'preact';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import { ChangePasswordSuccess } from '@/preferences/panes/account/changePassword/ChangePasswordSuccess';
|
||||
import { ChangePasswordForm } from '@/preferences/panes/account/changePassword/ChangePasswordForm';
|
||||
import { useBeforeUnload } from '@/hooks/useBeforeUnload';
|
||||
|
||||
enum SubmitButtonTitles {
|
||||
Default = 'Continue',
|
||||
GeneratingKeys = 'Generating Keys...',
|
||||
Finish = 'Finish'
|
||||
}
|
||||
|
||||
enum Steps {
|
||||
InitialStep,
|
||||
FinishStep
|
||||
}
|
||||
|
||||
type Props = {
|
||||
onCloseDialog: () => void;
|
||||
application: WebApplication;
|
||||
}
|
||||
|
||||
export const ChangePassword: FunctionalComponent<Props> = ({
|
||||
onCloseDialog,
|
||||
application
|
||||
}) => {
|
||||
const [currentPassword, setCurrentPassword] = useState('');
|
||||
const [newPassword, setNewPassword] = useState('');
|
||||
const [newPasswordConfirmation, setNewPasswordConfirmation] = useState('');
|
||||
const [isContinuing, setIsContinuing] = useState(false);
|
||||
const [lockContinue, setLockContinue] = useState(false);
|
||||
const [submitButtonTitle, setSubmitButtonTitle] = useState(SubmitButtonTitles.Default);
|
||||
const [currentStep, setCurrentStep] = useState(Steps.InitialStep);
|
||||
|
||||
useBeforeUnload();
|
||||
|
||||
const applicationAlertService = application.alertService;
|
||||
|
||||
const validateCurrentPassword = async () => {
|
||||
if (!currentPassword || currentPassword.length === 0) {
|
||||
applicationAlertService.alert(
|
||||
'Please enter your current password.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!newPassword || newPassword.length === 0) {
|
||||
applicationAlertService.alert(
|
||||
'Please enter a new password.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (newPassword !== newPasswordConfirmation) {
|
||||
applicationAlertService.alert(
|
||||
'Your new password does not match its confirmation.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!application.getUser()?.email) {
|
||||
applicationAlertService.alert(
|
||||
'We don\'t have your email stored. Please log out then log back in to fix this issue.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Validate current password */
|
||||
const success = await application.validateAccountPassword(currentPassword);
|
||||
if (!success) {
|
||||
applicationAlertService.alert(
|
||||
'The current password you entered is not correct. Please try again.'
|
||||
);
|
||||
}
|
||||
return success;
|
||||
};
|
||||
|
||||
const resetProgressState = () => {
|
||||
setSubmitButtonTitle(SubmitButtonTitles.Default);
|
||||
setIsContinuing(false);
|
||||
};
|
||||
|
||||
const processPasswordChange = async () => {
|
||||
await application.downloadBackup();
|
||||
|
||||
setLockContinue(true);
|
||||
|
||||
const response = await application.changePassword(
|
||||
currentPassword,
|
||||
newPassword
|
||||
);
|
||||
|
||||
const success = !response.error;
|
||||
|
||||
setLockContinue(false);
|
||||
|
||||
return success;
|
||||
};
|
||||
|
||||
const dismiss = () => {
|
||||
if (lockContinue) {
|
||||
applicationAlertService.alert(
|
||||
'Cannot close window until pending tasks are complete.'
|
||||
);
|
||||
} else {
|
||||
onCloseDialog();
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (lockContinue || isContinuing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentStep === Steps.FinishStep) {
|
||||
dismiss();
|
||||
return;
|
||||
}
|
||||
setIsContinuing(true);
|
||||
setSubmitButtonTitle(SubmitButtonTitles.GeneratingKeys);
|
||||
|
||||
const valid = await validateCurrentPassword();
|
||||
|
||||
if (!valid) {
|
||||
resetProgressState();
|
||||
return;
|
||||
}
|
||||
|
||||
const success = await processPasswordChange();
|
||||
if (!success) {
|
||||
resetProgressState();
|
||||
return;
|
||||
}
|
||||
setIsContinuing(false);
|
||||
setSubmitButtonTitle(SubmitButtonTitles.Finish);
|
||||
setCurrentStep(Steps.FinishStep);
|
||||
};
|
||||
|
||||
const handleDialogClose = () => {
|
||||
if (lockContinue) {
|
||||
applicationAlertService.alert(
|
||||
'Cannot close window until pending tasks are complete.'
|
||||
);
|
||||
} else {
|
||||
onCloseDialog();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ModalDialog>
|
||||
<ModalDialogLabel closeDialog={handleDialogClose}>
|
||||
Change Password
|
||||
</ModalDialogLabel>
|
||||
<ModalDialogDescription>
|
||||
{currentStep === Steps.InitialStep && (
|
||||
<ChangePasswordForm
|
||||
setCurrentPassword={setCurrentPassword}
|
||||
setNewPassword={setNewPassword}
|
||||
setNewPasswordConfirmation={setNewPasswordConfirmation}
|
||||
/>
|
||||
)}
|
||||
{currentStep === Steps.FinishStep && <ChangePasswordSuccess />}
|
||||
</ModalDialogDescription>
|
||||
<ModalDialogButtons>
|
||||
{currentStep === Steps.InitialStep && (
|
||||
<Button
|
||||
className='min-w-20'
|
||||
type='normal'
|
||||
label='Cancel'
|
||||
onClick={handleDialogClose}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
className='min-w-20'
|
||||
type='primary'
|
||||
label={submitButtonTitle}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
</ModalDialogButtons>
|
||||
</ModalDialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user