177 lines
5.2 KiB
TypeScript
177 lines
5.2 KiB
TypeScript
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<typeof setInterval> | 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 (
|
|
<ThemeProvider value={LIGHT_THEME}>
|
|
<View style={{ flex: 1 }}>
|
|
<Stack screenOptions={{ headerShown: false }} />
|
|
<PortalHost />
|
|
<GlobalLoadingOverlay />
|
|
<ChatwootFloatingButton />
|
|
</View>
|
|
</ThemeProvider>
|
|
);
|
|
}
|
|
|
|
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 <AppContent />;
|
|
}
|