session-desktop/ts/components/session/SessionPasswordPrompt.tsx

235 lines
6 KiB
TypeScript

import React from 'react';
import classNames from 'classnames';
import { SessionIcon, SessionIconType } from './icon';
import {
SessionButton,
SessionButtonColor,
SessionButtonType,
} from './SessionButton';
import { Constants } from '../../session';
import { DefaultTheme, withTheme } from 'styled-components';
interface State {
error: string;
errorCount: number;
clearDataView: boolean;
}
class SessionPasswordPromptInner extends React.PureComponent<
{ theme: DefaultTheme },
State
> {
private readonly inputRef: React.RefObject<HTMLInputElement>;
constructor(props: any) {
super(props);
this.state = {
error: '',
errorCount: 0,
clearDataView: false,
};
this.onKeyUp = this.onKeyUp.bind(this);
this.onPaste = this.onPaste.bind(this);
this.initLogin = this.initLogin.bind(this);
this.initClearDataView = this.initClearDataView.bind(this);
this.inputRef = React.createRef();
}
public componentDidMount() {
(this.inputRef.current as HTMLInputElement).focus();
}
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('clearAllData')
: window.i18n('passwordViewTitle');
const buttonGroup = this.state.clearDataView
? this.renderClearDataViewButtons()
: this.renderPasswordViewButtons();
const featureElement = this.state.clearDataView ? (
<p className="text-center">{window.i18n('deleteAccountWarning')}</p>
) : (
<input
id="password-prompt-input"
type="password"
defaultValue=""
placeholder={' '}
onKeyUp={this.onKeyUp}
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
onPaste={this.onPaste}
ref={this.inputRef}
/>
);
const infoIcon = this.state.clearDataView ? (
<SessionIcon
iconType={SessionIconType.Warning}
iconSize={35}
iconColor="#ce0000"
theme={this.props.theme}
/>
) : (
<SessionIcon
iconType={SessionIconType.Lock}
iconSize={35}
iconColor={Constants.UI.COLORS.GREEN}
theme={this.props.theme}
/>
);
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 onPaste(event: any) {
const clipboard = event.clipboardData.getData('text');
if (clipboard.length > window.CONSTANTS.MAX_PASSWORD_LENGTH) {
this.setState({
error: String(
window.i18n(
'pasteLongPasswordToastTitle',
window.CONSTANTS.MAX_PASSWORD_LENGTH
)
),
});
}
// Prevent pasting into input
return false;
}
public async onLogin(passPhrase: string) {
const passPhraseTrimmed = passPhrase.trim();
try {
await window.onLogin(passPhraseTrimmed);
} catch (error) {
// Increment the error counter and show the button if necessary
this.setState({
errorCount: this.state.errorCount + 1,
});
this.setState({ error });
}
}
private async initLogin() {
const passPhrase = String(
(this.inputRef.current as HTMLInputElement).value
);
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('clearAllData')}
buttonType={SessionButtonType.Default}
buttonColor={SessionButtonColor.Danger}
onClick={window.clearLocalData}
/>
</div>
);
}
}
export const SessionPasswordPrompt = withTheme(SessionPasswordPromptInner);