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 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 3x 1x 1x 1x 1x 1x 3x 3x 3x 3x 3x 3x 1x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1x 2x 2x 1x 1x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | 'use client'
// Abacus Studio — print-jobs roster + the two actions that RESOLVE a parked
// job (Gitea #9). All three go through abaci's ownership-gated proxy; the proxy
// relays THH byte-for-byte, so the roster read carries each job's `attention`
// reasons (see ./create/abacus/print-jobs projection) and the mutations post
// the exact bodies THH's start/cancel endpoints expect.
//
// State moves on doorbell rings (usePrintJobRing invalidates the roster key),
// so these mutations only need to invalidate the same key on success — the
// follow-up phase ring and this invalidate converge on a single refetch. No
// poll, no optimistic phase-guessing: the service is the source of truth.
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { type JobRow, normalizeJobs } from '@/components/create/abacus/print-jobs'
import { PrintServiceError } from '@/components/create/abacus/print-submit-failure'
import { api } from '@/lib/queryClient'
import { abacusPrintKeys } from '@/lib/queryKeys'
/** Start (or acknowledge-and-start) a parked job. `acknowledge` must list every
* reason code the job is parked on — THH refuses `acknowledgement_required`
* otherwise; a bare `ready` job takes `[]`. */
export interface StartPrintJobVars {
jobId: string
acknowledge: string[]
}
/** Cancel a job. A `printing` job requires `stopPrint: true` (THH refuses
* `job_printing` otherwise); a parked job cancels cleanly with `false`. */
export interface CancelPrintJobVars {
jobId: string
stopPrint: boolean
}
/**
* The jobs roster, normalized. Read-only companion to the ring: the same query
* key `usePrintJobRing` invalidates, so a ring or a mutation both land here.
*/
export function useAbacusPrintJobs(options: { enabled: boolean; connectionId?: string }) {
const { enabled, connectionId } = options
const query = useQuery({
queryKey: abacusPrintKeys.jobs(connectionId),
queryFn: async (): Promise<unknown> => {
const cq = connectionId ? `?connectionId=${encodeURIComponent(connectionId)}` : ''
const res = await api(`abacus/print/jobs${cq}`)
if (!res.ok) throw new Error(`jobs read failed: ${res.status}`)
return (await res.json()) as unknown
},
enabled,
staleTime: 5_000,
})
const jobRows: JobRow[] = normalizeJobs(query.data)
return { ...query, jobRows }
}
/** POST the start, throwing a {@link PrintServiceError} on refusal so the caller
* can render THH's honest, coded reason. Invalidates the roster on success. */
export function useStartPrintJob() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async ({ jobId, acknowledge }: StartPrintJobVars): Promise<unknown> => {
const res = await api(`abacus/print/jobs/${encodeURIComponent(jobId)}/start`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ acknowledge }),
})
const body: unknown = await res.json().catch(() => null)
if (!res.ok) throw new PrintServiceError(res.status, body)
return body
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: abacusPrintKeys.jobs() })
},
})
}
/** POST the cancel/stop, throwing a {@link PrintServiceError} on refusal.
* Invalidates the roster on success. */
export function useCancelPrintJob() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async ({ jobId, stopPrint }: CancelPrintJobVars): Promise<unknown> => {
const res = await api(`abacus/print/jobs/${encodeURIComponent(jobId)}/cancel`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ stopPrint }),
})
const body: unknown = await res.json().catch(() => null)
if (!res.ok) throw new PrintServiceError(res.status, body)
return body
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: abacusPrintKeys.jobs() })
},
})
}
|