mirror of
				https://github.com/cinnyapp/cinny.git
				synced 2025-11-04 14:30:29 +03:00 
			
		
		
		
	Add a setting for user pronouns
This commit is contained in:
		
							parent
							
								
									c7f6e33a2b
								
							
						
					
					
						commit
						c5b59ea122
					
				
					 1 changed files with 138 additions and 6 deletions
				
			
		| 
						 | 
				
			
			@ -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<K extends keyof FilterByValues<ExtendedProfile, string
 | 
			
		|||
              onChange={handleChange}
 | 
			
		||||
              variant="Secondary"
 | 
			
		||||
              radii="300"
 | 
			
		||||
              style={{ paddingRight: config.space.S200 }}
 | 
			
		||||
              readOnly={disabled}
 | 
			
		||||
              after={
 | 
			
		||||
                hasChanges &&
 | 
			
		||||
| 
						 | 
				
			
			@ -227,6 +234,130 @@ function ProfileTextField<K extends keyof FilterByValues<ExtendedProfile, string
 | 
			
		|||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ProfilePronouns() {
 | 
			
		||||
  const { busy, value, setValue } = useProfileField('io.fsky.nyx.pronouns');
 | 
			
		||||
  const disabled = !useProfileFieldAllowed('io.fsky.nyx.pronouns') || busy;
 | 
			
		||||
 | 
			
		||||
  const [menuCords, setMenuCords] = useState<RectCords>();
 | 
			
		||||
  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<HTMLFormElement> = (evt) => {
 | 
			
		||||
    evt.preventDefault();
 | 
			
		||||
    setMenuCords(undefined);
 | 
			
		||||
    if (pendingPronoun.length > 0) {
 | 
			
		||||
      setValue([...(value ?? []), { language: 'en', summary: pendingPronoun }]);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleKeyDown: KeyboardEventHandler<HTMLInputElement> = (evt) => {
 | 
			
		||||
    if (isKeyHotkey('escape', evt)) {
 | 
			
		||||
      evt.stopPropagation();
 | 
			
		||||
      setMenuCords(undefined);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleOpenMenu: MouseEventHandler<HTMLSpanElement> = (evt) => {
 | 
			
		||||
    setPendingPronoun('');
 | 
			
		||||
    setMenuCords(evt.currentTarget.getBoundingClientRect());
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <SettingTile
 | 
			
		||||
      title={
 | 
			
		||||
        <Text as="span" size="L400">
 | 
			
		||||
          Pronouns
 | 
			
		||||
        </Text>
 | 
			
		||||
      }
 | 
			
		||||
    >
 | 
			
		||||
      <Box alignItems="Center" gap="200" wrap="Wrap">
 | 
			
		||||
        {value?.map(({ summary }, index) => (
 | 
			
		||||
          <Chip
 | 
			
		||||
            // eslint-disable-next-line react/no-array-index-key
 | 
			
		||||
            key={index}
 | 
			
		||||
            variant="Secondary"
 | 
			
		||||
            radii="Pill"
 | 
			
		||||
            after={<Icon src={Icons.Cross} size="100" />}
 | 
			
		||||
            onClick={() => handleRemovePronoun(index)}
 | 
			
		||||
            disabled={disabled}
 | 
			
		||||
          >
 | 
			
		||||
            <Text size="T200" truncate>
 | 
			
		||||
              {summary}
 | 
			
		||||
            </Text>
 | 
			
		||||
          </Chip>
 | 
			
		||||
        ))}
 | 
			
		||||
        <Chip
 | 
			
		||||
          variant="Secondary"
 | 
			
		||||
          radii="Pill"
 | 
			
		||||
          disabled={disabled}
 | 
			
		||||
          after={<Icon src={menuCords ? Icons.ChevronRight : Icons.Plus} size="100" />}
 | 
			
		||||
          onClick={handleOpenMenu}
 | 
			
		||||
        >
 | 
			
		||||
          <Text size="T200">Add</Text>
 | 
			
		||||
        </Chip>
 | 
			
		||||
      </Box>
 | 
			
		||||
      <PopOut
 | 
			
		||||
        anchor={menuCords}
 | 
			
		||||
        offset={5}
 | 
			
		||||
        position="Right"
 | 
			
		||||
        align="Center"
 | 
			
		||||
        content={
 | 
			
		||||
          <FocusTrap
 | 
			
		||||
            focusTrapOptions={{
 | 
			
		||||
              onDeactivate: () => setMenuCords(undefined),
 | 
			
		||||
              clickOutsideDeactivates: true,
 | 
			
		||||
              isKeyForward: (evt: KeyboardEvent) =>
 | 
			
		||||
                evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
 | 
			
		||||
              isKeyBackward: (evt: KeyboardEvent) =>
 | 
			
		||||
                evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
 | 
			
		||||
              escapeDeactivates: stopPropagation,
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <Menu
 | 
			
		||||
              variant="SurfaceVariant"
 | 
			
		||||
              style={{
 | 
			
		||||
                padding: config.space.S200,
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              <Box as="form" onSubmit={handleSubmit} direction="Row" gap="200">
 | 
			
		||||
                <Input
 | 
			
		||||
                  variant="Secondary"
 | 
			
		||||
                  placeholder="they/them"
 | 
			
		||||
                  inputSize={10}
 | 
			
		||||
                  radii="300"
 | 
			
		||||
                  size="300"
 | 
			
		||||
                  outlined
 | 
			
		||||
                  value={pendingPronoun}
 | 
			
		||||
                  onChange={(evt) => setPendingPronoun(evt.currentTarget.value)}
 | 
			
		||||
                  onKeyDown={handleKeyDown}
 | 
			
		||||
                />
 | 
			
		||||
                <Button
 | 
			
		||||
                  type="submit"
 | 
			
		||||
                  size="300"
 | 
			
		||||
                  variant="Success"
 | 
			
		||||
                  radii="300"
 | 
			
		||||
                  before={<Icon size="100" src={Icons.Plus} />}
 | 
			
		||||
                >
 | 
			
		||||
                  <Text size="B300">Add</Text>
 | 
			
		||||
                </Button>
 | 
			
		||||
              </Box>
 | 
			
		||||
            </Menu>
 | 
			
		||||
          </FocusTrap>
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
    </SettingTile>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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<ExtendedProfile>({})
 | 
			
		||||
  const [fieldDefaults, setFieldDefaults] = useState<ExtendedProfile>({});
 | 
			
		||||
  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' }}
 | 
			
		||||
                >
 | 
			
		||||
                  <ProfileAvatar />
 | 
			
		||||
                  <ProfileTextField field="displayname" label="Display Name" />
 | 
			
		||||
                  <ProfilePronouns />
 | 
			
		||||
                  <ProfileTimezone />
 | 
			
		||||
                  <Box gap="300" alignItems='Center'>
 | 
			
		||||
                  <Box gap="300" alignItems="Center">
 | 
			
		||||
                    <Button
 | 
			
		||||
                      type="submit"
 | 
			
		||||
                      size="300"
 | 
			
		||||
| 
						 | 
				
			
			@ -500,7 +632,7 @@ export function Profile() {
 | 
			
		|||
                      radii="300"
 | 
			
		||||
                      onClick={reset}
 | 
			
		||||
                      disabled={!hasChanges || busy}
 | 
			
		||||
                      >
 | 
			
		||||
                    >
 | 
			
		||||
                      <Text size="B300">Cancel</Text>
 | 
			
		||||
                    </Button>
 | 
			
		||||
                    {saving && <Spinner size="300" />}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue