import { useCallback, useEffect, useRef, useState } from 'react' import { message } from './bridge.js' // One hook for every screen that loads and refreshes. // // It exists because the naive version has two bugs every screen would repeat: // a slow response arriving after the user navigated away sets state on an // unmounted component, and a poll that fires while the previous request is // still running stacks up requests against an engine that is already slow. export function usePolled(fn, intervalMs, deps = []) { const [data, setData] = useState(null) const [error, setError] = useState(null) const [loading, setLoading] = useState(true) const alive = useRef(true) const busy = useRef(false) const run = useCallback(async () => { if (busy.current) return busy.current = true try { const result = await fn() if (!alive.current) return setData(result) setError(null) } catch (err) { if (alive.current) setError(message(err)) } finally { busy.current = false if (alive.current) setLoading(false) } // eslint-disable-next-line react-hooks/exhaustive-deps }, deps) useEffect(() => { alive.current = true run() if (!intervalMs) return () => { alive.current = false } const id = setInterval(run, intervalMs) return () => { alive.current = false; clearInterval(id) } }, [run, intervalMs]) return { data, error, loading, reload: run } } export function fmtTime(ts) { if (!ts) return '—' const d = typeof ts === 'number' ? new Date(ts * 1000) : new Date(ts) if (isNaN(d)) return '—' return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) } export function fmtDate(ts) { if (!ts) return '—' const d = typeof ts === 'number' ? new Date(ts * 1000) : new Date(ts) if (isNaN(d)) return '—' return d.toLocaleDateString([], { day: 'numeric', month: 'short' }) } export function daysAgo(n) { const d = new Date() d.setDate(d.getDate() - n) return d.toISOString().slice(0, 10) } export function today() { return new Date().toISOString().slice(0, 10) }