about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/react-query.ts32
-rw-r--r--src/state/queries/notifications/types.ts1
-rw-r--r--src/state/queries/notifications/unread.tsx31
-rw-r--r--src/state/queries/preferences/index.ts2
-rw-r--r--src/state/queries/profile.ts2
-rw-r--r--src/view/com/feeds/FeedPage.tsx1
-rw-r--r--src/view/com/posts/Feed.tsx14
-rw-r--r--src/view/screens/Feeds.tsx8
-rw-r--r--src/view/screens/ProfileFeed.tsx1
-rw-r--r--src/view/screens/ProfileList.tsx1
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}