From 3a75eb12265ac83b9d88549b1b7f593905e5912d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 10 May 2021 14:39:10 +0100 Subject: [PATCH 1/4] Fix handling of enter/return in space creation menu --- .../views/spaces/SpaceBasicSettings.tsx | 50 ++++++++------- .../views/spaces/SpaceCreateMenu.tsx | 61 ++++++++++++++++--- src/i18n/strings/en_EN.json | 1 + 3 files changed, 83 insertions(+), 29 deletions(-) diff --git a/src/components/views/spaces/SpaceBasicSettings.tsx b/src/components/views/spaces/SpaceBasicSettings.tsx index bc378ab956..ec40f7bed8 100644 --- a/src/components/views/spaces/SpaceBasicSettings.tsx +++ b/src/components/views/spaces/SpaceBasicSettings.tsx @@ -32,17 +32,11 @@ interface IProps { setTopic(topic: string): void; } -const SpaceBasicSettings = ({ +export const SpaceAvatar = ({ avatarUrl, avatarDisabled = false, setAvatar, - name = "", - nameDisabled = false, - setName, - topic = "", - topicDisabled = false, - setTopic, -}: IProps) => { +}: Pick) => { const avatarUploadRef = useRef(); const [avatar, setAvatarDataUrl] = useState(avatarUrl); // avatar data url cache @@ -81,20 +75,34 @@ const SpaceBasicSettings = ({ } } + return
+ { avatarSection } + { + if (!e.target.files?.length) return; + const file = e.target.files[0]; + setAvatar(file); + const reader = new FileReader(); + reader.onload = (ev) => { + setAvatarDataUrl(ev.target.result as string); + }; + reader.readAsDataURL(file); + }} accept="image/*" /> +
; +}; + +const SpaceBasicSettings = ({ + avatarUrl, + avatarDisabled = false, + setAvatar, + name = "", + nameDisabled = false, + setName, + topic = "", + topicDisabled = false, + setTopic, +}: IProps) => { return
-
- { avatarSection } - { - if (!e.target.files?.length) return; - const file = e.target.files[0]; - setAvatar(file); - const reader = new FileReader(); - reader.onload = (ev) => { - setAvatarDataUrl(ev.target.result as string); - }; - reader.readAsDataURL(file); - }} accept="image/*" /> -
+ { return ( @@ -41,17 +43,39 @@ enum Visibility { Private, } +const spaceNameValidator = withValidation({ + rules: [ + { + key: "required", + test: async ({ value }) => !!value, + invalid: () => _t("Please enter a name for the space"), + }, + ], +}); + const SpaceCreateMenu = ({ onFinished }) => { const cli = useContext(MatrixClientContext); const [visibility, setVisibility] = useState(null); - const [name, setName] = useState(""); - const [avatar, setAvatar] = useState(null); - const [topic, setTopic] = useState(""); const [busy, setBusy] = useState(false); - const onSpaceCreateClick = async () => { + const [name, setName] = useState(""); + const spaceNameField = useRef(); + const [avatar, setAvatar] = useState(null); + const [topic, setTopic] = useState(""); + + const onSpaceCreateClick = async (e) => { + e.preventDefault(); if (busy) return; + setBusy(true); + // require & validate the space name field + if (!await spaceNameField.current.validate({ allowEmpty: false })) { + spaceNameField.current.focus(); + spaceNameField.current.validate({ allowEmpty: false, focused: true }); + setBusy(false); + return; + } + const initialState: IStateEvent[] = [ { type: EventType.RoomHistoryVisibility, @@ -146,9 +170,30 @@ const SpaceCreateMenu = ({ onFinished }) => { }

- +
+ - + setName(ev.target.value)} + ref={spaceNameField} + onValidate={spaceNameValidator} + /> + + setTopic(ev.target.value)} + rows={3} + /> + + + { busy ? _t("Creating...") : _t("Create") } ; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index dcad970300..26275c4325 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -996,6 +996,7 @@ "Upload": "Upload", "Name": "Name", "Description": "Description", + "Please enter a name for the space": "Please enter a name for the space", "Create a space": "Create a space", "Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.", "Public": "Public", From c96bb374ea6f8a3778f195d6a2e47d6b7eb62993 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 10 May 2021 16:06:23 +0100 Subject: [PATCH 2/4] Allow user to progress through space setup wizard using Enter/Return --- src/components/structures/SpaceRoomView.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index 5db54815b7..6566ccf939 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -60,6 +60,7 @@ import IconizedContextMenu, { IconizedContextMenuOptionList, } from "../views/context_menus/IconizedContextMenu"; import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton"; +import {Key} from "../../Keyboard"; interface IProps { space: Room; @@ -367,6 +368,7 @@ const SpaceLanding = ({ space }) => { const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => { const [busy, setBusy] = useState(false); const [error, setError] = useState(""); + let onClick = onFinished; const numFields = 3; const placeholders = [_t("General"), _t("Random"), _t("Support")]; // TODO vary default prefills for "Just Me" spaces @@ -382,10 +384,17 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => { value={roomNames[i]} onChange={ev => setRoomName(i, ev.target.value)} autoFocus={i === 2} + onKeyDown={ev => { + if (ev.key === Key.ENTER) { + ev.preventDefault(); + onClick(); + } + }} />; }); const onNextClick = async () => { + if (busy) return; setError(""); setBusy(true); try { @@ -410,7 +419,6 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => { setBusy(false); }; - let onClick = onFinished; let buttonLabel = _t("Skip for now"); if (roomNames.some(name => name.trim())) { onClick = onNextClick; @@ -556,6 +564,7 @@ const validateEmailRules = withValidation({ const SpaceSetupPrivateInvite = ({ space, onFinished }) => { const [busy, setBusy] = useState(false); const [error, setError] = useState(""); + let onClick = onFinished; const numFields = 3; const fieldRefs: RefObject[] = [useRef(), useRef(), useRef()]; const [emailAddresses, setEmailAddress] = useStateArray(numFields, ""); @@ -572,10 +581,17 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => { ref={fieldRefs[i]} onValidate={validateEmailRules} autoFocus={i === 0} + onKeyDown={ev => { + if (ev.key === Key.ENTER) { + ev.preventDefault(); + onClick(); + } + }} />; }); const onNextClick = async () => { + if (busy) return; setError(""); for (let i = 0; i < fieldRefs.length; i++) { const fieldRef = fieldRefs[i]; @@ -609,7 +625,6 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => { setBusy(false); }; - let onClick = onFinished; let buttonLabel = _t("Skip for now"); if (emailAddresses.some(name => name.trim())) { onClick = onNextClick; From de04d82395f39fdf0352040d6ad257ec48b9bb83 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 11 May 2021 10:01:40 +0100 Subject: [PATCH 3/4] Switch to using forms for implicit submission --- res/css/structures/_SpaceRoomView.scss | 4 ++ src/components/structures/SpaceRoomView.tsx | 56 ++++++++++++--------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/res/css/structures/_SpaceRoomView.scss b/res/css/structures/_SpaceRoomView.scss index ff51e28b7b..6b71f42671 100644 --- a/res/css/structures/_SpaceRoomView.scss +++ b/res/css/structures/_SpaceRoomView.scss @@ -103,6 +103,10 @@ $SpaceRoomViewInnerWidth: 428px; padding: 8px 22px; margin-left: 16px; } + + input.mx_AccessibleButton { + border: none; // override default styles + } } .mx_Field { diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index 6566ccf939..e3de90834e 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -368,7 +368,6 @@ const SpaceLanding = ({ space }) => { const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => { const [busy, setBusy] = useState(false); const [error, setError] = useState(""); - let onClick = onFinished; const numFields = 3; const placeholders = [_t("General"), _t("Random"), _t("Support")]; // TODO vary default prefills for "Just Me" spaces @@ -384,16 +383,11 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => { value={roomNames[i]} onChange={ev => setRoomName(i, ev.target.value)} autoFocus={i === 2} - onKeyDown={ev => { - if (ev.key === Key.ENTER) { - ev.preventDefault(); - onClick(); - } - }} />; }); - const onNextClick = async () => { + const onNextClick = async (ev) => { + ev.preventDefault(); if (busy) return; setError(""); setBusy(true); @@ -419,6 +413,10 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => { setBusy(false); }; + let onClick = (ev) => { + ev.preventDefault(); + onFinished(); + }; let buttonLabel = _t("Skip for now"); if (roomNames.some(name => name.trim())) { onClick = onNextClick; @@ -430,16 +428,20 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
{ description }
{ error &&
{ error }
} - { fields } +
+ { fields } +
- { buttonLabel } - + element="input" + type="submit" + form="mx_SpaceSetupFirstRooms" + value={buttonLabel} + />
; }; @@ -564,7 +566,6 @@ const validateEmailRules = withValidation({ const SpaceSetupPrivateInvite = ({ space, onFinished }) => { const [busy, setBusy] = useState(false); const [error, setError] = useState(""); - let onClick = onFinished; const numFields = 3; const fieldRefs: RefObject[] = [useRef(), useRef(), useRef()]; const [emailAddresses, setEmailAddress] = useStateArray(numFields, ""); @@ -581,16 +582,11 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => { ref={fieldRefs[i]} onValidate={validateEmailRules} autoFocus={i === 0} - onKeyDown={ev => { - if (ev.key === Key.ENTER) { - ev.preventDefault(); - onClick(); - } - }} />; }); - const onNextClick = async () => { + const onNextClick = async (ev) => { + ev.preventDefault(); if (busy) return; setError(""); for (let i = 0; i < fieldRefs.length; i++) { @@ -625,6 +621,10 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => { setBusy(false); }; + let onClick = (ev) => { + ev.preventDefault(); + onFinished(); + }; let buttonLabel = _t("Skip for now"); if (emailAddresses.some(name => name.trim())) { onClick = onNextClick; @@ -638,7 +638,9 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => { { error &&
{ error }
} - { fields } +
+ { fields } +
{
- - { buttonLabel } - +
; }; From 23001cb10d37b797971724025a353d57ad4c2cb5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 11 May 2021 10:11:51 +0100 Subject: [PATCH 4/4] remove unused import --- src/components/structures/SpaceRoomView.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index e3de90834e..eb9b8665d8 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -60,7 +60,6 @@ import IconizedContextMenu, { IconizedContextMenuOptionList, } from "../views/context_menus/IconizedContextMenu"; import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton"; -import {Key} from "../../Keyboard"; interface IProps { space: Room;