diff --git a/res/css/_components.scss b/res/css/_components.scss index eae67a84a2..9dd65d2a4f 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -78,6 +78,7 @@ @import "./views/dialogs/_MessageEditHistoryDialog.scss"; @import "./views/dialogs/_ModalWidgetDialog.scss"; @import "./views/dialogs/_NewSessionReviewDialog.scss"; +@import "./views/dialogs/_RegistrationEmailPromptDialog.scss"; @import "./views/dialogs/_RoomSettingsDialog.scss"; @import "./views/dialogs/_RoomSettingsDialogBridges.scss"; @import "./views/dialogs/_RoomUpgradeDialog.scss"; diff --git a/res/css/views/dialogs/_RegistrationEmailPromptDialog.scss b/res/css/views/dialogs/_RegistrationEmailPromptDialog.scss new file mode 100644 index 0000000000..31fc6d7a04 --- /dev/null +++ b/res/css/views/dialogs/_RegistrationEmailPromptDialog.scss @@ -0,0 +1,28 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_RegistrationEmailPromptDialog { + width: 417px; + + .mx_Dialog_content { + margin-bottom: 24px; + color: $tertiary-fg-color; + } + + .mx_Dialog_primary { + width: 100%; + } +} diff --git a/src/components/views/auth/RegistrationForm.tsx b/src/components/views/auth/RegistrationForm.tsx index 58a0f63b5f..764dfdd526 100644 --- a/src/components/views/auth/RegistrationForm.tsx +++ b/src/components/views/auth/RegistrationForm.tsx @@ -28,6 +28,9 @@ import withValidation from '../elements/Validation'; import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; import PassphraseField from "./PassphraseField"; import CountlyAnalytics from "../../../CountlyAnalytics"; +import Field from '../elements/Field'; +import RegistrationEmailPromptDialog from '../dialogs/RegistrationEmailPromptDialog'; +import QuestionDialog from '../dialogs/QuestionDialog'; enum RegistrationField { Email = "field_email", @@ -104,6 +107,7 @@ export default class RegistrationForm extends React.PureComponent { ev.preventDefault(); + ev.persist(); if (!this.props.canSubmit) return; @@ -116,36 +120,36 @@ export default class RegistrationForm extends React.PureComponent { + if (confirmed) this.doSubmit(ev); + }, + }); } else if (this.showEmail()) { - desc = _t( - "If you don't specify an email address, you won't be able to reset your password. " + - "Are you sure?", - ); + CountlyAnalytics.instance.track("onboarding_registration_submit_warn"); + Modal.createTrackedDialog("Email prompt dialog", '', RegistrationEmailPromptDialog, { + onFinished: async (confirmed: boolean, email?: string) => { + if (confirmed) { + this.setState({ + email, + }, () => { + this.doSubmit(ev); + }); + } + }, + }); } else { // user can't set an e-mail so don't prompt them to this.doSubmit(ev); return; } - - CountlyAnalytics.instance.track("onboarding_registration_submit_warn"); - - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - Modal.createTrackedDialog('If you don\'t specify an email address...', '', QuestionDialog, { - title: _t("Warning!"), - description: desc, - button: _t("Continue"), - onFinished: (confirmed) => { - if (confirmed) { - this.doSubmit(ev); - } - }, - }); } else { this.doSubmit(ev); } @@ -443,7 +447,6 @@ export default class RegistrationForm extends React.PureComponent this[RegistrationField.PasswordConfirm] = field} @@ -493,7 +495,6 @@ export default class RegistrationForm extends React.PureComponent this[RegistrationField.Username] = field} diff --git a/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx b/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx new file mode 100644 index 0000000000..8e91a07bf5 --- /dev/null +++ b/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx @@ -0,0 +1,96 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import * as React from "react"; + +import { _t } from '../../../languageHandler'; +import { IDialogProps } from "./IDialogProps"; +import {useRef, useState} from "react"; +import Field from "../elements/Field"; +import CountlyAnalytics from "../../../CountlyAnalytics"; +import withValidation from "../elements/Validation"; +import * as Email from "../../../email"; +import BaseDialog from "./BaseDialog"; +import DialogButtons from "../elements/DialogButtons"; + +interface IProps extends IDialogProps { + onFinished(continued: boolean, email?: string): void; +} + +const validation = withValidation({ + rules: [ + { + key: "email", + test: ({ value }) => !value || Email.looksValid(value), + invalid: () => _t("Doesn't look like a valid email address"), + }, + ], +}); + +const RegistrationEmailPromptDialog: React.FC = ({onFinished}) => { + const [email, setEmail] = useState(""); + const fieldRef = useRef(); + + const onSubmit = async () => { + if (email) { + const valid = await fieldRef.current.validate({ allowEmpty: false }); + + if (!valid) { + fieldRef.current.focus(); + fieldRef.current.validate({ allowEmpty: false, focused: true }); + return; + } + } + + onFinished(true, email); + }; + + return onFinished(false)} + fixedWidth={false} + > +
+

{_t("Just a heads up, if you don't add an email and forget your password, you could " + + "permanently lose access to your account.", {}, { + b: sub => {sub}, + })}

+
+ { + setEmail(ev.target.value); + }} + onValidate={async fieldState => await validation(fieldState)} + onFocus={() => CountlyAnalytics.instance.track("onboarding_registration_email2_focus")} + onBlur={() => CountlyAnalytics.instance.track("onboarding_registration_email2_blur")} + /> + +
+ +
; +}; + +export default RegistrationEmailPromptDialog; diff --git a/src/components/views/elements/Field.tsx b/src/components/views/elements/Field.tsx index fb34f51b60..58bd5226b6 100644 --- a/src/components/views/elements/Field.tsx +++ b/src/components/views/elements/Field.tsx @@ -167,7 +167,7 @@ export default class Field extends React.PureComponent { } }; - private async validate({ focused, allowEmpty = true }: {focused: boolean, allowEmpty?: boolean}) { + public async validate({ focused, allowEmpty = true }: {focused?: boolean, allowEmpty?: boolean}) { if (!this.props.onValidate) { return; } @@ -196,6 +196,8 @@ export default class Field extends React.PureComponent { feedbackVisible: false, }); } + + return valid; } public render() { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index e04c929f80..b66df6c761 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2045,6 +2045,10 @@ "Use this session to verify your new one, granting it access to encrypted messages:": "Use this session to verify your new one, granting it access to encrypted messages:", "If you didn’t sign in to this session, your account may be compromised.": "If you didn’t sign in to this session, your account may be compromised.", "This wasn't me": "This wasn't me", + "Doesn't look like a valid email address": "Doesn't look like a valid email address", + "Continuing without email": "Continuing without email", + "Just a heads up, if you don't add an email and forget your password, you could permanently lose access to your account.": "Just a heads up, if you don't add an email and forget your password, you could permanently lose access to your account.", + "Email (optional)": "Email (optional)", "Please fill why you're reporting.": "Please fill why you're reporting.", "Report Content to Your Homeserver Administrator": "Report Content to Your Homeserver Administrator", "Reporting this message will send its unique 'event ID' to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.": "Reporting this message will send its unique 'event ID' to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.", @@ -2226,7 +2230,6 @@ "Keep going...": "Keep going...", "Enter username": "Enter username", "Enter email address": "Enter email address", - "Doesn't look like a valid email address": "Doesn't look like a valid email address", "Enter phone number": "Enter phone number", "Doesn't look like a valid phone number": "Doesn't look like a valid phone number", "Email": "Email", @@ -2236,14 +2239,12 @@ "Sign in with": "Sign in with", "Sign in": "Sign in", "No identity server is configured so you cannot add an email address in order to reset your password in the future.": "No identity server is configured so you cannot add an email address in order to reset your password in the future.", - "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?", "Use an email address to recover your account": "Use an email address to recover your account", "Enter email address (required on this homeserver)": "Enter email address (required on this homeserver)", "Passwords don't match": "Passwords don't match", "Other users can invite you to rooms using your contact details": "Other users can invite you to rooms using your contact details", "Enter phone number (required on this homeserver)": "Enter phone number (required on this homeserver)", "Use lowercase letters, numbers, dashes and underscores only": "Use lowercase letters, numbers, dashes and underscores only", - "Email (optional)": "Email (optional)", "Phone (optional)": "Phone (optional)", "Register": "Register", "Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.": "Set an email for account recovery. Use email or phone to optionally be discoverable by existing contacts.",