From 214679decef4213986b2dce403006308e2d6f058 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Mon, 6 Jan 2020 11:30:22 +1100 Subject: [PATCH] add registration errors show to user --- _locales/en/messages.json | 6 + stylesheets/_session.scss | 65 +--------- stylesheets/_session_signin.scss | 10 +- ts/components/session/RegistrationTabs.tsx | 135 ++++++++++++++++----- ts/components/session/SessionButton.tsx | 1 + ts/components/session/SessionInput.tsx | 49 ++++++-- 6 files changed, 159 insertions(+), 107 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 2e5e9b971..d7b79e930 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -2532,5 +2532,11 @@ }, "devicesSettingsDescription": { "message": "Managed linked devices" + }, + "mnemonicEmpty": { + "message": "Seed is mandatory" + }, + "displayNameEmpty": { + "message": "Display Name Is Mandatory" } } diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss index 9fa3c5429..6c4aafce0 100644 --- a/stylesheets/_session.scss +++ b/stylesheets/_session.scss @@ -222,8 +222,6 @@ $session_message-container-border-radius: 5px; border: 2px solid $textAndBorderColor; } - - width: auto; border: none; display: flex; @@ -246,8 +244,7 @@ $session_message-container-border-radius: 5px; border: 2px solid $session-color-green; background-color: $session-color-green; - &.disabled, - &:disabled { + &.disabled { background-color: rgba($session-color-green, 0.6); } } @@ -294,7 +291,6 @@ $session_message-container-border-radius: 5px; &.square-outline { &.green { @include transparent-background($session-color-green); - } &.white { @include transparent-background($session-color-white); @@ -338,7 +334,6 @@ $session_message-container-border-radius: 5px; &.square-outline { border-radius: 0px; } - &:hover { filter: brightness(80%); @@ -520,64 +515,6 @@ label { cursor: pointer; } -.input-with-label-container { - height: 46.5px; - width: 280px; - color: $session-color-white; - padding: 2px 0 2px 0; - transition: opacity $session-transition-duration; - opacity: 1; - position: relative; - - label { - line-height: 14px; - opacity: 0; - color: #737373; - font-size: 10px; - line-height: 11px; - position: absolute; - top: 0px; - } - - &.filled { - opacity: 1; - } - - input { - border: none; - outline: 0; - height: 14px; - width: 280px; - background: transparent; - color: $session-color-white; - font-size: 12px; - line-height: 14px; - position: absolute; - top: 50%; - transform: translateY(-50%); - } - - hr { - border: 1px solid $session-color-white; - width: 100%; - position: absolute; - bottom: 0px; - } - - button { - position: absolute; - top: 50%; - transform: translateY(-50%); - right: 0px; - } -} - -.user-details-dialog { - .message { - word-break: break-all; - } -} - #session-toast-container { position: fixed; right: $session-margin-lg; diff --git a/stylesheets/_session_signin.scss b/stylesheets/_session_signin.scss index 2e4a573cd..1b067d71e 100644 --- a/stylesheets/_session_signin.scss +++ b/stylesheets/_session_signin.scss @@ -62,7 +62,6 @@ &__content { width: 100%; - overflow-y: auto; padding-top: 20px; } @@ -112,10 +111,11 @@ &__welcome-session { @include registration-label-mixin; - font-size: 12px; + font-size: 14px; font-weight: 700; - line-height: 12px; + line-height: 14px; padding-top: 2em; + text-align: center; } &__unique-session-id { @@ -157,6 +157,10 @@ opacity: 1; } + &.error { + color: red; + } + input { border: none; outline: 0; diff --git a/ts/components/session/RegistrationTabs.tsx b/ts/components/session/RegistrationTabs.tsx index 64d68486d..fed62801b 100644 --- a/ts/components/session/RegistrationTabs.tsx +++ b/ts/components/session/RegistrationTabs.tsx @@ -40,6 +40,8 @@ interface State { mnemonicSeed: string; hexGeneratedPubKey: string; primaryDevicePubKey: string; + mnemonicError: string | undefined; + displayNameError: string | undefined; } const Tab = ({ @@ -111,6 +113,8 @@ export class RegistrationTabs extends React.Component<{}, State> { mnemonicSeed: '', hexGeneratedPubKey: '', primaryDevicePubKey: '', + mnemonicError: undefined, + displayNameError: undefined, }; this.accountManager = window.getAccountManager(); @@ -196,25 +200,39 @@ export class RegistrationTabs extends React.Component<{}, State> { mnemonicSeed: '', hexGeneratedPubKey: '', primaryDevicePubKey: '', + mnemonicError: undefined, + displayNameError: undefined, }); }; private onSeedChanged(val: string) { - this.setState({ mnemonicSeed: val }); + this.setState({ + mnemonicSeed: val, + mnemonicError: !val ? window.i18n('mnemonicEmpty') : undefined, + }); } private onDisplayNameChanged(val: string) { const sanitizedName = this.sanitiseNameInput(val); - this.setState({ displayName: sanitizedName }); + this.setState({ + displayName: sanitizedName, + displayNameError: !sanitizedName + ? window.i18n('displayNameEmpty') + : undefined, + }); } private onPasswordChanged(val: string) { - this.setState({ password: val }); - this.onValidatePassword(); // FIXME add bubbles or something to help the user know what he did wrong + this.setState({ password: val }, () => { + this.validatePassword(); + }); } private onPasswordVerifyChanged(val: string) { this.setState({ validatePassword: val }); + this.setState({ validatePassword: val }, () => { + this.validatePassword(); + }); } private renderSections() { @@ -250,6 +268,21 @@ export class RegistrationTabs extends React.Component<{}, State> { ); 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 (
@@ -264,6 +297,7 @@ export class RegistrationTabs extends React.Component<{}, State> { buttonType={SessionButtonType.Brand} buttonColor={SessionButtonColor.Green} text={window.i18n('completeSignUp')} + enabled={enableCompleteSignUp} />
); @@ -399,6 +433,11 @@ export class RegistrationTabs extends React.Component<{}, State> { } private renderNamePasswordAndVerifyPasswordFields() { + const passwordDoesNotMatchString = + !this.state.passwordFieldsMatch && this.state.password + ? window.i18n('passwordsDoNotMatch') + : undefined; + return (
{ { @@ -428,6 +468,7 @@ export class RegistrationTabs extends React.Component<{}, State> { { @@ -488,7 +529,7 @@ export class RegistrationTabs extends React.Component<{}, State> {

{or}

{this.renderRestoreUsingSeedButton( SessionButtonType.BrandOutline, - SessionButtonColor.Green + SessionButtonColor.White )}
); @@ -525,6 +566,32 @@ export class RegistrationTabs extends React.Component<{}, State> { } private renderContinueYourSessionButton() { + const { + signUpMode, + signInMode, + passwordErrorString, + passwordFieldsMatch, + displayNameError, + mnemonicError, + primaryDevicePubKey, + displayName, + mnemonicSeed, + password, + } = this.state; + + let enableContinue = true; + const displayNameOK = !displayNameError && !!displayName; //display name required + const mnemonicOK = !mnemonicError && !!mnemonicSeed; //Mnemonic required + const passwordsOK = + !password || (!passwordErrorString && passwordFieldsMatch); // password is valid if empty, or if no error and fields are matching + if (signInMode === SignInMode.UsingSeed) { + enableContinue = displayNameOK && mnemonicOK && passwordsOK; + } else if (signInMode === SignInMode.LinkingDevice) { + enableContinue = !!primaryDevicePubKey; + } else if (signUpMode === SignUpMode.EnterDetails) { + enableContinue = displayNameOK && passwordsOK; + } + return ( { @@ -533,6 +600,7 @@ export class RegistrationTabs extends React.Component<{}, State> { buttonType={SessionButtonType.Brand} buttonColor={SessionButtonColor.Green} text={window.i18n('continueYourSession')} + enabled={enableContinue} /> ); } @@ -603,37 +671,37 @@ export class RegistrationTabs extends React.Component<{}, State> { // If user hasn't set a value then skip if (!input && !confirmationInput) { - return null; + this.setState({ + passwordErrorString: '', + passwordFieldsMatch: true, + }); + + return; } const error = window.passwordUtil.validatePassword(input, window.i18n); if (error) { - return error; - } + this.setState({ + passwordErrorString: error, + passwordFieldsMatch: true, + }); - if (input !== confirmationInput) { - return "Password don't match"; + return; } - return null; - } - - private onValidatePassword() { - const passwordValidation = this.validatePassword(); - if (passwordValidation) { - this.setState({ passwordErrorString: passwordValidation }); - } else { - // Show green box around inputs that match - const input = this.trim(this.state.password); - const confirmationInput = this.trim(this.state.validatePassword); - const passwordFieldsMatch = - input !== undefined && input === confirmationInput; - + if (input !== confirmationInput) { this.setState({ passwordErrorString: '', - passwordFieldsMatch, + passwordFieldsMatch: false, }); + + return; } + + this.setState({ + passwordErrorString: '', + passwordFieldsMatch: true, + }); } private sanitiseNameInput(val: string) { @@ -654,10 +722,21 @@ export class RegistrationTabs extends React.Component<{}, State> { } private async register(language: string) { - const { password, mnemonicSeed, displayName } = this.state; + const { + password, + mnemonicSeed, + displayName, + passwordErrorString, + passwordFieldsMatch, + } = this.state; // Make sure the password is valid - if (this.validatePassword()) { - //this.showToast(window.i18n('invalidPassword')); + if (passwordErrorString || passwordFieldsMatch) { + window.pushToast({ + title: window.i18n('invalidPassword'), + type: 'success', + id: 'invalidPassword', + }); + return; } if (!mnemonicSeed) { diff --git a/ts/components/session/SessionButton.tsx b/ts/components/session/SessionButton.tsx index 9b51f4091..7dc4c2a24 100644 --- a/ts/components/session/SessionButton.tsx +++ b/ts/components/session/SessionButton.tsx @@ -28,6 +28,7 @@ interface Props { buttonType: SessionButtonType; buttonColor: SessionButtonColor; onClick: any; + enabled?: boolean; } export class SessionButton extends React.PureComponent { diff --git a/ts/components/session/SessionInput.tsx b/ts/components/session/SessionInput.tsx index b4789ae9a..5c74762c5 100644 --- a/ts/components/session/SessionInput.tsx +++ b/ts/components/session/SessionInput.tsx @@ -5,6 +5,7 @@ import { SessionIconButton, SessionIconSize, SessionIconType } from './icon'; interface Props { label: string; + error?: string; type: string; value?: string; placeholder: string; @@ -32,23 +33,14 @@ export class SessionInput extends React.PureComponent { } public render() { - const { placeholder, type, label, value, enableShowHide } = this.props; - const { inputValue, forceShow } = this.state; + const { placeholder, type, value, enableShowHide, error } = this.props; + const { forceShow } = this.state; const correctType = forceShow ? 'text' : type; return (
- + {error ? this.renderError() : this.renderLabel()} { ); } + private renderLabel() { + const { inputValue } = this.state; + const { label } = this.props; + + return ( + + ); + } + + private renderError() { + const { error } = this.props; + + return ( + + ); + } + private renderShowHideButton() { return (