Files
paperclip/ui/src/hooks/useInboxBadge.ts
2026-03-10 22:55:45 -05:00

102 lines
2.9 KiB
TypeScript

import { useEffect, useMemo, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { accessApi } from "../api/access";
import { ApiError } from "../api/client";
import { approvalsApi } from "../api/approvals";
import { dashboardApi } from "../api/dashboard";
import { heartbeatsApi } from "../api/heartbeats";
import { issuesApi } from "../api/issues";
import { queryKeys } from "../lib/queryKeys";
import {
computeInboxBadgeData,
loadDismissedInboxItems,
saveDismissedInboxItems,
} from "../lib/inbox";
const TOUCHED_ISSUE_STATUSES = "backlog,todo,in_progress,in_review,blocked,done";
export function useDismissedInboxItems() {
const [dismissed, setDismissed] = useState<Set<string>>(loadDismissedInboxItems);
useEffect(() => {
const handleStorage = (event: StorageEvent) => {
if (event.key !== "paperclip:inbox:dismissed") return;
setDismissed(loadDismissedInboxItems());
};
window.addEventListener("storage", handleStorage);
return () => window.removeEventListener("storage", handleStorage);
}, []);
const dismiss = (id: string) => {
setDismissed((prev) => {
const next = new Set(prev);
next.add(id);
saveDismissedInboxItems(next);
return next;
});
};
return { dismissed, dismiss };
}
export function useInboxBadge(companyId: string | null | undefined) {
const { dismissed } = useDismissedInboxItems();
const { data: approvals = [] } = useQuery({
queryKey: queryKeys.approvals.list(companyId!),
queryFn: () => approvalsApi.list(companyId!),
enabled: !!companyId,
});
const { data: joinRequests = [] } = useQuery({
queryKey: queryKeys.access.joinRequests(companyId!),
queryFn: async () => {
try {
return await accessApi.listJoinRequests(companyId!, "pending_approval");
} catch (err) {
if (err instanceof ApiError && (err.status === 401 || err.status === 403)) {
return [];
}
throw err;
}
},
enabled: !!companyId,
retry: false,
});
const { data: dashboard } = useQuery({
queryKey: queryKeys.dashboard(companyId!),
queryFn: () => dashboardApi.summary(companyId!),
enabled: !!companyId,
});
const { data: touchedIssues = [] } = useQuery({
queryKey: queryKeys.issues.listTouchedByMe(companyId!),
queryFn: () =>
issuesApi.list(companyId!, {
touchedByUserId: "me",
status: TOUCHED_ISSUE_STATUSES,
}),
enabled: !!companyId,
});
const { data: heartbeatRuns = [] } = useQuery({
queryKey: queryKeys.heartbeats(companyId!),
queryFn: () => heartbeatsApi.list(companyId!),
enabled: !!companyId,
});
return useMemo(
() =>
computeInboxBadgeData({
approvals,
joinRequests,
dashboard,
heartbeatRuns,
touchedIssues,
dismissed,
}),
[approvals, joinRequests, dashboard, heartbeatRuns, touchedIssues, dismissed],
);
}