From a87c9b82b57708c2c5791afbc1468992e2f91575 Mon Sep 17 00:00:00 2001 From: Hubert Sosinski Date: Thu, 22 May 2025 15:05:21 +0200 Subject: [PATCH 1/5] Add debounce on SearchAutocompleteInput --- .../Search/SearchAutocompleteInput.tsx | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index 00d77f62ce2d..262c36653824 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -1,6 +1,6 @@ /* eslint-disable rulesdir/no-acc-spread-in-reduce */ import type {ForwardedRef, ReactNode, RefObject} from 'react'; -import React, {forwardRef, useCallback, useEffect, useLayoutEffect, useMemo} from 'react'; +import React, {forwardRef, useCallback, useEffect, useLayoutEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {StyleProp, TextInputProps, ViewStyle} from 'react-native'; import {useOnyx} from 'react-native-onyx'; @@ -133,6 +133,25 @@ function SearchAutocompleteInput( return focusedSharedValue.get() ? wrapperFocusedStyle : wrapperStyle ?? {}; }); + const [internalValue, setInternalValue] = useState(value); + + useEffect(() => { + setInternalValue(value); + }, [value]); + + const debouncedSearchQueryChange = useMemo(() => { + let timeoutId: NodeJS.Timeout; + return (searchTerm: string) => { + setInternalValue(searchTerm); + if (timeoutId) { + clearTimeout(timeoutId); + } + timeoutId = setTimeout(() => { + onSearchQueryChange(searchTerm); + }, 300); + }; + }, [onSearchQueryChange]); + useEffect(() => { runOnLiveMarkdownRuntime(() => { 'worklet'; @@ -192,8 +211,8 @@ function SearchAutocompleteInput( > Date: Thu, 22 May 2025 15:26:05 +0200 Subject: [PATCH 2/5] Use loadash.debounce --- .../Search/SearchAutocompleteInput.tsx | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/components/Search/SearchAutocompleteInput.tsx b/src/components/Search/SearchAutocompleteInput.tsx index 262c36653824..38def5425186 100644 --- a/src/components/Search/SearchAutocompleteInput.tsx +++ b/src/components/Search/SearchAutocompleteInput.tsx @@ -1,4 +1,5 @@ /* eslint-disable rulesdir/no-acc-spread-in-reduce */ +import debounce from 'lodash/debounce'; import type {ForwardedRef, ReactNode, RefObject} from 'react'; import React, {forwardRef, useCallback, useEffect, useLayoutEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; @@ -139,18 +140,27 @@ function SearchAutocompleteInput( setInternalValue(value); }, [value]); - const debouncedSearchQueryChange = useMemo(() => { - let timeoutId: NodeJS.Timeout; - return (searchTerm: string) => { - setInternalValue(searchTerm); - if (timeoutId) { - clearTimeout(timeoutId); - } - timeoutId = setTimeout(() => { + const debouncedSearchQueryChange = useMemo( + () => + debounce((searchTerm: string) => { onSearchQueryChange(searchTerm); - }, 300); + }, 300) as ReturnType, + [onSearchQueryChange], + ); + + const handleSearchQueryChange = useCallback( + (searchTerm: string) => { + setInternalValue(searchTerm); + debouncedSearchQueryChange(searchTerm); + }, + [debouncedSearchQueryChange], + ); + + useEffect(() => { + return () => { + debouncedSearchQueryChange.cancel(); }; - }, [onSearchQueryChange]); + }, [debouncedSearchQueryChange]); useEffect(() => { runOnLiveMarkdownRuntime(() => { @@ -212,7 +222,7 @@ function SearchAutocompleteInput( Date: Mon, 26 May 2025 10:52:12 +0200 Subject: [PATCH 3/5] Remove input value bliking while typing --- src/components/Search/SearchRouter/SearchRouter.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index 252b1c9e7e4b..0c19daf49c2d 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -182,7 +182,6 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla if (!textInputRef.current || !shouldScrollRef.current) { return; } - scrollToRight(textInputRef.current); shouldScrollRef.current = false; }, [textInputValue]); @@ -194,7 +193,6 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla } const singleLineUserQuery = StringUtils.lineBreaksToSpaces(userQuery, true); const updatedUserQuery = getAutocompleteQueryWithComma(textInputValue, singleLineUserQuery); - setTextInputValue(updatedUserQuery); setAutocompleteQueryValue(updatedUserQuery); const updatedSubstitutionsMap = getUpdatedSubstitutionsMap(singleLineUserQuery, autocompleteSubstitutions); @@ -234,7 +232,10 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla (text: string) => { setTextInputValue(text); shouldScrollRef.current = true; - setSelection({start: text.length, end: text.length}); + + InteractionManager.runAfterInteractions(() => { + setSelection({start: text.length, end: text.length}); + }); }, [setSelection, setTextInputValue], ); @@ -259,8 +260,8 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla if (item.searchItemType === CONST.SEARCH.SEARCH_ROUTER_ITEM_TYPE.CONTEXTUAL_SUGGESTION) { const searchQuery = getContextualSearchQuery(item); const newSearchQuery = `${searchQuery}\u00A0`; + setTextAndUpdateSelection(newSearchQuery); onSearchQueryChange(newSearchQuery, true); - setSelection({start: newSearchQuery.length, end: newSearchQuery.length}); const autocompleteKey = getContextualSearchAutocompleteKey(item); if (autocompleteKey && item.autocompleteID) { @@ -272,8 +273,8 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla } else if (item.searchItemType === CONST.SEARCH.SEARCH_ROUTER_ITEM_TYPE.AUTOCOMPLETE_SUGGESTION && textInputValue) { const trimmedUserSearchQuery = getQueryWithoutAutocompletedPart(textInputValue); const newSearchQuery = `${trimmedUserSearchQuery}${sanitizeSearchValue(item.searchQuery)}\u00A0`; + setTextAndUpdateSelection(newSearchQuery); onSearchQueryChange(newSearchQuery, true); - setSelection({start: newSearchQuery.length, end: newSearchQuery.length}); if (item.mapKey && item.autocompleteID) { const substitutions = {...autocompleteSubstitutions, [item.mapKey]: item.autocompleteID}; From 94c34005838fb24af641aceb8e40874522be5f1e Mon Sep 17 00:00:00 2001 From: Hubert Sosinski Date: Mon, 26 May 2025 11:07:20 +0200 Subject: [PATCH 4/5] Fix lint error --- src/components/Search/SearchRouter/SearchRouter.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index 0c19daf49c2d..e14b5af6cbe4 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -206,7 +206,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla listRef.current?.updateAndScrollToFocusedIndex(-1); } }, - [autocompleteSubstitutions, setTextInputValue, textInputValue], + [autocompleteSubstitutions, textInputValue], ); const submitSearch = useCallback( @@ -297,7 +297,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla }); } }, - [autocompleteSubstitutions, onRouterClose, onSearchQueryChange, submitSearch, textInputValue], + [autocompleteSubstitutions, onRouterClose, onSearchQueryChange, submitSearch, textInputValue, setTextAndUpdateSelection], ); const updateAutocompleteSubstitutions = useCallback( From a4dcc40b2d0216af81f3ba01adea26aeff649609 Mon Sep 17 00:00:00 2001 From: Hubert Sosinski Date: Thu, 29 May 2025 15:40:41 +0200 Subject: [PATCH 5/5] onSearchQueryChange will update textInputValue also --- src/components/Search/SearchRouter/SearchRouter.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx index 864b15304bfc..8de3fc1f8756 100644 --- a/src/components/Search/SearchRouter/SearchRouter.tsx +++ b/src/components/Search/SearchRouter/SearchRouter.tsx @@ -191,6 +191,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla if (autoScrollToRight) { shouldScrollRef.current = true; } + setTextInputValue(userQuery); const singleLineUserQuery = StringUtils.lineBreaksToSpaces(userQuery, true); const updatedUserQuery = getAutocompleteQueryWithComma(textInputValue, singleLineUserQuery); setAutocompleteQueryValue(updatedUserQuery); @@ -206,7 +207,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret, isSearchRouterDispla listRef.current?.updateAndScrollToFocusedIndex(-1); } }, - [autocompleteSubstitutions, textInputValue], + [autocompleteSubstitutions, textInputValue, setTextInputValue], ); const submitSearch = useCallback(