import { useEffect, useRef, useState } from "react"; import { Alert, Pressable, Text, View } from "react-native"; import { router, useLocalSearchParams } from "expo-router"; import { useKeepAwake } from "expo-keep-awake"; import * as Haptics from "expo-haptics"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { api } from "@/lib/api"; import { t } from "@/lib/i18n"; import { Body, Button, Card, ErrorView, Heading, LoadingView, Row, Screen, Small, Spacer, } from "@/components/ui"; import { colors, spacing } from "@/lib/theme"; /** * Cooking Mode (spec §41): stora steg, stora knappar, skärmen vaken, * timers per steg, portionsskalning. Avslutas med "jag lagade detta" * som drar lager, loggar måltid och kan skapa matlådor (spec §23–24). */ interface RecipeForCooking { id: string; titleSv: string; portions: number; steps: Array<{ id: string; stepNumber: number; instructionSv: string; timerSeconds: number | null; temperatureC: number | null; tip: string | null; }>; } export default function CookingScreen() { useKeepAwake(); const { id, portions: portionsParam } = useLocalSearchParams<{ id: string; portions?: string }>(); const queryClient = useQueryClient(); const [stepIndex, setStepIndex] = useState(0); const [timerLeft, setTimerLeft] = useState(null); const timerRef = useRef | null>(null); const [finishing, setFinishing] = useState(false); const [completedSessionId, setCompletedSessionId] = useState(null); const [mealBoxPortions, setMealBoxPortions] = useState(0); const [actualPortionsEaten, setActualPortionsEaten] = useState(null); const [leftoverEstimatePortions, setLeftoverEstimatePortions] = useState(null); const query = useQuery({ queryKey: ["recipe", id], queryFn: () => api(`/v1/recipes/${id}`), }); const cook = useMutation({ mutationFn: (body: unknown) => api<{ sessionId: string; mealBoxMutations?: Array<{ mealBoxId: string; deltaPortions: number; frozen: boolean }>; }>(`/v1/recipes/${id}/cook`, { method: "POST", body }), onSuccess: async (data) => { await queryClient.invalidateQueries({ queryKey: ["inventory"] }); await queryClient.invalidateQueries({ queryKey: ["day"] }); await queryClient.invalidateQueries({ queryKey: ["what-to-eat"] }); setCompletedSessionId(data.sessionId); }, onError: (err) => Alert.alert(t("common.oops"), err instanceof Error ? err.message : t("common.error")), }); const undo = useMutation({ mutationFn: () => api(`/v1/cooking-sessions/${completedSessionId}/undo`, { method: "POST" }), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["meal-boxes"] }); await queryClient.invalidateQueries({ queryKey: ["inventory"] }); await queryClient.invalidateQueries({ queryKey: ["day"] }); Alert.alert(t("common.done"), t("mealbox.undoSuccess")); router.dismissAll(); }, onError: (err) => Alert.alert(t("common.oops"), err instanceof Error ? err.message : t("common.error")), }); useEffect(() => { return () => { if (timerRef.current) clearInterval(timerRef.current); }; }, []); if (query.isLoading) return ; if (query.isError || !query.data) return void query.refetch()} />; const recipe = query.data; const portionsCooked = Number(portionsParam ?? recipe.portions) || recipe.portions; const step = recipe.steps[stepIndex]; const isLast = stepIndex >= recipe.steps.length - 1; const startTimer = (seconds: number) => { if (timerRef.current) clearInterval(timerRef.current); setTimerLeft(seconds); timerRef.current = setInterval(() => { setTimerLeft((prev) => { if (prev == null || prev <= 1) { if (timerRef.current) clearInterval(timerRef.current); void Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); return 0; } return prev - 1; }); }, 1000); }; const finish = () => { setFinishing(true); }; if (completedSessionId) { return ( ✅ {t("cooked.title")} {recipe.titleSv} {t("cooked.portionsCooked")}: {portionsCooked} {t("mealbox.guidanceNote")}