From d914193f23f1d00b40fac21d2fb0165f2bc6f2ab Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Thu, 21 Nov 2024 15:45:24 +0100 Subject: [PATCH] chore: search overlay on tab press (#11165) chore: dimsis overlay on tab press --- .../GlobalSearchInput.tests.tsx | 11 ++++-- .../GlobalSearchInput/GlobalSearchInput.tsx | 23 +++++++----- .../GlobalSearchInputOverlay.tsx | 12 ++++--- .../useDismissSearchOverlayOnTabBarPress.ts | 35 +++++++++++++++++++ .../Navigation/AuthenticatedRoutes/Tabs.tsx | 3 +- .../Scenes/HomeView/Components/HomeHeader.tsx | 3 +- src/app/Scenes/HomeView/HomeView.tsx | 2 +- src/app/Scenes/Search/Search.tsx | 4 +-- 8 files changed, 72 insertions(+), 21 deletions(-) create mode 100644 src/app/Components/GlobalSearchInput/utils/useDismissSearchOverlayOnTabBarPress.ts diff --git a/src/app/Components/GlobalSearchInput/GlobalSearchInput.tests.tsx b/src/app/Components/GlobalSearchInput/GlobalSearchInput.tests.tsx index c2283e9857b..dd4963a9e36 100644 --- a/src/app/Components/GlobalSearchInput/GlobalSearchInput.tests.tsx +++ b/src/app/Components/GlobalSearchInput/GlobalSearchInput.tests.tsx @@ -1,3 +1,4 @@ +import { OwnerType } from "@artsy/cohesion" import { fireEvent, screen } from "@testing-library/react-native" import { GlobalSearchInput } from "app/Components/GlobalSearchInput/GlobalSearchInput" import { useSelectedTab } from "app/utils/hooks/useSelectedTab" @@ -8,6 +9,10 @@ jest.mock("app/utils/hooks/useSelectedTab", () => ({ useSelectedTab: jest.fn(), })) +jest.mock("app/Components/GlobalSearchInput/utils/useDismissSearchOverlayOnTabBarPress", () => ({ + useDismissSearchOverlayOnTabBarPress: jest.fn(), +})) + describe("GlobalSearchInput", () => { const mockUseledTab = useSelectedTab as jest.Mock @@ -16,20 +21,20 @@ describe("GlobalSearchInput", () => { }) it("renders the search label properly", () => { - renderWithWrappers() + renderWithWrappers() expect(/Search Artsy/).toBeTruthy() }) it("tracks the search bar tapped event", () => { - renderWithWrappers() + renderWithWrappers() fireEvent.press(screen.getByTestId("search-button")) expect(mockTrackEvent).toHaveBeenCalledWith( expect.objectContaining({ action: "tappedGlobalSearchBar", - context_module: "home", + context_screen_owner_type: "home", }) ) }) diff --git a/src/app/Components/GlobalSearchInput/GlobalSearchInput.tsx b/src/app/Components/GlobalSearchInput/GlobalSearchInput.tsx index 8200c20bced..6d750f6abcc 100644 --- a/src/app/Components/GlobalSearchInput/GlobalSearchInput.tsx +++ b/src/app/Components/GlobalSearchInput/GlobalSearchInput.tsx @@ -1,15 +1,18 @@ -import { ActionType, ContextModule } from "@artsy/cohesion" +import { ActionType, OwnerType } from "@artsy/cohesion" 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 { useSelectedTab } from "app/utils/hooks/useSelectedTab" import { Fragment, useState } from "react" import { useTracking } from "react-tracking" -export const GlobalSearchInput: React.FC<{}> = () => { +export const GlobalSearchInput: React.FC<{ + ownerType: OwnerType +}> = ({ ownerType }) => { const [isVisible, setIsVisible] = useState(false) const tracking = useTracking() - const selectedTab = useSelectedTab() + + useDismissSearchOverlayOnTabBarPress({ isVisible, ownerType, setIsVisible }) return ( @@ -17,7 +20,7 @@ export const GlobalSearchInput: React.FC<{}> = () => { onPress={() => { tracking.trackEvent( tracks.tappedGlobalSearchBar({ - contextModule: selectedTab as ContextModule, + ownerType, }) ) setIsVisible(true) @@ -40,14 +43,18 @@ export const GlobalSearchInput: React.FC<{}> = () => { /> - setIsVisible(false)} /> + setIsVisible(false)} + /> ) } const tracks = { - tappedGlobalSearchBar: ({ contextModule }: { contextModule: ContextModule }) => ({ + tappedGlobalSearchBar: ({ ownerType }: { ownerType: OwnerType }) => ({ action: ActionType.tappedGlobalSearchBar, - context_module: contextModule, + context_screen_owner_type: ownerType, }), } diff --git a/src/app/Components/GlobalSearchInput/GlobalSearchInputOverlay.tsx b/src/app/Components/GlobalSearchInput/GlobalSearchInputOverlay.tsx index e0d2aa5026a..a81c08c00f4 100644 --- a/src/app/Components/GlobalSearchInput/GlobalSearchInputOverlay.tsx +++ b/src/app/Components/GlobalSearchInput/GlobalSearchInputOverlay.tsx @@ -1,3 +1,4 @@ +import { OwnerType } from "@artsy/cohesion" import { Box, Flex, RoundSearchInput, Spacer, Spinner, useSpace } from "@artsy/palette-mobile" import { Portal } from "@gorhom/portal" import { FadeIn } from "app/Components/FadeIn" @@ -78,10 +79,11 @@ const GlobalSearchInputOverlayContent: React.FC<{ query: string }> = ({ query }) ) } -export const GlobalSearchInputOverlay: React.FC<{ visible: boolean; hideModal: () => void }> = ({ - visible, - hideModal, -}) => { +export const GlobalSearchInputOverlay: React.FC<{ + ownerType: OwnerType + visible: boolean + hideModal: () => void +}> = ({ hideModal, ownerType, visible }) => { const [query, setQuery] = useState("") const insets = useSafeAreaInsets() @@ -104,7 +106,7 @@ export const GlobalSearchInputOverlay: React.FC<{ visible: boolean; hideModal: ( return ( - + diff --git a/src/app/Components/GlobalSearchInput/utils/useDismissSearchOverlayOnTabBarPress.ts b/src/app/Components/GlobalSearchInput/utils/useDismissSearchOverlayOnTabBarPress.ts new file mode 100644 index 00000000000..5b427fe72c0 --- /dev/null +++ b/src/app/Components/GlobalSearchInput/utils/useDismissSearchOverlayOnTabBarPress.ts @@ -0,0 +1,35 @@ +import { useIsFocused, useNavigation } from "@react-navigation/native" +import { GlobalStore } from "app/store/GlobalStore" +import { useEffect } from "react" + +/** + * Dismisses the GlobalSearchInputOverlay when the tab bar is pressed + */ +export const useDismissSearchOverlayOnTabBarPress = ({ + isVisible, + ownerType, + setIsVisible, +}: { + isVisible: boolean + ownerType: string + setIsVisible: React.Dispatch> +}) => { + const isFocused = useIsFocused() + const selectedTab = GlobalStore.useAppState((state) => state.bottomTabs.sessionState.selectedTab) + + const navigation = useNavigation() + + useEffect(() => { + const tabsNavigation = navigation?.getParent() + const unsubscribe = tabsNavigation?.addListener("tabPress" as any, () => { + if (!isFocused || !isVisible) { + return + } + + if (ownerType === selectedTab.toLowerCase()) { + setIsVisible(false) + } + }) + return unsubscribe + }, [isVisible, selectedTab, navigation, isFocused]) +} diff --git a/src/app/Navigation/AuthenticatedRoutes/Tabs.tsx b/src/app/Navigation/AuthenticatedRoutes/Tabs.tsx index 38d3a66cb61..11128615870 100644 --- a/src/app/Navigation/AuthenticatedRoutes/Tabs.tsx +++ b/src/app/Navigation/AuthenticatedRoutes/Tabs.tsx @@ -13,7 +13,7 @@ import { BottomTabsButton } from "app/Scenes/BottomTabs/BottomTabsButton" import { bottomTabsConfig } from "app/Scenes/BottomTabs/bottomTabsConfig" import { OnboardingQuiz } from "app/Scenes/Onboarding/OnboardingQuiz/OnboardingQuiz" import { GlobalStore } from "app/store/GlobalStore" -import { internal_navigationRef } from "app/system/navigation/navigate" +import { internal_navigationRef, switchTab } from "app/system/navigation/navigate" import { postEventToProviders } from "app/utils/track/providers" import { Platform } from "react-native" @@ -66,6 +66,7 @@ const AppTabs: React.FC = () => { const tabName = Object.keys(bottomTabsConfig).find((tab) => e.target?.startsWith(tab)) if (tabName) { + switchTab(tabName as BottomTabType) postEventToProviders( tappedTabBar({ tab: bottomTabsConfig[tabName as BottomTabType].analyticsDescription, diff --git a/src/app/Scenes/HomeView/Components/HomeHeader.tsx b/src/app/Scenes/HomeView/Components/HomeHeader.tsx index f926aaad9c9..076c4477589 100644 --- a/src/app/Scenes/HomeView/Components/HomeHeader.tsx +++ b/src/app/Scenes/HomeView/Components/HomeHeader.tsx @@ -1,3 +1,4 @@ +import { OwnerType } from "@artsy/cohesion" import { ArtsyLogoBlackIcon, Box, Flex } from "@artsy/palette-mobile" import { GlobalSearchInput } from "app/Components/GlobalSearchInput/GlobalSearchInput" import { PaymentFailureBanner } from "app/Scenes/HomeView/Components/PaymentFailureBanner" @@ -30,7 +31,7 @@ export const HomeHeader: React.FC = () => { alignItems="center" > - + diff --git a/src/app/Scenes/HomeView/HomeView.tsx b/src/app/Scenes/HomeView/HomeView.tsx index bf1d0c497e0..37ad6f8a33e 100644 --- a/src/app/Scenes/HomeView/HomeView.tsx +++ b/src/app/Scenes/HomeView/HomeView.tsx @@ -205,7 +205,7 @@ export const HomeViewScreen: React.FC = () => { }> - + ) diff --git a/src/app/Scenes/Search/Search.tsx b/src/app/Scenes/Search/Search.tsx index 6487e89d167..941bc69aed4 100644 --- a/src/app/Scenes/Search/Search.tsx +++ b/src/app/Scenes/Search/Search.tsx @@ -135,7 +135,7 @@ export const Search: React.FC = () => { {enableNewSearchModal ? ( - + ) : ( = () => { - + ) }