fix: chat UI
This commit is contained in:
@@ -50,6 +50,15 @@ const EVENTS: {
|
||||
label: "Sādhana reminders",
|
||||
description: "Daily nudges to chant your rounds and share a verse.",
|
||||
},
|
||||
{
|
||||
key: "messages",
|
||||
label: "Messages",
|
||||
description: "New direct messages and group chats.",
|
||||
// Push only. In-app delivery for chat is the inbox itself — a message
|
||||
// highlights its row rather than entering the notification tray — and
|
||||
// there is no such thing as an emailed chat message.
|
||||
channels: ["push"],
|
||||
},
|
||||
{
|
||||
key: "community",
|
||||
label: "Community",
|
||||
|
||||
@@ -34,6 +34,7 @@ import { useAuth } from "@/components/auth/auth-provider";
|
||||
import { channelAuthorizer } from "@/lib/realtime";
|
||||
import { feed, notifications } from "@/lib/social";
|
||||
import { sadhna } from "@/lib/sadhna";
|
||||
import { chat } from "@/lib/chat";
|
||||
|
||||
type NotificationsContextValue = {
|
||||
/** Live unread count for the header bell. */
|
||||
@@ -51,6 +52,11 @@ type NotificationsContextValue = {
|
||||
* event name and is surfaced here rather than bumping `unread`.
|
||||
*/
|
||||
lastChatActivity: ChatActivity | null;
|
||||
/** 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;
|
||||
/** Re-read the authoritative chat count (after opening a thread). */
|
||||
refreshChatUnread: () => void;
|
||||
};
|
||||
|
||||
/** Payload of the BFF's `chat_activity` event. */
|
||||
@@ -108,6 +114,7 @@ export function NotificationsProvider({
|
||||
}) {
|
||||
const { user } = useAuth();
|
||||
const [unread, setUnread] = useState(0);
|
||||
const [chatUnread, setChatUnread] = useState(0);
|
||||
const [lastEventAt, setLastEventAt] = useState(0);
|
||||
const [lastChatActivity, setLastChatActivity] = useState<ChatActivity | null>(
|
||||
null,
|
||||
@@ -124,12 +131,22 @@ export function NotificationsProvider({
|
||||
|
||||
const clearUnread = useCallback(() => setUnread(0), []);
|
||||
|
||||
const refreshChatUnread = useCallback(() => {
|
||||
chat
|
||||
.unreadCount()
|
||||
.then(setChatUnread)
|
||||
.catch(() => {
|
||||
/* badge is non-critical */
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Seed the count whenever the signed-in user changes (async — no
|
||||
// synchronous setState in the effect body).
|
||||
useEffect(() => {
|
||||
if (!user?.id) return;
|
||||
refreshUnread();
|
||||
}, [user?.id, refreshUnread]);
|
||||
refreshChatUnread();
|
||||
}, [user?.id, refreshUnread, refreshChatUnread]);
|
||||
|
||||
// Report the browser's timezone (once per signed-in session) so sādhana
|
||||
// reminders fire at the user's local clock time. Best-effort.
|
||||
@@ -158,7 +175,7 @@ export function NotificationsProvider({
|
||||
};
|
||||
window.addEventListener("pagehide", onLeave);
|
||||
return () => window.removeEventListener("pagehide", onLeave);
|
||||
}, [userId]);
|
||||
}, [userId, refreshChatUnread]);
|
||||
|
||||
// Open the realtime connection while signed in.
|
||||
useEffect(() => {
|
||||
@@ -197,13 +214,24 @@ 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.
|
||||
// Chat activity: no toast, nothing in the tray. The inbox subscribes to
|
||||
// this to highlight the affected row, and the nav badge re-reads its
|
||||
// count — refetched rather than incremented, because the badge counts
|
||||
// THREADS: a second message in a thread already counted must not bump
|
||||
// it, and the sender's own message must not either.
|
||||
channel.bind(
|
||||
"chat_activity",
|
||||
(data: Omit<ChatActivity, "at">) => {
|
||||
if (!data?.conversation_id) return;
|
||||
setLastChatActivity({ ...data, at: Date.now() });
|
||||
refreshChatUnread();
|
||||
// Ack delivery here rather than in the thread, so "delivered" means
|
||||
// "reached their device" rather than "they happened to have the chat
|
||||
// open". This event only reaches non-senders, so nobody acks their
|
||||
// own message.
|
||||
if (data.seq) {
|
||||
chat.markDelivered(data.conversation_id, data.seq).catch(() => {});
|
||||
}
|
||||
},
|
||||
);
|
||||
channel.bind("notification", (data: IncomingEvent) => {
|
||||
@@ -232,7 +260,7 @@ export function NotificationsProvider({
|
||||
cancelled = true;
|
||||
cleanup?.();
|
||||
};
|
||||
}, [userId]);
|
||||
}, [userId, refreshChatUnread]);
|
||||
|
||||
return (
|
||||
<NotificationsContext.Provider
|
||||
@@ -242,6 +270,8 @@ export function NotificationsProvider({
|
||||
refreshUnread,
|
||||
clearUnread,
|
||||
lastChatActivity,
|
||||
chatUnread,
|
||||
refreshChatUnread,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
Reference in New Issue
Block a user