Fix handling of enter/return in space creation menu

This commit is contained in:
Michael Telatynski 2021-05-10 14:39:10 +01:00
parent 45acf70b00
commit 3a75eb1226
3 changed files with 83 additions and 29 deletions

View file

@ -32,17 +32,11 @@ interface IProps {
setTopic(topic: string): void; setTopic(topic: string): void;
} }
const SpaceBasicSettings = ({ export const SpaceAvatar = ({
avatarUrl, avatarUrl,
avatarDisabled = false, avatarDisabled = false,
setAvatar, setAvatar,
name = "", }: Pick<IProps, "avatarUrl" | "avatarDisabled" | "setAvatar">) => {
nameDisabled = false,
setName,
topic = "",
topicDisabled = false,
setTopic,
}: IProps) => {
const avatarUploadRef = useRef<HTMLInputElement>(); const avatarUploadRef = useRef<HTMLInputElement>();
const [avatar, setAvatarDataUrl] = useState(avatarUrl); // avatar data url cache const [avatar, setAvatarDataUrl] = useState(avatarUrl); // avatar data url cache
@ -81,20 +75,34 @@ const SpaceBasicSettings = ({
} }
} }
return <div className="mx_SpaceBasicSettings_avatarContainer">
{ avatarSection }
<input type="file" ref={avatarUploadRef} onChange={(e) => {
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/*" />
</div>;
};
const SpaceBasicSettings = ({
avatarUrl,
avatarDisabled = false,
setAvatar,
name = "",
nameDisabled = false,
setName,
topic = "",
topicDisabled = false,
setTopic,
}: IProps) => {
return <div className="mx_SpaceBasicSettings"> return <div className="mx_SpaceBasicSettings">
<div className="mx_SpaceBasicSettings_avatarContainer"> <SpaceAvatar avatarUrl={avatarUrl} avatarDisabled={avatarDisabled} setAvatar={setAvatar} />
{ avatarSection }
<input type="file" ref={avatarUploadRef} onChange={(e) => {
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/*" />
</div>
<Field <Field
name="spaceName" name="spaceName"

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, {useContext, useState} from "react"; import React, {useContext, useRef, useState} from "react";
import classNames from "classnames"; import classNames from "classnames";
import {EventType, RoomType, RoomCreateTypeField} from "matrix-js-sdk/src/@types/event"; import {EventType, RoomType, RoomCreateTypeField} from "matrix-js-sdk/src/@types/event";
@ -23,9 +23,11 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import {ChevronFace, ContextMenu} from "../../structures/ContextMenu"; import {ChevronFace, ContextMenu} from "../../structures/ContextMenu";
import createRoom, {IStateEvent, Preset} from "../../../createRoom"; import createRoom, {IStateEvent, Preset} from "../../../createRoom";
import MatrixClientContext from "../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../contexts/MatrixClientContext";
import SpaceBasicSettings from "./SpaceBasicSettings"; import {SpaceAvatar} from "./SpaceBasicSettings";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton from "../elements/AccessibleButton";
import FocusLock from "react-focus-lock"; import FocusLock from "react-focus-lock";
import Field from "../elements/Field";
import withValidation from "../elements/Validation";
const SpaceCreateMenuType = ({ title, description, className, onClick }) => { const SpaceCreateMenuType = ({ title, description, className, onClick }) => {
return ( return (
@ -41,17 +43,39 @@ enum Visibility {
Private, Private,
} }
const spaceNameValidator = withValidation({
rules: [
{
key: "required",
test: async ({ value }) => !!value,
invalid: () => _t("Please enter a name for the space"),
},
],
});
const SpaceCreateMenu = ({ onFinished }) => { const SpaceCreateMenu = ({ onFinished }) => {
const cli = useContext(MatrixClientContext); const cli = useContext(MatrixClientContext);
const [visibility, setVisibility] = useState<Visibility>(null); const [visibility, setVisibility] = useState<Visibility>(null);
const [name, setName] = useState("");
const [avatar, setAvatar] = useState<File>(null);
const [topic, setTopic] = useState<string>("");
const [busy, setBusy] = useState<boolean>(false); const [busy, setBusy] = useState<boolean>(false);
const onSpaceCreateClick = async () => { const [name, setName] = useState("");
const spaceNameField = useRef<Field>();
const [avatar, setAvatar] = useState<File>(null);
const [topic, setTopic] = useState<string>("");
const onSpaceCreateClick = async (e) => {
e.preventDefault();
if (busy) return; if (busy) return;
setBusy(true); 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[] = [ const initialState: IStateEvent[] = [
{ {
type: EventType.RoomHistoryVisibility, type: EventType.RoomHistoryVisibility,
@ -146,9 +170,30 @@ const SpaceCreateMenu = ({ onFinished }) => {
} }
</p> </p>
<SpaceBasicSettings setAvatar={setAvatar} name={name} setName={setName} topic={topic} setTopic={setTopic} /> <form className="mx_SpaceBasicSettings" onSubmit={onSpaceCreateClick}>
<SpaceAvatar setAvatar={setAvatar} />
<AccessibleButton kind="primary" onClick={onSpaceCreateClick} disabled={!name || busy}> <Field
name="spaceName"
label={_t("Name")}
autoFocus={true}
value={name}
onChange={ev => setName(ev.target.value)}
ref={spaceNameField}
onValidate={spaceNameValidator}
/>
<Field
name="spaceTopic"
element="textarea"
label={_t("Description")}
value={topic}
onChange={ev => setTopic(ev.target.value)}
rows={3}
/>
</form>
<AccessibleButton kind="primary" onClick={onSpaceCreateClick} disabled={busy}>
{ busy ? _t("Creating...") : _t("Create") } { busy ? _t("Creating...") : _t("Create") }
</AccessibleButton> </AccessibleButton>
</React.Fragment>; </React.Fragment>;

View file

@ -996,6 +996,7 @@
"Upload": "Upload", "Upload": "Upload",
"Name": "Name", "Name": "Name",
"Description": "Description", "Description": "Description",
"Please enter a name for the space": "Please enter a name for the space",
"Create a space": "Create a 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.", "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", "Public": "Public",