about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/notifications/notifications.ts3
-rw-r--r--src/locale/locales/en/messages.po90
-rw-r--r--src/locale/locales/hi/messages.po90
-rw-r--r--src/state/queries/notifications/feed.ts51
-rw-r--r--src/state/queries/notifications/types.ts5
-rw-r--r--src/state/queries/notifications/unread.tsx17
-rw-r--r--src/state/queries/notifications/util.ts3
-rw-r--r--src/view/screens/Notifications.tsx17
-rw-r--r--src/view/shell/Drawer.tsx10
-rw-r--r--src/view/shell/bottom-bar/BottomBar.tsx10
-rw-r--r--src/view/shell/desktop/LeftNav.tsx10
11 files changed, 161 insertions, 145 deletions
diff --git a/src/lib/notifications/notifications.ts b/src/lib/notifications/notifications.ts
index d115dc98f..342839a7b 100644
--- a/src/lib/notifications/notifications.ts
+++ b/src/lib/notifications/notifications.ts
@@ -81,6 +81,7 @@ export function registerTokenChangeHandler(
 
 export function init(queryClient: QueryClient) {
   // handle notifications that are received, both in the foreground or background
+  // NOTE: currently just here for debug logging
   Notifications.addNotificationReceivedListener(event => {
     logger.debug(
       'Notifications: received',
@@ -88,8 +89,6 @@ export function init(queryClient: QueryClient) {
       logger.DebugContext.notifications,
     )
     if (event.request.trigger.type === 'push') {
-      // refresh notifications in the background
-      truncateAndInvalidate(queryClient, RQKEY_NOTIFS())
       // handle payload-based deeplinks
       let payload
       if (isIOS) {
diff --git a/src/locale/locales/en/messages.po b/src/locale/locales/en/messages.po
index 9df901fa7..7a4a182cc 100644
--- a/src/locale/locales/en/messages.po
+++ b/src/locale/locales/en/messages.po
@@ -38,12 +38,12 @@ msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite
 msgstr ""
 
 #: src/view/screens/Settings.tsx:407
-#: src/view/shell/Drawer.tsx:648
+#: src/view/shell/Drawer.tsx:640
 msgid "{invitesAvailable} invite code available"
 msgstr ""
 
 #: src/view/screens/Settings.tsx:409
-#: src/view/shell/Drawer.tsx:650
+#: src/view/shell/Drawer.tsx:642
 msgid "{invitesAvailable} invite codes available"
 msgstr ""
 
@@ -830,16 +830,16 @@ msgid "Feed Preferences"
 msgstr ""
 
 #: src/view/shell/desktop/RightNav.tsx:64
-#: src/view/shell/Drawer.tsx:300
+#: src/view/shell/Drawer.tsx:292
 msgid "Feedback"
 msgstr ""
 
 #: src/view/screens/Feeds.tsx:475
 #: src/view/screens/Profile.tsx:164
-#: src/view/shell/bottom-bar/BottomBar.tsx:168
-#: src/view/shell/desktop/LeftNav.tsx:341
-#: src/view/shell/Drawer.tsx:463
-#: src/view/shell/Drawer.tsx:464
+#: src/view/shell/bottom-bar/BottomBar.tsx:160
+#: src/view/shell/desktop/LeftNav.tsx:333
+#: src/view/shell/Drawer.tsx:455
+#: src/view/shell/Drawer.tsx:456
 msgid "Feeds"
 msgstr ""
 
@@ -928,7 +928,7 @@ msgstr ""
 #: src/view/com/auth/LoggedOut.tsx:68
 #: src/view/com/auth/LoggedOut.tsx:69
 #: src/view/com/util/moderation/ScreenHider.tsx:105
-#: src/view/shell/desktop/LeftNav.tsx:106
+#: src/view/shell/desktop/LeftNav.tsx:103
 msgid "Go back"
 msgstr ""
 
@@ -950,7 +950,7 @@ msgid "Handle"
 msgstr ""
 
 #: src/view/shell/desktop/RightNav.tsx:93
-#: src/view/shell/Drawer.tsx:310
+#: src/view/shell/Drawer.tsx:302
 msgid "Help"
 msgstr ""
 
@@ -994,10 +994,10 @@ msgstr ""
 #~ msgid "Hmmm, we're having trouble finding this feed. It may have been deleted."
 #~ msgstr ""
 
-#: src/view/shell/bottom-bar/BottomBar.tsx:124
-#: src/view/shell/desktop/LeftNav.tsx:305
-#: src/view/shell/Drawer.tsx:387
-#: src/view/shell/Drawer.tsx:388
+#: src/view/shell/bottom-bar/BottomBar.tsx:116
+#: src/view/shell/desktop/LeftNav.tsx:297
+#: src/view/shell/Drawer.tsx:379
+#: src/view/shell/Drawer.tsx:380
 msgid "Home"
 msgstr ""
 
@@ -1063,7 +1063,7 @@ msgstr ""
 msgid "Invite code not accepted. Check that you input it correctly and try again."
 msgstr ""
 
-#: src/view/shell/Drawer.tsx:629
+#: src/view/shell/Drawer.tsx:621
 msgid "Invite codes: {invitesAvailable} available"
 msgstr ""
 
@@ -1162,9 +1162,9 @@ msgid "List Name"
 msgstr ""
 
 #: src/view/screens/Profile.tsx:165
-#: src/view/shell/desktop/LeftNav.tsx:381
-#: src/view/shell/Drawer.tsx:479
-#: src/view/shell/Drawer.tsx:480
+#: src/view/shell/desktop/LeftNav.tsx:373
+#: src/view/shell/Drawer.tsx:471
+#: src/view/shell/Drawer.tsx:472
 msgid "Lists"
 msgstr ""
 
@@ -1173,7 +1173,7 @@ msgstr ""
 msgid "Load more posts"
 msgstr ""
 
-#: src/view/screens/Notifications.tsx:130
+#: src/view/screens/Notifications.tsx:141
 msgid "Load new notifications"
 msgstr ""
 
@@ -1224,9 +1224,9 @@ msgstr ""
 
 #: src/view/screens/Moderation.tsx:63
 #: src/view/screens/Settings.tsx:563
-#: src/view/shell/desktop/LeftNav.tsx:399
-#: src/view/shell/Drawer.tsx:498
-#: src/view/shell/Drawer.tsx:499
+#: src/view/shell/desktop/LeftNav.tsx:391
+#: src/view/shell/Drawer.tsx:490
+#: src/view/shell/Drawer.tsx:491
 msgid "Moderation"
 msgstr ""
 
@@ -1300,7 +1300,7 @@ msgstr ""
 msgid "My Feeds"
 msgstr ""
 
-#: src/view/shell/desktop/LeftNav.tsx:67
+#: src/view/shell/desktop/LeftNav.tsx:64
 msgid "My Profile"
 msgstr ""
 
@@ -1328,11 +1328,11 @@ msgstr ""
 #: src/view/screens/ProfileFeed.tsx:451
 #: src/view/screens/ProfileList.tsx:212
 #: src/view/screens/ProfileList.tsx:244
-#: src/view/shell/desktop/LeftNav.tsx:254
+#: src/view/shell/desktop/LeftNav.tsx:246
 msgid "New post"
 msgstr ""
 
-#: src/view/shell/desktop/LeftNav.tsx:264
+#: src/view/shell/desktop/LeftNav.tsx:256
 msgid "New Post"
 msgstr ""
 
@@ -1402,12 +1402,12 @@ msgstr ""
 #~ msgid "Note: Third-party apps that display Bluesky content may not respect this setting."
 #~ msgstr ""
 
-#: src/view/screens/Notifications.tsx:97
-#: src/view/screens/Notifications.tsx:121
-#: src/view/shell/bottom-bar/BottomBar.tsx:195
-#: src/view/shell/desktop/LeftNav.tsx:363
-#: src/view/shell/Drawer.tsx:424
-#: src/view/shell/Drawer.tsx:425
+#: src/view/screens/Notifications.tsx:108
+#: src/view/screens/Notifications.tsx:132
+#: src/view/shell/bottom-bar/BottomBar.tsx:187
+#: src/view/shell/desktop/LeftNav.tsx:355
+#: src/view/shell/Drawer.tsx:416
+#: src/view/shell/Drawer.tsx:417
 msgid "Notifications"
 msgstr ""
 
@@ -1432,7 +1432,7 @@ msgid "Opens configurable language settings"
 msgstr ""
 
 #: src/view/shell/desktop/RightNav.tsx:146
-#: src/view/shell/Drawer.tsx:630
+#: src/view/shell/Drawer.tsx:622
 msgid "Opens list of invite codes"
 msgstr ""
 
@@ -1592,10 +1592,10 @@ msgstr ""
 msgid "Processing..."
 msgstr ""
 
-#: src/view/shell/bottom-bar/BottomBar.tsx:237
-#: src/view/shell/Drawer.tsx:72
-#: src/view/shell/Drawer.tsx:533
-#: src/view/shell/Drawer.tsx:534
+#: src/view/shell/bottom-bar/BottomBar.tsx:229
+#: src/view/shell/Drawer.tsx:69
+#: src/view/shell/Drawer.tsx:525
+#: src/view/shell/Drawer.tsx:526
 msgid "Profile"
 msgstr ""
 
@@ -1813,12 +1813,12 @@ msgstr ""
 #: src/view/com/util/forms/SearchInput.tsx:64
 #: src/view/screens/Search/Search.tsx:381
 #: src/view/screens/Search/Search.tsx:533
-#: src/view/shell/bottom-bar/BottomBar.tsx:146
-#: src/view/shell/desktop/LeftNav.tsx:323
+#: src/view/shell/bottom-bar/BottomBar.tsx:138
+#: src/view/shell/desktop/LeftNav.tsx:315
 #: src/view/shell/desktop/Search.tsx:161
 #: src/view/shell/desktop/Search.tsx:170
-#: src/view/shell/Drawer.tsx:351
-#: src/view/shell/Drawer.tsx:352
+#: src/view/shell/Drawer.tsx:343
+#: src/view/shell/Drawer.tsx:344
 msgid "Search"
 msgstr ""
 
@@ -1870,8 +1870,8 @@ msgstr ""
 msgid "Send Email"
 msgstr ""
 
-#: src/view/shell/Drawer.tsx:284
-#: src/view/shell/Drawer.tsx:305
+#: src/view/shell/Drawer.tsx:276
+#: src/view/shell/Drawer.tsx:297
 msgid "Send feedback"
 msgstr ""
 
@@ -1904,9 +1904,9 @@ msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your f
 msgstr ""
 
 #: src/view/screens/Settings.tsx:277
-#: src/view/shell/desktop/LeftNav.tsx:435
-#: src/view/shell/Drawer.tsx:554
-#: src/view/shell/Drawer.tsx:555
+#: src/view/shell/desktop/LeftNav.tsx:427
+#: src/view/shell/Drawer.tsx:546
+#: src/view/shell/Drawer.tsx:547
 msgid "Settings"
 msgstr ""
 
@@ -2447,7 +2447,7 @@ msgstr ""
 
 #: src/view/screens/Settings.tsx:402
 #: src/view/shell/desktop/RightNav.tsx:127
-#: src/view/shell/Drawer.tsx:644
+#: src/view/shell/Drawer.tsx:636
 msgid "Your invite codes are hidden when logged in using an App Password"
 msgstr ""
 
diff --git a/src/locale/locales/hi/messages.po b/src/locale/locales/hi/messages.po
index 5b4aec768..3e907ee3c 100644
--- a/src/locale/locales/hi/messages.po
+++ b/src/locale/locales/hi/messages.po
@@ -38,12 +38,12 @@ msgid "{invitesAvailable, plural, one {Invite codes: # available} other {Invite
 msgstr ""
 
 #: src/view/screens/Settings.tsx:407
-#: src/view/shell/Drawer.tsx:648
+#: src/view/shell/Drawer.tsx:640
 msgid "{invitesAvailable} invite code available"
 msgstr ""
 
 #: src/view/screens/Settings.tsx:409
-#: src/view/shell/Drawer.tsx:650
+#: src/view/shell/Drawer.tsx:642
 msgid "{invitesAvailable} invite codes available"
 msgstr ""
 
@@ -826,16 +826,16 @@ msgid "Feed Preferences"
 msgstr "फ़ीड प्राथमिकता"
 
 #: src/view/shell/desktop/RightNav.tsx:64
-#: src/view/shell/Drawer.tsx:300
+#: src/view/shell/Drawer.tsx:292
 msgid "Feedback"
 msgstr "प्रतिक्रिया"
 
 #: src/view/screens/Feeds.tsx:475
 #: src/view/screens/Profile.tsx:164
-#: src/view/shell/bottom-bar/BottomBar.tsx:168
-#: src/view/shell/desktop/LeftNav.tsx:341
-#: src/view/shell/Drawer.tsx:463
-#: src/view/shell/Drawer.tsx:464
+#: src/view/shell/bottom-bar/BottomBar.tsx:160
+#: src/view/shell/desktop/LeftNav.tsx:333
+#: src/view/shell/Drawer.tsx:455
+#: src/view/shell/Drawer.tsx:456
 msgid "Feeds"
 msgstr "सभी फ़ीड"
 
@@ -920,7 +920,7 @@ msgstr "प्रारंभ करें"
 #: src/view/com/auth/LoggedOut.tsx:68
 #: src/view/com/auth/LoggedOut.tsx:69
 #: src/view/com/util/moderation/ScreenHider.tsx:105
-#: src/view/shell/desktop/LeftNav.tsx:106
+#: src/view/shell/desktop/LeftNav.tsx:103
 msgid "Go back"
 msgstr "वापस जाओ"
 
@@ -942,7 +942,7 @@ msgid "Handle"
 msgstr "हैंडल"
 
 #: src/view/shell/desktop/RightNav.tsx:93
-#: src/view/shell/Drawer.tsx:310
+#: src/view/shell/Drawer.tsx:302
 msgid "Help"
 msgstr "सहायता"
 
@@ -986,10 +986,10 @@ msgstr ""
 #~ msgid "Hmmm, we're having trouble finding this feed. It may have been deleted."
 #~ msgstr ""
 
-#: src/view/shell/bottom-bar/BottomBar.tsx:124
-#: src/view/shell/desktop/LeftNav.tsx:305
-#: src/view/shell/Drawer.tsx:387
-#: src/view/shell/Drawer.tsx:388
+#: src/view/shell/bottom-bar/BottomBar.tsx:116
+#: src/view/shell/desktop/LeftNav.tsx:297
+#: src/view/shell/Drawer.tsx:379
+#: src/view/shell/Drawer.tsx:380
 msgid "Home"
 msgstr "होम फीड"
 
@@ -1055,7 +1055,7 @@ msgstr "आमंत्रण कोड"
 msgid "Invite code not accepted. Check that you input it correctly and try again."
 msgstr ""
 
-#: src/view/shell/Drawer.tsx:629
+#: src/view/shell/Drawer.tsx:621
 msgid "Invite codes: {invitesAvailable} available"
 msgstr ""
 
@@ -1154,9 +1154,9 @@ msgid "List Name"
 msgstr "सूची का नाम"
 
 #: src/view/screens/Profile.tsx:165
-#: src/view/shell/desktop/LeftNav.tsx:381
-#: src/view/shell/Drawer.tsx:479
-#: src/view/shell/Drawer.tsx:480
+#: src/view/shell/desktop/LeftNav.tsx:373
+#: src/view/shell/Drawer.tsx:471
+#: src/view/shell/Drawer.tsx:472
 msgid "Lists"
 msgstr "सूची"
 
@@ -1165,7 +1165,7 @@ msgstr "सूची"
 msgid "Load more posts"
 msgstr "अधिक पोस्ट लोड करें"
 
-#: src/view/screens/Notifications.tsx:130
+#: src/view/screens/Notifications.tsx:141
 msgid "Load new notifications"
 msgstr "नई सूचनाएं लोड करें"
 
@@ -1216,9 +1216,9 @@ msgstr ""
 
 #: src/view/screens/Moderation.tsx:63
 #: src/view/screens/Settings.tsx:563
-#: src/view/shell/desktop/LeftNav.tsx:399
-#: src/view/shell/Drawer.tsx:498
-#: src/view/shell/Drawer.tsx:499
+#: src/view/shell/desktop/LeftNav.tsx:391
+#: src/view/shell/Drawer.tsx:490
+#: src/view/shell/Drawer.tsx:491
 msgid "Moderation"
 msgstr "मॉडरेशन"
 
@@ -1292,7 +1292,7 @@ msgstr "जन्मदिन"
 msgid "My Feeds"
 msgstr "मेरी फ़ीड"
 
-#: src/view/shell/desktop/LeftNav.tsx:67
+#: src/view/shell/desktop/LeftNav.tsx:64
 msgid "My Profile"
 msgstr "मेरी प्रोफाइल"
 
@@ -1320,11 +1320,11 @@ msgstr "नया"
 #: src/view/screens/ProfileFeed.tsx:451
 #: src/view/screens/ProfileList.tsx:212
 #: src/view/screens/ProfileList.tsx:244
-#: src/view/shell/desktop/LeftNav.tsx:254
+#: src/view/shell/desktop/LeftNav.tsx:246
 msgid "New post"
 msgstr "नई पोस्ट"
 
-#: src/view/shell/desktop/LeftNav.tsx:264
+#: src/view/shell/desktop/LeftNav.tsx:256
 msgid "New Post"
 msgstr "नई पोस्ट"
 
@@ -1394,12 +1394,12 @@ msgstr ""
 #~ msgid "Note: Third-party apps that display Bluesky content may not respect this setting."
 #~ msgstr ""
 
-#: src/view/screens/Notifications.tsx:97
-#: src/view/screens/Notifications.tsx:121
-#: src/view/shell/bottom-bar/BottomBar.tsx:195
-#: src/view/shell/desktop/LeftNav.tsx:363
-#: src/view/shell/Drawer.tsx:424
-#: src/view/shell/Drawer.tsx:425
+#: src/view/screens/Notifications.tsx:108
+#: src/view/screens/Notifications.tsx:132
+#: src/view/shell/bottom-bar/BottomBar.tsx:187
+#: src/view/shell/desktop/LeftNav.tsx:355
+#: src/view/shell/Drawer.tsx:416
+#: src/view/shell/Drawer.tsx:417
 msgid "Notifications"
 msgstr "सूचनाएं"
 
@@ -1424,7 +1424,7 @@ msgid "Opens configurable language settings"
 msgstr "भाषा सेटिंग्स खोलें"
 
 #: src/view/shell/desktop/RightNav.tsx:146
-#: src/view/shell/Drawer.tsx:630
+#: src/view/shell/Drawer.tsx:622
 msgid "Opens list of invite codes"
 msgstr ""
 
@@ -1584,10 +1584,10 @@ msgstr "गोपनीयता नीति"
 msgid "Processing..."
 msgstr "प्रसंस्करण..."
 
-#: src/view/shell/bottom-bar/BottomBar.tsx:237
-#: src/view/shell/Drawer.tsx:72
-#: src/view/shell/Drawer.tsx:533
-#: src/view/shell/Drawer.tsx:534
+#: src/view/shell/bottom-bar/BottomBar.tsx:229
+#: src/view/shell/Drawer.tsx:69
+#: src/view/shell/Drawer.tsx:525
+#: src/view/shell/Drawer.tsx:526
 msgid "Profile"
 msgstr "प्रोफ़ाइल"
 
@@ -1805,12 +1805,12 @@ msgstr "सहेजे गए फ़ीड"
 #: src/view/com/util/forms/SearchInput.tsx:64
 #: src/view/screens/Search/Search.tsx:381
 #: src/view/screens/Search/Search.tsx:533
-#: src/view/shell/bottom-bar/BottomBar.tsx:146
-#: src/view/shell/desktop/LeftNav.tsx:323
+#: src/view/shell/bottom-bar/BottomBar.tsx:138
+#: src/view/shell/desktop/LeftNav.tsx:315
 #: src/view/shell/desktop/Search.tsx:161
 #: src/view/shell/desktop/Search.tsx:170
-#: src/view/shell/Drawer.tsx:351
-#: src/view/shell/Drawer.tsx:352
+#: src/view/shell/Drawer.tsx:343
+#: src/view/shell/Drawer.tsx:344
 msgid "Search"
 msgstr "खोज"
 
@@ -1862,8 +1862,8 @@ msgstr "ईमेल भेजें"
 msgid "Send Email"
 msgstr "ईमेल भेजें"
 
-#: src/view/shell/Drawer.tsx:284
-#: src/view/shell/Drawer.tsx:305
+#: src/view/shell/Drawer.tsx:276
+#: src/view/shell/Drawer.tsx:297
 msgid "Send feedback"
 msgstr "प्रतिक्रिया भेजें"
 
@@ -1896,9 +1896,9 @@ msgid "Set this setting to \"Yes\" to show samples of your saved feeds in your f
 msgstr "इस सेटिंग को अपने निम्नलिखित फ़ीड में अपने सहेजे गए फ़ीड के नमूने दिखाने के लिए \"हाँ\" पर सेट करें। यह एक प्रयोगात्मक विशेषता है।।"
 
 #: src/view/screens/Settings.tsx:277
-#: src/view/shell/desktop/LeftNav.tsx:435
-#: src/view/shell/Drawer.tsx:554
-#: src/view/shell/Drawer.tsx:555
+#: src/view/shell/desktop/LeftNav.tsx:427
+#: src/view/shell/Drawer.tsx:546
+#: src/view/shell/Drawer.tsx:547
 msgid "Settings"
 msgstr "सेटिंग्स"
 
@@ -2439,7 +2439,7 @@ msgstr "आपका होस्टिंग प्रदाता"
 
 #: src/view/screens/Settings.tsx:402
 #: src/view/shell/desktop/RightNav.tsx:127
-#: src/view/shell/Drawer.tsx:644
+#: src/view/shell/Drawer.tsx:636
 msgid "Your invite codes are hidden when logged in using an App Password"
 msgstr ""
 
diff --git a/src/state/queries/notifications/feed.ts b/src/state/queries/notifications/feed.ts
index a74670b5b..b6aa3d753 100644
--- a/src/state/queries/notifications/feed.ts
+++ b/src/state/queries/notifications/feed.ts
@@ -16,7 +16,7 @@
  * 3. Don't call this query's `refetch()` if you're trying to sync latest; call `checkUnread()` instead.
  */
 
-import {useEffect} from 'react'
+import {useEffect, useRef} from 'react'
 import {AppBskyFeedDefs} from '@atproto/api'
 import {
   useInfiniteQuery,
@@ -49,6 +49,8 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
   const threadMutes = useMutedThreads()
   const unreads = useUnreadNotificationsApi()
   const enabled = opts?.enabled !== false
+  // state tracked across page fetches
+  const pageState = useRef({pageNum: 0, hasMarkedRead: false})
 
   const query = useInfiniteQuery<
     FeedPage,
@@ -60,17 +62,44 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
     staleTime: STALE.INFINITY,
     queryKey: RQKEY(),
     async queryFn({pageParam}: {pageParam: RQPageParam}) {
-      let page = await fetchPage({
-        limit: PAGE_SIZE,
-        cursor: pageParam,
-        queryClient,
-        moderationOpts,
-        threadMutes,
-      })
+      let page
+      if (!pageParam) {
+        // for the first page, we check the cached page held by the unread-checker first
+        page = unreads.getCachedUnreadPage()
+        // reset the page state
+        pageState.current = {pageNum: 0, hasMarkedRead: false}
+      }
+      if (!page) {
+        page = await fetchPage({
+          limit: PAGE_SIZE,
+          cursor: pageParam,
+          queryClient,
+          moderationOpts,
+          threadMutes,
+        })
+      }
 
-      // if the first page has an unread, mark all read
-      if (!pageParam && page.items[0] && !page.items[0].notification.isRead) {
-        unreads.markAllRead()
+      // NOTE
+      // this section checks to see if we need to mark notifs read
+      // we want to wait until we've seen a read notification because
+      // of a timing challenge; marking read on the first page would
+      // cause subsequent pages of unread notifs to incorrectly come
+      // back as "read". we use page 6 as an abort condition, which means
+      // after ~180 notifs we give up on tracking unread state correctly
+      // -prf
+      if (!pageState.current.hasMarkedRead) {
+        let hasMarkedRead = false
+        if (
+          pageState.current.pageNum > 5 ||
+          page.items.some(item => item.notification.isRead)
+        ) {
+          unreads.markAllRead()
+          hasMarkedRead = true
+        }
+        pageState.current = {
+          pageNum: pageState.current.pageNum + 1,
+          hasMarkedRead,
+        }
       }
 
       return page
diff --git a/src/state/queries/notifications/types.ts b/src/state/queries/notifications/types.ts
index 0e88f1071..b52341115 100644
--- a/src/state/queries/notifications/types.ts
+++ b/src/state/queries/notifications/types.ts
@@ -28,7 +28,10 @@ export interface FeedPage {
 }
 
 export interface CachedFeedPage {
-  sessDid: string // used to invalidate on session changes
+  /**
+   * if true, the cached page is recent enough to use as the response
+   */
+  usableInFeed: boolean
   syncedAt: Date
   data: FeedPage | undefined
 }
diff --git a/src/state/queries/notifications/unread.tsx b/src/state/queries/notifications/unread.tsx
index 6c130aaea..ba38463f2 100644
--- a/src/state/queries/notifications/unread.tsx
+++ b/src/state/queries/notifications/unread.tsx
@@ -37,7 +37,7 @@ const apiContext = React.createContext<ApiContext>({
 })
 
 export function Provider({children}: React.PropsWithChildren<{}>) {
-  const {hasSession, currentAccount} = useSession()
+  const {hasSession} = useSession()
   const queryClient = useQueryClient()
   const moderationOpts = useModerationOpts()
   const threadMutes = useMutedThreads()
@@ -46,7 +46,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
 
   const checkUnreadRef = React.useRef<ApiContext['checkUnread'] | null>(null)
   const cacheRef = React.useRef<CachedFeedPage>({
-    sessDid: currentAccount?.did || '',
+    usableInFeed: false,
     syncedAt: new Date(),
     data: undefined,
   })
@@ -65,7 +65,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
   React.useEffect(() => {
     const listener = ({data}: MessageEvent) => {
       cacheRef.current = {
-        sessDid: currentAccount?.did || '',
+        usableInFeed: false,
         syncedAt: new Date(),
         data: undefined,
       }
@@ -75,7 +75,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
     return () => {
       broadcast.removeEventListener('message', listener)
     }
-  }, [setNumUnread, currentAccount])
+  }, [setNumUnread])
 
   // create API
   const api = React.useMemo<ApiContext>(() => {
@@ -119,7 +119,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
           const lastIndexed =
             page.items[0] && new Date(page.items[0].notification.indexedAt)
           cacheRef.current = {
-            sessDid: currentAccount?.did || '',
+            usableInFeed: !!invalidate, // will be used immediately
             data: page,
             syncedAt: !lastIndexed || now > lastIndexed ? now : lastIndexed,
           }
@@ -136,14 +136,13 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
       },
 
       getCachedUnreadPage() {
-        // return cached page if was for the current user
-        // (protects against session changes serving data from the past session)
-        if (cacheRef.current.sessDid === currentAccount?.did) {
+        // return cached page if it's marked as fresh enough
+        if (cacheRef.current.usableInFeed) {
           return cacheRef.current.data
         }
       },
     }
-  }, [setNumUnread, queryClient, moderationOpts, threadMutes, currentAccount])
+  }, [setNumUnread, queryClient, moderationOpts, threadMutes])
   checkUnreadRef.current = api.checkUnread
 
   return (
diff --git a/src/state/queries/notifications/util.ts b/src/state/queries/notifications/util.ts
index b8f320473..48e1b8dd8 100644
--- a/src/state/queries/notifications/util.ts
+++ b/src/state/queries/notifications/util.ts
@@ -119,8 +119,7 @@ function groupNotifications(
           Math.abs(ts2 - ts) < MS_2DAY &&
           notif.reason === groupedNotif.notification.reason &&
           notif.reasonSubject === groupedNotif.notification.reasonSubject &&
-          notif.author.did !== groupedNotif.notification.author.did &&
-          notif.isRead === groupedNotif.notification.isRead
+          notif.author.did !== groupedNotif.notification.author.did
         ) {
           groupedNotif.additional = groupedNotif.additional || []
           groupedNotif.additional.push(notif)
diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx
index 0af6484f3..fceaa60c2 100644
--- a/src/view/screens/Notifications.tsx
+++ b/src/view/screens/Notifications.tsx
@@ -37,6 +37,7 @@ export function NotificationsScreen({}: Props) {
   const setMinimalShellMode = useSetMinimalShellMode()
   const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll()
   const scrollElRef = React.useRef<FlatList>(null)
+  const checkLatestRef = React.useRef<() => void | null>()
   const {screen} = useAnalytics()
   const pal = usePalette('default')
   const {isDesktop} = useWebMediaQueries()
@@ -63,16 +64,26 @@ export function NotificationsScreen({}: Props) {
     }
   }, [scrollToTop, queryClient, unreadApi, hasNew])
 
+  const onFocusCheckLatest = React.useCallback(() => {
+    // on focus, check for latest, but only invalidate if the user
+    // isnt scrolled down to avoid moving content underneath them
+    unreadApi.checkUnread({invalidate: !isScrolledDown})
+  }, [unreadApi, isScrolledDown])
+  checkLatestRef.current = onFocusCheckLatest
+
   // on-visible setup
   // =
   useFocusEffect(
     React.useCallback(() => {
       setMinimalShellMode(false)
-      logger.debug('NotificationsScreen: Updating feed')
+      logger.debug('NotificationsScreen: Focus')
       screen('Notifications')
-      return listenSoftReset(onPressLoadLatest)
-    }, [screen, onPressLoadLatest, setMinimalShellMode]),
+      checkLatestRef.current?.()
+    }, [screen, setMinimalShellMode]),
   )
+  React.useEffect(() => {
+    return listenSoftReset(onPressLoadLatest)
+  }, [onPressLoadLatest])
 
   const ListHeaderComponent = React.useCallback(() => {
     if (isDesktop) {
diff --git a/src/view/shell/Drawer.tsx b/src/view/shell/Drawer.tsx
index 29592cd8e..4d3a9531d 100644
--- a/src/view/shell/Drawer.tsx
+++ b/src/view/shell/Drawer.tsx
@@ -14,7 +14,6 @@ import {
   FontAwesomeIcon,
   FontAwesomeIconStyle,
 } from '@fortawesome/react-native-fontawesome'
-import {useQueryClient} from '@tanstack/react-query'
 import {s, colors} from 'lib/styles'
 import {FEEDBACK_FORM_URL, HELP_DESK_URL} from 'lib/constants'
 import {
@@ -51,9 +50,7 @@ import {useProfileQuery} from '#/state/queries/profile'
 import {useUnreadNotifications} from '#/state/queries/notifications/unread'
 import {emitSoftReset} from '#/state/events'
 import {useInviteCodesQuery} from '#/state/queries/invites'
-import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed'
 import {NavSignupCard} from '#/view/shell/NavSignupCard'
-import {truncateAndInvalidate} from '#/state/queries/util'
 
 let DrawerProfileCard = ({
   account,
@@ -109,7 +106,6 @@ export {DrawerProfileCard}
 let DrawerContent = ({}: {}): React.ReactNode => {
   const theme = useTheme()
   const pal = usePalette('default')
-  const queryClient = useQueryClient()
   const setDrawerOpen = useSetDrawerOpen()
   const navigation = useNavigation<NavigationProp>()
   const {track} = useAnalytics()
@@ -140,16 +136,12 @@ let DrawerContent = ({}: {}): React.ReactNode => {
         } else if (tabState === TabState.Inside) {
           navigation.dispatch(StackActions.popToTop())
         } else {
-          if (tab === 'Notifications') {
-            // fetch new notifs on view
-            truncateAndInvalidate(queryClient, NOTIFS_RQKEY())
-          }
           // @ts-ignore must be Home, Search, Notifications, or MyProfile
           navigation.navigate(`${tab}Tab`)
         }
       }
     },
-    [track, navigation, setDrawerOpen, currentAccount, queryClient],
+    [track, navigation, setDrawerOpen, currentAccount],
   )
 
   const onPressHome = React.useCallback(() => onPressTab('Home'), [onPressTab])
diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx
index 746b4d123..7f1ba8a5f 100644
--- a/src/view/shell/bottom-bar/BottomBar.tsx
+++ b/src/view/shell/bottom-bar/BottomBar.tsx
@@ -1,7 +1,6 @@
 import React, {ComponentProps} from 'react'
 import {GestureResponderEvent, TouchableOpacity, View} from 'react-native'
 import Animated from 'react-native-reanimated'
-import {useQueryClient} from '@tanstack/react-query'
 import {StackActions} from '@react-navigation/native'
 import {BottomTabBarProps} from '@react-navigation/bottom-tabs'
 import {useSafeAreaInsets} from 'react-native-safe-area-context'
@@ -31,8 +30,6 @@ import {useUnreadNotifications} from '#/state/queries/notifications/unread'
 import {emitSoftReset} from '#/state/events'
 import {useSession} from '#/state/session'
 import {useProfileQuery} from '#/state/queries/profile'
-import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed'
-import {truncateAndInvalidate} from '#/state/queries/util'
 
 type TabOptions = 'Home' | 'Search' | 'Notifications' | 'MyProfile' | 'Feeds'
 
@@ -41,7 +38,6 @@ export function BottomBar({navigation}: BottomTabBarProps) {
   const {hasSession, currentAccount} = useSession()
   const pal = usePalette('default')
   const {_} = useLingui()
-  const queryClient = useQueryClient()
   const safeAreaInsets = useSafeAreaInsets()
   const {track} = useAnalytics()
   const {footerHeight} = useShellLayout()
@@ -61,14 +57,10 @@ export function BottomBar({navigation}: BottomTabBarProps) {
       } else if (tabState === TabState.Inside) {
         navigation.dispatch(StackActions.popToTop())
       } else {
-        if (tab === 'Notifications') {
-          // fetch new notifs on view
-          truncateAndInvalidate(queryClient, NOTIFS_RQKEY())
-        }
         navigation.navigate(`${tab}Tab`)
       }
     },
-    [track, navigation, queryClient],
+    [track, navigation],
   )
   const onPressHome = React.useCallback(() => onPressTab('Home'), [onPressTab])
   const onPressSearch = React.useCallback(
diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx
index 2ed294501..e294431f3 100644
--- a/src/view/shell/desktop/LeftNav.tsx
+++ b/src/view/shell/desktop/LeftNav.tsx
@@ -45,10 +45,7 @@ import {useUnreadNotifications} from '#/state/queries/notifications/unread'
 import {useComposerControls} from '#/state/shell/composer'
 import {useFetchHandle} from '#/state/queries/handle'
 import {emitSoftReset} from '#/state/events'
-import {useQueryClient} from '@tanstack/react-query'
-import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed'
 import {NavSignupCard} from '#/view/shell/NavSignupCard'
-import {truncateAndInvalidate} from '#/state/queries/util'
 
 function ProfileCard() {
   const {currentAccount} = useSession()
@@ -123,7 +120,6 @@ interface NavItemProps {
 }
 function NavItem({count, href, icon, iconFilled, label}: NavItemProps) {
   const pal = usePalette('default')
-  const queryClient = useQueryClient()
   const {currentAccount} = useSession()
   const {isDesktop, isTablet} = useWebMediaQueries()
   const [pathName] = React.useMemo(() => router.matchPath(href), [href])
@@ -149,14 +145,10 @@ function NavItem({count, href, icon, iconFilled, label}: NavItemProps) {
       if (isCurrent) {
         emitSoftReset()
       } else {
-        if (href === '/notifications') {
-          // fetch new notifs on view
-          truncateAndInvalidate(queryClient, NOTIFS_RQKEY())
-        }
         onPress()
       }
     },
-    [onPress, isCurrent, queryClient, href],
+    [onPress, isCurrent],
   )
 
   return (