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 (