From 619a894b522bccd1e68a6e9af4ec065de6e7bf95 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 26 Feb 2021 15:29:30 +1100 Subject: [PATCH] fix up Signup --- .../session/registration/RegistrationTabs.tsx | 583 +++++------------- .../registration/RegistrationUserDetails.tsx | 14 +- .../session/registration/SignInTab.tsx | 156 ++++- .../session/registration/SignUpTab.tsx | 224 ++++--- 4 files changed, 461 insertions(+), 516 deletions(-) diff --git a/ts/components/session/registration/RegistrationTabs.tsx b/ts/components/session/registration/RegistrationTabs.tsx index 6238a508e..0f1a55fd1 100644 --- a/ts/components/session/registration/RegistrationTabs.tsx +++ b/ts/components/session/registration/RegistrationTabs.tsx @@ -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 class RegistrationTabs extends React.Component { - constructor(props: any) { - super(props); +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; + } - 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 + 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'}` ); - this.onCompleteSignUpClick = this.onCompleteSignUpClick.bind(this); + window.log.warn('exception during registration:', e); + } +} +export class RegistrationTabs extends React.Component { + 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 (
@@ -126,391 +185,79 @@ export class RegistrationTabs extends React.Component { 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 ( { - 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 ? : null; - } - return <>; - } - - private onCompleteSignUpClick() { - void this.register(); - } - - private renderSignIn() { - return ( -
- {this.renderRegistrationContent()} - - {this.renderSignInButtons()} - {this.getRenderTermsConditionAgreement()} -
- ); - } - - 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 ( - - ); - } - - if (signInMode === SignInMode.LinkDevice) { - return ( - - ); - } - } - - return null; - } - - private renderSignInButtons() { - const { signInMode } = this.state; - - const or = window.i18n('or'); - - if (signInMode === SignInMode.Default) { - return ( -
- {this.renderRestoreUsingRecoveryPhraseButton()} -
{or}
- {this.renderLinkDeviceButton()} -
- ); - } - - return ( - - ); - } - - private renderTermsConditionAgreement() { - return ; - } - - private handleContinueYourSessionClick() { - if (this.state.signInMode === SignInMode.UsingRecoveryPhrase) { - void this.register(); - } - } - - private renderRestoreUsingRecoveryPhraseButton() { - return ( - { - this.setState({ - signInMode: SignInMode.UsingRecoveryPhrase, - recoveryPhrase: '', - displayName: '', - signUpMode: SignUpMode.Default, - }); - }} - buttonType={SessionButtonType.BrandOutline} - buttonColor={SessionButtonColor.Green} - text={window.i18n('restoreUsingRecoveryPhrase')} - /> - ); - } - - private renderLinkDeviceButton() { - return ( - { - 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 ; } 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); + // } } } diff --git a/ts/components/session/registration/RegistrationUserDetails.tsx b/ts/components/session/registration/RegistrationUserDetails.tsx index 9b90475c3..709852ac2 100644 --- a/ts/components/session/registration/RegistrationUserDetails.tsx +++ b/ts/components/session/registration/RegistrationUserDetails.tsx @@ -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 (
{props.showSeedField && ( )}
diff --git a/ts/components/session/registration/SignInTab.tsx b/ts/components/session/registration/SignInTab.tsx index 939d129fc..cc372c643 100644 --- a/ts/components/session/registration/SignInTab.tsx +++ b/ts/components/session/registration/SignInTab.tsx @@ -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 ( + + ); +}; + +const RestoreUsingRecoveryPhraseButton = (props: { + onRecoveryButtonClicked: () => any; +}) => { + return ( + + ); +}; + +const ContinueYourSessionButton = (props: { + handleContinueYourSessionClick: () => any; +}) => { + return ( + + ); +}; + +const SignInButtons = (props: { + signInMode: SignInMode; + onRecoveryButtonClicked: () => any; + onLinkDeviceButtonClicked: () => any; + handleContinueYourSessionClick: () => any; +}) => { + if (props.signInMode === SignInMode.Default) { + return ( +
+ +
{window.i18n('or')}
+ +
+ ); + } + return ( + + ); +}; + +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 ( +
+ {signInMode !== SignInMode.Default && ( + // tslint:disable: use-simple-attributes + { + 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} + /> + )} + + { + setSignInMode(SignInMode.UsingRecoveryPhrase); + setRecoveryPhrase(''); + setDisplayName(''); + }} + onLinkDeviceButtonClicked={() => { + setSignInMode(SignInMode.LinkDevice); + setRecoveryPhrase(''); + setDisplayName(''); + }} + handleContinueYourSessionClick={() => { + throw new Error('TODO'); + }} + /> + {showTermsAndConditions && } +
+ ); +}; diff --git a/ts/components/session/registration/SignUpTab.tsx b/ts/components/session/registration/SignUpTab.tsx index cd80a572b..ae87619b3 100644 --- a/ts/components/session/registration/SignUpTab.tsx +++ b/ts/components/session/registration/SignUpTab.tsx @@ -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 }) => { ); }; +const SignUpDefault = (props: { createSessionID: () => void }) => { + const allUsersAreRandomly = window.i18n('allUsersAreRandomly...'); + return ( +
+
{allUsersAreRandomly}
+ +
+ ); +}; + +const SignUpSessionIDShown = (props: { continueSignUp: () => void }) => { + return ( +
+
+ {window.i18n('yourUniqueSessionID')} +
+ + + + +
+ ); +}; + export const SignUpTab = (props: Props) => { - const { signUpMode, continueSignUp, createSessionID } = props; - - switch (signUpMode) { - case SignUpMode.Default: - const allUsersAreRandomly = window.i18n('allUsersAreRandomly...'); - - return ( -
-
{allUsersAreRandomly}
- -
- ); - - case SignUpMode.SessionIDShown: - return ( -
-
- {window.i18n('yourUniqueSessionID')} -
- - - - -
- ); - - // 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 ( -
-
- {window.i18n('welcomeToYourSession')} -
- - - - -
- ); + 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 ( + { + setSignUpMode(SignUpMode.SessionIDShown); + }} + /> + ); + } + + if (signUpMode === SignUpMode.SessionIDShown) { + return ( + { + 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 ( +
+
+ {window.i18n('welcomeToYourSession')} +
+ + { + 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} + /> + { + await signUp({ + displayName, + generatedRecoveryPhrase: props.generatedRecoveryPhrase, + password, + verifyPassword: passwordVerify, + }); + }} + buttonType={SessionButtonType.Brand} + buttonColor={SessionButtonColor.Green} + text={window.i18n('getStarted')} + disabled={!enableCompleteSignUp} + /> + +
+ ); };