fix(kez-chat): don't count messages in the open thread as unread

Opening a thread marked it read, but messages arriving while it stayed
open still bumped its unread badge in the conversation list. Now an
inbound message marks the open thread read if the page is visible; a
message that lands while backgrounded stays unread until the user
returns to the tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jason Tudisco 2026-09-27 10:42:47 -06:00
parent b237ac2d9e
commit ac157345fa

View File

@ -572,7 +572,7 @@
// new message lands. The service is already running (it started on // new message lands. The service is already running (it started on
// session unlock in store.svelte.ts) regardless of which route the // session unlock in store.svelte.ts) regardless of which route the
// user was on. // user was on.
unsubscribe = inboxService.onMessage(() => void refresh()); unsubscribe = inboxService.onMessage(() => void onInboxMessage());
// Landing here = the user has seen new messages; reset the badge. // Landing here = the user has seen new messages; reset the badge.
inboxService.markAllRead(); inboxService.markAllRead();
// Relay state poll. // Relay state poll.
@ -580,6 +580,7 @@
relayStatuses = getRelayStatuses(); relayStatuses = getRelayStatuses();
}, 2_000); }, 2_000);
document.addEventListener("click", onDocumentClick); document.addEventListener("click", onDocumentClick);
document.addEventListener("visibilitychange", onVisibilityChange);
void evaluatePushNudge(); void evaluatePushNudge();
}); });
@ -587,8 +588,27 @@
unsubscribe?.(); unsubscribe?.();
if (relayPollTimer) clearInterval(relayPollTimer); if (relayPollTimer) clearInterval(relayPollTimer);
document.removeEventListener("click", onDocumentClick); document.removeEventListener("click", onDocumentClick);
document.removeEventListener("visibilitychange", onVisibilityChange);
}); });
/** A message landing in the thread the user is looking at is read on
* arrival — don't badge it. Only while the page is visible: a
* message that arrives while the app is backgrounded stays unread
* until they come back (onVisibilityChange). */
async function onInboxMessage() {
const pk = activePrimary;
if (pk && document.visibilityState === "visible") {
await markConversationRead(pk);
}
await refresh();
}
function onVisibilityChange() {
const pk = activePrimary;
if (document.visibilityState !== "visible" || !pk) return;
void markConversationRead(pk).then(refresh);
}
async function refresh() { async function refresh() {
conversations = await listConversations(); conversations = await listConversations();
// Kick off peer-profile fetches for any conversation whose peer // Kick off peer-profile fetches for any conversation whose peer