mirror of
https://github.com/standardnotes/app
synced 2026-10-03 06:14:51 -04:00
* feat: Add new icons
* Revert "feat: Add new icons"
This reverts commit 0acb403fe8.
* feat: Add new icons for account menu
* feat: Add new Icons
* feat: Add "currentPane" state to prefs view
* feat: Update account menu to new design
* feat: Add input component with icon & toggle
* fix: sync icon & function
* fix: Fix eye icon
* feat: Create re-usable checkbox
feat: Add "merge local" option
* feat: Allow using className on IconButton
* feat: Add disabled state on input
feat: Make toggle circle
* refactor: Move checkbox to components
* feat: Handle invalid email/password error
* feat: Implement new design for Create Account
* feat: Implement new account menu design
* feat: Add disabled option to IconButton
* feat: Set account menu pane from other component
* feat: Add 2fa account menu pane
feat: Add lock icon
* feat: Remove unnecessary 2FA menu pane
feat: Reset current menu pane on clickOutside
* feat: Change "Log in" to "Sign in"
* feat: Remove sync from footer
* feat: Change "Login" to "Sign in"
feat: Add spinner to "Syncing..."
refactor: Use then-catch-finally for sync
* feat: Use common enableCustomServer state
* feat: Animate account menu closing
* fix: Reset menu pane only after it's closed
* feat: Add keyDown handler to InputWithIcon
* feat: Handle Enter press in inputs
* Update app/assets/javascripts/components/InputWithIcon.tsx
Co-authored-by: Antonella Sgarlatta <[email protected]>
* Update app/assets/javascripts/components/InputWithIcon.tsx
Co-authored-by: Antonella Sgarlatta <[email protected]>
* refactor: Use server state from AccountMenuState
* Update app/assets/javascripts/components/AccountMenu/CreateAccount.tsx
Co-authored-by: Antonella Sgarlatta <[email protected]>
* Update app/assets/javascripts/components/AccountMenu/ConfirmPassword.tsx
Co-authored-by: Antonella Sgarlatta <[email protected]>
* feat: Use common AdvancedOptions
* feat: Add "eye-off" icon and toggle state
* feat: Allow undefined values
* refactor: Remove enableCustomServer state
* feat: Persist server option state
* feat: Add bottom-100 and cursor-auto util classes
refactor: Use bottom-100 and cursor-auto classes
* refactor: Invert ternary operator
* refactor: Remove unused imports
* refactor: Use toggled as prop instead of state
* refactor: Change "Log in/out" to "Sign in/out"
* refactor: Change "Login" to "Sign in"
* refactor: Remove hardcoded width/height
* refactor: Use success class
* feat: Remove hardcoded width & height from svg
* fix: Fix chevron-down icon
Co-authored-by: Antonella Sgarlatta <[email protected]>
Co-authored-by: Antonella Sgarlatta <[email protected]>
245 lines
5.9 KiB
TypeScript
245 lines
5.9 KiB
TypeScript
import { WebApplication } from '@/ui_models/application';
|
|
import {
|
|
PasswordWizardScope,
|
|
PasswordWizardType,
|
|
WebDirective,
|
|
} from './../../types';
|
|
import template from '%/directives/password-wizard.pug';
|
|
import { PureViewCtrl } from '@Views/abstract/pure_view_ctrl';
|
|
|
|
const DEFAULT_CONTINUE_TITLE = 'Continue';
|
|
enum Steps {
|
|
PasswordStep = 1,
|
|
FinishStep = 2,
|
|
}
|
|
|
|
type FormData = {
|
|
currentPassword?: string;
|
|
newPassword?: string;
|
|
newPasswordConfirmation?: string;
|
|
status?: string;
|
|
};
|
|
|
|
type State = {
|
|
lockContinue: boolean;
|
|
formData: FormData;
|
|
continueTitle: string;
|
|
step: Steps;
|
|
title: string;
|
|
showSpinner: boolean;
|
|
processing: boolean;
|
|
};
|
|
|
|
type Props = {
|
|
type: PasswordWizardType;
|
|
changePassword: boolean;
|
|
securityUpdate: boolean;
|
|
};
|
|
|
|
class PasswordWizardCtrl
|
|
extends PureViewCtrl<Props, State>
|
|
implements PasswordWizardScope
|
|
{
|
|
$element: JQLite;
|
|
application!: WebApplication;
|
|
type!: PasswordWizardType;
|
|
isContinuing = false;
|
|
|
|
/* @ngInject */
|
|
constructor($element: JQLite, $timeout: ng.ITimeoutService) {
|
|
super($timeout);
|
|
this.$element = $element;
|
|
this.registerWindowUnloadStopper();
|
|
}
|
|
|
|
$onInit() {
|
|
super.$onInit();
|
|
this.initProps({
|
|
type: this.type,
|
|
changePassword: this.type === PasswordWizardType.ChangePassword,
|
|
securityUpdate: this.type === PasswordWizardType.AccountUpgrade,
|
|
});
|
|
this.setState({
|
|
formData: {},
|
|
continueTitle: DEFAULT_CONTINUE_TITLE,
|
|
step: Steps.PasswordStep,
|
|
title: this.props.changePassword ? 'Change Password' : 'Account Update',
|
|
});
|
|
}
|
|
|
|
$onDestroy() {
|
|
super.$onDestroy();
|
|
window.onbeforeunload = null;
|
|
}
|
|
|
|
/** Confirms with user before closing tab */
|
|
registerWindowUnloadStopper() {
|
|
window.onbeforeunload = () => {
|
|
return true;
|
|
};
|
|
}
|
|
|
|
resetContinueState() {
|
|
this.setState({
|
|
showSpinner: false,
|
|
continueTitle: DEFAULT_CONTINUE_TITLE,
|
|
});
|
|
this.isContinuing = false;
|
|
}
|
|
|
|
async nextStep() {
|
|
if (this.state.lockContinue || this.isContinuing) {
|
|
return;
|
|
}
|
|
if (this.state.step === Steps.FinishStep) {
|
|
this.dismiss();
|
|
return;
|
|
}
|
|
|
|
this.isContinuing = true;
|
|
await this.setState({
|
|
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.isContinuing = false;
|
|
this.setState({
|
|
showSpinner: false,
|
|
continueTitle: 'Finish',
|
|
step: Steps.FinishStep,
|
|
});
|
|
}
|
|
|
|
async setFormDataState(formData: Partial<FormData>) {
|
|
return this.setState({
|
|
formData: {
|
|
...this.state.formData,
|
|
...formData,
|
|
},
|
|
});
|
|
}
|
|
|
|
async validateCurrentPassword() {
|
|
const currentPassword = this.state.formData.currentPassword;
|
|
const newPass = this.props.securityUpdate
|
|
? currentPassword
|
|
: this.state.formData.newPassword;
|
|
if (!currentPassword || currentPassword.length === 0) {
|
|
this.application.alertService!.alert(
|
|
'Please enter your current password.'
|
|
);
|
|
return false;
|
|
}
|
|
if (this.props.changePassword) {
|
|
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();
|
|
await this.setState({
|
|
lockContinue: true,
|
|
processing: true,
|
|
});
|
|
await this.setFormDataState({
|
|
status: 'Processing encryption keys…',
|
|
});
|
|
const newPassword = this.props.securityUpdate
|
|
? this.state.formData.currentPassword
|
|
: this.state.formData.newPassword;
|
|
const response = await this.application.changePassword(
|
|
this.state.formData.currentPassword!,
|
|
newPassword!
|
|
);
|
|
const success = !response.error;
|
|
await 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: this.props.changePassword
|
|
? 'Successfully changed password.'
|
|
: 'Successfully performed account update.',
|
|
},
|
|
});
|
|
}
|
|
return success;
|
|
}
|
|
|
|
dismiss() {
|
|
if (this.state.lockContinue) {
|
|
this.application.alertService!.alert(
|
|
'Cannot close window until pending tasks are complete.'
|
|
);
|
|
} else {
|
|
const elem = this.$element;
|
|
const scope = elem.scope();
|
|
scope.$destroy();
|
|
elem.remove();
|
|
}
|
|
}
|
|
}
|
|
|
|
export class PasswordWizard extends WebDirective {
|
|
constructor() {
|
|
super();
|
|
this.restrict = 'E';
|
|
this.template = template;
|
|
this.controller = PasswordWizardCtrl;
|
|
this.controllerAs = 'ctrl';
|
|
this.bindToController = true;
|
|
this.scope = {
|
|
type: '=',
|
|
application: '=',
|
|
};
|
|
}
|
|
}
|