UNPKG

stream-chat-react

Version:

React components to create chat conversations or livestream style chat

131 lines (130 loc) 5.4 kB
import { useCallback, useState } from 'react'; import throttle from 'lodash.throttle'; import { searchLocalUsers } from './utils'; import { UserItem } from '../../UserItem/UserItem'; import { useChatContext } from '../../../context/ChatContext'; import { useChannelStateContext } from '../../../context/ChannelStateContext'; export const useUserTrigger = (params) => { const { disableMentions, mentionAllAppUsers, mentionQueryParams = {}, onSelectUser, useMentionsTransliteration, } = params; const [searching, setSearching] = useState(false); const { client, mutes } = useChatContext('useUserTrigger'); const { channel } = useChannelStateContext('useUserTrigger'); const { members } = channel.state; const { watchers } = channel.state; const getMembersAndWatchers = useCallback(() => { const memberUsers = members ? Object.values(members).map(({ user }) => user) : []; const watcherUsers = watchers ? Object.values(watchers) : []; const users = [...memberUsers, ...watcherUsers]; // make sure we don't list users twice const uniqueUsers = {}; users.forEach((user) => { if (user && !uniqueUsers[user.id]) { uniqueUsers[user.id] = user; } }); return Object.values(uniqueUsers); }, [members, watchers]); // eslint-disable-next-line react-hooks/exhaustive-deps const queryMembersThrottled = useCallback(throttle(async (query, onReady) => { try { // @ts-expect-error valid query const response = await channel.queryMembers({ name: { $autocomplete: query }, }); const users = response.members.map((member) => member.user); if (onReady && users.length) { onReady(users); } else { onReady([]); } } catch (error) { console.log({ error }); } }, 200), [channel]); const queryUsers = async (query, onReady) => { if (!query || searching) return; setSearching(true); try { const { users } = await client.queryUsers( // @ts-expect-error valid query { $or: [{ id: { $autocomplete: query } }, { name: { $autocomplete: query } }], ...(typeof mentionQueryParams.filters === 'function' ? mentionQueryParams.filters(query) : mentionQueryParams.filters), }, Array.isArray(mentionQueryParams.sort) ? [{ id: 1 }, ...mentionQueryParams.sort] : { id: 1, ...mentionQueryParams.sort }, { limit: 10, ...mentionQueryParams.options }); if (onReady && users.length) { onReady(users); } else { onReady([]); } } catch (error) { console.log({ error }); } setSearching(false); }; const queryUsersThrottled = throttle(queryUsers, 200); return { callback: (item) => onSelectUser(item), component: UserItem, dataProvider: (query, text, onReady) => { if (disableMentions) return; const filterMutes = (data) => { if (text.includes('/unmute') && !mutes.length) { return []; } if (!mutes.length) return data; if (text.includes('/unmute')) { return data.filter((suggestion) => mutes.some((mute) => mute.target.id === suggestion.id)); } return data.filter((suggestion) => mutes.every((mute) => mute.target.id !== suggestion.id)); }; if (mentionAllAppUsers) { return queryUsersThrottled(query, (data) => { if (onReady) onReady(filterMutes(data), query); }); } /** * By default, we return maximum 100 members via queryChannels api call. * Thus it is safe to assume, that if number of members in channel.state is < 100, * then all the members are already available on client side and we don't need to * make any api call to queryMembers endpoint. */ if (!query || Object.values(members || {}).length < 100) { const users = getMembersAndWatchers(); const params = { ownUserId: client.userID, query, text, useMentionsTransliteration, users, }; const matchingUsers = searchLocalUsers(params); const usersToShow = mentionQueryParams.options?.limit ?? 7; const data = matchingUsers.slice(0, usersToShow); if (onReady) onReady(filterMutes(data), query); return data; } return queryMembersThrottled(query, (data) => { if (onReady) onReady(filterMutes(data), query); }); }, output: (entity) => ({ caretPosition: 'next', key: entity.id, text: `@${entity.name || entity.id}`, }), }; };