import React, { useEffect, useRef, useState } from 'react'; import { AppState, AppStateStatus, Platform, View, ActivityIndicator, StyleSheet } from 'react-native'; import { Stack, useRouter } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { PaperProvider } from 'react-native-paper'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import * as Notifications from 'expo-notifications'; import { registerNotificationPresentationHandler } from '../src/services/notificationPreferences'; import { paperTheme } from '../src/theme'; import { AppBackButton } from '../src/components/common'; import AppPromptBar from '../src/components/common/AppPromptBar'; import AppDialogHost from '../src/components/common/AppDialogHost'; import { installAlertOverride } from '../src/services/alertOverride'; import { useAuthStore } from '../src/stores'; import { colors } from '../src/theme'; registerNotificationPresentationHandler(); const queryClient = new QueryClient({ defaultOptions: { queries: { retry: 2, staleTime: 5 * 60 * 1000, }, }, }); installAlertOverride(); if (Platform.OS === 'web' && typeof document !== 'undefined') { const style = document.createElement('style'); style.textContent = ` input:focus, textarea:focus, select:focus { outline: none !important; outline-width: 0 !important; box-shadow: none !important; } input:focus-visible, textarea:focus-visible { outline: none !important; } *:focus { outline: none !important; } *:focus-visible { outline: none !important; } `; document.head.appendChild(style); } function SessionGate({ children }: { children: React.ReactNode }) { const [ready, setReady] = useState(false); const fetchCurrentUser = useAuthStore((s) => s.fetchCurrentUser); useEffect(() => { fetchCurrentUser().finally(() => setReady(true)); }, [fetchCurrentUser]); if (!ready) { return ( ); } return <>{children}; } function NotificationBootstrap() { const appState = useRef(AppState.currentState); const notificationResponseListener = useRef(null); useEffect(() => { const initNotifications = async () => { const { loadNotificationPreferences } = await import('../src/services/notificationPreferences'); await loadNotificationPreferences(); const { systemNotificationService } = await import('../src/services/systemNotificationService'); await systemNotificationService.initialize(); const { initBackgroundService } = await import('../src/services/backgroundService'); await initBackgroundService(); const subscription = AppState.addEventListener('change', (nextAppState) => { if (appState.current.match(/inactive|background/) && nextAppState === 'active') { systemNotificationService.clearBadge(); } appState.current = nextAppState; }); notificationResponseListener.current = Notifications.addNotificationResponseReceivedListener( (response) => { void response.notification.request.content.data; } ); const notificationReceivedSubscription = Notifications.addNotificationReceivedListener( (notification) => { void notification; } ); return () => { subscription.remove(); notificationResponseListener.current?.remove(); notificationReceivedSubscription.remove(); }; }; initNotifications(); }, []); return null; } export default function RootLayout() { const router = useRouter(); return ( router.back()} />, }} /> router.back()} />, }} /> ); } const gateStyles = StyleSheet.create({ loading: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: colors.background.default, }, });