diff --git a/src/components/SearchModal.tsx b/src/components/SearchModal.tsx index d42a5a0da..19aa57197 100644 --- a/src/components/SearchModal.tsx +++ b/src/components/SearchModal.tsx @@ -64,14 +64,14 @@ interface DestinationItem { } interface UnspecificRowProps { - key: string + key: string; destination: DestinationItem, - rowMode: string + rowMode: string; disabled: boolean, isSelected: boolean, onPressDestination: (destination: DestinationItem) => void, "aria-posinset": number, - "aria-setsize": number + "aria-setsize": number; } interface SpecificRowProps extends UnspecificRowProps { @@ -113,8 +113,18 @@ interface GuildResult { } type Result = UserResult | ChannelResult | GuildResult; +type SearchType = ("USERS" | "CHANNELS" | "GUILDS")[] | "USERS" | "CHANNELS" | "GUILDS" | "ALL"; -const searchTypesToResultTypes = (type: string | string[]) => { +export interface SearchModalProps { + modalProps: ModalProps; + onSubmit(selected: DestinationItem[]): void; + input?: string; + searchType?: SearchType; + subText?: string; + excludeIds?: string[], +} + +const searchTypesToResultTypes = (type: SearchType) => { if (type === "ALL") return ["USER", "TEXT_CHANNEL", "VOICE_CHANNEL", "GROUP_DM", "GUILD"]; if (typeof type === "string") { if (type === "USERS") return ["USER"]; @@ -125,10 +135,10 @@ const searchTypesToResultTypes = (type: string | string[]) => { } }; -function searchTypeToText(type: string | string[]) { +function searchTypeToText(type: SearchType) { if (type === undefined || type === "ALL") return "Users, Channels, and Servers"; if (typeof type === "string") { - if (type === "GUILD") return "Servers"; + if (type === "GUILDS") return "Servers"; else return type.charAt(0) + type.slice(1).toLowerCase(); } else { if (type.length === 1) { @@ -144,23 +154,17 @@ function searchTypeToText(type: string | string[]) { /** * SearchModal component for displaying a modal with search functionality, built after Discord's forwarding Modal. * - * @param {Object} props - The props for the SearchModal component. - * @param {ModalProps} props.modalProps - The modal props. + * @param {SearchModalProps} props - The props for the SearchModal component. + * @param {ModalProps} props.modalProps - The modal props. You get these from the `openModal` function. * @param {function} props.onSubmit - The function to call when the user submits their selection. * @param {string} [props.input] - The initial input value for the search bar. - * @param {("USERS" | "CHANNELS" | "GUILDS")[] | "USERS" | "CHANNELS" | "GUILDS" | "ALL"} [props.searchType="ALL"] - The type of items to search for. + * @param {SearchType} [props.searchType="ALL"] - The type of items to search for. * @param {string} [props.subText] - Additional text to display below the heading. * @param {string[]} [props.excludeIds] - An array of IDs to exclude from the search results. * @returns The rendered SearchModal component. */ -export default function SearchModal({ modalProps, onSubmit, input, searchType = "ALL", subText, excludeIds }: { - modalProps: ModalProps; - onSubmit(selected: DestinationItem[]): void; - input?: string; - searchType?: ("USERS" | "CHANNELS" | "GUILDS")[] | "USERS" | "CHANNELS" | "GUILDS" | "ALL"; - subText?: string - excludeIds?: string[], -}) { +export default function SearchModal({ modalProps, onSubmit, input, searchType = "ALL", subText, excludeIds }: SearchModalProps) { + const UserIcon = React.memo(function ({ user, size = SearchBarModule.AvatarSizes.SIZE_32, @@ -253,14 +257,14 @@ export default function SearchModal({ modalProps, onSubmit, input, searchType = return ( } - label={nickname ?? username} - subLabel={userTag} + icon={} + label={nickname ?? username} + subLabel={userTag} /> ); } @@ -321,13 +325,13 @@ export default function SearchModal({ modalProps, onSubmit, input, searchType = return ( } - label={guildName} - subLabel={""} + icon={} + label={guildName} + subLabel={""} /> ); } @@ -352,10 +356,10 @@ export default function SearchModal({ modalProps, onSubmit, input, searchType = return ( } - label={label} - subLabel={subLabelValue} + icon={} + label={label} + subLabel={subLabelValue} /> ); } @@ -396,7 +400,7 @@ export default function SearchModal({ modalProps, onSubmit, input, searchType = hasQuery: boolean; frequentChannels: Channel[]; channelHistory: string[]; - guilds: GuildResult[] + guilds: GuildResult[]; }): Result[] { const removeDuplicates = (arr: Result[]): Result[] => { const clean: any[] = []; @@ -422,7 +426,7 @@ export default function SearchModal({ modalProps, onSubmit, input, searchType = return removeDuplicates( [...(selected.length > 0 ? selected.map(e => getItem(e)) : []), - ...recentDestinations + ...recentDestinations ]); } @@ -433,39 +437,39 @@ export default function SearchModal({ modalProps, onSubmit, input, searchType = return ref_.current; } - function getSearchHandler(searchOptions: Record): { search: (e: { query: string, resultTypes: string[] }) => void, results: Result[], query: string } { - const [results, setResults] = useState<{ results: Result[], query: string }>({ + function getSearchHandler(searchOptions: Record): { search: (e: { query: string, resultTypes: string[]; }) => void, results: Result[], query: string; } { + const [results, setResults] = useState<{ results: Result[], query: string; }>({ results: [], query: "" }); const searchHandler: InstanceType = getRef(() => { - const searchHandler = new SearchHandler((r: Result[], q: string) => { - setResults({ - results: r, - query: q - }); - } - ); - searchHandler.setLimit(20); - searchHandler.search(""); - return searchHandler; + const searchHandler = new SearchHandler((r: Result[], q: string) => { + setResults({ + results: r, + query: q + }); } + ); + searchHandler.setLimit(20); + searchHandler.search(""); + return searchHandler; + } ); useEffect(() => () => searchHandler.destroy(), [searchHandler]); useEffect(() => { - searchOptions != null && searchOptions !== searchHandler.options && searchHandler.setOptions(searchOptions); - }, [searchHandler, searchOptions] + searchOptions != null && searchOptions !== searchHandler.options && searchHandler.setOptions(searchOptions); + }, [searchHandler, searchOptions] ); return { search: useCallback(e => { - const { query, resultTypes } = e; - if (searchHandler.resultTypes == null || !(resultTypes.length === searchHandler.resultTypes.size && resultTypes.every(e => searchHandler.resultTypes.has(e)))) { - searchHandler.setResultTypes(resultTypes); - searchHandler.setLimit(resultTypes.length === 1 ? 50 : 20); - } - searchHandler.search(query.trim() === "" ? "" : query); + const { query, resultTypes } = e; + if (searchHandler.resultTypes == null || !(resultTypes.length === searchHandler.resultTypes.size && resultTypes.every(e => searchHandler.resultTypes.has(e)))) { + searchHandler.setResultTypes(resultTypes); + searchHandler.setLimit(resultTypes.length === 1 ? 50 : 20); } + searchHandler.search(query.trim() === "" ? "" : query); + } , [searchHandler]), ...results }; @@ -530,11 +534,11 @@ export default function SearchModal({ modalProps, onSubmit, input, searchType = const rowHeight = useCallback(() => 48, []); - function ModalScroller({ rowData, handleToggleDestination, paddingBottom, paddingTop }: { rowData: Result[], handleToggleDestination: (destination: DestinationItem) => void, paddingBottom?: number, paddingTop?: number }) { + function ModalScroller({ rowData, handleToggleDestination, paddingBottom, paddingTop }: { rowData: Result[], handleToggleDestination: (destination: DestinationItem) => void, paddingBottom?: number, paddingTop?: number; }) { const sectionCount: number[] = useMemo(() => [rowData.length], [rowData.length]); - const callback = useCallback((e: { section: number, row: number }) => { + const callback = useCallback((e: { section: number, row: number; }) => { const { section, row } = e; if (section > 0) return; @@ -580,7 +584,7 @@ export default function SearchModal({ modalProps, onSubmit, input, searchType = sections={sectionCount} sectionHeight={0} renderRow={callback} - rowHeight={rowHeight}/>; + rowHeight={rowHeight} />; } @@ -612,11 +616,11 @@ export default function SearchModal({ modalProps, onSubmit, input, searchType = {"Search for " + searchTypeToText(searchType)} + style={{ flexGrow: 1 }}>{"Search for " + searchTypeToText(searchType)} {subText !== undefined && {subText}} + style={{ color: "var(--header-muted)" }}>{subText}} - + : - - - - - No results found - + + + + + No results found + } @@ -677,7 +681,7 @@ export default function SearchModal({ modalProps, onSubmit, input, searchType = look={Button.Looks.LINK} onClick={modalProps.onClose} > - Cancel + Cancel