// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import UserList from '@components/user_list'; import {General} from '@constants'; import useDidMount from '@hooks/did_mount'; import {useDebounce} from '@hooks/utils'; import {filterProfilesMatchingTerm} from '@utils/user'; import type {AvailableScreens} from '@typings/screens/navigation'; import type {SectionListData} from 'react-native'; type Props = { tutorialWatched: boolean; handleSelectProfile: (user: UserProfile) => void; term: string; selectedIds: Set; fetchFunction: (page: number) => Promise; searchFunction: (term: string) => Promise; createFilter: (exactMatches: UserProfile[], term: string) => ((p: UserProfile) => boolean); testID: string; location: AvailableScreens; customSection?: (profiles: UserProfile[]) => Array>; } export default function ServerUserList({ tutorialWatched, handleSelectProfile, term, selectedIds, fetchFunction, searchFunction, createFilter, testID, location, customSection, }: Props) { const searchTimeoutId = useRef(null); const next = useRef(true); const page = useRef(-1); const mounted = useRef(false); const [profiles, setProfiles] = useState([]); const [searchResults, setSearchResults] = useState([]); const [loading, setLoading] = useState(false); const isSearch = Boolean(term); const loadedProfiles = (users: UserProfile[]) => { if (mounted.current) { if (!users.length) { next.current = false; } page.current += 1; setLoading(false); setProfiles((current) => { if (users?.length) { return [...current, ...users]; } return current; }); } }; const getProfiles = useDebounce(useCallback(() => { if (next.current && !loading && !term && mounted.current) { setLoading(true); fetchFunction(page.current + 1).then(loadedProfiles); } }, [loading, term, fetchFunction]), 100); const searchUsers = useCallback(async (searchTerm: string) => { setLoading(true); const data = await searchFunction(searchTerm); setSearchResults(data); setLoading(false); }, [searchFunction]); useEffect(() => { if (term) { if (searchTimeoutId.current) { clearTimeout(searchTimeoutId.current); } searchTimeoutId.current = setTimeout(() => { searchUsers(term); }, General.SEARCH_TIMEOUT_MILLISECONDS); } else { setSearchResults([]); } // We only want to run the search when the term changes // eslint-disable-next-line react-hooks/exhaustive-deps }, [term]); useDidMount(() => { mounted.current = true; getProfiles(); return () => { mounted.current = false; }; }); const data = useMemo(() => { if (isSearch) { const exactMatches: UserProfile[] = []; const filterByTerm = createFilter(exactMatches, term); const profilesToFilter = searchResults.length ? searchResults : profiles; const results = filterProfilesMatchingTerm(profilesToFilter, term).filter(filterByTerm); return [...exactMatches, ...results]; } return profiles; }, [isSearch, profiles, createFilter, term, searchResults]); return ( ); }