From 1d4be33326e4e892e71c60ac0fa067a3755e386d Mon Sep 17 00:00:00 2001 From: Coracle-Bot Date: Wed, 2 Sep 2026 18:25:03 +0000 Subject: [PATCH] Make the tab title unread count match the notification dots (#402) Co-authored-by: Coracle-Bot --- src/app/notifications.ts | 47 ++++++++++++++++++++++++++++++--------- src/routes/+layout.svelte | 13 +++-------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/app/notifications.ts b/src/app/notifications.ts index a7468f1a..31ef9971 100644 --- a/src/app/notifications.ts +++ b/src/app/notifications.ts @@ -29,6 +29,7 @@ import {Relays, RoomLists} from "@welshman/app" import {deriveEventsByIdByUrl} from "@app/repository" import {app, fromApp} from "@app/core" import {makeRoomPath, makeSpaceChatPath, makeChatPath, makeContentPath} from "@app/routes" +import {decodeRelay} from "@app/relays" import {CONTENT_KINDS, makeCommentFilter} from "@app/content" import {getIsMuted, notificationSettings, userSettingsValues} from "@app/settings" import {chatsById} from "@app/chats" @@ -60,25 +61,41 @@ const getPaths = (path: string) => .map((_, i, segments) => segments.slice(0, i + 1).join("/")) .slice(1) +const getCheckedPaths = (pathname: string, relay?: string) => { + const paths = getPaths(pathname) + + if (relay) { + const url = decodeRelay(relay) + + if (CONTENT_KINDS.some(kind => makeContentPath(url, kind) === pathname)) { + paths.push(pathname + "*") + } + } + + return paths +} + export const syncChecked = () => { - let prev = "" + let prev: string[] = [] return page.subscribe($page => { // Set checked when we leave a given page checked.update($checked => { - for (const path of getPaths(prev)) { + for (const path of prev) { $checked[path] = now() } return $checked }) + const paths = getCheckedPaths($page.url.pathname, $page.params.relay) + // Set checked when we visit a given page - but delay it a tad setTimeout(() => { const defer = get(deferredRoomPath) checked.update($checked => { - for (const path of getPaths($page.url.pathname)) { + for (const path of paths) { if (defer && path === defer) continue $checked[path] = now() } @@ -87,7 +104,7 @@ export const syncChecked = () => { }) }, 300) - prev = $page.url.pathname + prev = paths }) } @@ -330,16 +347,26 @@ export const notifications = derived( ), ) +const countActivity = (activity: Map, paths: Set) => + [...activity.keys()].filter(path => paths.has(path)).length + +export const notificationCount = derived( + [latestActivityByPath, notifications], + ([$latestActivityByPath, $notifications]) => countActivity($latestActivityByPath, $notifications), +) + +export const backgroundNotificationCount = derived( + [latestActivityByPath, allNotifications], + ([$latestActivityByPath, $allNotifications]) => + countActivity($latestActivityByPath, $allNotifications), +) + // Badges export const syncBadges = () => - derived([latestActivityByPath, notifications, notificationSettings], identity).subscribe( - async ([$latestActivityByPath, $notifications, $notificationSettings]) => { + derived([notificationCount, notificationSettings], identity).subscribe( + async ([count, $notificationSettings]) => { if ($notificationSettings.badge) { - const count = [...$latestActivityByPath.keys()].filter(path => - $notifications.has(path), - ).length - try { await Badge.set({count}) } catch (err) { diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 417e0b8c..8da2c074 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -37,11 +37,7 @@ import {activeTheme, flTheme, theme} from "@app/theme" import {toast, pushToast} from "@app/toast" import * as notifications from "@app/notifications" - import { - notifications as notificationPaths, - allNotifications, - latestActivityByPath, - } from "@app/notifications" + import {notificationCount, backgroundNotificationCount} from "@app/notifications" import {Push} from "@app/push" import {onPushNotificationAction, pushState} from "@app/push/adapters/common" import {syncKeyboard} from "@app/keyboard" @@ -290,12 +286,9 @@ $effect(() => { const {user} = core.app.get() const title = getPageTitle({page: $page, pubkey: user?.pubkey}) - // While the tab isn't actively focused the user isn't actually looking at the - // active page, so count notifications for it too rather than treating it as read. - const paths = $documentActive ? $notificationPaths : $allNotifications - const unreadCount = [...$latestActivityByPath.keys()].filter(path => paths.has(path)).length + const count = $documentActive ? $notificationCount : $backgroundNotificationCount - document.title = unreadCount > 0 ? `(${unreadCount}) ${title}` : title + document.title = count > 0 ? `(${count}) ${title}` : title })