import { useEffect, useRef, useState } from 'react'; import { Animated, Easing, Image, StyleSheet, Text, View } from 'react-native'; import { Redirect, Stack, usePathname } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; import AsyncStorage from '@react-native-async-storage/async-storage'; import Purchases, { LOG_LEVEL } from 'react-native-purchases'; import { Platform } from 'react-native'; import Constants from 'expo-constants'; import { AppProvider, useApp } from '../context/AppContext'; import { CoachMarksProvider } from '../context/CoachMarksContext'; import { CoachMarksOverlay } from '../components/CoachMarksOverlay'; import { useColors } from '../constants/Colors'; import { initDatabase, AppMetaDb } from '../services/database'; import * as SecureStore from 'expo-secure-store'; import * as SplashScreen from 'expo-splash-screen'; import { AuthService } from '../services/authService'; import { PostHogProvider, usePostHog } from 'posthog-react-native'; // Prevent the splash screen from auto-hiding before asset loading is complete. SplashScreen.preventAutoHideAsync().catch(() => { }); const POSTHOG_API_KEY = process.env.EXPO_PUBLIC_POSTHOG_API_KEY || 'phc_FX6HRgx9NSpS5moxjMF6xyc37yMwjoeu6TbWUqNNKlk'; const SECURE_INSTALL_MARKER = 'greenlens_install_v1'; const ensureInstallConsistency = async (): Promise => { try { const sqliteMarker = AppMetaDb.get('install_marker_v2'); const secureMarker = await SecureStore.getItemAsync(SECURE_INSTALL_MARKER).catch(() => null); if (sqliteMarker === '1' && secureMarker === '1') { return false; // Alles gut, keine Neuinstallation } if (sqliteMarker === '1' || secureMarker === '1') { // Teilweise vorhanden -> heilen, nicht löschen AppMetaDb.set('install_marker_v2', '1'); await SecureStore.setItemAsync(SECURE_INSTALL_MARKER, '1'); return false; } // Fresh Install: Alles zurücksetzen await AuthService.logout(); await AsyncStorage.removeItem('greenlens_show_tour'); AppMetaDb.set('install_marker_v2', '1'); await SecureStore.setItemAsync(SECURE_INSTALL_MARKER, '1'); return true; } catch (error) { console.error('Failed to initialize install marker', error); return false; } }; import { AnimatedSplashScreen } from '../components/AnimatedSplashScreen'; function RootLayoutInner() { const { isDarkMode, colorPalette, signOut, session, isInitializing, isLoadingPlants } = useApp(); const colors = useColors(isDarkMode, colorPalette); const pathname = usePathname(); const [installCheckDone, setInstallCheckDone] = useState(false); const [splashAnimationComplete, setSplashAnimationComplete] = useState(false); const posthog = usePostHog(); useEffect(() => { // RevenueCat requires native store access — not available in Expo Go const isExpoGo = Constants.appOwnership === 'expo'; if (isExpoGo) { console.log('[RevenueCat] Skipping configure: running in Expo Go'); return; } Purchases.setLogLevel(LOG_LEVEL.VERBOSE); const iosApiKey = process.env.EXPO_PUBLIC_REVENUECAT_IOS_API_KEY || 'appl_hrSpsuUuVstbHhYIDnOqYxPOnmR'; const androidApiKey = process.env.EXPO_PUBLIC_REVENUECAT_ANDROID_API_KEY || 'goog_placeholder'; if (Platform.OS === 'ios') { Purchases.configure({ apiKey: iosApiKey }); } else if (Platform.OS === 'android') { Purchases.configure({ apiKey: androidApiKey }); } }, []); useEffect(() => { if (session?.serverUserId) { posthog.identify(session.serverUserId, { email: session.email, name: session.name, }); } else if (session === null) { posthog.reset(); } }, [session, posthog]); useEffect(() => { (async () => { const didResetSessionForFreshInstall = await ensureInstallConsistency(); if (didResetSessionForFreshInstall) { await signOut(); } setInstallCheckDone(true); })(); }, [signOut]); const isAppReady = installCheckDone && !isInitializing && !isLoadingPlants; let content = null; if (isAppReady) { if (!session) { // Only redirect if we are not already on an auth-related page or the scanner const isAuthPage = pathname.includes('onboarding') || pathname.includes('auth/') || pathname.includes('scanner'); if (!isAuthPage) { content = ; } else { content = ( ); } } else { content = ( <> ); } } return ( <> {content} {!splashAnimationComplete && ( setSplashAnimationComplete(true)} /> )} ); } export default function RootLayout() { initDatabase(); return ( ); }