diff --git a/src/components/views/dialogs/TextInputDialog.js b/src/components/views/dialogs/TextInputDialog.js index 4b21e8206e..e56e9f2b87 100644 --- a/src/components/views/dialogs/TextInputDialog.js +++ b/src/components/views/dialogs/TextInputDialog.js @@ -18,6 +18,7 @@ import React, {createRef} from 'react'; import createReactClass from 'create-react-class'; import PropTypes from 'prop-types'; import * as sdk from '../../../index'; +import Field from "../elements/Field"; export default createReactClass({ displayName: 'TextInputDialog', @@ -33,6 +34,7 @@ export default createReactClass({ focus: PropTypes.bool, onFinished: PropTypes.func.isRequired, hasCancel: PropTypes.bool, + validator: PropTypes.func, // result of withValidation }, getDefaultProps: function() { @@ -45,25 +47,57 @@ export default createReactClass({ }; }, + getInitialState: function() { + return { + value: this.props.value, + valid: false, + }; + }, + UNSAFE_componentWillMount: function() { - this._textinput = createRef(); + this._field = createRef(); }, componentDidMount: function() { if (this.props.focus) { // Set the cursor at the end of the text input - this._textinput.current.value = this.props.value; + // this._field.current.value = this.props.value; + this._field.current.focus(); } }, - onOk: function() { - this.props.onFinished(true, this._textinput.current.value); + onOk: async function(ev) { + ev.preventDefault(); + if (this.props.validator) { + await this._field.current.validate({ allowEmpty: false }); + + if (!this._field.current.state.valid) { + this._field.current.focus(); + this._field.current.validate({ allowEmpty: false, focused: true }); + return; + } + } + this.props.onFinished(true, this.state.value); }, onCancel: function() { this.props.onFinished(false); }, + onChange: function(ev) { + this.setState({ + value: ev.target.value, + }); + }, + + onValidate: async function(fieldState) { + const result = await this.props.validator(fieldState); + this.setState({ + valid: result.valid, + }); + return result; + }, + render: function() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); @@ -77,14 +111,17 @@ export default createReactClass({
- + ref={this._field} + type="text" + label={this.props.placeholder} + value={this.state.value} + onChange={this.onChange} + onValidate={this.props.validator ? this.onValidate : undefined} + size="64" + />
diff --git a/src/components/views/directory/NetworkDropdown.js b/src/components/views/directory/NetworkDropdown.js index c4a70c112e..6d1501ca57 100644 --- a/src/components/views/directory/NetworkDropdown.js +++ b/src/components/views/directory/NetworkDropdown.js @@ -35,6 +35,7 @@ import {useSettingValue} from "../../../hooks/useSettings"; import * as sdk from "../../../index"; import Modal from "../../../Modal"; import SettingsStore from "../../../settings/SettingsStore"; +import withValidation from "../elements/Validation"; export const ALL_ROOMS = Symbol("ALL_ROOMS"); @@ -47,6 +48,34 @@ const inPlaceOf = (elementRect) => ({ chevronFace: "none", }); +const validServer = withValidation({ + rules: [ + { + key: "required", + test: async ({ value }) => !!value, + invalid: () => _t("Enter a server address"), + }, { + key: "available", + final: true, + test: async ({ value }) => { + try { + const opts = { + limit: 1, + server: value, + }; + // check if we can successfully load this server's room directory + await MatrixClientPeg.get().publicRooms(opts); + return true; + } catch (e) { + return false; + } + }, + valid: () => _t("Looks good"), + invalid: () => _t("Can't find this server or its room list"), + }, + ], +}); + // This dropdown sources homeservers from three places: // + your currently connected homeserver // + homeservers in config.json["roomDirectory"] @@ -188,6 +217,7 @@ const NetworkDropdown = ({onOptionChange, protocols = {}, selectedServerName, se button: _t("Add"), hasCancel: false, placeholder: _t("Server address"), + validator: validServer, }); const [ok, newServer] = await finished; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 59bc643539..3f20d4ec61 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1437,6 +1437,9 @@ "And %(count)s more...|other": "And %(count)s more...", "ex. @bob:example.com": "ex. @bob:example.com", "Add User": "Add User", + "Enter a server address": "Enter a server address", + "Looks good": "Looks good", + "Can't find this server or its room list": "Can't find this server or its room list", "All rooms": "All rooms", "Your server": "Your server", "Are you sure you want to remove %(serverName)s": "Are you sure you want to remove %(serverName)s",