From 6b3eb401b04ee442005a9690dcf90e16d46c35e0 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Sat, 9 Dec 2023 15:09:31 -0800 Subject: [PATCH] Multiple notifications fixes (#2154) * Dont reset notifications feed on push notification event * Dont separate notifications by read state to avoid jank * On notifications screen focus, check latest and only rerender if not scrolled down * Reuse the cached notifs page when its not stale * Bump ios build number * Improve comments * Change the 'mark all read' condition to avoid firing too early --- app.config.js | 2 +- src/lib/notifications/notifications.ts | 3 +- src/locale/locales/en/messages.po | 90 +++++++++++----------- src/locale/locales/hi/messages.po | 90 +++++++++++----------- src/state/queries/notifications/feed.ts | 51 +++++++++--- src/state/queries/notifications/types.ts | 5 +- src/state/queries/notifications/unread.tsx | 17 ++-- src/state/queries/notifications/util.ts | 3 +- src/view/screens/Notifications.tsx | 17 +++- src/view/shell/Drawer.tsx | 10 +-- src/view/shell/bottom-bar/BottomBar.tsx | 10 +-- src/view/shell/desktop/LeftNav.tsx | 10 +-- 12 files changed, 162 insertions(+), 146 deletions(-) diff --git a/app.config.js b/app.config.js index 2f6c3b10..415693af 100644 --- a/app.config.js +++ b/app.config.js @@ -9,7 +9,7 @@ module.exports = function () { /** * iOS build number. Must be incremented for each TestFlight version. */ - const IOS_BUILD_NUMBER = '12' + const IOS_BUILD_NUMBER = '13' /** * Android build number. Must be incremented for each release. diff --git a/src/lib/notifications/notifications.ts b/src/lib/notifications/notifications.ts index d115dc98..342839a7 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 9df901fa..7a4a182c 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 5b4aec76..3e907ee3 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 a74670b5..b6aa3d75 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 0e88f107..b5234111 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 6c130aae..ba38463f 100644 --- a/src/state/queries/notifications/unread.tsx +++ b/src/state/queries/notifications/unread.tsx @@ -37,7 +37,7 @@ const apiContext = React.createContext({ }) 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(null) const cacheRef = React.useRef({ - 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(() => { @@ -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 b8f32047..48e1b8dd 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 0af6484f..fceaa60c 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(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 29592cd8..4d3a9531 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() 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 746b4d12..7f1ba8a5 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 2ed29450..e294431f 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 (