From a0a096b729fc20b26470430bc79308b479b5135d Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 21 Nov 2024 19:12:03 +0100 Subject: [PATCH 1/2] feat: focus input on search tab tap --- .../GlobalSearchInput/GlobalSearchInput.tsx | 93 +++++++++++-------- src/app/Scenes/Search/Search.tsx | 11 ++- 2 files changed, 62 insertions(+), 42 deletions(-) diff --git a/src/app/Components/GlobalSearchInput/GlobalSearchInput.tsx b/src/app/Components/GlobalSearchInput/GlobalSearchInput.tsx index 6d750f6abcc..10103bb9c7f 100644 --- a/src/app/Components/GlobalSearchInput/GlobalSearchInput.tsx +++ b/src/app/Components/GlobalSearchInput/GlobalSearchInput.tsx @@ -3,54 +3,69 @@ import { Flex, RoundSearchInput, Touchable } from "@artsy/palette-mobile" import { GlobalSearchInputOverlay } from "app/Components/GlobalSearchInput/GlobalSearchInputOverlay" import { useDismissSearchOverlayOnTabBarPress } from "app/Components/GlobalSearchInput/utils/useDismissSearchOverlayOnTabBarPress" import { ICON_HIT_SLOP } from "app/Components/constants" -import { Fragment, useState } from "react" +import { forwardRef, Fragment, useImperativeHandle, useState } from "react" import { useTracking } from "react-tracking" -export const GlobalSearchInput: React.FC<{ +type GlobalSearchInputProps = { ownerType: OwnerType -}> = ({ ownerType }) => { - const [isVisible, setIsVisible] = useState(false) - const tracking = useTracking() +} +export type GlobalSearchInput = { + focus: () => void +} + +export const GlobalSearchInput = forwardRef( + ({ ownerType }, ref) => { + const [isVisible, setIsVisible] = useState(false) + const tracking = useTracking() - useDismissSearchOverlayOnTabBarPress({ isVisible, ownerType, setIsVisible }) + useDismissSearchOverlayOnTabBarPress({ isVisible, ownerType, setIsVisible }) - return ( - - { - tracking.trackEvent( - tracks.tappedGlobalSearchBar({ - ownerType, - }) - ) + useImperativeHandle(ref, () => ({ + focus: () => { + if (!isVisible) { setIsVisible(true) - }} - hitSlop={ICON_HIT_SLOP} - testID="search-button" - > - {/* In order to make the search input behave like a button here, we wrapped it with a + } + }, + })) + + return ( + + { + tracking.trackEvent( + tracks.tappedGlobalSearchBar({ + ownerType, + }) + ) + setIsVisible(true) + }} + hitSlop={ICON_HIT_SLOP} + testID="search-button" + > + {/* In order to make the search input behave like a button here, we wrapped it with a Touchable and set pointerEvents to none. This will prevent the input from receiving touch events and make sure they are being handled by the Touchable. */} - - - - - setIsVisible(false)} - /> - - ) -} + + + + + setIsVisible(false)} + /> + + ) + } +) const tracks = { tappedGlobalSearchBar: ({ ownerType }: { ownerType: OwnerType }) => ({ diff --git a/src/app/Scenes/Search/Search.tsx b/src/app/Scenes/Search/Search.tsx index 96ca8db4936..f24f7729b3e 100644 --- a/src/app/Scenes/Search/Search.tsx +++ b/src/app/Scenes/Search/Search.tsx @@ -44,6 +44,7 @@ export const searchQueryDefaultVariables: SearchQuery$variables = { } export const Search: React.FC = () => { + const searchInputRef = useRef(null) const enableNewSearchModal = useFeatureFlag("AREnableNewSearchModal") const searchPillsRef = useRef(null) const [searchQuery, setSearchQuery] = useState("") @@ -62,9 +63,13 @@ export const Search: React.FC = () => { useRefetchWhenQueryChanged({ query: searchQuery, refetch }) + // Focus input and open keyboard on bottom nav Search tab double-tab const scrollableRef = useBottomTabsScrollToTop("search", () => { - // Focus input and open keyboard on bottom nav Search tab double-tab - searchProviderValues.inputRef.current?.focus() + if (enableNewSearchModal) { + searchInputRef.current?.focus() + } else { + searchProviderValues.inputRef.current?.focus() + } }) // TODO: to be removed on ES results PR @@ -135,7 +140,7 @@ export const Search: React.FC = () => { {enableNewSearchModal ? ( - + ) : ( Date: Tue, 26 Nov 2024 12:25:18 +0100 Subject: [PATCH 2/2] fix: race condition --- src/app/Components/GlobalSearchInput/GlobalSearchInput.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/Components/GlobalSearchInput/GlobalSearchInput.tsx b/src/app/Components/GlobalSearchInput/GlobalSearchInput.tsx index 10103bb9c7f..e7c0aa8c162 100644 --- a/src/app/Components/GlobalSearchInput/GlobalSearchInput.tsx +++ b/src/app/Components/GlobalSearchInput/GlobalSearchInput.tsx @@ -3,6 +3,7 @@ import { Flex, RoundSearchInput, Touchable } from "@artsy/palette-mobile" import { GlobalSearchInputOverlay } from "app/Components/GlobalSearchInput/GlobalSearchInputOverlay" import { useDismissSearchOverlayOnTabBarPress } from "app/Components/GlobalSearchInput/utils/useDismissSearchOverlayOnTabBarPress" import { ICON_HIT_SLOP } from "app/Components/constants" +import { useDebouncedValue } from "app/utils/hooks/useDebouncedValue" import { forwardRef, Fragment, useImperativeHandle, useState } from "react" import { useTracking } from "react-tracking" @@ -16,13 +17,14 @@ export type GlobalSearchInput = { export const GlobalSearchInput = forwardRef( ({ ownerType }, ref) => { const [isVisible, setIsVisible] = useState(false) + const debouncedIsVisible = useDebouncedValue({ value: isVisible }) const tracking = useTracking() useDismissSearchOverlayOnTabBarPress({ isVisible, ownerType, setIsVisible }) useImperativeHandle(ref, () => ({ focus: () => { - if (!isVisible) { + if (!debouncedIsVisible.debouncedValue) { setIsVisible(true) } },