102 lines
4.2 KiB
TypeScript
102 lines
4.2 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { Stack } from "expo-router";
|
|
import { StatusBar } from "expo-status-bar";
|
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
import { QueryClient } from "@tanstack/react-query";
|
|
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
|
|
import { createAsyncStoragePersister } from "@tanstack/query-async-storage-persister";
|
|
import { useAuth } from "@/lib/auth";
|
|
import { AnalyticsProvider } from "@/lib/analytics";
|
|
import { BRAND } from "@/lib/brand";
|
|
import { t, useI18nVersion } from "@/lib/i18n";
|
|
import { colors } from "@/lib/theme";
|
|
import { LoadingView } from "@/components/ui";
|
|
|
|
/**
|
|
* Rotlayout: React Query med AsyncStorage-persistens för Smart Cache/offline
|
|
* (spec §44: cachelagrade recept, lager och inköpslista fungerar offline).
|
|
*/
|
|
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
staleTime: 30_000,
|
|
gcTime: 7 * 24 * 3600_000, // behåll cache en vecka för offline-läge
|
|
retry: 1,
|
|
},
|
|
},
|
|
});
|
|
|
|
const persister = createAsyncStoragePersister({
|
|
storage: AsyncStorage,
|
|
key: `${BRAND.slug}-query-cache`,
|
|
});
|
|
|
|
export default function RootLayout() {
|
|
const hydrate = useAuth((s) => s.hydrate);
|
|
const hydrated = useAuth((s) => s.hydrated);
|
|
const [ready, setReady] = useState(false);
|
|
// Språkbyte utan omstart (i18n M4): versionen bumpar vid setLocale och
|
|
// key={} re-monterar hela trädet så att alla t()-strängar byts direkt.
|
|
const i18nVersion = useI18nVersion();
|
|
|
|
useEffect(() => {
|
|
void hydrate().then(() => setReady(true));
|
|
}, [hydrate]);
|
|
|
|
if (!hydrated || !ready) return <LoadingView />;
|
|
|
|
return (
|
|
<PersistQueryClientProvider
|
|
key={`i18n-${i18nVersion}`}
|
|
client={queryClient}
|
|
persistOptions={{ persister }}
|
|
>
|
|
<AnalyticsProvider>
|
|
<StatusBar style="dark" />
|
|
<Stack
|
|
screenOptions={{
|
|
headerStyle: { backgroundColor: colors.background },
|
|
headerTintColor: colors.text,
|
|
headerShadowVisible: false,
|
|
headerBackTitle: t("common.back"),
|
|
contentStyle: { backgroundColor: colors.background },
|
|
fullScreenGestureEnabled: true,
|
|
}}
|
|
>
|
|
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
|
<Stack.Screen name="(auth)/login" options={{ headerShown: false }} />
|
|
<Stack.Screen name="(auth)/register" options={{ headerShown: false }} />
|
|
<Stack.Screen name="(auth)/forgot-password" options={{ headerShown: false }} />
|
|
<Stack.Screen name="(auth)/reset-password" options={{ headerShown: false }} />
|
|
<Stack.Screen name="(auth)/verify-email" options={{ headerShown: false }} />
|
|
<Stack.Screen name="onboarding" options={{ headerShown: false, gestureEnabled: false }} />
|
|
<Stack.Screen name="recipe/[id]" options={{ title: "" }} />
|
|
<Stack.Screen
|
|
name="cooking/[id]"
|
|
options={{ title: "", presentation: "fullScreenModal" }}
|
|
/>
|
|
<Stack.Screen name="scan-review/[jobId]" options={{ title: t("scan.review.title") }} />
|
|
<Stack.Screen name="meal-review/[jobId]" options={{ title: t("scan.meal.title") }} />
|
|
<Stack.Screen name="reconciliation" options={{ title: t("reconciliation.title") }} />
|
|
<Stack.Screen name="scan-diff-review/[jobId]" options={{ title: t("scan.review.title") }} />
|
|
<Stack.Screen name="shopping" options={{ title: t("shopping.title") }} />
|
|
<Stack.Screen name="meal-boxes" options={{ title: t("mealbox.title") }} />
|
|
<Stack.Screen name="household" options={{ title: t("home.household") }} />
|
|
<Stack.Screen name="memory" options={{ title: t("memory.title") }} />
|
|
<Stack.Screen name="profile" options={{ title: t("profile.title") }} />
|
|
<Stack.Screen
|
|
name="paywall"
|
|
options={{ title: t("paywall.title"), presentation: "modal" }}
|
|
/>
|
|
<Stack.Screen
|
|
name="log-meal"
|
|
options={{ title: t("myday.logMeal"), presentation: "modal" }}
|
|
/>
|
|
<Stack.Screen name="barcode" options={{ title: "Streckkod" }} />
|
|
</Stack>
|
|
</AnalyticsProvider>
|
|
</PersistQueryClientProvider>
|
|
);
|
|
}
|