111 lines
3.6 KiB
TypeScript
111 lines
3.6 KiB
TypeScript
import { Redirect, Tabs } from "expo-router";
|
||
import { Text } from "react-native";
|
||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||
import { api, getConsent, type ConsentStatus } from "@/lib/api";
|
||
import { persistLanguageTag, useAuth } from "@/lib/auth";
|
||
import { colors } from "@/lib/theme";
|
||
import { getLanguageTag, t } from "@/lib/i18n";
|
||
import { setMeasurementSystem, setUnitLanguage } from "@/lib/units";
|
||
import { ConsentScreen } from "@/components/ConsentScreen";
|
||
import type { MeasurementSystem } from "@app/shared-types";
|
||
import { TERMS_VERSION } from "@app/shared-types";
|
||
|
||
function TabIcon({ glyph, focused }: { glyph: string; focused: boolean }) {
|
||
return <Text style={{ fontSize: 22, opacity: focused ? 1 : 0.45 }}>{glyph}</Text>;
|
||
}
|
||
|
||
interface LocalePrefs {
|
||
languageTag: string;
|
||
measurementSystem: MeasurementSystem;
|
||
}
|
||
|
||
/** Applicera backend-preferenser på språk/mått vid inloggad start (i18n M4–M5). */
|
||
function useApplyLocalePreferences(enabled: boolean) {
|
||
useQuery({
|
||
queryKey: ["locale-preferences"],
|
||
queryFn: async () => {
|
||
const prefs = await api<LocalePrefs>("/v1/me/locale-preferences");
|
||
setMeasurementSystem(prefs.measurementSystem);
|
||
setUnitLanguage(prefs.languageTag.split("-")[0] ?? "sv");
|
||
if (prefs.languageTag !== getLanguageTag()) await persistLanguageTag(prefs.languageTag);
|
||
return prefs;
|
||
},
|
||
enabled,
|
||
staleTime: 3600_000,
|
||
});
|
||
}
|
||
|
||
export default function TabsLayout() {
|
||
const accessToken = useAuth((s) => s.accessToken);
|
||
const onboardingCompleted = useAuth((s) => s.onboardingCompleted);
|
||
const onboardingStep = useAuth((s) => s.onboardingStep);
|
||
useApplyLocalePreferences(Boolean(accessToken));
|
||
const queryClient = useQueryClient();
|
||
|
||
const { data: consent, isLoading: consentLoading } = useQuery<ConsentStatus>({
|
||
queryKey: ["consent"],
|
||
queryFn: getConsent,
|
||
enabled: Boolean(accessToken),
|
||
staleTime: 60_000,
|
||
});
|
||
|
||
if (!accessToken) return <Redirect href="/(auth)/login" />;
|
||
if (consentLoading) return null;
|
||
if (!consent?.accepted || consent.version !== TERMS_VERSION) {
|
||
return (
|
||
<ConsentScreen
|
||
onAccepted={() => void queryClient.invalidateQueries({ queryKey: ["consent"] })}
|
||
/>
|
||
);
|
||
}
|
||
// Progressive onboarding (FAS 1b): only block if step A not done
|
||
if (!onboardingCompleted && onboardingStep === "a") return <Redirect href="/onboarding" />;
|
||
|
||
return (
|
||
<Tabs
|
||
screenOptions={{
|
||
headerShown: false,
|
||
tabBarActiveTintColor: colors.primaryDark,
|
||
tabBarInactiveTintColor: colors.textMuted,
|
||
tabBarStyle: { backgroundColor: colors.surface, borderTopColor: colors.border },
|
||
}}
|
||
>
|
||
<Tabs.Screen
|
||
name="index"
|
||
options={{
|
||
title: t("tabs.whatToEat"),
|
||
tabBarIcon: ({ focused }) => <TabIcon glyph="🍽️" focused={focused} />,
|
||
}}
|
||
/>
|
||
<Tabs.Screen
|
||
name="scan"
|
||
options={{
|
||
title: t("tabs.scan"),
|
||
tabBarIcon: ({ focused }) => <TabIcon glyph="📷" focused={focused} />,
|
||
}}
|
||
/>
|
||
<Tabs.Screen
|
||
name="my-day"
|
||
options={{
|
||
title: t("tabs.myDay"),
|
||
tabBarIcon: ({ focused }) => <TabIcon glyph="📊" focused={focused} />,
|
||
}}
|
||
/>
|
||
<Tabs.Screen
|
||
name="plan"
|
||
options={{
|
||
title: t("tabs.plan"),
|
||
tabBarIcon: ({ focused }) => <TabIcon glyph="📅" focused={focused} />,
|
||
}}
|
||
/>
|
||
<Tabs.Screen
|
||
name="home"
|
||
options={{
|
||
title: t("tabs.home"),
|
||
tabBarIcon: ({ focused }) => <TabIcon glyph="🏠" focused={focused} />,
|
||
}}
|
||
/>
|
||
</Tabs>
|
||
);
|
||
}
|