fix up Signup

This commit is contained in:
Audric Ackermann 2021-02-26 15:29:30 +11:00
parent 683fa84970
commit 619a894b52
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4
4 changed files with 465 additions and 520 deletions

View File

@ -1,80 +1,139 @@
import React from 'react';
import {
SessionButton,
SessionButtonColor,
SessionButtonType,
} from '../SessionButton';
import { trigger } from '../../../shims/events';
import { StringUtils, ToastUtils, UserUtils } from '../../../session/utils';
import { ConversationController } from '../../../session/conversations';
import { PasswordUtil } from '../../../util';
import { removeAll } from '../../../data/data';
import { SignUpMode, SignUpTab } from './SignUpTab';
import { SignInMode } from './SignInTab';
import { RegistrationUserDetails } from './RegistrationUserDetails';
import { TermsAndConditions } from './TermsAndConditions';
import { SignUpTab } from './SignUpTab';
import { SignInTab } from './SignInTab';
import { TabLabel, TabType } from './TabLabel';
import { PasswordUtil } from '../../../util';
import { trigger } from '../../../shims/events';
export const MAX_USERNAME_LENGTH = 20;
// tslint:disable: use-simple-attributes
interface State {
selectedTab: TabType;
signInMode: SignInMode;
signUpMode: SignUpMode;
secretWords: string | undefined;
displayName: string;
password: string;
validatePassword: string;
passwordErrorString: string;
passwordFieldsMatch: boolean;
recoveryPhrase: string;
generatedRecoveryPhrase: string;
hexGeneratedPubKey: string;
mnemonicError: string | undefined;
displayNameError: string | undefined;
}
export function validatePassword(password: string, verifyPassword: string) {
const trimmedPassword = password.trim();
const trimmedVerifyPassword = verifyPassword.trim();
// If user hasn't set a value then skip
if (!trimmedPassword && !trimmedVerifyPassword) {
return {
passwordErrorString: '',
passwordFieldsMatch: true,
};
}
const error = PasswordUtil.validatePassword(trimmedPassword, window.i18n);
if (error) {
return {
passwordErrorString: error,
passwordFieldsMatch: true,
};
}
if (trimmedPassword !== trimmedVerifyPassword) {
return {
passwordErrorString: '',
passwordFieldsMatch: false,
};
}
return {
passwordErrorString: '',
passwordFieldsMatch: true,
};
}
export async function resetRegistration() {
await removeAll();
await window.storage.reset();
await window.storage.fetch();
ConversationController.getInstance().reset();
await ConversationController.getInstance().load();
}
export async function signUp(signUpDetails: {
displayName: string;
generatedRecoveryPhrase: string;
password: string;
verifyPassword: string;
}) {
const {
displayName,
password,
verifyPassword,
generatedRecoveryPhrase,
} = signUpDetails;
window.log.info('starting Signing up');
const trimName = displayName.trim();
if (!trimName) {
window.log.warn('invalid trimmed name for registration');
ToastUtils.pushToastError(
'invalidDisplayName',
window.i18n('displayNameEmpty')
);
return;
}
const passwordErrors = validatePassword(password, verifyPassword);
if (passwordErrors.passwordErrorString) {
window.log.warn('invalid password for registration');
ToastUtils.pushToastError(
'invalidPassword',
window.i18n('invalidPassword')
);
return;
}
if (!!password && !passwordErrors.passwordFieldsMatch) {
window.log.warn('passwords does not match for registration');
ToastUtils.pushToastError(
'invalidPassword',
window.i18n('passwordsDoNotMatch')
);
return;
}
try {
await resetRegistration();
await window.setPassword(password);
UserUtils.setRestoringFromSeed(false);
await window
.getAccountManager()
.registerSingleDevice(generatedRecoveryPhrase, 'english', trimName);
// We are just creating a new account, no need to wait for a configuration message
trigger('openInbox');
} catch (e) {
ToastUtils.pushToastError(
'registrationError',
`Error: ${e.message || 'Something went wrong'}`
);
window.log.warn('exception during registration:', e);
}
}
export class RegistrationTabs extends React.Component<any, State> {
constructor(props: any) {
super(props);
this.onSeedChanged = this.onSeedChanged.bind(this);
this.onDisplayNameChanged = this.onDisplayNameChanged.bind(this);
this.onPasswordChanged = this.onPasswordChanged.bind(this);
this.onPasswordVerifyChanged = this.onPasswordVerifyChanged.bind(this);
this.handlePressEnter = this.handlePressEnter.bind(this);
this.handleContinueYourSessionClick = this.handleContinueYourSessionClick.bind(
this
);
this.onCompleteSignUpClick = this.onCompleteSignUpClick.bind(this);
constructor() {
super({});
this.state = {
selectedTab: TabType.SignUp,
signInMode: SignInMode.Default,
signUpMode: SignUpMode.Default,
secretWords: undefined,
displayName: '',
password: '',
validatePassword: '',
passwordErrorString: '',
passwordFieldsMatch: false,
recoveryPhrase: '',
generatedRecoveryPhrase: '',
hexGeneratedPubKey: '',
mnemonicError: undefined,
displayNameError: undefined,
};
}
public componentDidMount() {
void this.generateMnemonicAndKeyPair();
void this.resetRegistration();
void resetRegistration();
}
public render() {
const { selectedTab } = this.state;
// tslint:disable: use-simple-attributes
return (
<div className="session-registration-container">
@ -126,391 +185,79 @@ export class RegistrationTabs extends React.Component<any, State> {
private readonly handleTabSelect = (tabType: TabType): void => {
this.setState({
selectedTab: tabType,
signInMode: SignInMode.Default,
signUpMode: SignUpMode.Default,
displayName: '',
password: '',
validatePassword: '',
passwordErrorString: '',
passwordFieldsMatch: false,
recoveryPhrase: '',
mnemonicError: undefined,
displayNameError: undefined,
});
};
private onSeedChanged(val: string) {
this.setState({
recoveryPhrase: val,
mnemonicError: !val ? window.i18n('recoveryPhraseEmpty') : undefined,
});
}
private onDisplayNameChanged(val: string) {
const sanitizedName = this.sanitiseNameInput(val);
const trimName = sanitizedName.trim();
this.setState({
displayName: sanitizedName,
displayNameError: !trimName ? window.i18n('displayNameEmpty') : undefined,
});
}
private onPasswordChanged(val: string) {
this.setState({ password: val }, () => {
this.validatePassword();
});
}
private onPasswordVerifyChanged(val: string) {
this.setState({ validatePassword: val });
this.setState({ validatePassword: val }, () => {
this.validatePassword();
});
}
private renderSections() {
const { selectedTab } = this.state;
const {
selectedTab,
generatedRecoveryPhrase,
hexGeneratedPubKey,
} = this.state;
if (selectedTab === TabType.SignUp) {
return (
<SignUpTab
signUpMode={this.state.signUpMode}
continueSignUp={() => {
this.setState({
signUpMode: SignUpMode.EnterDetails,
});
}}
createSessionID={() => {
this.setState(
{
signUpMode: SignUpMode.SessionIDShown,
},
() => {
window.Session.setNewSessionID(this.state.hexGeneratedPubKey);
}
);
}}
onCompleteSignUpClick={this.onCompleteSignUpClick}
displayName={this.state.displayName}
password={this.state.password}
passwordErrorString={this.state.passwordErrorString}
passwordFieldsMatch={this.state.passwordFieldsMatch}
displayNameError={this.state.displayNameError}
recoveryPhrase={this.state.recoveryPhrase}
onPasswordVerifyChanged={this.onPasswordVerifyChanged}
handlePressEnter={this.handlePressEnter}
onPasswordChanged={this.onPasswordChanged}
onDisplayNameChanged={this.onDisplayNameChanged}
onSeedChanged={this.onSeedChanged}
generatedRecoveryPhrase={generatedRecoveryPhrase}
hexGeneratedPubKey={hexGeneratedPubKey}
/>
);
}
return this.renderSignIn();
}
private getRenderTermsConditionAgreement() {
const { selectedTab, signInMode } = this.state;
if (selectedTab !== TabType.SignUp) {
return signInMode !== SignInMode.Default ? <TermsAndConditions /> : null;
}
return <></>;
}
private onCompleteSignUpClick() {
void this.register();
}
private renderSignIn() {
return (
<div className="session-registration__content">
{this.renderRegistrationContent()}
{this.renderSignInButtons()}
{this.getRenderTermsConditionAgreement()}
</div>
);
}
private renderRegistrationContent() {
const { signInMode } = this.state;
const isSignInNotDefault = signInMode !== SignInMode.Default;
if (isSignInNotDefault) {
const sharedProps = {
displayName: this.state.displayName,
handlePressEnter: this.handlePressEnter,
onDisplayNameChanged: this.onDisplayNameChanged,
onPasswordChanged: this.onPasswordChanged,
onPasswordVerifyChanged: this.onPasswordVerifyChanged,
onSeedChanged: this.onSeedChanged,
password: this.state.password,
passwordErrorString: this.state.passwordErrorString,
passwordFieldsMatch: this.state.passwordFieldsMatch,
recoveryPhrase: this.state.recoveryPhrase,
stealAutoFocus: true,
};
if (signInMode === SignInMode.UsingRecoveryPhrase) {
return (
<RegistrationUserDetails
showDisplayNameField={true}
showSeedField={true}
{...sharedProps}
/>
);
}
if (signInMode === SignInMode.LinkDevice) {
return (
<RegistrationUserDetails
showDisplayNameField={false}
showSeedField={true}
{...sharedProps}
/>
);
}
}
return null;
}
private renderSignInButtons() {
const { signInMode } = this.state;
const or = window.i18n('or');
if (signInMode === SignInMode.Default) {
return (
<div>
{this.renderRestoreUsingRecoveryPhraseButton()}
<div className="or">{or}</div>
{this.renderLinkDeviceButton()}
</div>
);
}
return (
<SessionButton
onClick={this.handleContinueYourSessionClick}
buttonType={SessionButtonType.Brand}
buttonColor={SessionButtonColor.Green}
text={window.i18n('continueYourSession')}
/>
);
}
private renderTermsConditionAgreement() {
return <TermsAndConditions />;
}
private handleContinueYourSessionClick() {
if (this.state.signInMode === SignInMode.UsingRecoveryPhrase) {
void this.register();
}
}
private renderRestoreUsingRecoveryPhraseButton() {
return (
<SessionButton
onClick={() => {
this.setState({
signInMode: SignInMode.UsingRecoveryPhrase,
recoveryPhrase: '',
displayName: '',
signUpMode: SignUpMode.Default,
});
}}
buttonType={SessionButtonType.BrandOutline}
buttonColor={SessionButtonColor.Green}
text={window.i18n('restoreUsingRecoveryPhrase')}
/>
);
}
private renderLinkDeviceButton() {
return (
<SessionButton
onClick={() => {
this.setState({
signInMode: SignInMode.LinkDevice,
recoveryPhrase: '',
displayName: '',
signUpMode: SignUpMode.Default,
});
}}
buttonType={SessionButtonType.BrandOutline}
buttonColor={SessionButtonColor.Green}
text={window.i18n('linkDevice')}
/>
);
}
private handlePressEnter() {
const { signInMode, signUpMode } = this.state;
if (signUpMode === SignUpMode.EnterDetails) {
this.onCompleteSignUpClick();
return;
}
if (signInMode === SignInMode.UsingRecoveryPhrase) {
this.handleContinueYourSessionClick();
return;
}
}
private trim(value: string) {
return value ? value.trim() : value;
}
private validatePassword() {
const input = this.trim(this.state.password);
const confirmationInput = this.trim(this.state.validatePassword);
// If user hasn't set a value then skip
if (!input && !confirmationInput) {
this.setState({
passwordErrorString: '',
passwordFieldsMatch: true,
});
return;
}
const error = PasswordUtil.validatePassword(input, window.i18n);
if (error) {
this.setState({
passwordErrorString: error,
passwordFieldsMatch: true,
});
return;
}
if (input !== confirmationInput) {
this.setState({
passwordErrorString: '',
passwordFieldsMatch: false,
});
return;
}
this.setState({
passwordErrorString: '',
passwordFieldsMatch: true,
});
}
private sanitiseNameInput(val: string) {
return val.replace(window.displayNameRegex, '');
}
private async resetRegistration() {
await removeAll();
await window.storage.reset();
await window.storage.fetch();
ConversationController.getInstance().reset();
await ConversationController.getInstance().load();
this.setState({
secretWords: undefined,
});
return <SignInTab />;
}
private async register() {
const {
password,
recoveryPhrase,
generatedRecoveryPhrase,
signInMode,
displayName,
passwordErrorString,
passwordFieldsMatch,
} = this.state;
// Make sure the password is valid
window.log.info('starting registration');
const trimName = displayName.trim();
if (!trimName) {
window.log.warn('invalid trimmed name for registration');
ToastUtils.pushToastError(
'invalidDisplayName',
window.i18n('displayNameEmpty')
);
return;
}
if (passwordErrorString) {
window.log.warn('invalid password for registration');
ToastUtils.pushToastError(
'invalidPassword',
window.i18n('invalidPassword')
);
return;
}
if (!!password && !passwordFieldsMatch) {
window.log.warn('passwords does not match for registration');
ToastUtils.pushToastError(
'invalidPassword',
window.i18n('passwordsDoNotMatch')
);
return;
}
if (signInMode === SignInMode.UsingRecoveryPhrase && !recoveryPhrase) {
window.log.warn('empty mnemonic seed passed in seed restoration mode');
return;
} else if (!generatedRecoveryPhrase) {
window.log.warn('empty generated seed');
return;
}
const seedToUse =
signInMode === SignInMode.UsingRecoveryPhrase
? recoveryPhrase
: generatedRecoveryPhrase;
try {
await this.resetRegistration();
await window.setPassword(password);
const isRestoringFromSeed = signInMode === SignInMode.UsingRecoveryPhrase;
UserUtils.setRestoringFromSeed(isRestoringFromSeed);
await window
.getAccountManager()
.registerSingleDevice(seedToUse, 'english', trimName);
// if we are just creating a new account, no need to wait for a configuration message
if (!isRestoringFromSeed) {
trigger('openInbox');
} else {
// We have to pull for all messages of the user of this menmonic
// We are looking for the most recent ConfigurationMessage he sent to himself.
// When we find it, we can just get the displayName, avatar and groups saved in it.
// If we do not find one, we will need to ask for a display name.
window.log.warn('isRestoringFromSeed');
}
} catch (e) {
ToastUtils.pushToastError(
'registrationError',
`Error: ${e.message || 'Something went wrong'}`
);
let exmsg = '';
if (e.message) {
exmsg += e.message;
}
if (e.stack) {
exmsg += ` | stack: + ${e.stack}`;
}
window.log.warn('exception during registration:', exmsg);
}
// const {
// password,
// recoveryPhrase,
// generatedRecoveryPhrase,
// signInMode,
// displayName,
// passwordErrorString,
// passwordFieldsMatch,
// } = this.state;
// if (signInMode === SignInMode.UsingRecoveryPhrase && !recoveryPhrase) {
// window.log.warn('empty mnemonic seed passed in seed restoration mode');
// return;
// } else if (!generatedRecoveryPhrase) {
// window.log.warn('empty generated seed');
// return;
// }
// const seedToUse =
// signInMode === SignInMode.UsingRecoveryPhrase
// ? recoveryPhrase
// : generatedRecoveryPhrase;
// try {
// await this.resetRegistration();
// await window.setPassword(password);
// const isRestoringFromSeed = signInMode === SignInMode.UsingRecoveryPhrase;
// UserUtils.setRestoringFromSeed(isRestoringFromSeed);
// await window
// .getAccountManager()
// .registerSingleDevice(seedToUse, 'english', trimName);
// // if we are just creating a new account, no need to wait for a configuration message
// if (!isRestoringFromSeed) {
// trigger('openInbox');
// } else {
// // We have to pull for all messages of the user of this menmonic
// // We are looking for the most recent ConfigurationMessage he sent to himself.
// // When we find it, we can just get the displayName, avatar and groups saved in it.
// // If we do not find one, we will need to ask for a display name.
// window.log.warn('isRestoringFromSeed');
// }
// } catch (e) {
// ToastUtils.pushToastError(
// 'registrationError',
// `Error: ${e.message || 'Something went wrong'}`
// );
// let exmsg = '';
// if (e.message) {
// exmsg += e.message;
// }
// if (e.stack) {
// exmsg += ` | stack: + ${e.stack}`;
// }
// window.log.warn('exception during registration:', exmsg);
// }
}
}

View File

@ -92,26 +92,32 @@ export interface Props {
showDisplayNameField: boolean;
showSeedField: boolean;
stealAutoFocus?: boolean;
recoveryPhrase: string;
recoveryPhrase?: string;
displayName: string;
password: string;
passwordErrorString: string;
passwordFieldsMatch: boolean;
handlePressEnter: () => any;
onSeedChanged: (val: string) => any;
onSeedChanged?: (val: string) => any;
onDisplayNameChanged: (val: string) => any;
onPasswordChanged: (val: string) => any;
onPasswordVerifyChanged: (val: string) => any;
}
export const RegistrationUserDetails = (props: Props) => {
if (
props.showSeedField &&
(props.recoveryPhrase === undefined || !props.onSeedChanged)
) {
throw new Error('if show seed is true, we need callback + value');
}
return (
<div className={classNames('session-registration__entry-fields')}>
{props.showSeedField && (
<RecoveryPhraseInput
recoveryPhrase={props.recoveryPhrase}
recoveryPhrase={props.recoveryPhrase as string}
handlePressEnter={props.handlePressEnter}
onSeedChanged={props.onSeedChanged}
onSeedChanged={props.onSeedChanged as any}
/>
)}
<div className="inputfields">

View File

@ -1,4 +1,12 @@
import React from 'react';
import React, { useState } from 'react';
import {
SessionButton,
SessionButtonColor,
SessionButtonType,
} from '../SessionButton';
import { validatePassword } from './RegistrationTabs';
import { RegistrationUserDetails } from './RegistrationUserDetails';
import { TermsAndConditions } from './TermsAndConditions';
export enum SignInMode {
Default,
@ -7,7 +15,149 @@ export enum SignInMode {
}
export interface Props {
signInMode: SignInMode;
// tslint:disable: react-unused-props-and-state
}
export const SignInTab = (props: Props) => {};
const LinkDeviceButton = (props: { onLinkDeviceButtonClicked: () => any }) => {
return (
<SessionButton
onClick={props.onLinkDeviceButtonClicked}
buttonType={SessionButtonType.BrandOutline}
buttonColor={SessionButtonColor.Green}
text={window.i18n('linkDevice')}
/>
);
};
const RestoreUsingRecoveryPhraseButton = (props: {
onRecoveryButtonClicked: () => any;
}) => {
return (
<SessionButton
onClick={props.onRecoveryButtonClicked}
buttonType={SessionButtonType.BrandOutline}
buttonColor={SessionButtonColor.Green}
text={window.i18n('restoreUsingRecoveryPhrase')}
/>
);
};
const ContinueYourSessionButton = (props: {
handleContinueYourSessionClick: () => any;
}) => {
return (
<SessionButton
onClick={props.handleContinueYourSessionClick}
buttonType={SessionButtonType.Brand}
buttonColor={SessionButtonColor.Green}
text={window.i18n('continueYourSession')}
/>
);
};
const SignInButtons = (props: {
signInMode: SignInMode;
onRecoveryButtonClicked: () => any;
onLinkDeviceButtonClicked: () => any;
handleContinueYourSessionClick: () => any;
}) => {
if (props.signInMode === SignInMode.Default) {
return (
<div>
<RestoreUsingRecoveryPhraseButton
onRecoveryButtonClicked={props.onRecoveryButtonClicked}
/>
<div className="or">{window.i18n('or')}</div>
<LinkDeviceButton
onLinkDeviceButtonClicked={props.onLinkDeviceButtonClicked}
/>
</div>
);
}
return (
<ContinueYourSessionButton
handleContinueYourSessionClick={props.handleContinueYourSessionClick}
/>
);
};
export const SignInTab = (props: Props) => {
const [signInMode, setSignInMode] = useState(SignInMode.Default);
const [recoveryPhrase, setRecoveryPhrase] = useState('');
const [recoveryPhraseError, setRecoveryPhraseError] = useState(
undefined as string | undefined
);
const [displayName, setDisplayName] = useState('');
const [displayNameError, setDisplayNameError] = useState('');
const [password, setPassword] = useState('');
const [passwordVerify, setPasswordVerify] = useState('');
const [passwordErrorString, setPasswordErrorString] = useState('');
const [passwordFieldsMatch, setPasswordFieldsMatch] = useState(false);
const showTermsAndConditions = signInMode !== SignInMode.Default;
return (
<div className="session-registration__content">
{signInMode !== SignInMode.Default && (
// tslint:disable: use-simple-attributes
<RegistrationUserDetails
showDisplayNameField={signInMode === SignInMode.UsingRecoveryPhrase}
showSeedField={true}
displayName={displayName}
handlePressEnter={() => {
throw new Error('TODO');
}}
onDisplayNameChanged={(name: string) => {
const sanitizedName = name.replace(window.displayNameRegex, '');
const trimName = sanitizedName.trim();
setDisplayName(sanitizedName);
setDisplayNameError(
!trimName ? window.i18n('displayNameEmpty') : undefined
);
}}
onPasswordChanged={(val: string) => {
setPassword(val);
const errors = validatePassword(val, passwordVerify);
setPasswordErrorString(errors.passwordErrorString);
setPasswordFieldsMatch(errors.passwordFieldsMatch);
}}
onPasswordVerifyChanged={(val: string) => {
setPasswordVerify(val);
const errors = validatePassword(password, val);
setPasswordErrorString(errors.passwordErrorString);
setPasswordFieldsMatch(errors.passwordFieldsMatch);
}}
onSeedChanged={(seed: string) => {
setRecoveryPhrase(seed);
setRecoveryPhraseError(
!seed ? window.i18n('recoveryPhraseEmpty') : undefined
);
}}
password={password}
passwordErrorString={passwordErrorString}
passwordFieldsMatch={passwordFieldsMatch}
recoveryPhrase={recoveryPhrase}
stealAutoFocus={true}
/>
)}
<SignInButtons
signInMode={signInMode}
onRecoveryButtonClicked={() => {
setSignInMode(SignInMode.UsingRecoveryPhrase);
setRecoveryPhrase('');
setDisplayName('');
}}
onLinkDeviceButtonClicked={() => {
setSignInMode(SignInMode.LinkDevice);
setRecoveryPhrase('');
setDisplayName('');
}}
handleContinueYourSessionClick={() => {
throw new Error('TODO');
}}
/>
{showTermsAndConditions && <TermsAndConditions />}
</div>
);
};

View File

@ -1,10 +1,11 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import {
SessionButton,
SessionButtonColor,
SessionButtonType,
} from '../SessionButton';
import { SessionIdEditable } from '../SessionIdEditable';
import { signUp, validatePassword } from './RegistrationTabs';
import { RegistrationUserDetails } from './RegistrationUserDetails';
import { TermsAndConditions } from './TermsAndConditions';
@ -16,22 +17,8 @@ export enum SignUpMode {
export interface Props {
// tslint:disable: react-unused-props-and-state
signUpMode: SignUpMode;
continueSignUp: () => any;
createSessionID: () => any;
onCompleteSignUpClick: () => any;
passwordErrorString: string;
passwordFieldsMatch: boolean;
displayNameError?: string;
displayName: string;
password: string;
recoveryPhrase: string;
stealAutoFocus?: boolean;
handlePressEnter: () => any;
onSeedChanged: (val: string) => any;
onDisplayNameChanged: (val: string) => any;
onPasswordChanged: (val: string) => any;
onPasswordVerifyChanged: (val: string) => any;
generatedRecoveryPhrase: string;
hexGeneratedPubKey: string;
}
const CreateSessionIdButton = ({
@ -60,80 +47,135 @@ const ContinueSignUpButton = ({ continueSignUp }: { continueSignUp: any }) => {
);
};
export const SignUpTab = (props: Props) => {
const { signUpMode, continueSignUp, createSessionID } = props;
switch (signUpMode) {
case SignUpMode.Default:
const allUsersAreRandomly = window.i18n('allUsersAreRandomly...');
return (
<div className="session-registration__content">
<div className="session-description-long">{allUsersAreRandomly}</div>
<CreateSessionIdButton createSessionID={createSessionID} />
</div>
);
case SignUpMode.SessionIDShown:
return (
<div className="session-registration__content">
<div className="session-registration__unique-session-id">
{window.i18n('yourUniqueSessionID')}
</div>
<SessionIdEditable editable={false} placeholder={undefined} />
<ContinueSignUpButton continueSignUp={continueSignUp} />
<TermsAndConditions />
</div>
);
// can only be the EnterDetails step
default:
const {
passwordErrorString,
passwordFieldsMatch,
displayNameError,
displayName,
password,
} = props;
let enableCompleteSignUp = true;
const displayNameOK = !displayNameError && !!displayName; //display name required
const passwordsOK =
!password || (!passwordErrorString && passwordFieldsMatch); // password is valid if empty, or if no error and fields are matching
enableCompleteSignUp = displayNameOK && passwordsOK;
return (
<div className="session-registration__content">
<div className="session-registration__welcome-session">
{window.i18n('welcomeToYourSession')}
</div>
<RegistrationUserDetails
showDisplayNameField={true}
showSeedField={false}
displayName={props.displayName}
handlePressEnter={props.handlePressEnter}
onDisplayNameChanged={props.onDisplayNameChanged}
onPasswordChanged={props.onPasswordChanged}
onPasswordVerifyChanged={props.onPasswordVerifyChanged}
onSeedChanged={props.onSeedChanged}
password={props.password}
passwordErrorString={props.passwordErrorString}
passwordFieldsMatch={props.passwordFieldsMatch}
recoveryPhrase={props.recoveryPhrase}
stealAutoFocus={true}
/>
<SessionButton
onClick={props.onCompleteSignUpClick}
buttonType={SessionButtonType.Brand}
buttonColor={SessionButtonColor.Green}
text={window.i18n('getStarted')}
disabled={!enableCompleteSignUp}
/>
<TermsAndConditions />
</div>
);
}
const SignUpDefault = (props: { createSessionID: () => void }) => {
const allUsersAreRandomly = window.i18n('allUsersAreRandomly...');
return (
<div className="session-registration__content">
<div className="session-description-long">{allUsersAreRandomly}</div>
<CreateSessionIdButton createSessionID={props.createSessionID} />
</div>
);
};
const SignUpSessionIDShown = (props: { continueSignUp: () => void }) => {
return (
<div className="session-registration__content">
<div className="session-registration__unique-session-id">
{window.i18n('yourUniqueSessionID')}
</div>
<SessionIdEditable editable={false} placeholder={undefined} />
<ContinueSignUpButton continueSignUp={props.continueSignUp} />
<TermsAndConditions />
</div>
);
};
export const SignUpTab = (props: Props) => {
const [signUpMode, setSignUpMode] = useState(SignUpMode.Default);
const [displayName, setDisplayName] = useState('');
const [password, setPassword] = useState('');
const [passwordVerify, setPasswordVerify] = useState('');
const [passwordErrorString, setPasswordErrorString] = useState('');
const [displayNameError, setDisplayNameError] = useState('');
const [passwordFieldsMatch, setPasswordFieldsMatch] = useState(false);
useEffect(() => {
if (signUpMode === SignUpMode.SessionIDShown) {
window.Session.setNewSessionID(props.hexGeneratedPubKey);
}
}, [signUpMode]);
if (signUpMode === SignUpMode.Default) {
return (
<SignUpDefault
createSessionID={() => {
setSignUpMode(SignUpMode.SessionIDShown);
}}
/>
);
}
if (signUpMode === SignUpMode.SessionIDShown) {
return (
<SignUpSessionIDShown
continueSignUp={() => {
setSignUpMode(SignUpMode.EnterDetails);
}}
/>
);
}
// can only be the EnterDetails step
// Display name is required
const displayNameOK = !displayNameError && !!displayName;
// Password is valid if empty, or if no error and fields are matching
const passwordsOK =
!password || (!passwordErrorString && passwordFieldsMatch);
const enableCompleteSignUp = displayNameOK && passwordsOK;
return (
<div className="session-registration__content">
<div className="session-registration__welcome-session">
{window.i18n('welcomeToYourSession')}
</div>
<RegistrationUserDetails
showDisplayNameField={true}
showSeedField={false}
displayName={displayName}
handlePressEnter={() => {
throw new Error('TODO');
}}
onDisplayNameChanged={(name: string) => {
const sanitizedName = name.replace(window.displayNameRegex, '');
const trimName = sanitizedName.trim();
setDisplayName(sanitizedName);
setDisplayNameError(
!trimName ? window.i18n('displayNameEmpty') : undefined
);
}}
onPasswordChanged={(val: string) => {
setPassword(val);
if (!val) {
setPasswordErrorString('');
setPasswordFieldsMatch(true);
setPasswordVerify('');
return;
}
const errors = validatePassword(val, passwordVerify);
setPasswordErrorString(errors.passwordErrorString);
setPasswordFieldsMatch(errors.passwordFieldsMatch);
}}
onPasswordVerifyChanged={(val: string) => {
setPasswordVerify(val);
const errors = validatePassword(password, val);
setPasswordErrorString(errors.passwordErrorString);
setPasswordFieldsMatch(errors.passwordFieldsMatch);
}}
password={password}
passwordErrorString={passwordErrorString}
passwordFieldsMatch={passwordFieldsMatch}
stealAutoFocus={true}
/>
<SessionButton
onClick={async () => {
await signUp({
displayName,
generatedRecoveryPhrase: props.generatedRecoveryPhrase,
password,
verifyPassword: passwordVerify,
});
}}
buttonType={SessionButtonType.Brand}
buttonColor={SessionButtonColor.Green}
text={window.i18n('getStarted')}
disabled={!enableCompleteSignUp}
/>
<TermsAndConditions />
</div>
);
};