import { useState } from "react"; import { Alert, StyleSheet, View } from "react-native"; import { router } from "expo-router"; import { CameraView, useCameraPermissions } from "expo-camera"; import { useQueryClient } from "@tanstack/react-query"; import { api } from "@/lib/api"; import { t } from "@/lib/i18n"; import { Body, Button, Card, Heading, LoadingView, Screen, Small, Spacer } from "@/components/ui"; import { colors } from "@/lib/theme"; /** * Streckkodsskanning (spec §11): läses LOKALT med kameran, slås upp mot * egen produktdatabas → Open Food Facts. Saknas produkten uppmanas * användaren att fota förpackningen (READ_NUTRITION_LABEL-flödet). */ interface BarcodeScanResponse { product: { id: string; name: string; brand: string | null; nutrition: { values: { kcal: number } } | null; } | null; } export default function BarcodeScreen() { const [permission, requestPermission] = useCameraPermissions(); const [busy, setBusy] = useState(false); const [lastCode, setLastCode] = useState(null); const queryClient = useQueryClient(); const onScanned = async (gtin: string) => { if (busy || gtin === lastCode) return; setBusy(true); setLastCode(gtin); try { const result = await api("/v1/scans", { method: "POST", body: { scanType: "barcode", barcode: gtin, imageCount: 0 }, }); if (result.product) { Alert.alert( result.product.name, `${result.product.brand ?? ""}\n${result.product.nutrition ? t("barcode.kcalPer100", { kcal: result.product.nutrition.values.kcal }) : t("barcode.noNutrition")}\n\n${t("barcode.addPrompt")}`, [ { text: t("common.cancel"), style: "cancel", onPress: () => setBusy(false) }, { text: t("common.add"), onPress: () => { void addToInventory(result.product!.name); }, }, ], ); } else { Alert.alert(t("barcode.unknownTitle"), t("barcode.unknownBody"), [ { text: t("common.cancel"), style: "cancel", onPress: () => setBusy(false) }, { text: t("barcode.photoPackage"), onPress: () => router.replace("/(tabs)/scan") }, ]); } } catch (err) { Alert.alert(t("common.oops"), err instanceof Error ? err.message : t("common.error")); setBusy(false); } }; const addToInventory = async (name: string) => { try { const me = await api<{ activeHouseholdId: string | null }>("/v1/me"); if (!me.activeHouseholdId) throw new Error(t("barcode.needHousehold")); const household = await api<{ storageLocations: Array<{ id: string; type: string }> }>( `/v1/households/${me.activeHouseholdId}`, ); const fridge = household.storageLocations.find((l) => l.type === "fridge") ?? household.storageLocations[0]; if (!fridge) throw new Error(t("barcode.noLocation")); await api("/v1/inventory/items", { method: "POST", body: { displayName: name, quantity: 1, unit: "COUNT", storageLocationId: fridge.id, source: "barcode", }, }); await queryClient.invalidateQueries({ queryKey: ["inventory"] }); router.back(); } catch (err) { Alert.alert(t("common.oops"), err instanceof Error ? err.message : t("common.error")); setBusy(false); } }; if (!permission) return ; if (!permission.granted) { return ( {t("barcode.cameraTitle")} {t("barcode.cameraBody")}