feat: broadcast

This commit is contained in:
gosangam
2026-08-29 18:34:21 +05:30
parent 011b5bba91
commit 03c8799f22
4 changed files with 181 additions and 0 deletions
@@ -52,6 +52,9 @@ type NotificationsContextValue = {
* event name and is surfaced here rather than bumping `unread`.
*/
lastChatActivity: ChatActivity | null;
/** Bumps when an announcement is published. Carries no content — consumers
* refetch /broadcasts/pending, which answers per user. */
lastBroadcastAt: number;
/** Threads holding unread messages — the Chats nav badge. Muted threads are
* excluded by the BFF, so muting a busy group clears the badge. */
chatUnread: number;
@@ -115,6 +118,7 @@ export function NotificationsProvider({
const { user } = useAuth();
const [unread, setUnread] = useState(0);
const [chatUnread, setChatUnread] = useState(0);
const [lastBroadcastAt, setLastBroadcastAt] = useState(0);
const [lastEventAt, setLastEventAt] = useState(0);
const [lastChatActivity, setLastChatActivity] = useState<ChatActivity | null>(
null,
@@ -249,7 +253,18 @@ export function NotificationsProvider({
});
},
);
// Announcements ride a PUBLIC channel: one publish reaches everyone in
// a single Soketi call rather than one per user, and the event is safe
// to broadcast because it carries nothing — the content comes from the
// authenticated fetch it triggers.
const announcements = pusher.subscribe("broadcasts");
announcements.bind("broadcast_published", () =>
setLastBroadcastAt(Date.now()),
);
cleanup = () => {
announcements.unbind_all();
pusher.unsubscribe("broadcasts");
channel.unbind_all();
pusher.unsubscribe(channelName);
pusher.disconnect();
@@ -272,6 +287,7 @@ export function NotificationsProvider({
lastChatActivity,
chatUnread,
refreshChatUnread,
lastBroadcastAt,
}}
>
{children}