/** * React hook for loading and controlling the jail overview list. */ import { useCallback, useState } from "react"; import { fetchJails, reloadAllJails, reloadJail, setJailIdle, startJail, stopJail, } from "../api/jails"; import { useListData } from "./useListData"; import type { JailSummary, JailListResponse } from "../types/jail"; import type { FetchError } from "../types/api"; export interface UseJailsResult { jails: JailSummary[]; total: number; loading: boolean; error: FetchError | null; refresh: () => void; startJail: (name: string) => Promise; stopJail: (name: string) => Promise; setIdle: (name: string, on: boolean) => Promise; reloadJail: (name: string) => Promise; reloadAll: () => Promise; } /** * Fetch and manage the jail overview list. */ export function useJails(): UseJailsResult { const [total, setTotal] = useState(0); const fetcher = useCallback( (signal: AbortSignal) => fetchJails(signal), [], ); const selector = useCallback((response: JailListResponse) => response.jails, []); const onSuccess = useCallback((response: JailListResponse) => { setTotal(response.total); }, []); const { items: jails, loading, error, refresh } = useListData({ fetcher, selector, errorMessage: "Failed to load jails", onSuccess, }); const startJailMemo = useCallback( async (name: string): Promise => { await startJail(name); refresh(); }, [refresh], ); const stopJailMemo = useCallback( async (name: string): Promise => { await stopJail(name); refresh(); }, [refresh], ); const reloadJailMemo = useCallback( async (name: string): Promise => { await reloadJail(name); refresh(); }, [refresh], ); const setIdleMemo = useCallback( (name: string, on: boolean): Promise => setJailIdle(name, on).then(() => { refresh(); }), [refresh], ); const reloadAllMemo = useCallback( (): Promise => reloadAllJails().then(() => { refresh(); }), [refresh], ); return { jails, total, loading, error, refresh, startJail: startJailMemo, stopJail: stopJailMemo, setIdle: setIdleMemo, reloadJail: reloadJailMemo, reloadAll: reloadAllMemo, }; }