From 01085244bdab7dd46277b22641db048fce510fe5 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Fri, 26 Feb 2021 11:27:29 +1100 Subject: [PATCH] split up registration signup tab logic to sub components --- _locales/de/messages.json | 5 +- _locales/en/messages.json | 6 +- _locales/es/messages.json | 5 +- _locales/fr/messages.json | 5 +- _locales/id/messages.json | 5 +- _locales/it/messages.json | 5 +- _locales/ja/messages.json | 5 +- _locales/pl/messages.json | 5 +- _locales/pt_BR/messages.json | 5 +- _locales/ru/messages.json | 5 +- _locales/vi/messages.json | 5 +- ts/components/EditProfileDialog.tsx | 2 +- .../session/SessionRegistrationView.tsx | 2 +- .../{ => registration}/RegistrationTabs.tsx | 476 +++++------------- .../registration/RegistrationUserDetails.tsx | 137 +++++ .../session/registration/SignInTab.tsx | 13 + .../session/registration/SignUpTab.tsx | 139 +++++ .../session/registration/TabLabel.tsx | 42 ++ .../registration/TermsAndConditions.tsx | 10 + ts/session/utils/User.ts | 9 +- 20 files changed, 482 insertions(+), 404 deletions(-) rename ts/components/session/{ => registration}/RegistrationTabs.tsx (50%) create mode 100644 ts/components/session/registration/RegistrationUserDetails.tsx create mode 100644 ts/components/session/registration/SignInTab.tsx create mode 100644 ts/components/session/registration/SignUpTab.tsx create mode 100644 ts/components/session/registration/TabLabel.tsx create mode 100644 ts/components/session/registration/TermsAndConditions.tsx diff --git a/_locales/de/messages.json b/_locales/de/messages.json index 1774c694f..04982e315 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -508,7 +508,7 @@ "welcomeToYourSession": { "message": "Willkommen bei Session" }, - "generateSessionID": { + "createSessionID": { "message": "Session ID erstellen" }, "yourUniqueSessionID": { @@ -1410,9 +1410,6 @@ "enterDisplayName": { "message": "Geben Sie einen Anzeigenamen ein" }, - "enterSessionIDHere": { - "message": "Geben Sie Ihre Session ID ein." - }, "continueYourSession": { "message": "Ihre Session fortsetzen" }, diff --git a/_locales/en/messages.json b/_locales/en/messages.json index d52d18610..37eec6bf8 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -2008,7 +2008,7 @@ "getStarted": { "message": "Get started" }, - "generateSessionID": { + "createSessionID": { "message": "Create Session ID", "androidKey": "activity_landing_register_button_title" }, @@ -2050,10 +2050,6 @@ "devicePairingHeader_Step4": { "message": "Enter your Session ID below to link this device to your Session ID." }, - "enterSessionIDHere": { - "message": "Enter your Session ID", - "androidKey": "fragment_enter_session_id_edit_text_hint" - }, "continueYourSession": { "message": "Continue Your Session", "androidKey": "activity_landing_restore_button_title" diff --git a/_locales/es/messages.json b/_locales/es/messages.json index 07022ba9c..bf24c3501 100644 --- a/_locales/es/messages.json +++ b/_locales/es/messages.json @@ -1308,7 +1308,7 @@ "allUsersAreRandomly...": { "message": "Tu ID de Session es la dirección única que las personas pueden usar para contactarte en Session. Por diseño, tu ID de Session es totalmente anónima y privada, sin vínculo con tu identidad real." }, - "generateSessionID": { + "createSessionID": { "message": "Crear ID de Session" }, "recoveryPhrase": { @@ -1320,9 +1320,6 @@ "enterDisplayName": { "message": "Ingresa un nombre para mostrar" }, - "enterSessionIDHere": { - "message": "Ingresa tu ID de Session" - }, "continueYourSession": { "message": "Continúa tu Session" }, diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json index 1fad04b32..f12bb793d 100644 --- a/_locales/fr/messages.json +++ b/_locales/fr/messages.json @@ -1308,7 +1308,7 @@ "allUsersAreRandomly...": { "message": "Votre Session ID est l'identifiant unique que les gens utilisent pour vous contacter dans Session. Sans lien avec votre identité réelle, votre Session ID est complètement anonyme et privé." }, - "generateSessionID": { + "createSessionID": { "message": "Créer un Session ID" }, "recoveryPhrase": { @@ -1320,9 +1320,6 @@ "enterDisplayName": { "message": "Saisissez un nom d'utilisateur" }, - "enterSessionIDHere": { - "message": "Saisissez votre Session ID" - }, "continueYourSession": { "message": "Continuez votre Session" }, diff --git a/_locales/id/messages.json b/_locales/id/messages.json index b1fae3465..ad190ae56 100644 --- a/_locales/id/messages.json +++ b/_locales/id/messages.json @@ -1302,7 +1302,7 @@ "allUsersAreRandomly...": { "message": "Session ID adalah alamat unik yang bisa digunakan untuk mengontak anda. Tanpa koneksi dengan identitas asli, Session ID anda didesain bersifat anonim dan rahasia." }, - "generateSessionID": { + "createSessionID": { "message": "Buat Session ID" }, "recoveryPhrase": { @@ -1314,9 +1314,6 @@ "enterDisplayName": { "message": "Masukkan nama" }, - "enterSessionIDHere": { - "message": "Masuk ke Session ID" - }, "continueYourSession": { "message": "Lanjutkan Session" }, diff --git a/_locales/it/messages.json b/_locales/it/messages.json index 16aec837d..79d4176ed 100644 --- a/_locales/it/messages.json +++ b/_locales/it/messages.json @@ -1308,7 +1308,7 @@ "allUsersAreRandomly...": { "message": "La Sessione ID è l'indirizzo univoco che le persone possono utilizzare per contattarti su una Sessione. Senza alcuna connessione con la tua vera identità, la Sessione ID è totalmente anonimo e privato fin dal incezione." }, - "generateSessionID": { + "createSessionID": { "message": "Crea Sessione ID" }, "recoveryPhrase": { @@ -1320,9 +1320,6 @@ "enterDisplayName": { "message": "Inserisci il nome da visualizzare" }, - "enterSessionIDHere": { - "message": "Inserisci la Sessione ID" - }, "continueYourSession": { "message": "Continua la Sessione" }, diff --git a/_locales/ja/messages.json b/_locales/ja/messages.json index 0aa0ce8be..9510b283e 100644 --- a/_locales/ja/messages.json +++ b/_locales/ja/messages.json @@ -1308,7 +1308,7 @@ "allUsersAreRandomly...": { "message": "Session ID は、Session で連絡を取るために使用できる一意のアドレスです。本当のアイデンティティに関係なく、あなたの Session ID は設計上完全に匿名でプライベートです。" }, - "generateSessionID": { + "createSessionID": { "message": "Session ID を作成する" }, "recoveryPhrase": { @@ -1320,9 +1320,6 @@ "enterDisplayName": { "message": "表示名を入力してください" }, - "enterSessionIDHere": { - "message": "Session ID を入力してください" - }, "continueYourSession": { "message": "Session を続ける" }, diff --git a/_locales/pl/messages.json b/_locales/pl/messages.json index 81da95a5d..760cb7d73 100644 --- a/_locales/pl/messages.json +++ b/_locales/pl/messages.json @@ -1308,7 +1308,7 @@ "allUsersAreRandomly...": { "message": "Twój identyfikator Session to unikalny adres, za pomocą którego można się z Tobą kontaktować w Sesji. Bez połączenia z twoją prawdziwą tożsamością, identyfikator Session jest z założenia całkowicie anonimowy i prywatny." }, - "generateSessionID": { + "createSessionID": { "message": "Utwórz identyfikator Session" }, "recoveryPhrase": { @@ -1320,9 +1320,6 @@ "enterDisplayName": { "message": "Wprowadź wyświetlaną nazwe" }, - "enterSessionIDHere": { - "message": "Wpisz swój identyfikator Session" - }, "continueYourSession": { "message": "Kontynuuj swoją sesję" }, diff --git a/_locales/pt_BR/messages.json b/_locales/pt_BR/messages.json index 47619cea5..3cbce99ab 100644 --- a/_locales/pt_BR/messages.json +++ b/_locales/pt_BR/messages.json @@ -1308,7 +1308,7 @@ "allUsersAreRandomly...": { "message": "Seu ID Session é o endereço exclusivo que as pessoas podem usar para entrar em contato com você no Session. Sem conexão com sua identidade real, seu ID Session é totalmente anônimo e privado por definição." }, - "generateSessionID": { + "createSessionID": { "message": "Criar ID Session" }, "recoveryPhrase": { @@ -1320,9 +1320,6 @@ "enterDisplayName": { "message": "Digite um nome de exibição" }, - "enterSessionIDHere": { - "message": "Digite seu ID Session" - }, "continueYourSession": { "message": "Continuar com seu Session" }, diff --git a/_locales/ru/messages.json b/_locales/ru/messages.json index a2bd8c18a..be2be9403 100644 --- a/_locales/ru/messages.json +++ b/_locales/ru/messages.json @@ -1308,7 +1308,7 @@ "allUsersAreRandomly...": { "message": "Ваш Session ID - это уникальный адрес, который другие пользователи могут использовать для связи с вами при помощи Session. Поскольку ваш Session ID никак не связан с вашей настоящей личностью, он по определению является полностью анонимным и конфиденциальным." }, - "generateSessionID": { + "createSessionID": { "message": "Создать Session ID" }, "recoveryPhrase": { @@ -1320,9 +1320,6 @@ "enterDisplayName": { "message": "Введите отображаемое имя" }, - "enterSessionIDHere": { - "message": "Введите свой Session ID" - }, "continueYourSession": { "message": "Восстановить Session ID" }, diff --git a/_locales/vi/messages.json b/_locales/vi/messages.json index 007b47164..b51f3bdea 100644 --- a/_locales/vi/messages.json +++ b/_locales/vi/messages.json @@ -1272,7 +1272,7 @@ "allUsersAreRandomly...": { "message": "Session ID của bạn là địa chỉ duy nhất mà mọi người có thể dùng để liên lạc với bạn trên ứng dụng Session. Session ID của bạn được thiết kế đảm bảo tuyệt đối ẩn danh và riêng tư vì nó không liên kết với danh tính thật của bạn." }, - "generateSessionID": { + "createSessionID": { "message": "Tạo Session ID" }, "recoveryPhrase": { @@ -1284,9 +1284,6 @@ "enterDisplayName": { "message": "Nhập một tên hiển thị" }, - "enterSessionIDHere": { - "message": "Nhập Session ID của bạn" - }, "continueYourSession": { "message": "Tiếp tục Session của bạn" }, diff --git a/ts/components/EditProfileDialog.tsx b/ts/components/EditProfileDialog.tsx index 6dc69c7a2..328d26792 100644 --- a/ts/components/EditProfileDialog.tsx +++ b/ts/components/EditProfileDialog.tsx @@ -19,7 +19,7 @@ import { SessionModal } from './session/SessionModal'; import { PillDivider } from './session/PillDivider'; import { ToastUtils, UserUtils } from '../session/utils'; import { DefaultTheme } from 'styled-components'; -import { MAX_USERNAME_LENGTH } from './session/RegistrationTabs'; +import { MAX_USERNAME_LENGTH } from './session/registration/RegistrationTabs'; interface Props { i18n: any; diff --git a/ts/components/session/SessionRegistrationView.tsx b/ts/components/session/SessionRegistrationView.tsx index f1bfe47a3..186061762 100644 --- a/ts/components/session/SessionRegistrationView.tsx +++ b/ts/components/session/SessionRegistrationView.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { AccentText } from './AccentText'; -import { RegistrationTabs } from './RegistrationTabs'; +import { RegistrationTabs } from './registration/RegistrationTabs'; import { SessionIconButton, SessionIconSize, SessionIconType } from './icon'; import { SessionToastContainer } from './SessionToastContainer'; import { lightTheme, SessionTheme } from '../../state/ducks/SessionTheme'; diff --git a/ts/components/session/RegistrationTabs.tsx b/ts/components/session/registration/RegistrationTabs.tsx similarity index 50% rename from ts/components/session/RegistrationTabs.tsx rename to ts/components/session/registration/RegistrationTabs.tsx index 6b7a128e0..6238a508e 100644 --- a/ts/components/session/RegistrationTabs.tsx +++ b/ts/components/session/registration/RegistrationTabs.tsx @@ -1,39 +1,23 @@ import React from 'react'; -import classNames from 'classnames'; -import { SessionInput } from './SessionInput'; import { SessionButton, SessionButtonColor, SessionButtonType, -} from './SessionButton'; -import { trigger } from '../../shims/events'; -import { SessionHtmlRenderer } from './SessionHTMLRenderer'; -import { SessionIdEditable } from './SessionIdEditable'; -import { StringUtils, ToastUtils, UserUtils } from '../../session/utils'; -import { lightTheme } from '../../state/ducks/SessionTheme'; -import { ConversationController } from '../../session/conversations'; -import { PasswordUtil } from '../../util'; -import { removeAll } from '../../data/data'; +} 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 { TabLabel, TabType } from './TabLabel'; export const MAX_USERNAME_LENGTH = 20; -enum SignInMode { - Default, - UsingRecoveryPhrase, -} - -enum SignUpMode { - Default, - SessionIDShown, - EnterDetails, -} - -enum TabType { - Create, - SignIn, -} - interface State { selectedTab: TabType; signInMode: SignInMode; @@ -47,46 +31,11 @@ interface State { recoveryPhrase: string; generatedRecoveryPhrase: string; hexGeneratedPubKey: string; - primaryDevicePubKey: string; mnemonicError: string | undefined; displayNameError: string | undefined; - loading: boolean; } -const Tab = ({ - isSelected, - label, - onSelect, - type, -}: { - isSelected: boolean; - label: string; - onSelect?: (event: TabType) => void; - type: TabType; -}) => { - const handleClick = onSelect - ? () => { - onSelect(type); - } - : undefined; - - return ( -
- {label} -
- ); -}; - export class RegistrationTabs extends React.Component { - private readonly accountManager: any; - constructor(props: any) { super(props); @@ -94,21 +43,14 @@ export class RegistrationTabs extends React.Component { this.onDisplayNameChanged = this.onDisplayNameChanged.bind(this); this.onPasswordChanged = this.onPasswordChanged.bind(this); this.onPasswordVerifyChanged = this.onPasswordVerifyChanged.bind(this); - this.onSignUpGenerateSessionIDClick = this.onSignUpGenerateSessionIDClick.bind( - this - ); - this.onSignUpGetStartedClick = this.onSignUpGetStartedClick.bind(this); - this.onSecondDeviceSessionIDChanged = this.onSecondDeviceSessionIDChanged.bind( - this - ); - this.onCompleteSignUpClick = this.onCompleteSignUpClick.bind(this); this.handlePressEnter = this.handlePressEnter.bind(this); this.handleContinueYourSessionClick = this.handleContinueYourSessionClick.bind( this ); + this.onCompleteSignUpClick = this.onCompleteSignUpClick.bind(this); this.state = { - selectedTab: TabType.Create, + selectedTab: TabType.SignUp, signInMode: SignInMode.Default, signUpMode: SignUpMode.Default, secretWords: undefined, @@ -120,14 +62,9 @@ export class RegistrationTabs extends React.Component { recoveryPhrase: '', generatedRecoveryPhrase: '', hexGeneratedPubKey: '', - primaryDevicePubKey: '', mnemonicError: undefined, displayNameError: undefined, - loading: false, }; - - this.accountManager = window.getAccountManager(); - // Clean status in case the app closed unexpectedly } public componentDidMount() { @@ -137,25 +74,19 @@ export class RegistrationTabs extends React.Component { public render() { const { selectedTab } = this.state; - - const createAccount = window.i18n('createAccount'); - const signIn = window.i18n('signIn'); - const isCreateSelected = selectedTab === TabType.Create; - const isSignInSelected = selectedTab === TabType.SignIn; + // tslint:disable: use-simple-attributes return (
- -
@@ -167,7 +98,9 @@ export class RegistrationTabs extends React.Component { private async generateMnemonicAndKeyPair() { if (this.state.generatedRecoveryPhrase === '') { const language = 'english'; - const mnemonic = await this.accountManager.generateMnemonic(language); + const mnemonic = await window + .getAccountManager() + .generateMnemonic(language); let seedHex = window.mnemonic.mn_decode(mnemonic, language); // handle shorter than 32 bytes seeds @@ -201,7 +134,6 @@ export class RegistrationTabs extends React.Component { passwordErrorString: '', passwordFieldsMatch: false, recoveryPhrase: '', - primaryDevicePubKey: '', mnemonicError: undefined, displayNameError: undefined, }); @@ -239,143 +171,54 @@ export class RegistrationTabs extends React.Component { private renderSections() { const { selectedTab } = this.state; - if (selectedTab === TabType.Create) { - return this.renderSignUp(); + 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} + /> + ); } return this.renderSignIn(); } - private renderSignUp() { - const { signUpMode } = this.state; - switch (signUpMode) { - case SignUpMode.Default: - return ( -
- {this.renderSignUpHeader()} - {this.renderSignUpButton()} -
- ); - case SignUpMode.SessionIDShown: - return ( -
- {this.renderSignUpHeader()} -
- {window.i18n('yourUniqueSessionID')} -
- {this.renderEnterSessionID(false)} - {this.renderSignUpButton()} - {this.getRenderTermsConditionAgreement()} -
- ); - - default: - const { - passwordErrorString, - passwordFieldsMatch, - displayNameError, - displayName, - password, - } = this.state; - - 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')} -
- - {this.renderRegistrationContent()} - -
- ); - } - } - private getRenderTermsConditionAgreement() { - const { selectedTab, signInMode, signUpMode } = this.state; - if (selectedTab === TabType.Create) { - return signUpMode !== SignUpMode.Default - ? this.renderTermsConditionAgreement() - : null; - } else { - return signInMode !== SignInMode.Default - ? this.renderTermsConditionAgreement() - : null; + const { selectedTab, signInMode } = this.state; + if (selectedTab !== TabType.SignUp) { + return signInMode !== SignInMode.Default ? : null; } - } - - private renderSignUpHeader() { - const allUsersAreRandomly = window.i18n('allUsersAreRandomly...'); - - return ( -
{allUsersAreRandomly}
- ); - } - - private renderSignUpButton() { - const { signUpMode } = this.state; - - let buttonType: SessionButtonType; - let buttonColor: SessionButtonColor; - let buttonText: string; - if (signUpMode !== SignUpMode.Default) { - buttonType = SessionButtonType.Brand; - buttonColor = SessionButtonColor.Green; - buttonText = window.i18n('continue'); - } else { - buttonType = SessionButtonType.BrandOutline; - buttonColor = SessionButtonColor.Green; - buttonText = window.i18n('generateSessionID'); - } - - return ( - { - if (signUpMode === SignUpMode.Default) { - this.onSignUpGenerateSessionIDClick(); - } else { - this.onSignUpGetStartedClick(); - } - }} - buttonType={buttonType} - buttonColor={buttonColor} - text={buttonText} - /> - ); - } - - private onSignUpGenerateSessionIDClick() { - this.setState( - { - signUpMode: SignUpMode.SessionIDShown, - }, - () => { - window.Session.setNewSessionID(this.state.hexGeneratedPubKey); - } - ); - } - - private onSignUpGetStartedClick() { - this.setState({ - signUpMode: SignUpMode.EnterDetails, - }); + return <>; } private onCompleteSignUpClick() { - void this.register('english'); + void this.register(); } private renderSignIn() { @@ -390,133 +233,60 @@ export class RegistrationTabs extends React.Component { } private renderRegistrationContent() { - const { signInMode, signUpMode } = this.state; + const { signInMode } = this.state; - if (signInMode === SignInMode.UsingRecoveryPhrase) { - return ( -
- { - this.onSeedChanged(val); - }} - onEnterPressed={() => { - this.handlePressEnter(); - }} - theme={lightTheme} + 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 ( + - {this.renderNamePasswordAndVerifyPasswordFields(false)} -
- ); - } + ); + } - if (signUpMode === SignUpMode.EnterDetails) { - return ( -
- {this.renderNamePasswordAndVerifyPasswordFields(true)} -
- ); + if (signInMode === SignInMode.LinkDevice) { + return ( + + ); + } } return null; } - private renderNamePasswordAndVerifyPasswordFields( - stealAutoFocus: boolean = false - ) { - const { password, passwordFieldsMatch } = this.state; - const passwordsDoNotMatch = - !passwordFieldsMatch && this.state.password - ? window.i18n('passwordsDoNotMatch') - : undefined; - - return ( -
- { - this.onDisplayNameChanged(val); - }} - onEnterPressed={() => { - this.handlePressEnter(); - }} - theme={lightTheme} - /> - - { - this.onPasswordChanged(val); - }} - onEnterPressed={() => { - this.handlePressEnter(); - }} - theme={lightTheme} - /> - - {!!password && ( - { - this.onPasswordVerifyChanged(val); - }} - onEnterPressed={() => { - this.handlePressEnter(); - }} - theme={lightTheme} - /> - )} -
- ); - } - - private renderEnterSessionID(contentEditable: boolean) { - const enterSessionIDHere = window.i18n('enterSessionIDHere'); - - return ( - { - this.onSecondDeviceSessionIDChanged(value); - }} - /> - ); - } - - private onSecondDeviceSessionIDChanged(value: string) { - this.setState({ - primaryDevicePubKey: value, - }); - } - private renderSignInButtons() { const { signInMode } = this.state; - // const or = window.i18n('or'); + const or = window.i18n('or'); if (signInMode === SignInMode.Default) { return (
- {this.renderRestoreUsingRecoveryPhraseButton( - SessionButtonType.BrandOutline, - SessionButtonColor.Green - )} + {this.renderRestoreUsingRecoveryPhraseButton()} +
{or}
+ {this.renderLinkDeviceButton()}
); } @@ -532,41 +302,51 @@ export class RegistrationTabs extends React.Component { } private renderTermsConditionAgreement() { - return ( -
- -
- ); + return ; } private handleContinueYourSessionClick() { if (this.state.signInMode === SignInMode.UsingRecoveryPhrase) { - void this.register('english'); + void this.register(); } } - private renderRestoreUsingRecoveryPhraseButton( - buttonType: SessionButtonType, - buttonColor: SessionButtonColor - ) { + private renderRestoreUsingRecoveryPhraseButton() { return ( { this.setState({ signInMode: SignInMode.UsingRecoveryPhrase, - primaryDevicePubKey: '', recoveryPhrase: '', displayName: '', signUpMode: SignUpMode.Default, }); }} - buttonType={buttonType} - buttonColor={buttonColor} + 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) { @@ -631,17 +411,17 @@ export class RegistrationTabs extends React.Component { private async resetRegistration() { await removeAll(); + await window.storage.reset(); await window.storage.fetch(); ConversationController.getInstance().reset(); await ConversationController.getInstance().load(); this.setState({ - loading: false, secretWords: undefined, }); } - private async register(language: string) { + private async register() { const { password, recoveryPhrase, @@ -705,11 +485,9 @@ export class RegistrationTabs extends React.Component { const isRestoringFromSeed = signInMode === SignInMode.UsingRecoveryPhrase; UserUtils.setRestoringFromSeed(isRestoringFromSeed); - await this.accountManager.registerSingleDevice( - seedToUse, - language, - trimName - ); + 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'); diff --git a/ts/components/session/registration/RegistrationUserDetails.tsx b/ts/components/session/registration/RegistrationUserDetails.tsx new file mode 100644 index 000000000..9b90475c3 --- /dev/null +++ b/ts/components/session/registration/RegistrationUserDetails.tsx @@ -0,0 +1,137 @@ +import classNames from 'classnames'; +import React from 'react'; +import { lightTheme } from '../../../state/ducks/SessionTheme'; +import { SessionInput } from '../SessionInput'; +import { MAX_USERNAME_LENGTH } from './RegistrationTabs'; + +const DisplayNameInput = (props: { + stealAutoFocus?: boolean; + displayName: string; + onDisplayNameChanged: (val: string) => any; + handlePressEnter: () => any; +}) => { + return ( + // tslint:disable-next-line: use-simple-attributes + + ); +}; + +const RecoveryPhraseInput = (props: { + recoveryPhrase: string; + onSeedChanged: (val: string) => any; + handlePressEnter: () => any; +}) => { + return ( + + ); +}; + +const PasswordAndVerifyPasswordFields = (props: { + password: string; + passwordFieldsMatch: boolean; + passwordErrorString: string; + onPasswordChanged: (val: string) => any; + onPasswordVerifyChanged: (val: string) => any; + handlePressEnter: () => any; +}) => { + const { password, passwordFieldsMatch, passwordErrorString } = props; + const passwordsDoNotMatch = + !passwordFieldsMatch && password + ? window.i18n('passwordsDoNotMatch') + : undefined; + + return ( + <> + + + {!!password && ( + + )} + + ); +}; + +export interface Props { + // tslint:disable: react-unused-props-and-state + showDisplayNameField: boolean; + showSeedField: boolean; + stealAutoFocus?: boolean; + recoveryPhrase: string; + displayName: string; + password: string; + passwordErrorString: string; + passwordFieldsMatch: boolean; + handlePressEnter: () => any; + onSeedChanged: (val: string) => any; + onDisplayNameChanged: (val: string) => any; + onPasswordChanged: (val: string) => any; + onPasswordVerifyChanged: (val: string) => any; +} + +export const RegistrationUserDetails = (props: Props) => { + return ( +
+ {props.showSeedField && ( + + )} +
+ {props.showDisplayNameField && ( + + )} + +
+
+ ); +}; diff --git a/ts/components/session/registration/SignInTab.tsx b/ts/components/session/registration/SignInTab.tsx new file mode 100644 index 000000000..939d129fc --- /dev/null +++ b/ts/components/session/registration/SignInTab.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +export enum SignInMode { + Default, + UsingRecoveryPhrase, + LinkDevice, +} + +export interface Props { + signInMode: SignInMode; +} + +export const SignInTab = (props: Props) => {}; diff --git a/ts/components/session/registration/SignUpTab.tsx b/ts/components/session/registration/SignUpTab.tsx new file mode 100644 index 000000000..cd80a572b --- /dev/null +++ b/ts/components/session/registration/SignUpTab.tsx @@ -0,0 +1,139 @@ +import React from 'react'; +import { + SessionButton, + SessionButtonColor, + SessionButtonType, +} from '../SessionButton'; +import { SessionIdEditable } from '../SessionIdEditable'; +import { RegistrationUserDetails } from './RegistrationUserDetails'; +import { TermsAndConditions } from './TermsAndConditions'; + +export enum SignUpMode { + Default, + SessionIDShown, + EnterDetails, +} + +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; +} + +const CreateSessionIdButton = ({ + createSessionID, +}: { + createSessionID: any; +}) => { + return ( + + ); +}; + +const ContinueSignUpButton = ({ continueSignUp }: { continueSignUp: any }) => { + return ( + + ); +}; + +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')} +
+ + + + +
+ ); + } +}; diff --git a/ts/components/session/registration/TabLabel.tsx b/ts/components/session/registration/TabLabel.tsx new file mode 100644 index 000000000..49855a0e1 --- /dev/null +++ b/ts/components/session/registration/TabLabel.tsx @@ -0,0 +1,42 @@ +import classNames from 'classnames'; +import React from 'react'; +import { SignUpMode, SignUpTab } from './SignUpTab'; + +export enum TabType { + SignUp, + SignIn, +} + +export const TabLabel = ({ + isSelected, + onSelect, + type, +}: { + isSelected: boolean; + onSelect?: (event: TabType) => void; + type: TabType; +}) => { + const handleClick = onSelect + ? () => { + onSelect(type); + } + : undefined; + + const label = + type === TabType.SignUp + ? window.i18n('createAccount') + : window.i18n('signIn'); + + return ( +
+ {label} +
+ ); +}; diff --git a/ts/components/session/registration/TermsAndConditions.tsx b/ts/components/session/registration/TermsAndConditions.tsx new file mode 100644 index 000000000..2a65dd3bf --- /dev/null +++ b/ts/components/session/registration/TermsAndConditions.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { SessionHtmlRenderer } from '../SessionHTMLRenderer'; + +export const TermsAndConditions = () => { + return ( +
+ +
+ ); +}; diff --git a/ts/session/utils/User.ts b/ts/session/utils/User.ts index cf418e146..4d0a2e8f1 100644 --- a/ts/session/utils/User.ts +++ b/ts/session/utils/User.ts @@ -70,15 +70,8 @@ export async function getUserED25519KeyPair(): Promise { return undefined; } -/** - * Returns the public key of this current device as a STRING, or throws an error - */ export function isRestoringFromSeed(): boolean { - const ourNumber = window.textsecure.storage.user.isRestoringFromSeed(); - if (!ourNumber) { - throw new Error('ourNumber is not set'); - } - return ourNumber; + return window.textsecure.storage.user.isRestoringFromSeed(); } export function setRestoringFromSeed(isRestoring: boolean) {