import { Box, Button, Chip, config, Icon, Icons, Input, Line, Menu, MenuItem, PopOut, RectCords, Scroll, Text, toRem, } from 'folds'; import { isKeyHotkey } from 'is-hotkey'; import FocusTrap from 'focus-trap-react'; import React, { ChangeEventHandler, KeyboardEventHandler, MouseEventHandler, useMemo, useState, } from 'react'; import { getMxIdLocalPart, getMxIdServer, isUserId } from '../../utils/matrix'; import { useDirectUsers } from '../../hooks/useDirectUsers'; import { SettingTile } from '../setting-tile'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { stopPropagation } from '../../utils/keyboard'; import { useAsyncSearch, UseAsyncSearchOptions } from '../../hooks/useAsyncSearch'; import { highlightText, makeHighlightRegex } from '../../plugins/react-custom-html-parser'; export const useAdditionalCreators = (defaultCreators?: string[]) => { const mx = useMatrixClient(); const [additionalCreators, setAdditionalCreators] = useState( () => defaultCreators?.filter((id) => id !== mx.getSafeUserId()) ?? [] ); const addAdditionalCreator = (userId: string) => { if (userId === mx.getSafeUserId()) return; setAdditionalCreators((creators) => { const creatorsSet = new Set(creators); creatorsSet.add(userId); return Array.from(creatorsSet); }); }; const removeAdditionalCreator = (userId: string) => { setAdditionalCreators((creators) => { const creatorsSet = new Set(creators); creatorsSet.delete(userId); return Array.from(creatorsSet); }); }; return { additionalCreators, addAdditionalCreator, removeAdditionalCreator, }; }; const SEARCH_OPTIONS: UseAsyncSearchOptions = { limit: 1000, matchOptions: { contain: true, }, }; const getUserIdString = (userId: string) => getMxIdLocalPart(userId) ?? userId; type AdditionalCreatorInputProps = { additionalCreators: string[]; onSelect: (userId: string) => void; onRemove: (userId: string) => void; disabled?: boolean; }; export function AdditionalCreatorInput({ additionalCreators, onSelect, onRemove, disabled, }: AdditionalCreatorInputProps) { const mx = useMatrixClient(); const [menuCords, setMenuCords] = useState(); const directUsers = useDirectUsers(); const [validUserId, setValidUserId] = useState(); const filteredUsers = useMemo( () => directUsers.filter((userId) => !additionalCreators.includes(userId)), [directUsers, additionalCreators] ); const [result, search, resetSearch] = useAsyncSearch( filteredUsers, getUserIdString, SEARCH_OPTIONS ); const queryHighlighRegex = result?.query ? makeHighlightRegex([result.query]) : undefined; const suggestionUsers = result ? result.items : filteredUsers.sort((a, b) => (a.toLocaleLowerCase() >= b.toLocaleLowerCase() ? 1 : -1)); const handleOpenMenu: MouseEventHandler = (evt) => { setMenuCords(evt.currentTarget.getBoundingClientRect()); }; const handleCloseMenu = () => { setMenuCords(undefined); setValidUserId(undefined); resetSearch(); }; const handleCreatorChange: ChangeEventHandler = (evt) => { const creatorInput = evt.currentTarget; const creator = creatorInput.value.trim(); if (isUserId(creator)) { setValidUserId(creator); } else { setValidUserId(undefined); const term = getMxIdLocalPart(creator) ?? (creator.startsWith('@') ? creator.slice(1) : creator); if (term) { search(term); } else { resetSearch(); } } }; const handleSelectUserId = (userId?: string) => { if (userId && isUserId(userId)) { onSelect(userId); handleCloseMenu(); } }; const handleCreatorKeyDown: KeyboardEventHandler = (evt) => { if (isKeyHotkey('enter', evt)) { evt.preventDefault(); const creator = evt.currentTarget.value.trim(); handleSelectUserId(isUserId(creator) ? creator : suggestionUsers[0]); } }; const handleEnterClick = () => { handleSelectUserId(validUserId); }; return ( {mx.getSafeUserId()} {additionalCreators.map((creator) => ( } onClick={() => onRemove(creator)} disabled={disabled} > {creator} ))} evt.key === 'ArrowDown', isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp', escapeDeactivates: stopPropagation, }} > {!validUserId && suggestionUsers.length > 0 ? ( {suggestionUsers.map((userId) => ( handleSelectUserId(userId)} after={ {getMxIdServer(userId)} } > {queryHighlighRegex ? highlightText(queryHighlighRegex, [ getMxIdLocalPart(userId) ?? userId, ]) : getMxIdLocalPart(userId)} ))} ) : ( No Suggestions Please provide the user ID and hit Enter. )} } > ); }