From ee33a4e9ba6349136c68a5165c14e914b0d02135 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 13 May 2019 16:30:34 -0600 Subject: [PATCH 1/6] Refactor "Next" button into ServerConfig components TODO still remains about making ModularServerConfig extend ServerConfig instead of duplicating everything. See https://github.com/vector-im/riot-web/issues/9290 --- .../structures/auth/ForgotPassword.js | 30 +++----- src/components/structures/auth/Login.js | 38 ++++------ .../structures/auth/Registration.js | 31 +++----- .../views/auth/ModularServerConfig.js | 51 ++++++++++--- src/components/views/auth/ServerConfig.js | 71 ++++++++++++++----- 5 files changed, 125 insertions(+), 96 deletions(-) diff --git a/src/components/structures/auth/ForgotPassword.js b/src/components/structures/auth/ForgotPassword.js index 5316235fe0..a772e72c5a 100644 --- a/src/components/structures/auth/ForgotPassword.js +++ b/src/components/structures/auth/ForgotPassword.js @@ -124,13 +124,7 @@ module.exports = React.createClass({ }); }, - async onServerDetailsNextPhaseClick(ev) { - ev.stopPropagation(); - // TODO: TravisR - Capture the user's input somehow else - if (this._serverConfigRef) { - // Just to make sure the user's input gets captured - await this._serverConfigRef.validateServer(); - } + async onServerDetailsNextPhaseClick() { this.setState({ phase: PHASE_FORGOT, }); @@ -160,25 +154,19 @@ module.exports = React.createClass({ renderServerDetails() { const ServerConfig = sdk.getComponent("auth.ServerConfig"); - const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); if (SdkConfig.get()['disable_custom_urls']) { return null; } - // TODO: TravisR - Pull out server discovery from ServerConfig to disable the next button? - return
- this._serverConfigRef = r} - serverConfig={this.props.serverConfig} - onServerConfigChange={this.props.onServerConfigChange} - delayTimeMs={0} /> - - {_t("Next")} - -
; + return ; }, renderForgot() { diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index af9370f2db..68b440d064 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -20,11 +20,11 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import { _t, _td } from '../../../languageHandler'; +import {_t, _td} from '../../../languageHandler'; import sdk from '../../../index'; import Login from '../../../Login'; import SdkConfig from '../../../SdkConfig'; -import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; +import {messageForResourceLimitError} from '../../../utils/ErrorUtils'; import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; // For validating phone numbers without country codes @@ -283,13 +283,7 @@ module.exports = React.createClass({ this.props.onRegisterClick(); }, - async onServerDetailsNextPhaseClick(ev) { - ev.stopPropagation(); - // TODO: TravisR - Capture the user's input somehow else - if (this._serverConfigRef) { - // Just to make sure the user's input gets captured - await this._serverConfigRef.validateServer(); - } + async onServerDetailsNextPhaseClick() { this.setState({ phase: PHASE_LOGIN, }); @@ -421,7 +415,6 @@ module.exports = React.createClass({ renderServerComponent() { const ServerConfig = sdk.getComponent("auth.ServerConfig"); - const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); if (SdkConfig.get()['disable_custom_urls']) { return null; @@ -431,26 +424,19 @@ module.exports = React.createClass({ return null; } - const serverDetails = this._serverConfigRef = r} + const serverDetailsProps = {}; + if (PHASES_ENABLED) { + serverDetailsProps.onAfterSubmit = this.onServerDetailsNextPhaseClick; + serverDetailsProps.submitText = _t("Next"); + serverDetailsProps.submitClass = "mx_Login_submit"; + } + + return ; - - let nextButton = null; - if (PHASES_ENABLED) { - // TODO: TravisR - Pull out server discovery from ServerConfig to disable the next button? - nextButton = - {_t("Next")} - ; - } - - return
- {serverDetails} - {nextButton} -
; }, renderLoginComponentForStep() { diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index faab8190bd..f516816033 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -286,13 +286,7 @@ module.exports = React.createClass({ }); }, - async onServerDetailsNextPhaseClick(ev) { - ev.stopPropagation(); - // TODO: TravisR - Capture the user's input somehow else - if (this._serverConfigRef) { - // Just to make sure the user's input gets captured - await this._serverConfigRef.validateServer(); - } + async onServerDetailsNextPhaseClick() { this.setState({ phase: PHASE_REGISTRATION, }); @@ -337,7 +331,6 @@ module.exports = React.createClass({ const ServerTypeSelector = sdk.getComponent("auth.ServerTypeSelector"); const ServerConfig = sdk.getComponent("auth.ServerConfig"); const ModularServerConfig = sdk.getComponent("auth.ModularServerConfig"); - const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); if (SdkConfig.get()['disable_custom_urls']) { return null; @@ -354,45 +347,41 @@ module.exports = React.createClass({ ; } + const serverDetailsProps = {}; + if (PHASES_ENABLED) { + serverDetailsProps.onAfterSubmit = this.onServerDetailsNextPhaseClick; + serverDetailsProps.submitText = _t("Next"); + serverDetailsProps.submitClass = "mx_Login_submit"; + } + let serverDetails = null; switch (this.state.serverType) { case ServerType.FREE: break; case ServerType.PREMIUM: serverDetails = this._serverConfigRef = r} serverConfig={this.props.serverConfig} onServerConfigChange={this.props.onServerConfigChange} delayTimeMs={250} + {...serverDetailsProps} />; break; case ServerType.ADVANCED: serverDetails = this._serverConfigRef = r} serverConfig={this.props.serverConfig} onServerConfigChange={this.props.onServerConfigChange} delayTimeMs={250} + {...serverDetailsProps} />; break; } - let nextButton = null; - if (PHASES_ENABLED) { - // TODO: TravisR - Pull out server discovery from ServerConfig to disable the next button? - nextButton = - {_t("Next")} - ; - } - return
{serverDetails} - {nextButton}
; }, diff --git a/src/components/views/auth/ModularServerConfig.js b/src/components/views/auth/ModularServerConfig.js index ea22577dbd..5a3bc23596 100644 --- a/src/components/views/auth/ModularServerConfig.js +++ b/src/components/views/auth/ModularServerConfig.js @@ -41,6 +41,16 @@ export default class ModularServerConfig extends React.PureComponent { serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired, delayTimeMs: PropTypes.number, // time to wait before invoking onChanged + + // Called after the component calls onServerConfigChange + onAfterSubmit: PropTypes.func, + + // Optional text for the submit button. If falsey, no button will be shown. + submitText: PropTypes.string, + + // Optional class for the submit button. Only applies if the submit button + // is to be rendered. + submitClass: PropTypes.string, }; static defaultProps = { @@ -119,6 +129,16 @@ export default class ModularServerConfig extends React.PureComponent { this.setState({ hsUrl }); }; + onSubmit = async (ev) => { + ev.preventDefault(); + ev.stopPropagation(); + await this.validateServer(); + + if (this.props.onAfterSubmit) { + this.props.onAfterSubmit(); + } + }; + _waitThenInvoke(existingTimeoutId, fn) { if (existingTimeoutId) { clearTimeout(existingTimeoutId); @@ -128,6 +148,16 @@ export default class ModularServerConfig extends React.PureComponent { render() { const Field = sdk.getComponent('elements.Field'); + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + + const submitButton = this.props.submitText + ? {this.props.submitText} + : null; return (
@@ -141,15 +171,18 @@ export default class ModularServerConfig extends React.PureComponent { , }, )} -
- -
+
+
+ +
+ {submitButton} +
); } diff --git a/src/components/views/auth/ServerConfig.js b/src/components/views/auth/ServerConfig.js index 096e461efe..3967f49f18 100644 --- a/src/components/views/auth/ServerConfig.js +++ b/src/components/views/auth/ServerConfig.js @@ -30,12 +30,22 @@ import SdkConfig from "../../../SdkConfig"; export default class ServerConfig extends React.PureComponent { static propTypes = { - onServerConfigChange: PropTypes.func, + onServerConfigChange: PropTypes.func.isRequired, // The current configuration that the user is expecting to change. serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired, delayTimeMs: PropTypes.number, // time to wait before invoking onChanged + + // Called after the component calls onServerConfigChange + onAfterSubmit: PropTypes.func, + + // Optional text for the submit button. If falsey, no button will be shown. + submitText: PropTypes.string, + + // Optional class for the submit button. Only applies if the submit button + // is to be rendered. + submitClass: PropTypes.string, }; static defaultProps = { @@ -124,6 +134,16 @@ export default class ServerConfig extends React.PureComponent { this.setState({ isUrl }); }; + onSubmit = async (ev) => { + ev.preventDefault(); + ev.stopPropagation(); + await this.validateServer(); + + if (this.props.onAfterSubmit) { + this.props.onAfterSubmit(); + } + }; + _waitThenInvoke(existingTimeoutId, fn) { if (existingTimeoutId) { clearTimeout(existingTimeoutId); @@ -138,11 +158,21 @@ export default class ServerConfig extends React.PureComponent { render() { const Field = sdk.getComponent('elements.Field'); + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const errorText = this.state.errorText ? {this.state.errorText} : null; + const submitButton = this.props.submitText + ? {this.props.submitText} + : null; + return (

{_t("Other servers")}

@@ -152,24 +182,27 @@ export default class ServerConfig extends React.PureComponent { , })} {errorText} -
- - -
+
+
+ + +
+ {submitButton} +
); } From e4576dac28cad4e5e18b04d4f5a1dbd433dfa59f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 13 May 2019 17:16:40 -0600 Subject: [PATCH 2/6] Render underlines and tooltips on custom server names in auth pages See https://github.com/vector-im/riot-web/issues/9290 --- res/css/_components.scss | 1 + res/css/structures/auth/_Login.scss | 4 ++ res/css/views/elements/_TextWithTooltip.scss | 19 +++++++ .../structures/auth/ForgotPassword.js | 11 +++- src/components/views/auth/PasswordLogin.js | 11 +++- src/components/views/auth/RegistrationForm.js | 11 +++- .../views/elements/TextWithTooltip.js | 56 +++++++++++++++++++ src/components/views/elements/Tooltip.js | 4 ++ 8 files changed, 111 insertions(+), 6 deletions(-) create mode 100644 res/css/views/elements/_TextWithTooltip.scss create mode 100644 src/components/views/elements/TextWithTooltip.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 6e681894e3..ff22ad9eab 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -97,6 +97,7 @@ @import "./views/elements/_RoleButton.scss"; @import "./views/elements/_Spinner.scss"; @import "./views/elements/_SyntaxHighlight.scss"; +@import "./views/elements/_TextWithTooltip.scss"; @import "./views/elements/_ToggleSwitch.scss"; @import "./views/elements/_ToolTipButton.scss"; @import "./views/elements/_Tooltip.scss"; diff --git a/res/css/structures/auth/_Login.scss b/res/css/structures/auth/_Login.scss index 2cf6276557..4eff5c33e4 100644 --- a/res/css/structures/auth/_Login.scss +++ b/res/css/structures/auth/_Login.scss @@ -79,3 +79,7 @@ limitations under the License. .mx_Login_type_dropdown { min-width: 200px; } + +.mx_Login_underlinedServerName { + border-bottom: 1px dashed $accent-color; +} diff --git a/res/css/views/elements/_TextWithTooltip.scss b/res/css/views/elements/_TextWithTooltip.scss new file mode 100644 index 0000000000..5b34e7a3be --- /dev/null +++ b/res/css/views/elements/_TextWithTooltip.scss @@ -0,0 +1,19 @@ +/* +Copyright 2019 New Vector Ltd. + +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_TextWithTooltip_tooltip { + display: none; +} \ No newline at end of file diff --git a/src/components/structures/auth/ForgotPassword.js b/src/components/structures/auth/ForgotPassword.js index 5316235fe0..91ed1aa3ae 100644 --- a/src/components/structures/auth/ForgotPassword.js +++ b/src/components/structures/auth/ForgotPassword.js @@ -194,9 +194,16 @@ module.exports = React.createClass({ serverName: this.props.serverConfig.hsName, }); if (this.props.serverConfig.hsNameIsDifferent) { - // TODO: TravisR - Use tooltip to underline + const TextWithTooltip = sdk.getComponent("elements.TextWithTooltip"); + yourMatrixAccountText = _t('Your Matrix account on ', {}, { - 'underlinedServerName': () => {this.props.serverConfig.hsName}, + 'underlinedServerName': () => { + return ; + }, }); } diff --git a/src/components/views/auth/PasswordLogin.js b/src/components/views/auth/PasswordLogin.js index f5b2aec210..80716b766c 100644 --- a/src/components/views/auth/PasswordLogin.js +++ b/src/components/views/auth/PasswordLogin.js @@ -279,9 +279,16 @@ export default class PasswordLogin extends React.Component { serverName: this.props.serverConfig.hsName, }); if (this.props.serverConfig.hsNameIsDifferent) { - // TODO: TravisR - Use tooltip to underline + const TextWithTooltip = sdk.getComponent("elements.TextWithTooltip"); + signInToText = _t('Sign in to your Matrix account on ', {}, { - 'underlinedServerName': () => {this.props.serverConfig.hsName}, + 'underlinedServerName': () => { + return ; + }, }); } diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js index 0eecc6b826..be0142e6c6 100644 --- a/src/components/views/auth/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -516,9 +516,16 @@ module.exports = React.createClass({ serverName: this.props.serverConfig.hsName, }); if (this.props.serverConfig.hsNameIsDifferent) { - // TODO: TravisR - Use tooltip to underline + const TextWithTooltip = sdk.getComponent("elements.TextWithTooltip"); + yourMatrixAccountText = _t('Create your Matrix account on ', {}, { - 'underlinedServerName': () => {this.props.serverConfig.hsName}, + 'underlinedServerName': () => { + return ; + }, }); } diff --git a/src/components/views/elements/TextWithTooltip.js b/src/components/views/elements/TextWithTooltip.js new file mode 100644 index 0000000000..757bcc9891 --- /dev/null +++ b/src/components/views/elements/TextWithTooltip.js @@ -0,0 +1,56 @@ +/* + Copyright 2019 New Vector Ltd. + + 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 React from 'react'; +import PropTypes from 'prop-types'; +import sdk from '../../../index'; + +export default class TextWithTooltip extends React.Component { + static propTypes = { + class: PropTypes.string, + tooltip: PropTypes.string.isRequired, + }; + + constructor() { + super(); + + this.state = { + hover: false, + }; + } + + onMouseOver = () => { + this.setState({hover: true}); + }; + + onMouseOut = () => { + this.setState({hover: false}); + }; + + render() { + const Tooltip = sdk.getComponent("elements.Tooltip"); + + return ( + + {this.props.children} + + + ); + } +} \ No newline at end of file diff --git a/src/components/views/elements/Tooltip.js b/src/components/views/elements/Tooltip.js index 1cc82978ed..1d6b54f413 100644 --- a/src/components/views/elements/Tooltip.js +++ b/src/components/views/elements/Tooltip.js @@ -79,6 +79,10 @@ module.exports = React.createClass({ let offset = 0; if (parentBox.height > MIN_TOOLTIP_HEIGHT) { offset = Math.floor((parentBox.height - MIN_TOOLTIP_HEIGHT) / 2); + } else { + // The tooltip is larger than the parent height: figure out what offset + // we need so that we're still centered. + offset = Math.floor(parentBox.height - MIN_TOOLTIP_HEIGHT); } style.top = (parentBox.top - 2) + window.pageYOffset + offset; style.left = 6 + parentBox.right + window.pageXOffset; From 25e3f7888e5453cb25415e2c27ecf09f3c0c4b04 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 13 May 2019 18:31:43 -0600 Subject: [PATCH 3/6] newline for the linter --- src/components/views/elements/TextWithTooltip.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/TextWithTooltip.js b/src/components/views/elements/TextWithTooltip.js index 757bcc9891..61c3a2125a 100644 --- a/src/components/views/elements/TextWithTooltip.js +++ b/src/components/views/elements/TextWithTooltip.js @@ -53,4 +53,4 @@ export default class TextWithTooltip extends React.Component { ); } -} \ No newline at end of file +} From bb163576360e0defc3265f12fc08c5949f97ee69 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 14 May 2019 13:06:56 -0600 Subject: [PATCH 4/6] Flag all generated configs as non-default by default The app is expected to flag a particular config themselves as default. This is primarily intended so that other parts of the app can determine what to do based on whether or not the config is a default config. See https://github.com/vector-im/riot-web/issues/9290 --- src/utils/AutoDiscoveryUtils.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils/AutoDiscoveryUtils.js b/src/utils/AutoDiscoveryUtils.js index 318c706136..0850039344 100644 --- a/src/utils/AutoDiscoveryUtils.js +++ b/src/utils/AutoDiscoveryUtils.js @@ -26,6 +26,8 @@ export class ValidatedServerConfig { isUrl: string; identityEnabled: boolean; + + isDefault: boolean; } export default class AutoDiscoveryUtils { @@ -99,6 +101,7 @@ export default class AutoDiscoveryUtils { hsNameIsDifferent: url.hostname !== preferredHomeserverName, isUrl: preferredIdentityUrl, identityEnabled: !SdkConfig.get()['disable_identity_server'], + isDefault: false, }); } } From 34719b9a2e8f4734d9a9367823142ffdb7a32655 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 14 May 2019 13:10:02 -0600 Subject: [PATCH 5/6] Only expose the fallback_hs_url if the homeserver is the default HS See https://github.com/vector-im/riot-web/issues/9290 --- src/components/structures/MatrixChat.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 4198980a17..38f597f673 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -203,9 +203,12 @@ export default React.createClass({ }; }, - // TODO: TravisR - Remove this or put it somewhere else getFallbackHsUrl: function() { - return this.props.config.fallback_hs_url; + if (this.props.serverConfig.isDefault) { + return this.props.config.fallback_hs_url; + } else { + return null; + } }, getServerProperties() { From 6e3b06f3646b83d4e271e51ca894c718474d8c27 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 15 May 2019 13:55:50 -0600 Subject: [PATCH 6/6] Human de-linting --- src/components/structures/auth/ForgotPassword.js | 3 ++- src/components/views/auth/PasswordLogin.js | 5 +++-- src/components/views/auth/RegistrationForm.js | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/structures/auth/ForgotPassword.js b/src/components/structures/auth/ForgotPassword.js index 91ed1aa3ae..42ca23256c 100644 --- a/src/components/structures/auth/ForgotPassword.js +++ b/src/components/structures/auth/ForgotPassword.js @@ -200,7 +200,8 @@ module.exports = React.createClass({ 'underlinedServerName': () => { return ; }, diff --git a/src/components/views/auth/PasswordLogin.js b/src/components/views/auth/PasswordLogin.js index 80716b766c..825bffdc84 100644 --- a/src/components/views/auth/PasswordLogin.js +++ b/src/components/views/auth/PasswordLogin.js @@ -284,8 +284,9 @@ export default class PasswordLogin extends React.Component { signInToText = _t('Sign in to your Matrix account on ', {}, { 'underlinedServerName': () => { return ; }, diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js index be0142e6c6..b1af6ea42c 100644 --- a/src/components/views/auth/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -522,7 +522,8 @@ module.exports = React.createClass({ 'underlinedServerName': () => { return ; },