diff --git a/src/App.tsx b/src/App.tsx index e7dd3684ef39..5d2450347b10 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -39,6 +39,7 @@ import CONFIG from './CONFIG'; import Expensify from './Expensify'; import {CurrentReportIDContextProvider} from './hooks/useCurrentReportID'; import useDefaultDragAndDrop from './hooks/useDefaultDragAndDrop'; +import HybridAppHandler from './HybridAppHandler'; import OnyxUpdateManager from './libs/actions/OnyxUpdateManager'; import {ReportAttachmentsProvider} from './pages/home/report/ReportAttachmentsContext'; import type {Route} from './ROUTES'; @@ -54,8 +55,6 @@ type AppProps = { url?: Route; /** Serialized configuration data required to initialize the React Native app (e.g. authentication details) */ hybridAppSettings?: string; - /** A timestamp indicating when the initial properties were last updated, used to detect changes */ - timestamp?: string; }; LogBox.ignoreLogs([ @@ -71,18 +70,14 @@ const fill = {flex: 1}; const StrictModeWrapper = CONFIG.USE_REACT_STRICT_MODE_IN_DEV ? React.StrictMode : ({children}: {children: React.ReactElement}) => children; -function App({url, hybridAppSettings, timestamp}: AppProps) { +function App({url, hybridAppSettings}: AppProps) { useDefaultDragAndDrop(); OnyxUpdateManager(); return ( - + + {CONFIG.IS_HYBRID_APP && ( + + )} diff --git a/src/Expensify.tsx b/src/Expensify.tsx index 5c60815cbedb..ad33fca33fbe 100644 --- a/src/Expensify.tsx +++ b/src/Expensify.tsx @@ -215,6 +215,10 @@ function Expensify() { // Open chat report from a deep link (only mobile native) Linking.addEventListener('url', (state) => { + // We use custom deeplink handler in HybridAppHandler + if (CONFIG.IS_HYBRID_APP) { + return; + } Report.openReportFromDeepLink(state.url); }); diff --git a/src/HybridAppHandler.tsx b/src/HybridAppHandler.tsx new file mode 100644 index 000000000000..61401c63e5a2 --- /dev/null +++ b/src/HybridAppHandler.tsx @@ -0,0 +1,57 @@ +import {findFocusedRoute} from '@react-navigation/native'; +import {useContext, useEffect, useState} from 'react'; +import {Linking} from 'react-native'; +import type {AppProps} from './App'; +import CONST from './CONST'; +import {signInAfterTransitionFromOldDot} from './libs/actions/Session'; +import Navigation, {navigationRef} from './libs/Navigation/Navigation'; +import type {Route} from './ROUTES'; +import ROUTES from './ROUTES'; +import SCREENS from './SCREENS'; +import SplashScreenStateContext from './SplashScreenStateContext'; + +function handleHybridUrlNavigation(url: Route) { + const parsedUrl = Navigation.parseHybridAppUrl(url); + + Navigation.isNavigationReady().then(() => { + if (parsedUrl.startsWith(`/${ROUTES.SHARE_ROOT}`)) { + const focusRoute = findFocusedRoute(navigationRef.getRootState()); + if (focusRoute?.name === SCREENS.SHARE.SHARE_DETAILS || focusRoute?.name === SCREENS.SHARE.SUBMIT_DETAILS) { + Navigation.goBack(ROUTES.SHARE_ROOT); + return; + } + } + Navigation.navigate(parsedUrl); + }); +} + +function HybridAppHandler({url, hybridAppSettings}: AppProps) { + const [signInHandled, setSignInHandled] = useState(false); + const {setSplashScreenState} = useContext(SplashScreenStateContext); + + useEffect(() => { + const listener = Linking.addEventListener('url', (state) => { + handleHybridUrlNavigation(state.url as Route); + }); + + return () => { + listener.remove(); + }; + }, []); + + if (!url || !hybridAppSettings || signInHandled) { + return null; + } + + signInAfterTransitionFromOldDot(hybridAppSettings).then(() => { + handleHybridUrlNavigation(url); + setSplashScreenState(CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN); + setSignInHandled(true); + }); + + return null; +} + +HybridAppHandler.displayName = 'HybridAppHandler'; + +export default HybridAppHandler; diff --git a/src/components/InitialURLContextProvider.tsx b/src/components/InitialURLContextProvider.tsx index c1a0ce3cbd89..1aa40d6c1d7a 100644 --- a/src/components/InitialURLContextProvider.tsx +++ b/src/components/InitialURLContextProvider.tsx @@ -1,15 +1,8 @@ -import {findFocusedRoute} from '@react-navigation/native'; import React, {createContext, useEffect, useMemo, useState} from 'react'; import type {ReactNode} from 'react'; import {Linking} from 'react-native'; -import {signInAfterTransitionFromOldDot} from '@libs/actions/Session'; -import Navigation, {navigationRef} from '@navigation/Navigation'; import type {AppProps} from '@src/App'; -import CONST from '@src/CONST'; import type {Route} from '@src/ROUTES'; -import ROUTES from '@src/ROUTES'; -import SCREENS from '@src/SCREENS'; -import {useSplashScreenStateContext} from '@src/SplashScreenStateContext'; type InitialUrlContextType = { initialURL: Route | undefined; @@ -27,40 +20,18 @@ type InitialURLContextProviderProps = AppProps & { children: ReactNode; }; -function InitialURLContextProvider({children, url, hybridAppSettings, timestamp}: InitialURLContextProviderProps) { +function InitialURLContextProvider({children, url}: InitialURLContextProviderProps) { const [initialURL, setInitialURL] = useState(); - const {splashScreenState, setSplashScreenState} = useSplashScreenStateContext(); useEffect(() => { - if (url && hybridAppSettings) { - signInAfterTransitionFromOldDot(hybridAppSettings).then(() => { - setInitialURL(url); - - const parsedUrl = Navigation.parseHybridAppUrl(url); - - Navigation.isNavigationReady().then(() => { - if (parsedUrl.startsWith(`/${ROUTES.SHARE_ROOT}`)) { - const focusRoute = findFocusedRoute(navigationRef.getRootState()); - if (focusRoute?.name === SCREENS.SHARE.SHARE_DETAILS || focusRoute?.name === SCREENS.SHARE.SUBMIT_DETAILS) { - Navigation.goBack(ROUTES.SHARE_ROOT); - return; - } - } - Navigation.navigate(parsedUrl); - }); - - if (splashScreenState === CONST.BOOT_SPLASH_STATE.HIDDEN) { - return; - } - setSplashScreenState(CONST.BOOT_SPLASH_STATE.READY_TO_BE_HIDDEN); - }); + if (url) { + setInitialURL(url); return; } Linking.getInitialURL().then((initURL) => { setInitialURL(initURL as Route); }); - // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [url, hybridAppSettings, timestamp]); + }, [url]); const initialUrlContext = useMemo( () => ({