Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | 'use client' // Abacus Studio — print-service connection management (Gitea epic #5, #8.2). // // The browser half of the pairing flow: list the account's paired print // services, add one by redeeming a `CODE@host` pairing artifact, rename it, // remove it, and probe it for reachability. The server does all the secret // handling — the minted token never reaches here; a connection is only ever // the browser-safe {@link PrintConnectionDTO}. // // Transport is centralized in the module-level `*Connection` functions — the // seam PrintConnectionsManager's kit adapter closes over, and the one a future // shared transport package would inject. import { useQuery, useQueryClient } from '@tanstack/react-query' import { api } from '@/lib/queryClient' import { abacusPrintKeys } from '@/lib/queryKeys' /** Browser-safe connection view. Mirrors `PrintConnectionView` from * `@/lib/abacus/print/connections`, but Dates arrive as JSON strings. */ export interface PrintConnectionDTO { id: string name: string origin: string webhookRegistered: boolean createdAt: string updatedAt: string } /** On-demand reachability verdict from the `[id]/probe` route. Never thrown — * a broken connection is a state to show, not an error. */ export interface PrintProbeResult { ok: boolean reason?: string printerCount?: number } /** Pull the server's `{ error }` message, falling back to a generic line. */ async function readError(res: Response, fallback: string): Promise<string> { const data = (await res.json().catch(() => null)) as { error?: unknown } | null return typeof data?.error === 'string' && data.error ? data.error : fallback } export async function fetchConnections(): Promise<PrintConnectionDTO[]> { const res = await api('abacus/print/connections') if (!res.ok) throw new Error(await readError(res, 'Failed to load print connections')) const data = (await res.json()) as { connections?: PrintConnectionDTO[] } return data.connections ?? [] } /** Redeem a `CODE@host` pairing artifact. The server splits, exchanges the * code for a durable token, seals it, and registers the doorbell webhook. */ export async function pairConnection(artifact: string): Promise<PrintConnectionDTO> { const res = await api('abacus/print/connections', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ artifact }), }) if (!res.ok) throw new Error(await readError(res, 'Pairing failed')) const data = (await res.json()) as { connection: PrintConnectionDTO } return data.connection } export async function renameConnection(id: string, name: string): Promise<PrintConnectionDTO> { const res = await api(`abacus/print/connections/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name }), }) if (!res.ok) throw new Error(await readError(res, 'Rename failed')) const data = (await res.json()) as { connection: PrintConnectionDTO } return data.connection } export async function removeConnection(id: string): Promise<void> { const res = await api(`abacus/print/connections/${id}`, { method: 'DELETE' }) if (!res.ok) throw new Error(await readError(res, 'Remove failed')) } export async function probeConnection(id: string): Promise<PrintProbeResult> { const res = await api(`abacus/print/connections/${id}/probe`) if (!res.ok) throw new Error(await readError(res, 'Probe failed')) return (await res.json()) as PrintProbeResult } /** * The roster query plus one cache-invalidation closure. Any connection change * invalidates `abacusPrintKeys.all` so printer discovery, the filament * roster, and the job queue all re-read against the new set. * * `invalidateAll` RETURNS its promise on purpose: the shared kit awaits it as * `onChanged`, so a row's pending state holds until the refetched roster * lands. All mutations (pair/rename/remove/probe) go through the kit adapter * in PrintConnectionsManager, which calls the module transport functions * above directly. */ export function useAbacusPrintConnections() { const queryClient = useQueryClient() const invalidateAll = () => queryClient.invalidateQueries({ queryKey: abacusPrintKeys.all }) const connectionsQuery = useQuery({ queryKey: abacusPrintKeys.connections(), queryFn: fetchConnections, }) return { connectionsQuery, invalidateAll } } |