Files
kandev/apps/web/hooks/use-subtask-count.ts
T

50 lines
2.1 KiB
TypeScript

import { useEffect, useState } from "react";
import { getSubtaskCount } from "@/lib/api";
// useSubtaskCount fetches the subtask count for an archive / delete
// confirmation dialog when it opens. Returns 0 while the request for
// the current set of ids is still in flight (or fails, or no ids were
// supplied) — in all of those cases the dialog's cascade checkbox
// stays hidden.
//
// The stored count is keyed by a stable string of the requested ids
// AND only returned while the dialog reports `open=true`. Closing the
// dialog clears the cache, so reopening — even for the same task —
// shows 0 until the fresh fetch lands. This avoids a stale count from
// a previous opening flashing before the new request resolves and
// stops the bulk toolbar's per-render `[...selectedIds]` from fanning
// out a new Promise.all on every render.
export function useSubtaskCount(open: boolean, taskId?: string, taskIds?: string[]): number {
const idsKey = taskIds?.join(",") ?? taskId ?? "";
const [result, setResult] = useState<{ key: string; total: number }>({
key: "",
total: 0,
});
useEffect(() => {
if (!open) {
// Clear so the next open starts from a known-empty state —
// this is what gates stale counts from leaking across separate
// dialog openings for the same id.
setResult({ key: "", total: 0 });
return;
}
if (!idsKey) return;
const ids = taskIds ?? (taskId ? [taskId] : []);
let cancelled = false;
// Per-id .catch already maps every failure to { count: 0 }, so
// Promise.all never rejects — no outer .catch needed.
Promise.all(ids.map((id) => getSubtaskCount(id).catch(() => ({ count: 0 })))).then(
(results) => {
if (cancelled) return;
setResult({ key: idsKey, total: results.reduce((sum, r) => sum + r.count, 0) });
},
);
return () => {
cancelled = true;
};
// taskId / taskIds intentionally excluded — idsKey is their stable summary.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [open, idsKey]);
return open && result.key === idsKey ? result.total : 0;
}