diff --git a/kez-chat/web/src/routes/Messages.svelte b/kez-chat/web/src/routes/Messages.svelte index 70c8086..2f5316e 100644 --- a/kez-chat/web/src/routes/Messages.svelte +++ b/kez-chat/web/src/routes/Messages.svelte @@ -572,7 +572,7 @@ // new message lands. The service is already running (it started on // session unlock in store.svelte.ts) regardless of which route the // user was on. - unsubscribe = inboxService.onMessage(() => void refresh()); + unsubscribe = inboxService.onMessage(() => void onInboxMessage()); // Landing here = the user has seen new messages; reset the badge. inboxService.markAllRead(); // Relay state poll. @@ -580,6 +580,7 @@ relayStatuses = getRelayStatuses(); }, 2_000); document.addEventListener("click", onDocumentClick); + document.addEventListener("visibilitychange", onVisibilityChange); void evaluatePushNudge(); }); @@ -587,8 +588,27 @@ unsubscribe?.(); if (relayPollTimer) clearInterval(relayPollTimer); 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() { conversations = await listConversations(); // Kick off peer-profile fetches for any conversation whose peer