First commit
This commit is contained in:
+135
@@ -0,0 +1,135 @@
|
||||
import 'react-native-get-random-values';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { View, ActivityIndicator } from 'react-native';
|
||||
import { Stack } from 'expo-router';
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
import Toast from 'react-native-toast-message';
|
||||
import { useHouseholdStore, useRealtimeSync } from '../src/hooks/useHousehold';
|
||||
import { useNetworkSync } from '../src/hooks/useNetworkSync';
|
||||
import { getOrCreateDeviceId, getStoredHouseholdId, getHousehold, registerMember } from '../src/services/household';
|
||||
import { getFcmToken } from '../src/services/notifications';
|
||||
import { COLORS } from '../src/constants';
|
||||
|
||||
function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
|
||||
return Promise.race([
|
||||
promise,
|
||||
new Promise<never>((_, reject) =>
|
||||
setTimeout(() => reject(new Error('timeout')), ms)
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
function AppInit() {
|
||||
const setHousehold = useHouseholdStore((s) => s.setHousehold);
|
||||
const setDeviceId = useHouseholdStore((s) => s.setDeviceId);
|
||||
const setInitialized = useHouseholdStore((s) => s.setInitialized);
|
||||
const [hydrated, setHydrated] = useState(() => useHouseholdStore.persist.hasHydrated());
|
||||
|
||||
useRealtimeSync();
|
||||
useNetworkSync();
|
||||
|
||||
// Step 1: wait for Zustand AsyncStorage hydration
|
||||
useEffect(() => {
|
||||
if (hydrated) return;
|
||||
console.log('[AppInit] waiting for store hydration...');
|
||||
const unsub = useHouseholdStore.persist.onFinishHydration(() => {
|
||||
console.log('[AppInit] store hydrated');
|
||||
setHydrated(true);
|
||||
});
|
||||
return unsub;
|
||||
}, [hydrated]);
|
||||
|
||||
// Step 2: run init logic after hydration is confirmed
|
||||
useEffect(() => {
|
||||
if (!hydrated) return;
|
||||
|
||||
console.log('[AppInit] init start');
|
||||
(async () => {
|
||||
try {
|
||||
const deviceId = await getOrCreateDeviceId();
|
||||
console.log('[AppInit] deviceId:', deviceId.slice(0, 8));
|
||||
setDeviceId(deviceId);
|
||||
|
||||
const alreadyHasHousehold = useHouseholdStore.getState().household != null;
|
||||
console.log('[AppInit] alreadyHasHousehold:', alreadyHasHousehold);
|
||||
|
||||
if (!alreadyHasHousehold) {
|
||||
const householdId = await getStoredHouseholdId();
|
||||
console.log('[AppInit] storedHouseholdId:', householdId);
|
||||
|
||||
if (householdId) {
|
||||
try {
|
||||
console.log('[AppInit] fetching household from PocketBase...');
|
||||
const household = await withTimeout(getHousehold(householdId), 5000);
|
||||
if (household) {
|
||||
console.log('[AppInit] household loaded:', household.name);
|
||||
setHousehold(household);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('[AppInit] getHousehold failed (offline/timeout):', String(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[AppInit] unexpected error:', e);
|
||||
} finally {
|
||||
console.log('[AppInit] setInitialized()');
|
||||
setInitialized();
|
||||
const h = useHouseholdStore.getState().household;
|
||||
if (h) {
|
||||
getFcmToken()
|
||||
.catch(() => null)
|
||||
.then((token) => registerMember(h.id, token).catch(() => {}));
|
||||
}
|
||||
}
|
||||
})();
|
||||
}, [hydrated]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default function RootLayout() {
|
||||
const isInitialized = useHouseholdStore((s) => s.isInitialized);
|
||||
const setInitialized = useHouseholdStore((s) => s.setInitialized);
|
||||
|
||||
// Absolute safety net: force init after 8s regardless of what happened above
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
if (!useHouseholdStore.getState().isInitialized) {
|
||||
console.warn('[RootLayout] safety timeout fired — forcing init');
|
||||
setInitialized();
|
||||
}
|
||||
}, 8000);
|
||||
return () => clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||
<SafeAreaProvider>
|
||||
<AppInit />
|
||||
{!isInitialized ? (
|
||||
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: COLORS.white }}>
|
||||
<ActivityIndicator size="large" color={COLORS.primary} />
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<Stack>
|
||||
<Stack.Screen name="onboarding" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||
<Stack.Screen
|
||||
name="modals/add-item"
|
||||
options={{ presentation: 'modal', title: 'Artikel hinzufügen' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="modals/item-detail"
|
||||
options={{ presentation: 'modal', title: 'Artikel' }}
|
||||
/>
|
||||
</Stack>
|
||||
<Toast />
|
||||
</>
|
||||
)}
|
||||
</SafeAreaProvider>
|
||||
</GestureHandlerRootView>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user