Spaces:
Sleeping
Sleeping
| /** Shared pending-resolve queue: Home banner / Log after-save / Entry detail. */ | |
| import { createContext } from "preact"; | |
| import { useCallback, useContext, useMemo, useState } from "preact/hooks"; | |
| import type { ComponentChildren } from "preact"; | |
| import { listPending, type Entry } from "../api"; | |
| import { ResolvePendingSheet } from "./ResolvePendingSheet"; | |
| type PendingApi = { | |
| openResolve: (entries?: Entry[]) => void; | |
| openResolveId: (id: string, fallback?: Entry) => void; | |
| refreshPendingSignal: number; | |
| bumpRefresh: () => void; | |
| }; | |
| const PendingContext = createContext<PendingApi | null>(null); | |
| export function usePendingResolve(): PendingApi { | |
| const ctx = useContext(PendingContext); | |
| if (!ctx) throw new Error("usePendingResolve requires PendingResolveProvider"); | |
| return ctx; | |
| } | |
| export function PendingResolveProvider({ children }: { children: ComponentChildren }) { | |
| const [queue, setQueue] = useState<Entry[]>([]); | |
| const [open, setOpen] = useState(false); | |
| const [refreshPendingSignal, setRefresh] = useState(0); | |
| const bumpRefresh = useCallback(() => { | |
| setRefresh((n) => n + 1); | |
| }, []); | |
| const openResolve = useCallback(async (entries?: Entry[]) => { | |
| if (entries && entries.length) { | |
| setQueue(entries); | |
| setOpen(true); | |
| return; | |
| } | |
| try { | |
| const data = await listPending(50); | |
| if (!data.items.length) return; | |
| setQueue(data.items); | |
| setOpen(true); | |
| } catch { | |
| // leave closed | |
| } | |
| }, []); | |
| const openResolveId = useCallback(async (id: string, fallback?: Entry) => { | |
| if (fallback && fallback.id === id) { | |
| setQueue([fallback]); | |
| setOpen(true); | |
| return; | |
| } | |
| try { | |
| const data = await listPending(50); | |
| const hit = data.items.find((item) => item.id === id); | |
| const rest = data.items.filter((item) => item.id !== id); | |
| setQueue(hit ? [hit, ...rest] : fallback ? [fallback, ...rest] : rest); | |
| setOpen(true); | |
| } catch { | |
| if (fallback) { | |
| setQueue([fallback]); | |
| setOpen(true); | |
| } | |
| } | |
| }, []); | |
| const api = useMemo( | |
| () => ({ openResolve, openResolveId, refreshPendingSignal, bumpRefresh }), | |
| [openResolve, openResolveId, refreshPendingSignal, bumpRefresh], | |
| ); | |
| return ( | |
| <PendingContext.Provider value={api}> | |
| {children} | |
| <ResolvePendingSheet | |
| open={open} | |
| queue={queue} | |
| onClose={() => { | |
| setOpen(false); | |
| setQueue([]); | |
| }} | |
| onQueueChange={setQueue} | |
| onResolved={bumpRefresh} | |
| /> | |
| </PendingContext.Provider> | |
| ); | |
| } | |