From c5b59ea12223a247e8b7d7fde7befa5789ae64c3 Mon Sep 17 00:00:00 2001 From: Ginger Date: Mon, 15 Sep 2025 14:46:08 -0400 Subject: [PATCH] Add a setting for user pronouns --- src/app/features/settings/account/Profile.tsx | 144 +++++++++++++++++- 1 file changed, 138 insertions(+), 6 deletions(-) diff --git a/src/app/features/settings/account/Profile.tsx b/src/app/features/settings/account/Profile.tsx index 2a380505..2a542964 100644 --- a/src/app/features/settings/account/Profile.tsx +++ b/src/app/features/settings/account/Profile.tsx @@ -1,5 +1,8 @@ import React, { ChangeEventHandler, + FormEventHandler, + KeyboardEventHandler, + MouseEventHandler, ReactNode, useCallback, useEffect, @@ -26,9 +29,14 @@ import { Dialog, Header, MenuItem, + Chip, + PopOut, + RectCords, + Menu, } from 'folds'; import FocusTrap from 'focus-trap-react'; import { UserEvent } from 'matrix-js-sdk'; +import { isKeyHotkey } from 'is-hotkey'; import { SequenceCard } from '../../../components/sequence-card'; import { SettingTile } from '../../../components/setting-tile'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; @@ -203,7 +211,6 @@ function ProfileTextField(); + const [pendingPronoun, setPendingPronoun] = useState(''); + + const handleRemovePronoun = (index: number) => { + const newPronouns = [...(value ?? [])]; + newPronouns.splice(index, 1); + if (newPronouns.length > 0) { + setValue(newPronouns); + } else { + setValue(undefined); + } + }; + + const handleSubmit: FormEventHandler = (evt) => { + evt.preventDefault(); + setMenuCords(undefined); + if (pendingPronoun.length > 0) { + setValue([...(value ?? []), { language: 'en', summary: pendingPronoun }]); + } + }; + + const handleKeyDown: KeyboardEventHandler = (evt) => { + if (isKeyHotkey('escape', evt)) { + evt.stopPropagation(); + setMenuCords(undefined); + } + }; + + const handleOpenMenu: MouseEventHandler = (evt) => { + setPendingPronoun(''); + setMenuCords(evt.currentTarget.getBoundingClientRect()); + }; + + return ( + + Pronouns + + } + > + + {value?.map(({ summary }, index) => ( + } + onClick={() => handleRemovePronoun(index)} + disabled={disabled} + > + + {summary} + + + ))} + } + onClick={handleOpenMenu} + > + Add + + + setMenuCords(undefined), + clickOutsideDeactivates: true, + isKeyForward: (evt: KeyboardEvent) => + evt.key === 'ArrowDown' || evt.key === 'ArrowRight', + isKeyBackward: (evt: KeyboardEvent) => + evt.key === 'ArrowUp' || evt.key === 'ArrowLeft', + escapeDeactivates: stopPropagation, + }} + > + + + setPendingPronoun(evt.currentTarget.value)} + onKeyDown={handleKeyDown} + /> + + + + + } + /> + + ); +} + function ProfileTimezone() { const { busy, value, setValue } = useProfileField('us.cloke.msc4175.tz'); const disabled = !useProfileFieldAllowed('us.cloke.msc4175.tz') || busy; @@ -392,7 +523,7 @@ export function Profile() { const extendedProfile = extendedProfileState.status === AsyncStatus.Success ? extendedProfileState.data : undefined; - const [fieldDefaults, setFieldDefaults] = useState({}) + const [fieldDefaults, setFieldDefaults] = useState({}); useLayoutEffect(() => { if (extendedProfile !== undefined) { setFieldDefaults(extendedProfile); @@ -471,14 +602,15 @@ export function Profile() { variant="SurfaceVariant" direction="Column" gap="300" - radii='0' + radii="0" outlined - style={{ borderLeftWidth: "0", borderRightWidth: "0", borderBottomWidth: "0" }} + style={{ borderLeftWidth: '0', borderRightWidth: '0', borderBottomWidth: '0' }} > + - + {saving && }