import * as React from "react"; import { useFetcher } from "react-router"; import type { SearchLoaderData } from "~/features/search/routes/search"; import { searchSearchParams } from "~/features/search/search-search-params"; import { Avatar } from "../Avatar"; import { SearchSelect, type SearchSelectFieldProps, SearchSelectItem, SearchSelectItemAdditionalText, } from "./SearchSelect"; import { useEntitySearch } from "./useEntitySearch"; export type UserSearchResult = Extract< NonNullable["results"][number], { type: "user" } >; interface UserSearchProps extends SearchSelectFieldProps { initialUserId?: number; onChange?: (user: UserSearchResult | null) => void; ref?: React.Ref; } export function UserSearch({ initialUserId, onChange, ref, ...rest }: UserSearchProps) { const initialUser = useInitialUser(initialUserId); const search = useEntitySearch({ buildUrl: (query) => searchSearchParams.href("/search", { q: query, type: "users", limit: 6, }), parseResults: (data, query) => parseUserResults(data, query, initialUser), initialItem: initialUser, initialSelectedId: initialUserId, onChange, }); return ( } /> ); } function parseUserResults( data: unknown, query: string, initialUser?: UserSearchResult, ): UserSearchResult[] | null { const searchData = data as SearchLoaderData; if (!searchData || searchData.query !== query) return null; return searchData.results .filter((result): result is UserSearchResult => result.type === "user") .filter((user) => user.id !== initialUser?.id); } /** Loads the preselected id's user once; later changes come from picked results which carry the full user. */ function useInitialUser(initialUserId?: number) { const fetcher = useFetcher(); const { load } = fetcher; const hasLoadedRef = React.useRef(false); React.useEffect(() => { if (!initialUserId || hasLoadedRef.current) { return; } hasLoadedRef.current = true; load(`/search?q=${initialUserId}&type=users&limit=1`); }, [initialUserId, load]); return fetcher.data?.results.find( (result): result is UserSearchResult => result.type === "user", ); } function UserItem({ item }: { item: UserSearchResult }) { const additionalText = () => { const plusServer = item.plusTier ? `+${item.plusTier}` : ""; const profileUrl = item.customUrl ? `/u/${item.customUrl}` : ""; if (plusServer && profileUrl) { return `${plusServer} • ${profileUrl}`; } if (plusServer) { return plusServer; } if (profileUrl) { return profileUrl; } return ""; }; return ( } > {item.name} {additionalText() ? ( {additionalText()} ) : null} ); }