From 0935a2dfa618c4e8e8fe335796f9ab50540c83fb Mon Sep 17 00:00:00 2001 From: Casey Brooks Date: Sat, 25 Apr 2026 17:32:10 +0000 Subject: [PATCH] fix(notifications): scope console subscriptions --- src/hooks/useNotifications.ts | 16 +++++++++++++--- src/pages/OrganizationActivityWorkloadsTab.tsx | 3 ++- src/pages/OrganizationOverviewTab.tsx | 3 ++- src/pages/RunnerDetailPage.tsx | 6 ++++-- src/pages/WorkloadDetailPage.tsx | 1 + 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/hooks/useNotifications.ts b/src/hooks/useNotifications.ts index 3c3aa31..63d7d93 100644 --- a/src/hooks/useNotifications.ts +++ b/src/hooks/useNotifications.ts @@ -5,25 +5,35 @@ import { notificationsClient } from '@/api/client'; type UseNotificationsOptions = { events: string[]; invalidateKeys: string[][]; + rooms: string[]; enabled?: boolean; }; export function useNotifications(options: UseNotificationsOptions): void { - const { events, invalidateKeys, enabled = true } = options; + const { events, invalidateKeys, rooms, enabled = true } = options; const queryClient = useQueryClient(); const eventsRef = useRef(events); const keysRef = useRef(invalidateKeys); + const roomsRef = useRef(rooms); + const roomsKey = rooms.join('|'); + const hasRooms = rooms.length > 0; eventsRef.current = events; keysRef.current = invalidateKeys; + roomsRef.current = rooms; useEffect(() => { if (!enabled) return; + if (!hasRooms) { + console.error('[useNotifications] rooms are required to subscribe'); + return; + } const controller = new AbortController(); + const requestRooms = roomsRef.current; (async () => { try { - for await (const response of notificationsClient.subscribe({}, { signal: controller.signal })) { + for await (const response of notificationsClient.subscribe({ rooms: requestRooms }, { signal: controller.signal })) { const envelope = response.envelope; if (!envelope) continue; if (!eventsRef.current.includes(envelope.event)) continue; @@ -42,5 +52,5 @@ export function useNotifications(options: UseNotificationsOptions): void { return () => { controller.abort(); }; - }, [enabled, queryClient]); + }, [enabled, hasRooms, queryClient, roomsKey]); } diff --git a/src/pages/OrganizationActivityWorkloadsTab.tsx b/src/pages/OrganizationActivityWorkloadsTab.tsx index acfd8fc..e9d58cf 100644 --- a/src/pages/OrganizationActivityWorkloadsTab.tsx +++ b/src/pages/OrganizationActivityWorkloadsTab.tsx @@ -13,7 +13,8 @@ export function OrganizationActivityWorkloadsTab() { const organizationId = id ?? ''; useNotifications({ - events: ['workload.status_changed'], + rooms: organizationId ? [`organization:${organizationId}`] : [], + events: ['workload.updated'], invalidateKeys: [['workloads', organizationId, 'list']], enabled: Boolean(organizationId), }); diff --git a/src/pages/OrganizationOverviewTab.tsx b/src/pages/OrganizationOverviewTab.tsx index 70f61df..cf70fcc 100644 --- a/src/pages/OrganizationOverviewTab.tsx +++ b/src/pages/OrganizationOverviewTab.tsx @@ -15,7 +15,8 @@ export function OrganizationOverviewTab() { const organizationId = id ?? ''; useNotifications({ - events: ['workload.status_changed', 'workload.updated'], + rooms: organizationId ? [`organization:${organizationId}`] : [], + events: ['workload.updated'], invalidateKeys: [['workloads', organizationId, 'overview']], enabled: Boolean(organizationId), }); diff --git a/src/pages/RunnerDetailPage.tsx b/src/pages/RunnerDetailPage.tsx index 393ec0c..409811f 100644 --- a/src/pages/RunnerDetailPage.tsx +++ b/src/pages/RunnerDetailPage.tsx @@ -38,11 +38,13 @@ export function RunnerDetailPage() { const [labelEntries, setLabelEntries] = useState([]); const [runnerName, setRunnerName] = useState(''); const [runnerNameError, setRunnerNameError] = useState(''); + const notificationRooms = organizationId ? [`organization:${organizationId}`] : []; useNotifications({ - events: ['workload.status_changed', 'workload.updated'], + rooms: notificationRooms, + events: ['workload.updated'], invalidateKeys: [['workloads', 'runner', runnerId]], - enabled: Boolean(runnerId), + enabled: Boolean(runnerId && organizationId), }); const runnerQuery = useQuery({ diff --git a/src/pages/WorkloadDetailPage.tsx b/src/pages/WorkloadDetailPage.tsx index 038bb45..264819d 100644 --- a/src/pages/WorkloadDetailPage.tsx +++ b/src/pages/WorkloadDetailPage.tsx @@ -255,6 +255,7 @@ export function WorkloadDetailPage() { const location = useLocation(); useNotifications({ + rooms: workloadId ? [`workload:${workloadId}`] : [], events: ['workload.status_changed', 'workload.updated'], invalidateKeys: [['workloads', workloadId, 'detail']], enabled: Boolean(workloadId),