NGU-Web/src/lib/useHistory.ts
Zaldimmar 22d5328885 Convert src/ JavaScript modules to TypeScript
Renames every .js module under src/ to .ts (api, useResource,
adminSchema, navConfig, adminNav and src/data/*) and points imports,
comments and the seed script's module paths at the new names. Their
types now come from inference; no .d.ts files and no shape
interfaces.

tsc stays strict (noImplicitAny off). The errors inference leaves
behind get the lightest fix that clears them: `any` on empty state,
contexts and list defaults, `: any` on components with optional or
spread props, class fields on ApiError, and option shapes on
get/useResource.

Kept as real types, since they belong to modules that were already
TypeScript and later features import them: PageShell's ShellSection
and props, useContent's corrected record types (website/email/
instagram are bare strings) and EventListItem, and TimelineRef's
orgKind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 15:39:42 -05:00

87 lines
2.6 KiB
TypeScript

/**
* Loads the history timeline from `GET /api/history`.
*
* Goes through the shared client, so the request is deduped and cached
* for 60s like every other read. Two consequences worth knowing:
*
* · `reload` has to invalidate before it refetches. Without that, the
* retry button inside the TTL would hand back the same settled
* promise and look like it did nothing. A *failed* request is
* already evicted by the client, so this matters for the refresh
* case rather than the error case.
*
* · there's no AbortController. `get` shares one promise between
* callers, so aborting on unmount would cancel someone else's
* request. The `live` flag drops the result instead.
*
* No `fallback` on purpose. Handing this the mock data would render a
* plausible-looking history with no indication the server is down, and
* the wrong history is worse than a visible error — the same reason
* `fallback: EMPTY` came out elsewhere.
*
* `undated` is the number of published entries the API left out because
* nothing gave them a date. Not rendered publicly — a visitor can't act
* on it — but returned so it's reachable if you want a warning in the
* admin later.
*/
import { useCallback, useEffect, useState } from 'react'
import { get, invalidate, ApiError } from './api.ts'
import type { TimelineItem } from './timeline'
const PATH = '/history'
type HistoryResponse = {
items?: TimelineItem[]
undated?: number
}
type State = {
items: TimelineItem[]
undated: number
loading: boolean
error: string | null
reload: () => void
}
export function useHistory(): State {
const [items, setItems] = useState<TimelineItem[]>([])
const [undated, setUndated] = useState(0)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [attempt, setAttempt] = useState(0)
const reload = useCallback(() => {
invalidate(PATH)
setAttempt((n) => n + 1)
}, [])
useEffect(() => {
let live = true
async function load() {
setLoading(true)
setError(null)
try {
const data: HistoryResponse = await get(PATH)
if (!live) return
setItems(Array.isArray(data?.items) ? data.items : [])
setUndated(Number(data?.undated) || 0)
} catch (err) {
if (!live) return
setError(
err instanceof ApiError ? err.message : "Couldn't reach the server.",
)
} finally {
if (live) setLoading(false)
}
}
load()
return () => {
live = false
}
}, [attempt])
return { items, undated, loading, error, reload }
}