import "../global.css"; import "~/lib/i18n"; import { Theme, ThemeProvider, DefaultTheme } from "@react-navigation/native"; import { Stack, SplashScreen, router, usePathname } from "expo-router"; import { StatusBar } from "expo-status-bar"; import * as React from "react"; import { useFonts } from "expo-font"; import { PortalHost } from "@rn-primitives/portal"; import { useEffect, useRef } from "react"; import { View } from "react-native"; import { useAuthStore, useContactsStore, useRecipientsStore, } from "~/lib/stores"; import { setGlobalLoading } from "~/lib/stores/uiStore"; import GlobalLoadingOverlay from "~/components/ui/GlobalLoadingOverlay"; import ChatwootFloatingButton from "~/components/other/ChatwootFloatingButton"; import { enableRouterLoader } from "~/lib/navigation/routerLoader"; import { useFCM } from "~/lib/hooks/useFCM"; import * as Network from "expo-network"; import { HOME } from "~/lib/routes"; const NAV_THEME = { light: { background: "hsl(0 0% 100%)", // background border: "hsla(30,100%,84%,0.24)", // border uses secondary color (FFB668) with 30% opacity card: "hsla(145, 45%, 50%, 0.80)", // card uses primary background color (4CD080 with 8% opacity) notification: "hsl(0 84.2% 60.2%)", // destructive primary: "hsl(145, 45%, 25%)", // primary color 105D38 text: "hsl(0, 0%, 100%)", // text is white }, }; const LIGHT_THEME: Theme = { ...DefaultTheme, colors: NAV_THEME.light, }; export { // Catch any errors thrown by the Layout component. ErrorBoundary, } from "expo-router"; // Component to initialize stores function AppContent() { const { user, loading, initializeAuth } = useAuthStore(); const { initialize: initializeContacts } = useContactsStore(); const { initialize: initializeRecipients } = useRecipientsStore(); const pathname = usePathname(); // Track if we've already shown the initial auth loader const hasInitializedAuth = useRef(false); // Initialize FCM (Android only) useFCM(); // Show opaque loader during initial auth check useEffect(() => { if (!hasInitializedAuth.current) { // Show opaque loader on mount (auth is loading by default) setGlobalLoading(true, { opaque: true }); hasInitializedAuth.current = true; } }, []); // Hide loader when auth state is determined useEffect(() => { if (!loading && hasInitializedAuth.current) { setGlobalLoading(false); } }, [loading]); // Initialize auth listener useEffect(() => { console.log("Initializing auth listener"); const unsubscribe = initializeAuth(); return unsubscribe; }, [initializeAuth]); useEffect(() => { enableRouterLoader(); }, []); // Initialize contacts - defer to not block navigation useEffect(() => { // Defer contacts initialization to avoid blocking navigation const initContacts = async () => { // Wait for navigation to settle first await new Promise((resolve) => setTimeout(resolve, 1000)); console.log("Initializing contacts"); await initializeContacts(); }; initContacts(); }, [initializeContacts]); // Initialize recipients when user changes useEffect(() => { if (user && !loading) { console.log("Initializing recipients for user:", user.uid); initializeRecipients(user); } }, [user, loading, initializeRecipients]); useEffect(() => { let interval: ReturnType | null = null; const checkNetwork = async () => { try { const state = await Network.getNetworkStateAsync(); const isOffline = !state.isConnected || state.isInternetReachable === false; const isOnNoInternet = pathname?.includes("nointernet"); if (isOffline) { if (!isOnNoInternet) { router.replace("/nointernet"); } } else { if (isOnNoInternet) { router.replace(HOME); } } } catch (error) { console.log("Network check error", error); } }; checkNetwork(); interval = setInterval(checkNetwork, 10000); return () => { if (interval) { clearInterval(interval); } }; }, [pathname]); return ( ); } export default function RootLayout() { const [fontsLoaded] = useFonts({ "DMSans-Regular": require("../../assets/fonts/DMSans-Regular.ttf"), "DMSans-Bold": require("../../assets/fonts/DMSans-Bold.ttf"), "DMSans-Black": require("../../assets/fonts/DMSans-Black.ttf"), "DMSans-Medium": require("../../assets/fonts/DMSans-Medium.ttf"), "DMSans-SemiBold": require("../../assets/fonts/DMSans-SemiBold.ttf"), "DMSans-Thin": require("../../assets/fonts/DMSans-Thin.ttf"), "DMSans-ExtraBold": require("../../assets/fonts/DMSans-ExtraBold.ttf"), "DMSans-ExtraLight": require("../../assets/fonts/DMSans-ExtraLight.ttf"), "DMSans-Light": require("../../assets/fonts/DMSans-Light.ttf"), }); useEffect(() => { if (fontsLoaded) { SplashScreen.hideAsync(); } }, [fontsLoaded]); if (!fontsLoaded) return null; return ; }