diff --git a/src/components/ChannelSearch/__tests__/ChannelSearch.test.js b/src/components/ChannelSearch/__tests__/ChannelSearch.test.js index dacbe5048..1ef5ceaed 100644 --- a/src/components/ChannelSearch/__tests__/ChannelSearch.test.js +++ b/src/components/ChannelSearch/__tests__/ChannelSearch.test.js @@ -187,6 +187,56 @@ describe('ChannelSearch', () => { jest.useRealTimers(); }); + it('search is performed on channels only', async () => { + const limit = 8; + const otherUsers = Array.from({ length: limit }, generateUser); + jest.useFakeTimers('modern'); + const client = await getTestClientWithUser(user); + jest.spyOn(client, 'queryUsers').mockResolvedValue({ users: [...otherUsers, user] }); + jest.spyOn(client, 'queryChannels').mockResolvedValue([channelResponseData]); + + const { typeText } = await renderSearch({ + client, + props: { searchForChannels: true, searchForUsers: false }, + }); + await act(() => { + typeText(typedText); + }); + + await act(() => { + jest.advanceTimersByTime(1000); + }); + + expect(client.queryUsers).not.toHaveBeenCalled(); + expect(client.queryChannels).toHaveBeenCalledTimes(1); + jest.useRealTimers(); + }); + + it('search is not performed on channels neither users', async () => { + const limit = 8; + const otherUsers = Array.from({ length: limit }, generateUser); + jest.useFakeTimers('modern'); + const client = await getTestClientWithUser(user); + jest.spyOn(client, 'queryUsers').mockResolvedValue({ users: [...otherUsers, user] }); + jest.spyOn(client, 'queryChannels').mockResolvedValue([channelResponseData]); + + const { typeText } = await renderSearch({ + client, + props: { searchForChannels: false, searchForUsers: false }, + }); + await act(() => { + typeText(typedText); + }); + + await act(() => { + jest.advanceTimersByTime(1000); + }); + + expect(client.queryUsers).not.toHaveBeenCalled(); + expect(client.queryChannels).not.toHaveBeenCalled(); + jest.useRealTimers(); + }); + it('does not perform search queries when the search is disabled', async () => { jest.useFakeTimers('modern'); const client = await getTestClientWithUser(user); diff --git a/src/components/ChannelSearch/hooks/useChannelSearch.ts b/src/components/ChannelSearch/hooks/useChannelSearch.ts index c6014b940..7a44f7a9d 100644 --- a/src/components/ChannelSearch/hooks/useChannelSearch.ts +++ b/src/components/ChannelSearch/hooks/useChannelSearch.ts @@ -70,8 +70,10 @@ export type ChannelSearchParams< ) => Promise | void; /** The number of milliseconds to debounce the search query. The default interval is 200ms. */ searchDebounceIntervalMs?: number; - /** Boolean to search for channels as well as users in the server query, default is false and just searches for users */ + /** Boolean to search for channels in the server query, default is false and just searches for users */ searchForChannels?: boolean; + /** Boolean to search for users in the server query, default is true and just searches for users */ + searchForUsers?: boolean; /** Custom search function to override the default implementation */ searchFunction?: ( params: ChannelSearchFunctionParams, @@ -99,6 +101,7 @@ export const useChannelSearch = < onSelectResult, searchDebounceIntervalMs = 300, searchForChannels = false, + searchForUsers = true, searchFunction, searchQueryParams, setChannels, @@ -110,10 +113,7 @@ export const useChannelSearch = < const [results, setResults] = useState>>([]); const [searching, setSearching] = useState(false); - const searchQueryPromiseInProgress = useRef< - | Promise> - | Promise<[Channel[], UsersAPIResponse]> - >(undefined); + const searchQueryPromiseInProgress = useRef(false); const shouldIgnoreQueryResults = useRef(false); const inputRef = useRef(null); @@ -124,9 +124,7 @@ export const useChannelSearch = < setResults([]); setSearching(false); - if (searchQueryPromiseInProgress.current) { - shouldIgnoreQueryResults.current = true; - } + shouldIgnoreQueryResults.current = searchQueryPromiseInProgress.current; }, []); const activateSearch = useCallback(() => { @@ -210,41 +208,59 @@ export const useChannelSearch = < const getChannels = useCallback( async (text: string) => { + if (!searchForChannels && !searchForUsers) return; let results: ChannelOrUserResponse[] = []; + const promises: Array< + Promise[]> | Promise> + > = []; try { - const userQueryPromise = client.queryUsers( - // @ts-expect-error - { - $or: [{ id: { $autocomplete: text } }, { name: { $autocomplete: text } }], - ...searchQueryParams?.userFilters?.filters, - }, - { id: 1, ...searchQueryParams?.userFilters?.sort }, - { limit: 8, ...searchQueryParams?.userFilters?.options }, - ); - - if (!searchForChannels) { - searchQueryPromiseInProgress.current = userQueryPromise; - const { users } = await searchQueryPromiseInProgress.current; - results = users.filter((u) => u.id !== client.user?.id); - } else { - const channelQueryPromise = client.queryChannels( - // @ts-expect-error - { - name: { $autocomplete: text }, - ...searchQueryParams?.channelFilters?.filters, - }, - searchQueryParams?.channelFilters?.sort || {}, - { limit: 5, ...searchQueryParams?.channelFilters?.options }, + if (searchForChannels) { + promises.push( + client.queryChannels( + // @ts-expect-error + { + members: { $in: [client.userID] }, + name: { $autocomplete: text }, + ...searchQueryParams?.channelFilters?.filters, + }, + searchQueryParams?.channelFilters?.sort || {}, + { limit: 5, ...searchQueryParams?.channelFilters?.options }, + ), ); + } - searchQueryPromiseInProgress.current = Promise.all([ - channelQueryPromise, - userQueryPromise, - ]); - - const [channels, { users }] = await searchQueryPromiseInProgress.current; + if (searchForUsers) { + promises.push( + client.queryUsers( + // @ts-expect-error + { + $or: [{ id: { $autocomplete: text } }, { name: { $autocomplete: text } }], + ...searchQueryParams?.userFilters?.filters, + }, + { id: 1, ...searchQueryParams?.userFilters?.sort }, + { limit: 8, ...searchQueryParams?.userFilters?.options }, + ), + ); + } - results = [...channels, ...users.filter((u) => u.id !== client.user?.id)]; + if (promises.length) { + searchQueryPromiseInProgress.current = true; + + const resolved = await Promise.all(promises); + + if (searchForChannels && searchForUsers) { + const [channels, { users }] = resolved as [ + Channel[], + UsersAPIResponse, + ]; + results = [...channels, ...users.filter((u) => u.id !== client.user?.id)]; + } else if (searchForChannels) { + const [channels] = resolved as [Channel[]]; + results = [...channels]; + } else if (searchForUsers) { + const [{ users }] = resolved as [UsersAPIResponse]; + results = [...users.filter((u) => u.id !== client.user?.id)]; + } } } catch (error) { console.error(error); @@ -257,9 +273,9 @@ export const useChannelSearch = < shouldIgnoreQueryResults.current = false; } - searchQueryPromiseInProgress.current = undefined; + searchQueryPromiseInProgress.current = false; }, - [client, searchForChannels, searchQueryParams], + [client, searchForChannels, searchForUsers, searchQueryParams], ); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -282,6 +298,8 @@ export const useChannelSearch = < }, event, ); + } else if (!searchForChannels && !searchForUsers) { + return; } else if (event.target.value) { setSearching(true); setQuery(event.target.value); @@ -292,7 +310,15 @@ export const useChannelSearch = < } onSearchCallback?.(event); }, - [clearState, disabled, scheduleGetChannels, onSearchCallback, searchFunction], + [ + clearState, + disabled, + scheduleGetChannels, + onSearchCallback, + searchForChannels, + searchForUsers, + searchFunction, + ], ); return {