Files
fusion/packages/dashboard/app/hooks/useMailboxUnread.ts
gsxdsm f4b25dd2c9 FN-7096: gate approval banner to mailbox requests
Limit the dashboard approval banner to real mailbox approval requests instead of task plan-approval states.

- Stop awaiting-approval task updates from fabricating Open Mailbox banner candidates or refreshing mailbox counts.
- Gate DashboardBanners rendering to approval:<id> candidates while preserving pending-count race protection.
- Update dashboard tests, documentation, and release notes for mailbox-only approval banners.

Files changed:
 .changeset/fn-7096-approval-banner-mailbox.md      |   7 +
 docs/dashboard-guide.md                            |   2 +-
 packages/dashboard/app/App.tsx                     |   3 +-
 .../app/components/__tests__/App.test.tsx          |  33 ++--
 .../app/components/dashboard/DashboardBanners.tsx  |   9 +-
 .../dashboard/__tests__/DashboardBanners.test.tsx  |  65 +++++++-
 .../hooks/__tests__/sseSplitIntegration.test.ts    |  28 ++--
 .../app/hooks/__tests__/useApprovalBanner.test.ts  | 177 ++++++++-------------
 packages/dashboard/app/hooks/useApprovalBanner.ts  |  34 +---
 packages/dashboard/app/hooks/useMailboxUnread.ts   |   5 +-
 10 files changed, 185 insertions(+), 178 deletions(-)

Fusion-Task-Id: FN-7096

Fusion-Task-Lineage: 40885aba-6a9f-4720-bea6-5a6f155c326d

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-06-27 09:28:29 -07:00

60 lines
2.2 KiB
TypeScript

/*
FNXC:MailboxBadge 2026-06-24-00:00:
Header/mobile-nav unread + pending-approval counts for the mailbox, refreshed on message and approval SSE events. Extracted from AppInner; exposes `refresh` for reconnect/SSE-driven count refresh and `setMailboxUnreadCount` because MailboxView reports its own count changes through onUnreadCountChange.
FNXC:MailboxBadge 2026-06-26-00:00:
Pending approval counts are mailbox-only and refresh from approval:* events backed by ApprovalRequest rows. Task awaiting-approval transitions must not refresh or inflate these mailbox counts.
*/
import { useCallback, useEffect, useState } from "react";
import { fetchUnreadCount } from "../api";
import { subscribeSse } from "../sse-bus";
export interface UseMailboxUnreadResult {
mailboxUnreadCount: number;
mailboxPendingApprovalCount: number;
setMailboxUnreadCount: (count: number) => void;
refresh: () => void;
}
export function useMailboxUnread(currentProjectId: string | undefined): UseMailboxUnreadResult {
const [mailboxUnreadCount, setMailboxUnreadCount] = useState(0);
const [mailboxPendingApprovalCount, setMailboxPendingApprovalCount] = useState(0);
const refresh = useCallback(() => {
fetchUnreadCount(currentProjectId)
.then((data: { unreadCount: number; pendingApprovalCount?: number }) => {
setMailboxUnreadCount(data.unreadCount);
setMailboxPendingApprovalCount(data.pendingApprovalCount ?? 0);
})
.catch((err) => {
console.warn("[App] Failed to fetch mailbox unread count:", err);
});
}, [currentProjectId]);
useEffect(() => {
refresh();
const params = new URLSearchParams();
if (currentProjectId) {
params.set("projectId", currentProjectId);
}
const query = params.size > 0 ? `?${params.toString()}` : "";
return subscribeSse(`/api/events${query}`, {
onReconnect: refresh,
events: {
"message:sent": refresh,
"message:received": refresh,
"message:read": refresh,
"message:deleted": refresh,
"approval:requested": refresh,
"approval:updated": refresh,
"approval:decided": refresh,
},
});
}, [currentProjectId, refresh]);
return { mailboxUnreadCount, mailboxPendingApprovalCount, setMailboxUnreadCount, refresh };
}