"use client"; import { compareColonyCodes } from "@/lib/colonies/sort"; import { useQuery } from "@tanstack/react-query"; import { fetchApiaries } from "@/lib/apiaries/api"; import { livingColoniesFromPacks } from "@/lib/apiaries/offline-pack"; import { useCurrentUserId } from "@/lib/auth/auth-context"; import { fetchColonies } from "@/lib/colonies/api"; import { isLivingColony } from "@/lib/colonies/status"; import type { Colony } from "@/lib/colonies/types"; import { withPackFallback } from "@/lib/offline/pack-fallback"; /** What a bulk choice needs of a colony - a server colony or one from an offline pack. */ export type BulkColony = Pick; export interface BulkApiary { id: string; name: string; } /** Where the choices came from: the server, or - only without network - the loaded packs. */ export type ChoiceSource = "server" | "packs"; interface Choices { items: T[]; source: ChoiceSource; } /** Prefix of the queries here; they read the server or this device's storage, never both as one. */ const BULK_CHOICES_KEY = ["bulk-choices"] as const; function byCode(a: BulkColony, b: BulkColony): number { return compareColonyCodes(a.code, b.code); } function livingOf(colonies: readonly BulkColony[]): BulkColony[] { return colonies .filter(isLivingColony) .map(({ id, code, status, apiaryId }) => ({ id, code, status, apiaryId })) .sort(byCode); } /** * The apiaries to choose from: the server's active ones, or - only when the * network failed - those of the user's offline packs. Names only: nothing * else of an apiary (location!) is needed or kept. */ export function useApiaryChoices() { const userId = useCurrentUserId(); return useQuery({ queryKey: [...BULK_CHOICES_KEY, userId, "apiaries"], queryFn: (): Promise> => withPackFallback>( async () => ({ source: "server", items: (await fetchApiaries({ status: "ACTIVE" })).map(({ id, name }) => ({ id, name })), }), (packs) => packs.length === 0 ? null : { source: "packs", items: packs .map(({ pack }) => ({ id: pack.apiary.id, name: pack.apiary.name })) .sort((a, b) => a.name.localeCompare(b.name, "de")), }, userId ?? "", ), // Reads this device's storage when offline, never waits for the network. networkMode: "always", }); } /** * The living colonies of one apiary - ended ones never appear. Without * network the apiary's offline pack answers; with no pack for it the * request fails and the caller says so. */ export function useApiaryColonies(apiaryId: string | null) { const userId = useCurrentUserId(); return useQuery({ queryKey: [...BULK_CHOICES_KEY, userId, "colonies", apiaryId], queryFn: (): Promise> => withPackFallback>( async () => ({ source: "server", items: livingOf(await fetchColonies({ apiaryId: apiaryId ?? undefined })), }), (packs) => packs.some((stored) => stored.apiaryId === apiaryId) ? { source: "packs", items: livingOf( livingColoniesFromPacks(packs).filter((colony) => colony.apiaryId === apiaryId), ), } : null, userId ?? "", ), enabled: apiaryId !== null && apiaryId !== "", networkMode: "always", }); }