import { useEffect } from 'react';
import { AppState } from 'react-native';
import { Stack } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
import * as Notifications from 'expo-notifications';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { KeyboardProvider } from 'react-native-keyboard-controller';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { ActionSheetProvider } from '@expo/react-native-action-sheet';
import * as SplashScreen from 'expo-splash-screen';
import {
useFonts,
Nunito_400Regular,
Nunito_600SemiBold,
Nunito_700Bold,
Nunito_800ExtraBold,
} from '@expo-google-fonts/nunito';
import { supabase } from '../lib/supabase';
import { useAuthStore } from '../stores/auth';
import { useThemeStore } from '../stores/theme';
import { useRealtimeDebugStore } from '../stores/realtimeDebug';
import { useColors } from '../lib/theme';
import { useLanguageStore } from '../stores/language';
import { useAppLockStore } from '../stores/appLock';
import { BrandSplash } from '../components/BrandSplash';
import { AppLockGate } from '../components/AppLockGate';
import { DeviceLimitReachedSheet } from '../components/DeviceLimitReachedSheet';
import '../lib/i18n'; // i18next-Init via Side-Effect
import '../global.css';
SplashScreen.preventAutoHideAsync();
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowBanner: true,
shouldShowList: true,
shouldPlaySound: true,
shouldSetBadge: false,
}),
});
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 2,
staleTime: 1000 * 60,
},
},
});
function RootLayoutInner() {
const { loading, init } = useAuthStore();
const initTheme = useThemeStore((s) => s.init);
const colorScheme = useThemeStore((s) => s.colorScheme);
const initLanguage = useLanguageStore((s) => s.init);
const initAppLock = useAppLockStore((s) => s.init);
const appLockReady = useAppLockStore((s) => s.ready);
const initRealtimeDebug = useRealtimeDebugStore((s) => s.init);
const colors = useColors();
const [fontsLoaded] = useFonts({
Nunito_400Regular,
Nunito_600SemiBold,
Nunito_700Bold,
Nunito_800ExtraBold,
});
useEffect(() => {
init();
initTheme();
initLanguage();
initAppLock();
if (__DEV__) initRealtimeDebug();
}, []);
// Supabase-Doku-Pattern für RN: Token-Auto-Refresh nur wenn App aktiv ist.
// Plus Foreground-Reconnect via onAuthStateChange (TOKEN_REFRESHED →
// realtime.setAuth wird intern getriggert). Fixt den Realtime-Disconnect-Bug
// bei lange eingeloggten Usern (siehe `project_session_2026-05-15_push.md`).
useEffect(() => {
const sub = AppState.addEventListener('change', (state) => {
if (state === 'active') {
supabase.auth.startAutoRefresh();
} else {
supabase.auth.stopAutoRefresh();
}
});
return () => sub.remove();
}, []);
useEffect(() => {
if (fontsLoaded && !loading && appLockReady) {
SplashScreen.hideAsync();
}
}, [fontsLoaded, loading, appLockReady]);
if (!fontsLoaded || loading || !appLockReady) {
return ;
}
return (
);
}
export default function RootLayout() {
return (
);
}