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",