diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 25ef676c1..1362d30a7 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -943,6 +943,9 @@ "ok": { "message": "OK" }, + "enter": { + "message": "Enter" + }, "yes": { "message": "Yes" }, diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss index bf6b71732..60744e8d6 100644 --- a/stylesheets/_session.scss +++ b/stylesheets/_session.scss @@ -320,11 +320,16 @@ $session_message-container-border-radius: 5px; &.brand { min-width: 165px; height: 45px; - line-height: 45px; + line-height: 40px; padding: 0; font-size: 15px; font-family: $session-font-family; border-radius: 500px; + + &:hover { + color: $session-color-white; + border-color: $session-color-white; + } } &.default, @@ -343,7 +348,7 @@ $session_message-container-border-radius: 5px; border-radius: 0px; } - & > *:not(svg):hover { + & > *:hover:not(svg) { filter: brightness(80%); } } @@ -909,6 +914,8 @@ label { .session-settings { width: 100%; height: 100vh; + display: flex; + flex-direction: column; &-list { overflow-y: scroll; @@ -972,6 +979,47 @@ label { } } } + + &__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%; + width: 600px; + + background-color: $session-shade-4; + border: 1px solid $session-shade-8; + border-radius: 5px; + + h3 { + padding: 0px; + margin-bottom: $session-margin-lg; + } + + input { + width: 100%; + color: $session-color-white; + background-color: $session-shade-7; + padding: $session-margin-xs $session-margin-md; + margin-bottom: $session-margin-lg; + outline: none; + border: none; + border-radius: 2px; + text-align: center; + font-size: 25px; + letter-spacing: 5px; + font-family: 'SF Pro Text'; + } + } + } } #qr svg { diff --git a/ts/components/session/settings/SessionSettings.tsx b/ts/components/session/settings/SessionSettings.tsx index 095988036..f555ed362 100644 --- a/ts/components/session/settings/SessionSettings.tsx +++ b/ts/components/session/settings/SessionSettings.tsx @@ -2,7 +2,11 @@ import React from 'react'; import { SettingsHeader } from './SessionSettingsHeader'; import { SessionSettingListItem } from './SessionSettingListItem'; -import { SessionButtonColor } from '../SessionButton'; +import { + SessionButton, + SessionButtonColor, + SessionButtonType, +} from '../SessionButton'; export enum SessionSettingCategory { General = 'general', @@ -26,6 +30,8 @@ export interface SettingsViewProps { interface State { hasPassword: boolean | null; + pwdLockError: string | null; + shouldLockSettings: boolean | null; linkedPubKeys: Array; } @@ -50,11 +56,14 @@ export class SettingsView extends React.Component { this.state = { hasPassword: null, + pwdLockError: null, + shouldLockSettings: true, linkedPubKeys: new Array(), }; this.settingsViewRef = React.createRef(); this.onPasswordUpdated = this.onPasswordUpdated.bind(this); + this.validatePasswordLock = this.validatePasswordLock.bind(this); this.hasPassword(); this.refreshLinkedDevice = this.refreshLinkedDevice.bind(this); @@ -136,15 +145,85 @@ export class SettingsView extends React.Component { ); } + public renderPasswordLock() { + return ( +
+
+

{window.i18n('password')}

+ + +
+ + {this.state.pwdLockError && ( + <> +
+ {this.state.pwdLockError} +
+
+ + )} + + +
+
+ ); + } + + public async validatePasswordLock() { + const enteredPassword = String($('#password-lock-input').val()); + + if (!enteredPassword) { + this.setState({ + pwdLockError: window.i18n('noGivenPassword'), + }); + + return false; + } + + // Check if the password matches the hash we have stored + const hash = await window.Signal.Data.getPasswordHash(); + if (hash && !window.passwordUtil.matchesHash(enteredPassword, hash)) { + this.setState({ + pwdLockError: window.i18n('invalidPassword'), + }); + + return false; + } + + // Unlocked settings + this.setState({ + shouldLockSettings: false, + pwdLockError: null, + }); + + return true; + } + public render() { const { category } = this.props; + const shouldRenderPasswordLock = + this.state.shouldLockSettings && this.state.hasPassword; return (
-
- {this.renderSettingInCategory()} -
+ {shouldRenderPasswordLock ? ( + this.renderPasswordLock() + ) : ( +
+ {this.renderSettingInCategory()} +
+ )}
); } @@ -180,9 +259,11 @@ export class SettingsView extends React.Component { } public onPasswordUpdated(action: string) { - if (action === 'set') { + if (action === 'set' || action === 'change') { this.setState({ hasPassword: true, + shouldLockSettings: true, + pwdLockError: null, }); }