import { WebApplication } from '@/ui_models/application'; import { createRef, JSX } from 'preact'; import { PureComponent } from './Abstract/PureComponent'; interface Props { application: WebApplication; } type State = { continueTitle: string; formData: FormData; isContinuing?: boolean; lockContinue?: boolean; processing?: boolean; showSpinner?: boolean; step: Steps; title: string; }; const DEFAULT_CONTINUE_TITLE = 'Continue'; enum Steps { PasswordStep = 1, FinishStep = 2, } type FormData = { currentPassword?: string; newPassword?: string; newPasswordConfirmation?: string; status?: string; }; export class PasswordWizard extends PureComponent { private currentPasswordInput = createRef(); constructor(props: Props) { super(props, props.application); this.registerWindowUnloadStopper(); this.state = { formData: {}, continueTitle: DEFAULT_CONTINUE_TITLE, step: Steps.PasswordStep, title: 'Change Password', }; } componentDidMount(): void { super.componentDidMount(); this.currentPasswordInput.current?.focus(); } componentWillUnmount(): void { super.componentWillUnmount(); window.onbeforeunload = null; } registerWindowUnloadStopper() { window.onbeforeunload = () => { return true; }; } resetContinueState() { this.setState({ showSpinner: false, continueTitle: DEFAULT_CONTINUE_TITLE, isContinuing: false, }); } nextStep = async () => { if (this.state.lockContinue || this.state.isContinuing) { return; } if (this.state.step === Steps.FinishStep) { this.dismiss(); return; } this.setState({ isContinuing: true, showSpinner: true, continueTitle: 'Generating Keys...', }); const valid = await this.validateCurrentPassword(); if (!valid) { this.resetContinueState(); return; } const success = await this.processPasswordChange(); if (!success) { this.resetContinueState(); return; } this.setState({ isContinuing: false, showSpinner: false, continueTitle: 'Finish', step: Steps.FinishStep, }); }; async validateCurrentPassword() { const currentPassword = this.state.formData.currentPassword; const newPass = this.state.formData.newPassword; if (!currentPassword || currentPassword.length === 0) { this.application.alertService.alert( 'Please enter your current password.' ); return false; } if (!newPass || newPass.length === 0) { this.application.alertService.alert('Please enter a new password.'); return false; } if (newPass !== this.state.formData.newPasswordConfirmation) { this.application.alertService.alert( 'Your new password does not match its confirmation.' ); this.setFormDataState({ status: undefined, }); return false; } if (!this.application.getUser()?.email) { this.application.alertService.alert( "We don't have your email stored. Please sign out then log back in to fix this issue." ); this.setFormDataState({ status: undefined, }); return false; } /** Validate current password */ const success = await this.application.validateAccountPassword( this.state.formData.currentPassword! ); if (!success) { this.application.alertService.alert( 'The current password you entered is not correct. Please try again.' ); } return success; } async processPasswordChange() { await this.application.downloadBackup(); this.setState({ lockContinue: true, processing: true, }); await this.setFormDataState({ status: 'Processing encryption keys…', }); const newPassword = this.state.formData.newPassword; const response = await this.application.changePassword( this.state.formData.currentPassword!, newPassword! ); const success = !response.error; this.setState({ processing: false, lockContinue: false, }); if (!success) { this.setFormDataState({ status: 'Unable to process your password. Please try again.', }); } else { this.setState({ formData: { ...this.state.formData, status: 'Successfully changed password.', }, }); } return success; } dismiss = () => { if (this.state.lockContinue) { this.application.alertService.alert( 'Cannot close window until pending tasks are complete.' ); } else { this.dismissModal(); } }; async setFormDataState(formData: Partial) { return this.setState({ formData: { ...this.state.formData, ...formData, }, }); } handleCurrentPasswordInputChange = ({ currentTarget, }: JSX.TargetedEvent) => { this.setFormDataState({ currentPassword: currentTarget.value, }); }; handleNewPasswordInputChange = ({ currentTarget, }: JSX.TargetedEvent) => { this.setFormDataState({ newPassword: currentTarget.value, }); }; handleNewPasswordConfirmationInputChange = ({ currentTarget, }: JSX.TargetedEvent) => { this.setFormDataState({ newPasswordConfirmation: currentTarget.value, }); }; render() { return (
{this.state.title}
Close
{this.state.step === Steps.PasswordStep && (
)} {this.state.step === Steps.FinishStep && (
Your password has been successfully changed.

Please ensure you are running the latest version of Standard Notes on all platforms to ensure maximum compatibility.

)}
); } }