about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authordan <dan.abramov@gmail.com>2024-02-28 16:04:51 +0000
committerGitHub <noreply@github.com>2024-02-28 08:04:51 -0800
commit0dd3f9432b061359492f18eda84638f26863f640 (patch)
tree94a9bd5fb7975b926de67dd85359cf294f411230 /src
parent88c66c4bc52336bc8f32c99eb178b0769c4bfe67 (diff)
downloadvoidsky-0dd3f9432b061359492f18eda84638f26863f640.tar.zst
Fix wrong feed being shown (#3015)
Diffstat (limited to 'src')
-rw-r--r--src/state/queries/feed.ts129
-rw-r--r--src/view/com/home/HomeHeader.tsx15
-rw-r--r--src/view/screens/Home.tsx5
-rw-r--r--src/view/shell/desktop/Feeds.tsx6
4 files changed, 69 insertions, 86 deletions
diff --git a/src/state/queries/feed.ts b/src/state/queries/feed.ts
index 67294ece2..1fa92c291 100644
--- a/src/state/queries/feed.ts
+++ b/src/state/queries/feed.ts
@@ -1,11 +1,9 @@
-import React from 'react'
 import {
   useQuery,
   useInfiniteQuery,
   InfiniteData,
   QueryKey,
   useMutation,
-  useQueryClient,
 } from '@tanstack/react-query'
 import {
   AtUri,
@@ -15,7 +13,6 @@ import {
   AppBskyUnspeccedGetPopularFeedGenerators,
 } from '@atproto/api'
 
-import {logger} from '#/logger'
 import {router} from '#/routes'
 import {sanitizeDisplayName} from '#/lib/strings/display-names'
 import {sanitizeHandle} from '#/lib/strings/handles'
@@ -219,83 +216,59 @@ const FOLLOWING_FEED_STUB: FeedSourceInfo = {
   likeUri: '',
 }
 
-export function usePinnedFeedsInfos(): {
-  feeds: FeedSourceInfo[]
-  hasPinnedCustom: boolean
-  isLoading: boolean
-} {
-  const queryClient = useQueryClient()
-  const [tabs, setTabs] = React.useState<FeedSourceInfo[]>([
-    FOLLOWING_FEED_STUB,
-  ])
-  const [isLoading, setLoading] = React.useState(true)
-  const {data: preferences} = usePreferencesQuery()
+export function usePinnedFeedsInfos() {
+  const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery()
+  const pinnedUris = preferences?.feeds?.pinned ?? []
 
-  const hasPinnedCustom = React.useMemo<boolean>(() => {
-    return tabs.some(tab => tab !== FOLLOWING_FEED_STUB)
-  }, [tabs])
-
-  React.useEffect(() => {
-    if (!preferences?.feeds?.pinned) return
-    const uris = preferences.feeds.pinned
-
-    async function fetchFeedInfo() {
-      const reqs = []
-
-      for (const uri of uris) {
-        const cached = queryClient.getQueryData<FeedSourceInfo>(
-          feedSourceInfoQueryKey({uri}),
-        )
-
-        if (cached) {
-          reqs.push(cached)
-        } else {
-          reqs.push(
-            (async () => {
-              // these requests can fail, need to filter those out
-              try {
-                return await queryClient.fetchQuery({
-                  staleTime: STALE.SECONDS.FIFTEEN,
-                  queryKey: feedSourceInfoQueryKey({uri}),
-                  queryFn: async () => {
-                    const type = getFeedTypeFromUri(uri)
+  return useQuery({
+    staleTime: STALE.INFINITY,
+    enabled: !isLoadingPrefs,
+    queryKey: ['pinnedFeedsInfos', pinnedUris.join(',')],
+    queryFn: async () => {
+      let resolved = new Map()
+
+      // Get all feeds. We can do this in a batch.
+      const feedUris = pinnedUris.filter(
+        uri => getFeedTypeFromUri(uri) === 'feed',
+      )
+      let feedsPromise = Promise.resolve()
+      if (feedUris.length > 0) {
+        feedsPromise = getAgent()
+          .app.bsky.feed.getFeedGenerators({
+            feeds: feedUris,
+          })
+          .then(res => {
+            for (let feedView of res.data.feeds) {
+              resolved.set(feedView.uri, hydrateFeedGenerator(feedView))
+            }
+          })
+      }
 
-                    if (type === 'feed') {
-                      const res =
-                        await getAgent().app.bsky.feed.getFeedGenerator({
-                          feed: uri,
-                        })
-                      return hydrateFeedGenerator(res.data.view)
-                    } else {
-                      const res = await getAgent().app.bsky.graph.getList({
-                        list: uri,
-                        limit: 1,
-                      })
-                      return hydrateList(res.data.list)
-                    }
-                  },
-                })
-              } catch (e) {
-                // expected failure
-                logger.info(`usePinnedFeedsInfos: failed to fetch ${uri}`, {
-                  error: e,
-                })
-              }
-            })(),
-          )
+      // Get all lists. This currently has to be done individually.
+      const listUris = pinnedUris.filter(
+        uri => getFeedTypeFromUri(uri) === 'list',
+      )
+      const listsPromises = listUris.map(listUri =>
+        getAgent()
+          .app.bsky.graph.getList({
+            list: listUri,
+            limit: 1,
+          })
+          .then(res => {
+            const listView = res.data.list
+            resolved.set(listView.uri, hydrateList(listView))
+          }),
+      )
+
+      // The returned result will have the original order.
+      const result = [FOLLOWING_FEED_STUB]
+      await Promise.allSettled([feedsPromise, ...listsPromises])
+      for (let pinnedUri of pinnedUris) {
+        if (resolved.has(pinnedUri)) {
+          result.push(resolved.get(pinnedUri))
         }
       }
-
-      const views = (await Promise.all(reqs)).filter(
-        Boolean,
-      ) as FeedSourceInfo[]
-
-      setTabs([FOLLOWING_FEED_STUB].concat(views))
-      setLoading(false)
-    }
-
-    fetchFeedInfo()
-  }, [queryClient, setTabs, preferences?.feeds?.pinned])
-
-  return {feeds: tabs, hasPinnedCustom, isLoading}
+      return result
+    },
+  })
 }
diff --git a/src/view/com/home/HomeHeader.tsx b/src/view/com/home/HomeHeader.tsx
index 3df3858ba..bbd16465a 100644
--- a/src/view/com/home/HomeHeader.tsx
+++ b/src/view/com/home/HomeHeader.tsx
@@ -1,7 +1,7 @@
 import React from 'react'
 import {RenderTabBarFnProps} from 'view/com/pager/Pager'
 import {HomeHeaderLayout} from './HomeHeaderLayout'
-import {usePinnedFeedsInfos} from '#/state/queries/feed'
+import {FeedSourceInfo} from '#/state/queries/feed'
 import {useNavigation} from '@react-navigation/native'
 import {NavigationProp} from 'lib/routes/types'
 import {isWeb} from 'platform/detection'
@@ -9,15 +9,22 @@ import {TabBar} from '../pager/TabBar'
 import {usePalette} from '#/lib/hooks/usePalette'
 
 export function HomeHeader(
-  props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void},
+  props: RenderTabBarFnProps & {
+    testID?: string
+    onPressSelected: () => void
+    feeds: FeedSourceInfo[]
+  },
 ) {
+  const {feeds} = props
   const navigation = useNavigation<NavigationProp>()
-  const {feeds, hasPinnedCustom} = usePinnedFeedsInfos()
   const pal = usePalette('default')
 
+  const hasPinnedCustom = React.useMemo<boolean>(() => {
+    return feeds.some(tab => tab.uri !== '')
+  }, [feeds])
+
   const items = React.useMemo(() => {
     const pinnedNames = feeds.map(f => f.displayName)
-
     if (!hasPinnedCustom) {
       return pinnedNames.concat('Feeds ✨')
     }
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 856c237f6..8f2f96180 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -21,7 +21,7 @@ import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed'
 type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
 export function HomeScreen(props: Props) {
   const {data: preferences} = usePreferencesQuery()
-  const {feeds: pinnedFeedInfos, isLoading: isPinnedFeedsLoading} =
+  const {data: pinnedFeedInfos, isLoading: isPinnedFeedsLoading} =
     usePinnedFeedsInfos()
   if (preferences && pinnedFeedInfos && !isPinnedFeedsLoading) {
     return (
@@ -124,10 +124,11 @@ function HomeScreenReady({
           onSelect={props.onSelect}
           testID="homeScreenFeedTabs"
           onPressSelected={onPressSelected}
+          feeds={pinnedFeedInfos}
         />
       )
     },
-    [onPressSelected],
+    [onPressSelected, pinnedFeedInfos],
   )
 
   const renderFollowingEmptyState = React.useCallback(() => {
diff --git a/src/view/shell/desktop/Feeds.tsx b/src/view/shell/desktop/Feeds.tsx
index c3b1caa35..f447490b3 100644
--- a/src/view/shell/desktop/Feeds.tsx
+++ b/src/view/shell/desktop/Feeds.tsx
@@ -15,7 +15,7 @@ import {emitSoftReset} from '#/state/events'
 export function DesktopFeeds() {
   const pal = usePalette('default')
   const {_} = useLingui()
-  const {feeds: pinnedFeedInfos} = usePinnedFeedsInfos()
+  const {data: pinnedFeedInfos} = usePinnedFeedsInfos()
   const selectedFeed = useSelectedFeed()
   const setSelectedFeed = useSetSelectedFeed()
   const navigation = useNavigation<NavigationProp>()
@@ -25,7 +25,9 @@ export function DesktopFeeds() {
     }
     return getCurrentRoute(state)
   })
-
+  if (!pinnedFeedInfos) {
+    return null
+  }
   return (
     <View style={[styles.container, pal.view]}>
       {pinnedFeedInfos.map(feedInfo => {