import React, { useEffect, useMemo, useRef, useState } from "react"; import { Linking } from "react-native"; import { NavigationContainer, type LinkingOptions, useNavigationContainerRef, } from "@react-navigation/native"; import { SafeAreaProvider } from "react-native-safe-area-context"; import { StatusBar } from "expo-status-bar"; import AppNavigator from "./navigation/AppNavigator"; import type { RootStackParamList } from "./navigation/types"; import { SessionProvider, useSession } from "./state/session-context"; import { getNavigationTheme, useTheme } from "./theme"; function extractGameId(url: string): string | null { try { const parsed = new URL(url); const path = parsed.pathname || ""; if (parsed.protocol === "https:" || parsed.protocol === "http:") { const match = path.match(/^\/play\/?([^/]+)?/); const id = match?.[1]?.trim(); return id ? id : null; } if (parsed.host === "play") { const id = path.replace(/^\//, "").trim(); return id ? id : null; } const fallbackMatch = path.match(/^\/play\/?([^/]+)?/); const fallbackId = fallbackMatch?.[1]?.trim(); return fallbackId ? fallbackId : null; } catch { return null; } } function logDeepLink(url: string) { if (!__DEV__) return; const gameId = extractGameId(url); console.log(`[deep-link] url=${url} gameId=${gameId ?? "invalid"}`); } function RootNavigationGate() { const manager = useSession(); const navigationRef = useNavigationContainerRef(); const [navReady, setNavReady] = useState(false); const lastTargetRef = useRef(null); const lastLinkRef = useRef(null); const theme = useTheme(); const navigationTheme = getNavigationTheme(theme); const linking = useMemo>( () => ({ prefixes: ["negopoly://", "https://negopoly.fr"], config: { screens: { Entry: "play/:gameId", }, }, getInitialURL: async () => { const url = await Linking.getInitialURL(); if (url) { lastLinkRef.current = url; logDeepLink(url); } return url; }, subscribe: (listener) => { const onReceiveURL = ({ url }: { url: string }) => { if (!url) return; if (lastLinkRef.current === url) return; lastLinkRef.current = url; logDeepLink(url); listener(url); }; const subscription = Linking.addEventListener("url", onReceiveURL); return () => subscription.remove(); }, }), [], ); useEffect(() => { if (!navReady || !navigationRef.isReady()) return; let target: keyof RootStackParamList; if (!manager.sessionId) { target = "Entry"; } else if (!manager.session) { target = manager.connectionState === "error" ? "Entry" : "Lobby"; } else if (manager.session.status === "lobby") { target = "Lobby"; } else if (manager.isBanker) { target = "BankerTabs"; } else { target = "PlayerTabs"; } const currentRoute = navigationRef.getCurrentRoute(); if (currentRoute?.name === target || lastTargetRef.current === target) { return; } navigationRef.reset({ index: 0, routes: [{ name: target }], }); lastTargetRef.current = target; }, [ manager.sessionId, manager.session, manager.isBanker, manager.connectionState, navReady, navigationRef, ]); return ( setNavReady(true)} linking={linking} theme={navigationTheme} > ); } export default function App() { const theme = useTheme(); return ( ); }