diff options
author | Paul Frazee <pfrazee@gmail.com> | 2024-01-10 22:27:14 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-10 22:27:14 -0800 |
commit | 7ab4be6f7d5a89f752ebd989d9c730a9e135a989 (patch) | |
tree | b6d45ac19bf83af827268fb3c282d6db3643dcc1 /src | |
parent | 0442dcc1a01a613985155c86a5ee042085553f33 (diff) | |
download | voidsky-7ab4be6f7d5a89f752ebd989d9c730a9e135a989.tar.zst |
Reduce polling (#2465)
* Move profile and preference polling to polls-on-foreground * Refetch prefs on feeds screen refresh since polling no longer occurs * Reduce notifications polling by 50% if there's already an unread * Disable feed polling if we know we have content * Disable the hard refresh after 1 hour in case it's the cause of the random feed refresh bug * Fix types
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/react-query.ts | 32 | ||||
-rw-r--r-- | src/state/queries/notifications/types.ts | 1 | ||||
-rw-r--r-- | src/state/queries/notifications/unread.tsx | 31 | ||||
-rw-r--r-- | src/state/queries/preferences/index.ts | 2 | ||||
-rw-r--r-- | src/state/queries/profile.ts | 2 | ||||
-rw-r--r-- | src/view/com/feeds/FeedPage.tsx | 1 | ||||
-rw-r--r-- | src/view/com/posts/Feed.tsx | 14 | ||||
-rw-r--r-- | src/view/screens/Feeds.tsx | 8 | ||||
-rw-r--r-- | src/view/screens/ProfileFeed.tsx | 1 | ||||
-rw-r--r-- | src/view/screens/ProfileList.tsx | 1 |
10 files changed, 79 insertions, 14 deletions
diff --git a/src/lib/react-query.ts b/src/lib/react-query.ts index 6ec620f74..7fe3fe7a4 100644 --- a/src/lib/react-query.ts +++ b/src/lib/react-query.ts @@ -1,11 +1,39 @@ -import {QueryClient} from '@tanstack/react-query' +import {AppState, AppStateStatus} from 'react-native' +import {QueryClient, focusManager} from '@tanstack/react-query' +import {isNative} from '#/platform/detection' + +focusManager.setEventListener(onFocus => { + if (isNative) { + const subscription = AppState.addEventListener( + 'change', + (status: AppStateStatus) => { + focusManager.setFocused(status === 'active') + }, + ) + + return () => subscription.remove() + } else if (typeof window !== 'undefined' && window.addEventListener) { + // these handlers are a bit redundant but focus catches when the browser window + // is blurred/focused while visibilitychange seems to only handle when the + // window minimizes (both of them catch tab changes) + // there's no harm to redundant fires because refetchOnWindowFocus is only + // used with queries that employ stale data times + const handler = () => onFocus() + window.addEventListener('focus', handler, false) + window.addEventListener('visibilitychange', handler, false) + return () => { + window.removeEventListener('visibilitychange', handler) + window.removeEventListener('focus', handler) + } + } +}) export const queryClient = new QueryClient({ defaultOptions: { queries: { // NOTE // refetchOnWindowFocus breaks some UIs (like feeds) - // so we NEVER want to enable this + // so we only selectively want to enable this // -prf refetchOnWindowFocus: false, // Structural sharing between responses makes it impossible to rely on diff --git a/src/state/queries/notifications/types.ts b/src/state/queries/notifications/types.ts index 86a9bb139..812236cf0 100644 --- a/src/state/queries/notifications/types.ts +++ b/src/state/queries/notifications/types.ts @@ -35,4 +35,5 @@ export interface CachedFeedPage { usableInFeed: boolean syncedAt: Date data: FeedPage | undefined + unreadCount: number } diff --git a/src/state/queries/notifications/unread.tsx b/src/state/queries/notifications/unread.tsx index d604e8fe0..49bb5a29d 100644 --- a/src/state/queries/notifications/unread.tsx +++ b/src/state/queries/notifications/unread.tsx @@ -25,7 +25,10 @@ type StateContext = string interface ApiContext { markAllRead: () => Promise<void> - checkUnread: (opts?: {invalidate?: boolean}) => Promise<void> + checkUnread: (opts?: { + invalidate?: boolean + isPoll?: boolean + }) => Promise<void> getCachedUnreadPage: () => FeedPage | undefined } @@ -50,6 +53,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) { usableInFeed: false, syncedAt: new Date(), data: undefined, + unreadCount: 0, }) // periodic sync @@ -58,7 +62,10 @@ export function Provider({children}: React.PropsWithChildren<{}>) { return } checkUnreadRef.current() // fire on init - const interval = setInterval(checkUnreadRef.current, UPDATE_INTERVAL) + const interval = setInterval( + () => checkUnreadRef.current?.({isPoll: true}), + UPDATE_INTERVAL, + ) return () => clearInterval(interval) }, [hasSession]) @@ -69,6 +76,12 @@ export function Provider({children}: React.PropsWithChildren<{}>) { usableInFeed: false, syncedAt: new Date(), data: undefined, + unreadCount: + data.event === '30+' + ? 30 + : data.event === '' + ? 0 + : parseInt(data.event, 10) || 1, } setNumUnread(data.event) } @@ -95,13 +108,24 @@ export function Provider({children}: React.PropsWithChildren<{}>) { } }, - async checkUnread({invalidate}: {invalidate?: boolean} = {}) { + async checkUnread({ + invalidate, + isPoll, + }: {invalidate?: boolean; isPoll?: boolean} = {}) { try { if (!getAgent().session) return if (AppState.currentState !== 'active') { return } + // reduce polling if unread count is set + if (isPoll && cacheRef.current?.unreadCount !== 0) { + // if hit 30+ then don't poll, otherwise reduce polling by 50% + if (cacheRef.current?.unreadCount >= 30 || Math.random() >= 0.5) { + return + } + } + // count const page = await fetchPage({ cursor: undefined, @@ -133,6 +157,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) { usableInFeed: !!invalidate, // will be used immediately data: page, syncedAt: !lastIndexed || now > lastIndexed ? now : lastIndexed, + unreadCount, } // update & broadcast diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts index a9aa7f26e..632d31a13 100644 --- a/src/state/queries/preferences/index.ts +++ b/src/state/queries/preferences/index.ts @@ -31,7 +31,7 @@ export function usePreferencesQuery() { return useQuery({ staleTime: STALE.SECONDS.FIFTEEN, structuralSharing: true, - refetchInterval: STALE.SECONDS.FIFTEEN, + refetchOnWindowFocus: true, queryKey: preferencesQueryKey, queryFn: async () => { const agent = getAgent() diff --git a/src/state/queries/profile.ts b/src/state/queries/profile.ts index 40ba0653c..21e2e5775 100644 --- a/src/state/queries/profile.ts +++ b/src/state/queries/profile.ts @@ -35,7 +35,7 @@ export function useProfileQuery({did}: {did: string | undefined}) { // if you remove it, the UI infinite-loops // -prf staleTime: isCurrentAccount ? STALE.SECONDS.THIRTY : STALE.MINUTES.FIVE, - refetchInterval: STALE.MINUTES.FIVE, + refetchOnWindowFocus: true, queryKey: RQKEY(did || ''), queryFn: async () => { const res = await getAgent().getProfile({actor: did || ''}) diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx index 2d0b17f38..49f280981 100644 --- a/src/view/com/feeds/FeedPage.tsx +++ b/src/view/com/feeds/FeedPage.tsx @@ -174,6 +174,7 @@ export function FeedPage({ feed={feed} feedParams={feedParams} pollInterval={POLL_FREQ} + disablePoll={hasNew} scrollElRef={scrollElRef} onScrolledDownChange={setIsScrolledDown} onHasNew={setHasNew} diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx index 6b6ad6e92..cd9f26463 100644 --- a/src/view/com/posts/Feed.tsx +++ b/src/view/com/posts/Feed.tsx @@ -36,7 +36,8 @@ const EMPTY_FEED_ITEM = {_reactKey: '__empty__'} const ERROR_ITEM = {_reactKey: '__error__'} const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} -const REFRESH_AFTER = STALE.HOURS.ONE +// DISABLED need to check if this is causing random feed refreshes -prf +// const REFRESH_AFTER = STALE.HOURS.ONE const CHECK_LATEST_AFTER = STALE.SECONDS.THIRTY let Feed = ({ @@ -46,6 +47,7 @@ let Feed = ({ style, enabled, pollInterval, + disablePoll, scrollElRef, onScrolledDownChange, onHasNew, @@ -63,6 +65,7 @@ let Feed = ({ style?: StyleProp<ViewStyle> enabled?: boolean pollInterval?: number + disablePoll?: boolean scrollElRef?: ListRef onHasNew?: (v: boolean) => void onScrolledDownChange?: (isScrolledDown: boolean) => void @@ -107,7 +110,7 @@ let Feed = ({ ) const checkForNew = React.useCallback(async () => { - if (!data?.pages[0] || isFetching || !onHasNew || !enabled) { + if (!data?.pages[0] || isFetching || !onHasNew || !enabled || disablePoll) { return } try { @@ -117,7 +120,7 @@ let Feed = ({ } catch (e) { logger.error('Poll latest failed', {feed, error: String(e)}) } - }, [feed, data, isFetching, onHasNew, enabled]) + }, [feed, data, isFetching, onHasNew, enabled, disablePoll]) const myDid = currentAccount?.did || '' const onPostCreated = React.useCallback(() => { @@ -146,11 +149,12 @@ let Feed = ({ React.useEffect(() => { if (enabled) { const timeSinceFirstLoad = Date.now() - lastFetchRef.current - if (timeSinceFirstLoad > REFRESH_AFTER) { + // DISABLED need to check if this is causing random feed refreshes -prf + /*if (timeSinceFirstLoad > REFRESH_AFTER) { // do a full refresh scrollElRef?.current?.scrollToOffset({offset: 0, animated: false}) queryClient.resetQueries({queryKey: RQKEY(feed)}) - } else if ( + } else*/ if ( timeSinceFirstLoad > CHECK_LATEST_AFTER && checkForNewRef.current ) { diff --git a/src/view/screens/Feeds.tsx b/src/view/screens/Feeds.tsx index e0126bd48..a913364d4 100644 --- a/src/view/screens/Feeds.tsx +++ b/src/view/screens/Feeds.tsx @@ -97,6 +97,7 @@ export function FeedsScreen(_props: Props) { data: preferences, isLoading: isPreferencesLoading, error: preferencesError, + refetch: refetchPreferences, } = usePreferencesQuery() const { data: popularFeeds, @@ -151,9 +152,12 @@ export function FeedsScreen(_props: Props) { }, [query, debouncedSearch]) const onPullToRefresh = React.useCallback(async () => { setIsPTR(true) - await refetchPopularFeeds() + await Promise.all([ + refetchPreferences().catch(_e => undefined), + refetchPopularFeeds().catch(_e => undefined), + ]) setIsPTR(false) - }, [setIsPTR, refetchPopularFeeds]) + }, [setIsPTR, refetchPreferences, refetchPopularFeeds]) const onEndReached = React.useCallback(() => { if ( isPopularFeedsFetching || diff --git a/src/view/screens/ProfileFeed.tsx b/src/view/screens/ProfileFeed.tsx index 4f5f300af..61282497c 100644 --- a/src/view/screens/ProfileFeed.tsx +++ b/src/view/screens/ProfileFeed.tsx @@ -490,6 +490,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>( enabled={isFocused} feed={feed} pollInterval={60e3} + disablePoll={hasNew} scrollElRef={scrollElRef} onHasNew={setHasNew} onScrolledDownChange={setIsScrolledDown} diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx index 30999b518..cb7962a9b 100644 --- a/src/view/screens/ProfileList.tsx +++ b/src/view/screens/ProfileList.tsx @@ -646,6 +646,7 @@ const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>( enabled={isFocused} feed={feed} pollInterval={60e3} + disablePoll={hasNew} scrollElRef={scrollElRef} onHasNew={setHasNew} onScrolledDownChange={setIsScrolledDown} |