From 55d76809578ed5ac1ce352ea1cd0ab043bea3aa9 Mon Sep 17 00:00:00 2001 From: Anton Arnautov Date: Fri, 1 Dec 2023 15:33:25 +0100 Subject: [PATCH 1/3] Refactor trigger, switch to debounce, remove unused stuff --- .../MessageInput/hooks/useUserTrigger.ts | 115 ++++++++---------- 1 file changed, 53 insertions(+), 62 deletions(-) diff --git a/src/components/MessageInput/hooks/useUserTrigger.ts b/src/components/MessageInput/hooks/useUserTrigger.ts index 6e7872e66e..63ce681954 100644 --- a/src/components/MessageInput/hooks/useUserTrigger.ts +++ b/src/components/MessageInput/hooks/useUserTrigger.ts @@ -1,5 +1,5 @@ -import { useCallback, useState } from 'react'; -import throttle from 'lodash.throttle'; +import { useCallback } from 'react'; +import debounce from 'lodash.debounce'; import { SearchLocalUserParams, searchLocalUsers } from './utils'; @@ -25,6 +25,8 @@ export type UserTriggerParams< useMentionsTransliteration?: boolean; }; +const DEBOUNCE_DELAY = 200; + export const useUserTrigger = < StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics >( @@ -38,8 +40,6 @@ export const useUserTrigger = < useMentionsTransliteration, } = params; - const [searching, setSearching] = useState(false); - const { client, mutes, themeVersion } = useChatContext('useUserTrigger'); const { channel } = useChannelStateContext('useUserTrigger'); @@ -52,7 +52,7 @@ export const useUserTrigger = < const users = [...memberUsers, ...watcherUsers]; // make sure we don't list users twice - const uniqueUsers = {} as Record>; + const uniqueUsers: Record> = {}; users.forEach((user) => { if (user && !uniqueUsers[user.id]) { @@ -63,8 +63,8 @@ export const useUserTrigger = < return Object.values(uniqueUsers); }, [members, watchers]); - const queryMembersThrottled = useCallback( - throttle( + const queryMembersDebounced = useCallback( + debounce( async (query: string, onReady: (users: UserResponse[]) => void) => { try { // @ts-expect-error @@ -85,71 +85,63 @@ export const useUserTrigger = < console.log({ error }); } }, - 200, + DEBOUNCE_DELAY, ), [channel], ); - const queryUsers = async ( - query: string, - onReady: (users: UserResponse[]) => void, - ) => { - if (!query || searching) return; - setSearching(true); - - try { - const { users } = await client.queryUsers( - // @ts-expect-error - { - $or: [{ id: { $autocomplete: query } }, { name: { $autocomplete: query } }], - id: { $ne: client.userID }, - ...(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 }); + const queryUsersDebounced = useCallback( + debounce( + async (query: string, onReady: (users: UserResponse[]) => void) => { + if (!query) return; + + try { + const { users } = await client.queryUsers( + // @ts-expect-error + { + $or: [{ id: { $autocomplete: query } }, { name: { $autocomplete: query } }], + id: { $ne: client.userID }, + ...(typeof mentionQueryParams.filters === 'function' + ? mentionQueryParams.filters(query) + : mentionQueryParams.filters), + }, + Array.isArray(mentionQueryParams.sort) + ? [{ id: 1 }, ...mentionQueryParams.sort] + : { id: 1, ...mentionQueryParams.sort }, + // TODO: adjust limit + { limit: 10, ...mentionQueryParams.options }, + ); + onReady?.(users); + } catch (error) { + console.log({ error }); + } + }, + DEBOUNCE_DELAY, + ), + [client, mentionQueryParams], + ); + + const filterMutes = (data: UserResponse[], text: string) => { + if (text.includes('/unmute') && !mutes.length) { + return []; } + if (!mutes.length) return data; - setSearching(false); + 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)); }; - const queryUsersThrottled = throttle(queryUsers, 200); - return { - callback: (item) => onSelectUser(item), + callback: onSelectUser, component: UserItem, dataProvider: (query, text, onReady) => { if (disableMentions) return; - const filterMutes = (data: UserResponse[]) => { - 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: UserResponse[]) => { - if (onReady) onReady(filterMutes(data), query); + return queryUsersDebounced(query, (data: UserResponse[]) => { + onReady?.(filterMutes(data, text), query); }); } @@ -175,12 +167,11 @@ export const useUserTrigger = < const usersToShow = mentionQueryParams.options?.limit ?? (themeVersion === '2' ? 7 : 10); const data = matchingUsers.slice(0, usersToShow); - if (onReady) onReady(filterMutes(data), query); - return data; + return onReady?.(filterMutes(data, text), query); } - return queryMembersThrottled(query, (data: UserResponse[]) => { - if (onReady) onReady(filterMutes(data), query); + queryMembersDebounced(query, (data: UserResponse[]) => { + onReady?.(filterMutes(data, text), query); }); }, output: (entity) => ({ From 8282956f163ba06e5d751f55eb0220e1f73f06e8 Mon Sep 17 00:00:00 2001 From: Anton Arnautov Date: Fri, 1 Dec 2023 15:44:44 +0100 Subject: [PATCH 2/3] Proposed, but breaking solution --- .../MessageInput/hooks/useUserTrigger.ts | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/components/MessageInput/hooks/useUserTrigger.ts b/src/components/MessageInput/hooks/useUserTrigger.ts index 63ce681954..b8c712ab23 100644 --- a/src/components/MessageInput/hooks/useUserTrigger.ts +++ b/src/components/MessageInput/hooks/useUserTrigger.ts @@ -95,22 +95,20 @@ export const useUserTrigger = < async (query: string, onReady: (users: UserResponse[]) => void) => { if (!query) return; + const { + filters = { id: { $ne: client.user?.id }, name: { $autocomplete: query } }, + sort = { name: 1 }, + options = { limit: 10 }, + } = mentionQueryParams; + try { const { users } = await client.queryUsers( // @ts-expect-error - { - $or: [{ id: { $autocomplete: query } }, { name: { $autocomplete: query } }], - id: { $ne: client.userID }, - ...(typeof mentionQueryParams.filters === 'function' - ? mentionQueryParams.filters(query) - : mentionQueryParams.filters), - }, - Array.isArray(mentionQueryParams.sort) - ? [{ id: 1 }, ...mentionQueryParams.sort] - : { id: 1, ...mentionQueryParams.sort }, - // TODO: adjust limit - { limit: 10, ...mentionQueryParams.options }, + typeof filters === 'function' ? filters(query) : filters, + sort, + options, ); + onReady?.(users); } catch (error) { console.log({ error }); @@ -140,9 +138,9 @@ export const useUserTrigger = < if (disableMentions) return; if (mentionAllAppUsers) { - return queryUsersDebounced(query, (data: UserResponse[]) => { - onReady?.(filterMutes(data, text), query); - }); + return queryUsersDebounced(query, (data: UserResponse[]) => + onReady?.(filterMutes(data, text), query), + ); } /** @@ -170,9 +168,9 @@ export const useUserTrigger = < return onReady?.(filterMutes(data, text), query); } - queryMembersDebounced(query, (data: UserResponse[]) => { - onReady?.(filterMutes(data, text), query); - }); + queryMembersDebounced(query, (data: UserResponse[]) => + onReady?.(filterMutes(data, text), query), + ); }, output: (entity) => ({ caretPosition: 'next', From 376d7b7a6841f92f55b746a53667fd1be978581e Mon Sep 17 00:00:00 2001 From: Anton Arnautov Date: Wed, 13 Dec 2023 15:30:35 +0100 Subject: [PATCH 3/3] Change mechanism back to throttle, increase delay --- .../MessageInput/hooks/useUserTrigger.ts | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/components/MessageInput/hooks/useUserTrigger.ts b/src/components/MessageInput/hooks/useUserTrigger.ts index b8c712ab23..ff66cf32bf 100644 --- a/src/components/MessageInput/hooks/useUserTrigger.ts +++ b/src/components/MessageInput/hooks/useUserTrigger.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react'; -import debounce from 'lodash.debounce'; +import throttle from 'lodash.throttle'; import { SearchLocalUserParams, searchLocalUsers } from './utils'; @@ -25,7 +25,7 @@ export type UserTriggerParams< useMentionsTransliteration?: boolean; }; -const DEBOUNCE_DELAY = 200; +const THROTTLE_DELAY = 500; export const useUserTrigger = < StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics @@ -63,8 +63,8 @@ export const useUserTrigger = < return Object.values(uniqueUsers); }, [members, watchers]); - const queryMembersDebounced = useCallback( - debounce( + const queryMembersThrottled = useCallback( + throttle( async (query: string, onReady: (users: UserResponse[]) => void) => { try { // @ts-expect-error @@ -85,13 +85,14 @@ export const useUserTrigger = < console.log({ error }); } }, - DEBOUNCE_DELAY, + THROTTLE_DELAY, + { leading: true, trailing: true }, ), [channel], ); - const queryUsersDebounced = useCallback( - debounce( + const queryUsersThrottled = useCallback( + throttle( async (query: string, onReady: (users: UserResponse[]) => void) => { if (!query) return; @@ -114,7 +115,8 @@ export const useUserTrigger = < console.log({ error }); } }, - DEBOUNCE_DELAY, + THROTTLE_DELAY, + { leading: true, trailing: true }, ), [client, mentionQueryParams], ); @@ -138,7 +140,7 @@ export const useUserTrigger = < if (disableMentions) return; if (mentionAllAppUsers) { - return queryUsersDebounced(query, (data: UserResponse[]) => + return queryUsersThrottled(query, (data: UserResponse[]) => onReady?.(filterMutes(data, text), query), ); } @@ -168,7 +170,7 @@ export const useUserTrigger = < return onReady?.(filterMutes(data, text), query); } - queryMembersDebounced(query, (data: UserResponse[]) => + queryMembersThrottled(query, (data: UserResponse[]) => onReady?.(filterMutes(data, text), query), ); },