feat: chat

This commit is contained in:
gosangam
2026-08-29 03:49:58 +05:30
parent 3257efb06a
commit 523b6fa7e8
34 changed files with 5381 additions and 11 deletions
@@ -54,7 +54,7 @@ const EVENTS: {
key: "community",
label: "Community",
description:
"Rare announcements — such as a direct disciple of Śrīla Prabhupāda joining.",
"Rare announcements — such as new feature, downtime etc.",
// Announcements are in-app/push only; we deliberately don't email them.
channels: ["in_app", "push"],
},
@@ -44,6 +44,22 @@ type NotificationsContextValue = {
refreshUnread: () => void;
/** Zero the badge locally (after the page marks everything read). */
clearUnread: () => void;
/**
* Last chat message seen over realtime, or null. Chat deliberately does NOT
* flow through the notification tray — a new message highlights its row in
* the inbox instead — so it rides the same private channel under its own
* event name and is surfaced here rather than bumping `unread`.
*/
lastChatActivity: ChatActivity | null;
};
/** Payload of the BFF's `chat_activity` event. */
export type ChatActivity = {
conversation_id: string;
seq: number;
sender_id: string;
/** Client-side receipt time, so consumers can react to repeats. */
at: number;
};
const NotificationsContext = createContext<NotificationsContextValue | null>(
@@ -93,6 +109,9 @@ export function NotificationsProvider({
const { user } = useAuth();
const [unread, setUnread] = useState(0);
const [lastEventAt, setLastEventAt] = useState(0);
const [lastChatActivity, setLastChatActivity] = useState<ChatActivity | null>(
null,
);
const refreshUnread = useCallback(() => {
notifications
@@ -178,6 +197,15 @@ export function NotificationsProvider({
channel.bind("pusher:subscription_error", (status: unknown) => {
console.warn("notifications: realtime subscription failed", status);
});
// Chat activity: no toast, no unread bump, nothing in the tray. The
// inbox subscribes to this to highlight the affected row.
channel.bind(
"chat_activity",
(data: Omit<ChatActivity, "at">) => {
if (!data?.conversation_id) return;
setLastChatActivity({ ...data, at: Date.now() });
},
);
channel.bind("notification", (data: IncomingEvent) => {
setUnread((u) => u + 1);
setLastEventAt(Date.now());
@@ -208,7 +236,13 @@ export function NotificationsProvider({
return (
<NotificationsContext.Provider
value={{ unread, lastEventAt, refreshUnread, clearUnread }}
value={{
unread,
lastEventAt,
refreshUnread,
clearUnread,
lastChatActivity,
}}
>
{children}
</NotificationsContext.Provider>