Password lock screen and delete data screen
This commit is contained in:
parent
4a6ed12992
commit
4a7e2dece7
|
@ -1376,7 +1376,7 @@
|
|||
"description": "Description of the media permission description"
|
||||
},
|
||||
"clearDataHeader": {
|
||||
"message": "Clear Data",
|
||||
"message": "Clear All Local Data",
|
||||
"description":
|
||||
"Header in the settings dialog for the section dealing with data deletion"
|
||||
},
|
||||
|
@ -2281,7 +2281,7 @@
|
|||
},
|
||||
|
||||
"passwordViewTitle": {
|
||||
"message": "Type in your password",
|
||||
"message": "Type In Your Password",
|
||||
"description":
|
||||
"The title shown when user needs to type in a password to unlock the messenger"
|
||||
},
|
||||
|
@ -2309,6 +2309,9 @@
|
|||
"message": "Remove Password",
|
||||
"description": "Button action that the user can click to remove a password"
|
||||
},
|
||||
"maxPasswordAttempts": {
|
||||
"message": "Invalid Password. Would you like to reset the database?"
|
||||
},
|
||||
"typeInOldPassword": {
|
||||
"message": "Please type in your old password"
|
||||
},
|
||||
|
@ -2316,7 +2319,7 @@
|
|||
"message": "Old password is invalid"
|
||||
},
|
||||
"invalidPassword": {
|
||||
"message": "Invalid password"
|
||||
"message": "Invalid Password"
|
||||
},
|
||||
"noGivenPassword": {
|
||||
"message": "Please enter your password"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset='utf-8'>
|
||||
<meta content='width=device-width, user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0' name='viewport'>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Signal</title>
|
||||
<title>Session</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href='/images/loki/loki_icon_128.png' rel='shortcut icon'>
|
||||
|
|
|
@ -74,6 +74,9 @@ const {
|
|||
const {
|
||||
SessionPasswordModal,
|
||||
} = require('../../ts/components/session/SessionPasswordModal');
|
||||
const {
|
||||
SessionPasswordPrompt,
|
||||
} = require('../../ts/components/session/SessionPasswordPrompt');
|
||||
|
||||
const {
|
||||
SessionConfirm,
|
||||
|
@ -293,6 +296,7 @@ exports.setup = (options = {}) => {
|
|||
SessionQRModal,
|
||||
SessionSeedModal,
|
||||
SessionPasswordModal,
|
||||
SessionPasswordPrompt,
|
||||
SessionDropdown,
|
||||
SessionScrollButton,
|
||||
MediaGallery,
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
/* global i18n: false */
|
||||
/* global Whisper: false */
|
||||
|
||||
/* eslint-disable no-new */
|
||||
|
||||
// eslint-disable-next-line func-names
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
window.Whisper = window.Whisper || {};
|
||||
const { Logs } = window.Signal;
|
||||
|
||||
const CLEAR_DATA_STEPS = {
|
||||
CHOICE: 1,
|
||||
DELETING: 2,
|
||||
};
|
||||
window.Whisper.ClearDataView = Whisper.View.extend({
|
||||
templateName: 'clear-data',
|
||||
className: 'full-screen-flow overlay',
|
||||
events: {
|
||||
'click .cancel': 'onCancel',
|
||||
'click .delete-all-data': 'onDeleteAllData',
|
||||
},
|
||||
initialize(onClear = null) {
|
||||
this.step = CLEAR_DATA_STEPS.CHOICE;
|
||||
this.onClear = onClear;
|
||||
},
|
||||
onCancel() {
|
||||
this.remove();
|
||||
},
|
||||
async onDeleteAllData() {
|
||||
window.log.info('Deleting everything!');
|
||||
this.step = CLEAR_DATA_STEPS.DELETING;
|
||||
this.render();
|
||||
|
||||
await this.clearAllData();
|
||||
},
|
||||
async clearAllData() {
|
||||
if (this.onClear) {
|
||||
this.onClear();
|
||||
} else {
|
||||
try {
|
||||
await Logs.deleteAll();
|
||||
|
||||
await window.Signal.Data.removeAll();
|
||||
await window.Signal.Data.close();
|
||||
await window.Signal.Data.removeDB();
|
||||
|
||||
await window.Signal.Data.removeOtherData();
|
||||
} catch (error) {
|
||||
window.log.error(
|
||||
'Something went wrong deleting all data:',
|
||||
error && error.stack ? error.stack : error
|
||||
);
|
||||
}
|
||||
window.restart();
|
||||
}
|
||||
},
|
||||
render_attributes() {
|
||||
return {
|
||||
isStep1: this.step === CLEAR_DATA_STEPS.CHOICE,
|
||||
header: i18n('deleteAllDataHeader'),
|
||||
body: i18n('deleteAllDataBody'),
|
||||
cancelButton: i18n('cancel'),
|
||||
deleteButton: i18n('deleteAllDataButton'),
|
||||
|
||||
isStep2: this.step === CLEAR_DATA_STEPS.DELETING,
|
||||
deleting: i18n('deleteAllDataProgress'),
|
||||
};
|
||||
},
|
||||
});
|
||||
})();
|
|
@ -1,7 +1,4 @@
|
|||
/* global i18n: false */
|
||||
/* global Whisper: false */
|
||||
|
||||
/* eslint-disable no-new */
|
||||
/* global Whisper */
|
||||
|
||||
// eslint-disable-next-line func-names
|
||||
(function() {
|
||||
|
@ -9,63 +6,20 @@
|
|||
|
||||
window.Whisper = window.Whisper || {};
|
||||
|
||||
const MIN_LOGIN_TRIES = 3;
|
||||
|
||||
Whisper.PasswordView = Whisper.View.extend({
|
||||
className: 'password full-screen-flow standalone-fullscreen',
|
||||
templateName: 'password',
|
||||
events: {
|
||||
keyup: 'onKeyup',
|
||||
'click #unlock-button': 'onLogin',
|
||||
'click #reset-button': 'onReset',
|
||||
},
|
||||
initialize() {
|
||||
this.errorCount = 0;
|
||||
this.render();
|
||||
},
|
||||
render_attributes() {
|
||||
return {
|
||||
title: i18n('passwordViewTitle'),
|
||||
buttonText: i18n('unlock'),
|
||||
resetText: i18n('resetDatabase'),
|
||||
showReset: this.errorCount >= MIN_LOGIN_TRIES,
|
||||
};
|
||||
},
|
||||
onKeyup(event) {
|
||||
switch (event.key) {
|
||||
case 'Enter':
|
||||
this.onLogin();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
},
|
||||
async onLogin() {
|
||||
const passPhrase = this.$('#passPhrase').val();
|
||||
const trimmed = passPhrase ? passPhrase.trim() : passPhrase;
|
||||
this.setError('');
|
||||
try {
|
||||
await window.onLogin(trimmed);
|
||||
} catch (e) {
|
||||
// Increment the error counter and show the button if necessary
|
||||
this.errorCount += 1;
|
||||
if (this.errorCount >= MIN_LOGIN_TRIES) {
|
||||
this.render();
|
||||
}
|
||||
|
||||
this.setError(`Error: ${e}`);
|
||||
}
|
||||
},
|
||||
setError(string) {
|
||||
this.$('.error').text(string);
|
||||
},
|
||||
onReset() {
|
||||
const clearDataView = new window.Whisper.ClearDataView(() => {
|
||||
window.resetDatabase();
|
||||
render() {
|
||||
this.passwordView = new window.Whisper.ReactWrapperView({
|
||||
className: 'password overlay',
|
||||
Component: window.Signal.Components.SessionPasswordPrompt,
|
||||
props: {},
|
||||
});
|
||||
clearDataView.render();
|
||||
this.$el.append(clearDataView.el);
|
||||
|
||||
this.$el.append(this.passwordView.el);
|
||||
return this;
|
||||
},
|
||||
});
|
||||
})();
|
||||
|
|
|
@ -16,63 +16,12 @@
|
|||
<link href="stylesheets/manifest.css" rel="stylesheet" type="text/css" />
|
||||
<style>
|
||||
</style>
|
||||
<script type='text/x-tmpl-mustache' id='password'>
|
||||
<div class='content-wrapper standalone'>
|
||||
<div class='content'>
|
||||
<h2>{{ title }}</h2>
|
||||
<div class='inputs'>
|
||||
<input class='form-control' type='password' id='passPhrase' placeholder='Password' autocomplete='off' spellcheck='false' />
|
||||
<a class='button session-button brand green' id='unlock-button'>{{ buttonText }}</a>
|
||||
<div class='error'></div>
|
||||
{{ #showReset }}
|
||||
<div class='reset'>
|
||||
<a id='reset-button'>{{ resetText }}</a>
|
||||
</div>
|
||||
{{ /showReset }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type='text/x-tmpl-mustache' id='clear-data'>
|
||||
{{#isStep1}}
|
||||
<div class='step'>
|
||||
<div class='inner'>
|
||||
<div class='step-body'>
|
||||
<span class='banner-icon alert-outline-red'></span>
|
||||
<div class='header'>{{ header }}</div>
|
||||
<div class='body-text-wide'>{{ body }}</div>
|
||||
</div>
|
||||
<div class='nav'>
|
||||
<div>
|
||||
<a class='button neutral cancel'>{{ cancelButton }}</a>
|
||||
<a class='button destructive delete-all-data'>{{ deleteButton }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/isStep1}}
|
||||
{{#isStep2}}
|
||||
<div id='step3' class='step'>
|
||||
<div class='inner'>
|
||||
<div class='step-body'>
|
||||
<span class='banner-icon delete'></span>
|
||||
<div class='header'>{{ deleting }}</div>
|
||||
</div>
|
||||
<div class='progress'>
|
||||
<div class='bar-container'>
|
||||
<div class='bar progress-bar progress-bar-striped active'></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/isStep2}}
|
||||
</script>
|
||||
|
||||
|
||||
<script type='text/javascript' src='js/components.js'></script>
|
||||
<script type='text/javascript' src='js/views/whisper_view.js'></script>
|
||||
<script type='text/javascript' src='js/views/react_wrapper_view.js'></script>
|
||||
<script type='text/javascript' src='js/views/password_view.js'></script>
|
||||
<script type='text/javascript' src='js/views/clear_data_view.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class='app-loading-screen'>
|
||||
|
|
|
@ -8,6 +8,9 @@ const config = url.parse(window.location.toString(), true).query;
|
|||
const { locale } = config;
|
||||
const localeMessages = ipcRenderer.sendSync('locale-data');
|
||||
|
||||
window.React = require('react');
|
||||
window.ReactDOM = require('react-dom');
|
||||
|
||||
window.theme = config.theme;
|
||||
window.i18n = i18n.setup(locale, localeMessages);
|
||||
|
||||
|
@ -17,6 +20,9 @@ window.getAppInstance = () => config.appInstance;
|
|||
|
||||
// So far we're only using this for Signal.Types
|
||||
const Signal = require('./js/modules/signal');
|
||||
const electron = require('electron');
|
||||
|
||||
const ipc = electron.ipcRenderer;
|
||||
|
||||
window.Signal = Signal.setup({
|
||||
Attachments: null,
|
||||
|
@ -24,12 +30,32 @@ window.Signal = Signal.setup({
|
|||
getRegionCode: () => null,
|
||||
});
|
||||
|
||||
window.Signal.Logs = require('./js/modules/logs');
|
||||
|
||||
window.CONSTANTS = {
|
||||
MAX_LOGIN_TRIES: 3,
|
||||
MAX_PASSWORD_LENGTH: 32,
|
||||
MAX_USERNAME_LENGTH: 20,
|
||||
};
|
||||
|
||||
window.passwordUtil = require('./app/password_util');
|
||||
window.Signal.Logs = require('./js/modules/logs');
|
||||
|
||||
window.resetDatabase = () => {
|
||||
window.log.info('reset database');
|
||||
ipcRenderer.send('resetDatabase');
|
||||
};
|
||||
|
||||
window.restart = () => {
|
||||
window.log.info('restart');
|
||||
ipc.send('restart');
|
||||
};
|
||||
|
||||
window.clearLocalData = async () => {
|
||||
window.resetDatabase();
|
||||
window.restart();
|
||||
};
|
||||
|
||||
window.onLogin = passPhrase =>
|
||||
new Promise((resolve, reject) => {
|
||||
ipcRenderer.once('password-window-login-response', (event, error) => {
|
||||
|
|
10
preload.js
10
preload.js
|
@ -60,8 +60,9 @@ window.isBeforeVersion = (toCheck, baseVersion) => {
|
|||
};
|
||||
|
||||
window.CONSTANTS = {
|
||||
maxPasswordLength: 32,
|
||||
maxUsernameLength: 20,
|
||||
MAX_LOGIN_TRIES: 3,
|
||||
MAX_PASSWORD_LENGTH: 32,
|
||||
MAX_USERNAME_LENGTH: 20,
|
||||
};
|
||||
|
||||
window.versionInfo = {
|
||||
|
@ -134,6 +135,11 @@ window.restart = () => {
|
|||
ipc.send('restart');
|
||||
};
|
||||
|
||||
window.resetDatabase = () => {
|
||||
window.log.info('reset database');
|
||||
ipc.send('resetDatabase');
|
||||
};
|
||||
|
||||
// Events for updating block number states across different windows.
|
||||
// In this case we need these to update the blocked number
|
||||
// collection on the main window from the settings window.
|
||||
|
|
|
@ -225,6 +225,7 @@ $session_message-container-border-radius: 5px;
|
|||
}
|
||||
|
||||
.button-group > div {
|
||||
display: inline-flex;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
@ -258,6 +259,10 @@ $session_message-container-border-radius: 5px;
|
|||
&.brand {
|
||||
color: $session-color-white;
|
||||
|
||||
&:hover {
|
||||
filter: brightness(90%);
|
||||
}
|
||||
|
||||
&.green,
|
||||
&.white,
|
||||
&.primary,
|
||||
|
@ -1438,3 +1443,89 @@ input {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.clear-data,
|
||||
.password-prompt {
|
||||
&-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
background-color: $session-color-black;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
padding: 3 * $session-margin-lg;
|
||||
}
|
||||
|
||||
&-error-section {
|
||||
width: 100%;
|
||||
color: $session-color-white;
|
||||
margin: -$session-margin-sm 0px 2 * $session-margin-lg 0px;
|
||||
|
||||
.session-label {
|
||||
&.primary {
|
||||
background-color: rgba($session-color-primary, 0.3);
|
||||
}
|
||||
padding: $session-margin-xs $session-margin-sm;
|
||||
font-size: $session-font-xs;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
&-container {
|
||||
font-family: 'SF Pro Text';
|
||||
color: $session-color-white;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 600px;
|
||||
min-width: 420px;
|
||||
padding: 3 * $session-margin-lg 2 * $session-margin-lg;
|
||||
box-sizing: border-box;
|
||||
background-color: $session-shade-4;
|
||||
border: 1px solid $session-shade-8;
|
||||
border-radius: 2px;
|
||||
|
||||
.warning-info-area,
|
||||
.password-info-area {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
h1 {
|
||||
color: $session-color-white;
|
||||
}
|
||||
svg {
|
||||
margin-right: $session-margin-lg;
|
||||
}
|
||||
}
|
||||
|
||||
p,
|
||||
input {
|
||||
margin: $session-margin-lg 0px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
#password-prompt-input {
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
background-color: #2e2e2e;
|
||||
margin-top: 2 * $session-margin-lg;
|
||||
padding: $session-margin-xs $session-margin-lg;
|
||||
outline: none;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
letter-spacing: 5px;
|
||||
font-family: 'SF Pro Text';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -208,7 +208,7 @@ export class EditProfileDialog extends React.Component<Props, State> {
|
|||
value={this.state.profileName}
|
||||
placeholder={placeholderText}
|
||||
onChange={this.onNameEdited}
|
||||
maxLength={window.CONSTANTS.maxUsernameLength}
|
||||
maxLength={window.CONSTANTS.MAX_USERNAME_LENGTH}
|
||||
tabIndex={0}
|
||||
required={true}
|
||||
aria-required={true}
|
||||
|
@ -302,7 +302,10 @@ export class EditProfileDialog extends React.Component<Props, State> {
|
|||
private onClickOK() {
|
||||
const newName = this.state.profileName.trim();
|
||||
|
||||
if (newName.length === 0 || newName.length > window.CONSTANTS.maxUsernameLength) {
|
||||
if (
|
||||
newName.length === 0 ||
|
||||
newName.length > window.CONSTANTS.MAX_USERNAME_LENGTH
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,8 @@ export class ActionsPanel extends React.Component<Props, State> {
|
|||
const handleClick = onSelect
|
||||
? () => {
|
||||
type === SectionType.Profile
|
||||
? this.editProfileHandle()
|
||||
? /* tslint:disable-next-line:no-void-expression */
|
||||
this.editProfileHandle()
|
||||
: /* tslint:disable-next-line:no-void-expression */
|
||||
onSelect(type);
|
||||
}
|
||||
|
|
|
@ -449,7 +449,7 @@ export class RegistrationTabs extends React.Component<{}, State> {
|
|||
type="text"
|
||||
placeholder={window.i18n('enterDisplayName')}
|
||||
value={this.state.displayName}
|
||||
maxLength={window.CONSTANTS.maxUsernameLength}
|
||||
maxLength={window.CONSTANTS.MAX_USERNAME_LENGTH}
|
||||
onValueChanged={(val: string) => {
|
||||
this.onDisplayNameChanged(val);
|
||||
}}
|
||||
|
@ -463,7 +463,7 @@ export class RegistrationTabs extends React.Component<{}, State> {
|
|||
error={this.state.passwordErrorString}
|
||||
type="password"
|
||||
placeholder={window.i18n('enterOptionalPassword')}
|
||||
maxLength={window.CONSTANTS.maxPasswordLength}
|
||||
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
|
||||
onValueChanged={(val: string) => {
|
||||
this.onPasswordChanged(val);
|
||||
}}
|
||||
|
@ -477,7 +477,7 @@ export class RegistrationTabs extends React.Component<{}, State> {
|
|||
error={passwordsDoNotMatch}
|
||||
type="password"
|
||||
placeholder={window.i18n('optionalPassword')}
|
||||
maxLength={window.CONSTANTS.maxPasswordLength}
|
||||
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
|
||||
onValueChanged={(val: string) => {
|
||||
this.onPasswordVerifyChanged(val);
|
||||
}}
|
||||
|
|
|
@ -34,7 +34,14 @@ export class SessionInput extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const { placeholder, type, value, maxLength, enableShowHide, error } = this.props;
|
||||
const {
|
||||
placeholder,
|
||||
type,
|
||||
value,
|
||||
maxLength,
|
||||
enableShowHide,
|
||||
error,
|
||||
} = this.props;
|
||||
const { forceShow } = this.state;
|
||||
|
||||
const correctType = forceShow ? 'text' : type;
|
||||
|
|
|
@ -58,14 +58,14 @@ export class SessionPasswordModal extends React.Component<Props, State> {
|
|||
type="password"
|
||||
id="password-modal-input"
|
||||
placeholder={placeholders[0]}
|
||||
maxLength={window.CONSTANTS.maxPasswordLength}
|
||||
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
|
||||
/>
|
||||
{action !== PasswordAction.Remove && (
|
||||
<input
|
||||
type="password"
|
||||
id="password-modal-input-confirm"
|
||||
placeholder={placeholders[1]}
|
||||
maxLength={window.CONSTANTS.maxPasswordLength}
|
||||
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -120,7 +120,7 @@ export class SessionPasswordModal extends React.Component<Props, State> {
|
|||
$('#password-modal-input-confirm').val()
|
||||
);
|
||||
|
||||
if (enteredPassword.length === 0 || enteredPasswordConfirm.length === 0){
|
||||
if (enteredPassword.length === 0 || enteredPasswordConfirm.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { SessionIcon, SessionIconType } from './icon';
|
||||
import {
|
||||
SessionButton,
|
||||
SessionButtonColor,
|
||||
SessionButtonType,
|
||||
} from './SessionButton';
|
||||
|
||||
interface State {
|
||||
error: string;
|
||||
errorCount: number;
|
||||
clearDataView: boolean;
|
||||
}
|
||||
|
||||
export class SessionPasswordPrompt extends React.PureComponent<{}, State> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
error: '',
|
||||
errorCount: 0,
|
||||
clearDataView: false,
|
||||
};
|
||||
|
||||
this.onKeyUp = this.onKeyUp.bind(this);
|
||||
this.initLogin = this.initLogin.bind(this);
|
||||
this.initClearDataView = this.initClearDataView.bind(this);
|
||||
|
||||
window.addEventListener('keyup', this.onKeyUp);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const showResetElements =
|
||||
this.state.errorCount >= window.CONSTANTS.MAX_LOGIN_TRIES;
|
||||
|
||||
const wrapperClass = this.state.clearDataView
|
||||
? 'clear-data-wrapper'
|
||||
: 'password-prompt-wrapper';
|
||||
const containerClass = this.state.clearDataView
|
||||
? 'clear-data-container'
|
||||
: 'password-prompt-container';
|
||||
const infoAreaClass = this.state.clearDataView
|
||||
? 'warning-info-area'
|
||||
: 'password-info-area';
|
||||
const infoTitle = this.state.clearDataView
|
||||
? window.i18n('clearDataHeader')
|
||||
: window.i18n('passwordViewTitle');
|
||||
const buttonGroup = this.state.clearDataView
|
||||
? this.renderClearDataViewButtons()
|
||||
: this.renderPasswordViewButtons();
|
||||
const featureElement = this.state.clearDataView ? (
|
||||
<p className="text-center">{window.i18n('clearDataExplanation')}</p>
|
||||
) : (
|
||||
<input
|
||||
id="password-prompt-input"
|
||||
type="password"
|
||||
autoFocus={true}
|
||||
placeholder=" "
|
||||
defaultValue=""
|
||||
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
|
||||
/>
|
||||
);
|
||||
const infoIcon = this.state.clearDataView ? (
|
||||
<SessionIcon
|
||||
iconType={SessionIconType.Warning}
|
||||
iconSize={35}
|
||||
iconColor="#ce0000"
|
||||
/>
|
||||
) : (
|
||||
<SessionIcon
|
||||
iconType={SessionIconType.Lock}
|
||||
iconSize={35}
|
||||
iconColor="#00f782"
|
||||
/>
|
||||
);
|
||||
const errorSection = !this.state.clearDataView && (
|
||||
<div className="password-prompt-error-section">
|
||||
{this.state.error && (
|
||||
<>
|
||||
{showResetElements ? (
|
||||
<div className="session-label warning">
|
||||
{window.i18n('maxPasswordAttempts')}
|
||||
</div>
|
||||
) : (
|
||||
<div className="session-label primary">{this.state.error}</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={wrapperClass}>
|
||||
<div className={containerClass}>
|
||||
<div className={infoAreaClass}>
|
||||
{infoIcon}
|
||||
|
||||
<h1>{infoTitle}</h1>
|
||||
</div>
|
||||
|
||||
{featureElement}
|
||||
{errorSection}
|
||||
{buttonGroup}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public async onKeyUp(event: any) {
|
||||
switch (event.key) {
|
||||
case 'Enter':
|
||||
await this.initLogin();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
public async onLogin(passPhrase: string) {
|
||||
const trimmed = passPhrase ? passPhrase.trim() : passPhrase;
|
||||
|
||||
try {
|
||||
await window.onLogin(trimmed);
|
||||
} catch (e) {
|
||||
// Increment the error counter and show the button if necessary
|
||||
this.setState({
|
||||
errorCount: this.state.errorCount + 1,
|
||||
});
|
||||
|
||||
this.setState({ error: e });
|
||||
}
|
||||
}
|
||||
|
||||
private async initLogin() {
|
||||
const passPhrase = String($('#password-prompt-input').val());
|
||||
await this.onLogin(passPhrase);
|
||||
}
|
||||
|
||||
private initClearDataView() {
|
||||
this.setState({
|
||||
error: '',
|
||||
errorCount: 0,
|
||||
clearDataView: true,
|
||||
});
|
||||
}
|
||||
|
||||
private renderPasswordViewButtons(): JSX.Element {
|
||||
const showResetElements =
|
||||
this.state.errorCount >= window.CONSTANTS.MAX_LOGIN_TRIES;
|
||||
|
||||
return (
|
||||
<div className={classNames(showResetElements && 'button-group')}>
|
||||
{showResetElements && (
|
||||
<>
|
||||
<SessionButton
|
||||
text="Reset Database"
|
||||
buttonType={SessionButtonType.BrandOutline}
|
||||
buttonColor={SessionButtonColor.Danger}
|
||||
onClick={this.initClearDataView}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<SessionButton
|
||||
text={window.i18n('unlock')}
|
||||
buttonType={SessionButtonType.BrandOutline}
|
||||
buttonColor={SessionButtonColor.Green}
|
||||
onClick={this.initLogin}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private renderClearDataViewButtons(): JSX.Element {
|
||||
return (
|
||||
<div className="button-group">
|
||||
<SessionButton
|
||||
text={window.i18n('cancel')}
|
||||
buttonType={SessionButtonType.Default}
|
||||
buttonColor={SessionButtonColor.Primary}
|
||||
onClick={() => {
|
||||
this.setState({ clearDataView: false });
|
||||
}}
|
||||
/>
|
||||
|
||||
<SessionButton
|
||||
text={window.i18n('deleteAllDataButton')}
|
||||
buttonType={SessionButtonType.Default}
|
||||
buttonColor={SessionButtonColor.Danger}
|
||||
onClick={window.clearLocalData}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,12 +1,8 @@
|
|||
import React from 'react';
|
||||
|
||||
import { SessionIconButton, SessionIconType, SessionIconSize } from './icon';
|
||||
import { SessionIconButton, SessionIconSize, SessionIconType } from './icon';
|
||||
|
||||
interface Props {
|
||||
count: number;
|
||||
}
|
||||
|
||||
export class SessionScrollButton extends React.PureComponent<Props> {
|
||||
export class SessionScrollButton extends React.PureComponent {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { icons, SessionIconSize, SessionIconType } from '../icon';
|
|||
|
||||
export interface Props {
|
||||
iconType: SessionIconType;
|
||||
iconSize: SessionIconSize;
|
||||
iconSize: SessionIconSize | number;
|
||||
iconColor: string;
|
||||
iconPadded: boolean;
|
||||
iconRotation: number;
|
||||
|
@ -33,21 +33,25 @@ export class SessionIcon extends React.PureComponent<Props> {
|
|||
} = this.props;
|
||||
|
||||
let iconDimensions;
|
||||
switch (iconSize) {
|
||||
case SessionIconSize.Small:
|
||||
iconDimensions = '15';
|
||||
break;
|
||||
case SessionIconSize.Medium:
|
||||
iconDimensions = '20';
|
||||
break;
|
||||
case SessionIconSize.Large:
|
||||
iconDimensions = '25';
|
||||
break;
|
||||
case SessionIconSize.Huge:
|
||||
iconDimensions = '30';
|
||||
break;
|
||||
default:
|
||||
iconDimensions = '20';
|
||||
if (typeof iconSize === 'number') {
|
||||
iconDimensions = iconSize;
|
||||
} else {
|
||||
switch (iconSize) {
|
||||
case SessionIconSize.Small:
|
||||
iconDimensions = '15';
|
||||
break;
|
||||
case SessionIconSize.Medium:
|
||||
iconDimensions = '20';
|
||||
break;
|
||||
case SessionIconSize.Large:
|
||||
iconDimensions = '25';
|
||||
break;
|
||||
case SessionIconSize.Huge:
|
||||
iconDimensions = '30';
|
||||
break;
|
||||
default:
|
||||
iconDimensions = '20';
|
||||
}
|
||||
}
|
||||
|
||||
const iconDef = icons[iconType];
|
||||
|
|
|
@ -4,6 +4,7 @@ interface Window {
|
|||
|
||||
Events: any;
|
||||
deleteAllData: any;
|
||||
clearLocalData: any;
|
||||
getAccountManager: any;
|
||||
mnemonic: any;
|
||||
clipboard: any;
|
||||
|
@ -23,6 +24,7 @@ interface Window {
|
|||
// Following function needs to be written in background.js
|
||||
// getMemberList: any;
|
||||
|
||||
onLogin: any;
|
||||
setPassword: any;
|
||||
textsecure: any;
|
||||
Session: any;
|
||||
|
@ -50,6 +52,8 @@ interface Window {
|
|||
getSettingValue: any;
|
||||
setSettingValue: any;
|
||||
lokiFeatureFlags: any;
|
||||
|
||||
resetDatabase: any;
|
||||
}
|
||||
|
||||
interface Promise<T> {
|
||||
|
|
Loading…
Reference in New Issue