50 lines
2.1 KiB
TypeScript
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;
|
|
}
|