From 4ea8724c0c3d428a7bf4ccb692090c9eae6de1ca Mon Sep 17 00:00:00 2001 From: Mounir Dhahri Date: Fri, 15 Nov 2024 17:56:18 +0100 Subject: [PATCH] fix: avoid making home view query requests when opening deep links Co-authored-by: Ole Co-authored-by: Anandaroop Roy --- src/app/Scenes/Home/HomeContainer.tsx | 9 ++++ src/app/Scenes/HomeView/HomeView.tsx | 27 +++-------- src/app/utils/hooks/useIsDeepLink.tests.ts | 54 ++++++++++++++++++++++ src/app/utils/hooks/useIsDeepLink.ts | 42 +++++++++++++++++ 4 files changed, 111 insertions(+), 21 deletions(-) create mode 100644 src/app/utils/hooks/useIsDeepLink.tests.ts create mode 100644 src/app/utils/hooks/useIsDeepLink.ts diff --git a/src/app/Scenes/Home/HomeContainer.tsx b/src/app/Scenes/Home/HomeContainer.tsx index af6bee79638..d6db7e28c8e 100644 --- a/src/app/Scenes/Home/HomeContainer.tsx +++ b/src/app/Scenes/Home/HomeContainer.tsx @@ -6,6 +6,7 @@ import { GlobalStore } from "app/store/GlobalStore" import { navigate } from "app/system/navigation/navigate" import { useDevToggle } from "app/utils/hooks/useDevToggle" import { useFeatureFlag } from "app/utils/hooks/useFeatureFlag" +import { useIsDeepLink } from "app/utils/hooks/useIsDeepLink" import { useSwitchStatusBarStyle } from "app/utils/useStatusBarStyle" import { useEffect } from "react" @@ -17,6 +18,8 @@ export const InnerHomeContainer = () => { const enableDynamicHomeView = useFeatureFlag("AREnableDynamicHomeView") const preferLegacyHomeScreen = useFeatureFlag("ARPreferLegacyHomeScreen") + const { isDeepLink } = useIsDeepLink() + const shouldDisplayNewHomeView = enableDynamicHomeView && !preferLegacyHomeScreen const navigateToArtQuiz = async () => { @@ -32,6 +35,12 @@ export const InnerHomeContainer = () => { } }, [artQuizState, navigateToArtQuiz, isNavigationReady]) + // We want to avoid rendering the home view when the user comes back from a deep link + // Because it triggers a lot of queries that affect the user's experience and can be avoided + if (isDeepLink !== false) { + return null + } + if (artQuizState === "incomplete") { return null } diff --git a/src/app/Scenes/HomeView/HomeView.tsx b/src/app/Scenes/HomeView/HomeView.tsx index f634c3e4752..198df652f39 100644 --- a/src/app/Scenes/HomeView/HomeView.tsx +++ b/src/app/Scenes/HomeView/HomeView.tsx @@ -23,18 +23,8 @@ import { ProvidePlaceholderContext } from "app/utils/placeholders" import { usePrefetch } from "app/utils/queryPrefetching" import { requestPushNotificationsPermission } from "app/utils/requestPushNotificationsPermission" import { useMaybePromptForReview } from "app/utils/useMaybePromptForReview" -import { - RefObject, - Suspense, - useCallback, - useEffect, - useState, - Suspense, - useCallback, - useEffect, - useState, -} from "react" -import { FlatList, RefreshControl, FlatList, Linking, RefreshControl } from "react-native" +import { RefObject, Suspense, useCallback, useEffect, useState } from "react" +import { FlatList, RefreshControl } from "react-native" import { fetchQuery, graphql, useLazyLoadQuery, usePaginationFragment } from "react-relay" export const NUMBER_OF_SECTIONS_TO_LOAD = 10 @@ -88,15 +78,10 @@ export const HomeView: React.FC = () => { const sections = extractNodes(data?.homeView.sectionsConnection) useEffect(() => { - Linking.getInitialURL().then((url) => { - const isDeepLink = !!url - if (!isDeepLink) { - prefetchUrl("search", searchQueryDefaultVariables) - prefetchUrl("my-profile") - prefetchUrl("inbox") - prefetchUrl("sell") - } - }) + prefetchUrl("search", searchQueryDefaultVariables) + prefetchUrl("my-profile") + prefetchUrl("inbox") + prefetchUrl("sell") }, []) useEffect(() => { diff --git a/src/app/utils/hooks/useIsDeepLink.tests.ts b/src/app/utils/hooks/useIsDeepLink.tests.ts new file mode 100644 index 00000000000..2da1e423980 --- /dev/null +++ b/src/app/utils/hooks/useIsDeepLink.tests.ts @@ -0,0 +1,54 @@ +import { renderHook } from "@testing-library/react-hooks" +import { useIsDeepLink } from "app/utils/hooks/useIsDeepLink" +import { Linking } from "react-native" + +// Existing mocks +const mockUseIsFocusedMock = jest.fn() +jest.mock("@react-navigation/native", () => ({ + useIsFocused: () => mockUseIsFocusedMock(), +})) + +jest.mock("react-native", () => ({ + Linking: { + getInitialURL: jest.fn(), + }, +})) + +// Start of the new test case +describe("useIsDeepLink", () => { + const mockLinkingGetInitialURL = Linking.getInitialURL as jest.Mock + + it("should return true if opened from a deep link", async () => { + // Setup the mock to return the specific URL + mockLinkingGetInitialURL.mockResolvedValue("artst.net/foo/bar") + mockUseIsFocusedMock.mockReturnValue(true) + + // Render the hook under test + const { result, waitForNextUpdate } = renderHook(() => useIsDeepLink()) + + // Wait for async effects to resolve + await waitForNextUpdate() + + expect(result.current).toEqual({ + isDeepLink: true, + }) + expect(mockUseIsFocusedMock).toHaveBeenCalled() + expect(mockLinkingGetInitialURL).toHaveBeenCalled() + }) + + it("should return false if not opened from a deep link", async () => { + // Setup the mock to return null + mockLinkingGetInitialURL.mockResolvedValue(null) + mockUseIsFocusedMock.mockReturnValue(true) + + // Render the hook under test + const { result, waitForNextUpdate } = renderHook(() => useIsDeepLink()) + + // Wait for async effects to resolve + await waitForNextUpdate() + + expect(result.current.isDeepLink).toEqual(false) + expect(mockUseIsFocusedMock).toHaveBeenCalled() + expect(mockLinkingGetInitialURL).toHaveBeenCalled() + }) +}) diff --git a/src/app/utils/hooks/useIsDeepLink.ts b/src/app/utils/hooks/useIsDeepLink.ts new file mode 100644 index 00000000000..f691edc9409 --- /dev/null +++ b/src/app/utils/hooks/useIsDeepLink.ts @@ -0,0 +1,42 @@ +import { useIsFocused } from "@react-navigation/native" +import { useEffect, useState } from "react" +import { Linking } from "react-native" + +/** + * This is a hook that returns whether the user came from a deep link or not + * This can be used to avoid rendering content in previous screens in react-navigation history + * @returns {isDeepLink: boolean | null} isDeepLink is true if the user came from a deep link + */ +export const useIsDeepLink = () => { + const [isDeepLink, setIsDeepLink] = useState(null) + + const isFocused = useIsFocused() + + useEffect(() => { + // When the user comes back from a deep link, + // we want to show content again + if (isFocused && isDeepLink === true) { + setIsDeepLink(false) + } + }, [isFocused]) + + useEffect(() => { + Linking.getInitialURL() + .then((url) => { + const isDeepLink = !!url + if (!isDeepLink) { + setIsDeepLink(false) + } else { + setIsDeepLink(true) + } + }) + .catch((error) => { + console.error("Error getting initial URL", error) + setIsDeepLink(false) + }) + }, []) + + return { + isDeepLink, + } +}