From 0d10667d6e8e262228565cb9e1b30db230e3a62b Mon Sep 17 00:00:00 2001 From: William Grant Date: Thu, 6 Oct 2022 16:18:01 +1100 Subject: [PATCH] feat: updated settings password modal --- stylesheets/_session.scss | 23 --- stylesheets/manifest.scss | 2 - .../dialog/SessionPasswordDialog.tsx | 69 +++++--- ts/components/settings/SessionSettings.tsx | 161 ++++++------------ .../settings/section/CategoryPrivacy.tsx | 18 +- ts/test/automation/password.spec.ts | 6 +- 6 files changed, 101 insertions(+), 178 deletions(-) diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss index 7f805b7de..4ef3669c4 100644 --- a/stylesheets/_session.scss +++ b/stylesheets/_session.scss @@ -536,29 +536,6 @@ label { display: flex; flex-direction: column; background-color: var(--background-secondary-color); - - &__password-lock { - display: flex; - align-items: center; - justify-content: center; - flex-grow: 1; - - &-box { - padding: 45px 60px; - display: flex; - flex-direction: column; - align-items: center; - - max-width: 90%; - min-width: 400px; - - background: var(--background-primary-color); - color: var(--text-primary-color); - - border: 1px solid var(--border-color); - border-radius: 5px; - } - } } #qr svg, diff --git a/stylesheets/manifest.scss b/stylesheets/manifest.scss index 569ff46aa..b1f694233 100644 --- a/stylesheets/manifest.scss +++ b/stylesheets/manifest.scss @@ -42,5 +42,3 @@ @import 'session_slider'; @import 'session_conversation'; - -@import '_session_password'; diff --git a/ts/components/dialog/SessionPasswordDialog.tsx b/ts/components/dialog/SessionPasswordDialog.tsx index 082c655a2..787bf9a6e 100644 --- a/ts/components/dialog/SessionPasswordDialog.tsx +++ b/ts/components/dialog/SessionPasswordDialog.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { missingCaseError } from '../../util'; import { ToastUtils } from '../../session/utils'; import { Data } from '../../data/data'; -import { SpacerLG, SpacerSM } from '../basic/Text'; +import { SpacerSM } from '../basic/Text'; import autoBind from 'auto-bind'; import { sessionPassword } from '../../state/ducks/modalDialog'; import { LocalizerKeys } from '../../types/LocalizerKeys'; @@ -11,7 +11,7 @@ import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/S import { SessionWrapperModal } from '../SessionWrapperModal'; import { matchesHash, validatePassword } from '../../util/passwordUtils'; -export type PasswordAction = 'set' | 'change' | 'remove'; +export type PasswordAction = 'set' | 'change' | 'remove' | 'enter'; interface Props { passwordAction: PasswordAction; @@ -62,6 +62,9 @@ export class SessionPasswordDialog extends React.Component { case 'remove': placeholders = [window.i18n('enterPassword')]; break; + case 'enter': + placeholders = [window.i18n('enterPassword')]; + break; default: placeholders = [window.i18n('createPassword'), window.i18n('confirmPassword')]; } @@ -72,6 +75,8 @@ export class SessionPasswordDialog extends React.Component { ? 'changePassword' : passwordAction === 'remove' ? 'removePassword' + : passwordAction === 'enter' + ? 'passwordViewTitle' : 'setPassword'; return ( @@ -89,7 +94,7 @@ export class SessionPasswordDialog extends React.Component { onKeyUp={this.onPasswordInput} data-testid="password-input" /> - {passwordAction !== 'remove' && ( + {passwordAction !== 'enter' && passwordAction !== 'remove' && ( { - {this.showError()}
{ buttonType={SessionButtonType.Simple} onClick={this.setPassword} /> - + {passwordAction !== 'enter' && ( + + )}
); @@ -141,18 +147,9 @@ export class SessionPasswordDialog extends React.Component { } private showError() { - const message = this.state.error; - - return ( - <> - {message && ( - <> -
{message}
- - - )} - - ); + if (this.state.error) { + ToastUtils.pushToastError('enterPasswordErrorToast', this.state.error); + } } /** @@ -166,6 +163,7 @@ export class SessionPasswordDialog extends React.Component { this.setState({ error: errorFirstInput, }); + this.showError(); return false; } return true; @@ -182,6 +180,7 @@ export class SessionPasswordDialog extends React.Component { this.setState({ error: window.i18n('setPasswordInvalid'), }); + this.showError(); return; } await window.setPassword(enteredPassword, null); @@ -211,6 +210,7 @@ export class SessionPasswordDialog extends React.Component { this.setState({ error: window.i18n('passwordsDoNotMatch'), }); + this.showError(); return; } @@ -219,6 +219,7 @@ export class SessionPasswordDialog extends React.Component { this.setState({ error: window.i18n('changePasswordInvalid'), }); + this.showError(); return; } await window.setPassword(newPassword, oldPassword); @@ -240,6 +241,7 @@ export class SessionPasswordDialog extends React.Component { this.setState({ error: window.i18n('removePasswordInvalid'), }); + this.showError(); return; } await window.setPassword(null, oldPassword); @@ -254,6 +256,25 @@ export class SessionPasswordDialog extends React.Component { this.closeDialog(); } + private async handleActionEnter(enteredPassword: string) { + // be sure the password is valid + if (!this.validatePassword(enteredPassword)) { + return; + } + + const isValidWithStoredInDB = Boolean(await this.validatePasswordHash(enteredPassword)); + if (!isValidWithStoredInDB) { + this.setState({ + error: window.i18n('invalidPassword'), + }); + this.showError(); + return; + } + + this.props.onOk(); + this.closeDialog(); + } + // tslint:disable-next-line: cyclomatic-complexity private async setPassword() { const { passwordAction } = this.props; @@ -285,6 +306,10 @@ export class SessionPasswordDialog extends React.Component { await this.handleActionRemove(firstPasswordEntered); return; } + case 'enter': { + await this.handleActionEnter(firstPasswordEntered); + return; + } default: throw missingCaseError(passwordAction); } diff --git a/ts/components/settings/SessionSettings.tsx b/ts/components/settings/SessionSettings.tsx index 0f79f7b23..57569f5f3 100644 --- a/ts/components/settings/SessionSettings.tsx +++ b/ts/components/settings/SessionSettings.tsx @@ -9,13 +9,26 @@ import { SessionNotificationGroupSettings } from './SessionNotificationGroupSett import { CategoryConversations } from './section/CategoryConversations'; import { SettingsCategoryPrivacy } from './section/CategoryPrivacy'; import { SettingsCategoryAppearance } from './section/CategoryAppearance'; -import { SessionButton, SessionButtonType } from '../basic/SessionButton'; import { Data } from '../../data/data'; -import { matchesHash } from '../../util/passwordUtils'; import { SettingsCategoryPermissions } from './section/CategoryPermissions'; import { SettingsCategoryHelp } from './section/CategoryHelp'; import styled from 'styled-components'; -import { ToastUtils } from '../../session/utils'; +import { sessionPassword } from '../../state/ducks/modalDialog'; +import { PasswordAction } from '../dialog/SessionPasswordDialog'; + +export function displayPasswordModal( + passwordAction: PasswordAction, + onPasswordUpdated: (action: string) => void +) { + window.inboxStore?.dispatch( + sessionPassword({ + passwordAction, + onOk: () => { + onPasswordUpdated(passwordAction); + }, + }) + ); +} export function getMediaPermissionsSettings() { return window.getSettingValue('media-permissions'); @@ -92,58 +105,6 @@ const SessionInfo = () => { ); }; -const StyledPasswordInput = styled.input` - width: 100%; - background: var(--text-box-background-color); - color: var(--text-box-text-user-color); - - padding: var(--margins-xs) var(--margins-md); - margin-bottom: var(--margins-lg); - outline: none; - border: 1px solid var(--border-color); - border-radius: 7px; - text-align: center; - font-size: 16px; - font-family: var(--font-default); - - ::placeholder { - color: var(--text-box-text-control-color); - } -`; - -const StyledH3 = styled.h3` - padding: 0px; - margin-bottom: var(--margins-lg); -`; - -const PasswordLock = ({ - validatePasswordLock, -}: { - validatePasswordLock: () => Promise; -}) => { - return ( -
-
- {window.i18n('passwordViewTitle')} - - - -
-
- ); -}; - const SettingInCategory = (props: { category: SessionSettingCategory; hasPassword: boolean; @@ -216,38 +177,23 @@ export class SessionSettingsView extends React.Component { + if (action === 'enter') { + // Unlocked settings + this.setState({ + shouldLockSettings: false, + }); + } + }); } - - // Unlocked settings - this.setState({ - shouldLockSettings: false, - }); - - return true; } public render() { @@ -256,22 +202,23 @@ export class SessionSettingsView extends React.Component - - - - {shouldRenderPasswordLock ? ( - - ) : ( - - - - )} - - + {shouldRenderPasswordLock ? ( + <> + ) : ( + <> + + + + + + + + + )} ); } @@ -290,14 +237,4 @@ export class SessionSettingsView extends React.Component void -) { - window.inboxStore?.dispatch( - sessionPassword({ - passwordAction, - onOk: () => { - onPasswordUpdated(passwordAction); - }, - }) - ); -} +import { displayPasswordModal } from '../SessionSettings'; async function toggleLinkPreviews() { const newValue = !window.getSettingValue(SettingsKey.settingsLinkPreview); diff --git a/ts/test/automation/password.spec.ts b/ts/test/automation/password.spec.ts index f4506ec7c..15475b58c 100644 --- a/ts/test/automation/password.spec.ts +++ b/ts/test/automation/password.spec.ts @@ -51,7 +51,7 @@ test.describe('Password checks', () => { ); // Type password into input field - await typeIntoInput(window, 'password-lock-input', testPassword); + await typeIntoInput(window, 'password-input', testPassword); // Click OK await clickOnMatchingText(window, 'OK'); // Change password @@ -99,7 +99,7 @@ test.describe('Password checks', () => { // Click OK await window.keyboard.press('Enter'); // Type password into input field - await typeIntoInput(window, 'password-lock-input', testPassword); + await typeIntoInput(window, 'password-input', testPassword); await window.keyboard.press('Delete'); // Click OK await clickOnMatchingText(window, 'OK'); @@ -108,7 +108,7 @@ test.describe('Password checks', () => { // // Click on settings tab await clickOnTestIdWithText(window, 'settings-section'); // // Try with incorrect password - await typeIntoInput(window, 'password-lock-input', '0000'); + await typeIntoInput(window, 'password-input', '0000'); await window.keyboard.press('Delete'); // Confirm await clickOnMatchingText(window, 'OK');