about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--package.json1
-rw-r--r--src/App.native.tsx26
-rw-r--r--src/App.web.tsx28
-rw-r--r--src/lib/constants.ts107
-rw-r--r--src/lib/react-query.ts3
-rw-r--r--src/view/com/auth/onboarding/RecommendedFeeds.tsx70
-rw-r--r--src/view/com/auth/onboarding/RecommendedFeedsItem.tsx11
-rw-r--r--yarn.lock15
8 files changed, 108 insertions, 153 deletions
diff --git a/package.json b/package.json
index baf4f2843..32629d029 100644
--- a/package.json
+++ b/package.json
@@ -53,6 +53,7 @@
     "@segment/analytics-react-native": "^2.10.1",
     "@segment/sovran-react-native": "^0.4.5",
     "@sentry/react-native": "5.5.0",
+    "@tanstack/react-query": "^4.33.0",
     "@tiptap/core": "^2.0.0-beta.220",
     "@tiptap/extension-document": "^2.0.0-beta.220",
     "@tiptap/extension-hard-break": "^2.0.3",
diff --git a/src/App.native.tsx b/src/App.native.tsx
index 09782a875..d43155bf3 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -16,6 +16,8 @@ import * as notifications from 'lib/notifications/notifications'
 import * as analytics from 'lib/analytics/analytics'
 import * as Toast from './view/com/util/Toast'
 import {handleLink} from './Navigation'
+import {QueryClientProvider} from '@tanstack/react-query'
+import {queryClient} from 'lib/react-query'
 
 SplashScreen.preventAutoHideAsync()
 
@@ -51,17 +53,19 @@ const App = observer(function AppImpl() {
     return null
   }
   return (
-    <ThemeProvider theme={rootStore.shell.colorMode}>
-      <RootSiblingParent>
-        <analytics.Provider>
-          <RootStoreProvider value={rootStore}>
-            <GestureHandlerRootView style={s.h100pct}>
-              <Shell />
-            </GestureHandlerRootView>
-          </RootStoreProvider>
-        </analytics.Provider>
-      </RootSiblingParent>
-    </ThemeProvider>
+    <QueryClientProvider client={queryClient}>
+      <ThemeProvider theme={rootStore.shell.colorMode}>
+        <RootSiblingParent>
+          <analytics.Provider>
+            <RootStoreProvider value={rootStore}>
+              <GestureHandlerRootView style={s.h100pct}>
+                <Shell />
+              </GestureHandlerRootView>
+            </RootStoreProvider>
+          </analytics.Provider>
+        </RootSiblingParent>
+      </ThemeProvider>
+    </QueryClientProvider>
   )
 })
 
diff --git a/src/App.web.tsx b/src/App.web.tsx
index 41a7189d3..a9123cc58 100644
--- a/src/App.web.tsx
+++ b/src/App.web.tsx
@@ -9,6 +9,8 @@ import {Shell} from './view/shell/index'
 import {ToastContainer} from './view/com/util/Toast.web'
 import {ThemeProvider} from 'lib/ThemeContext'
 import {observer} from 'mobx-react-lite'
+import {QueryClientProvider} from '@tanstack/react-query'
+import {queryClient} from 'lib/react-query'
 
 const App = observer(function AppImpl() {
   const [rootStore, setRootStore] = useState<RootStoreModel | undefined>(
@@ -30,18 +32,20 @@ const App = observer(function AppImpl() {
   }
 
   return (
-    <ThemeProvider theme={rootStore.shell.colorMode}>
-      <RootSiblingParent>
-        <analytics.Provider>
-          <RootStoreProvider value={rootStore}>
-            <SafeAreaProvider>
-              <Shell />
-            </SafeAreaProvider>
-            <ToastContainer />
-          </RootStoreProvider>
-        </analytics.Provider>
-      </RootSiblingParent>
-    </ThemeProvider>
+    <QueryClientProvider client={queryClient}>
+      <ThemeProvider theme={rootStore.shell.colorMode}>
+        <RootSiblingParent>
+          <analytics.Provider>
+            <RootStoreProvider value={rootStore}>
+              <SafeAreaProvider>
+                <Shell />
+              </SafeAreaProvider>
+              <ToastContainer />
+            </RootStoreProvider>
+          </analytics.Provider>
+        </RootSiblingParent>
+      </ThemeProvider>
+    </QueryClientProvider>
   )
 })
 
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 94551e6ef..001cdf8c3 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -148,110 +148,3 @@ export const HITSLOP_10 = createHitslop(10)
 export const HITSLOP_20 = createHitslop(20)
 export const HITSLOP_30 = createHitslop(30)
 export const BACK_HITSLOP = HITSLOP_30
-
-export const RECOMMENDED_FEEDS = [
-  {
-    did: 'did:plc:hsqwcidfez66lwm3gxhfv5in',
-    rkey: 'aaaf2pqeodmpy',
-  },
-  {
-    did: 'did:plc:gekdk2nd47gkk3utfz2xf7cn',
-    rkey: 'aaap4tbjcfe5y',
-  },
-  {
-    did: 'did:plc:5rw2on4i56btlcajojaxwcat',
-    rkey: 'aaao6g552b33o',
-  },
-  {
-    did: 'did:plc:jfhpnnst6flqway4eaeqzj2a',
-    rkey: 'for-science',
-  },
-  {
-    did: 'did:plc:7q4nnnxawajbfaq7to5dpbsy',
-    rkey: 'bsky-news',
-  },
-  {
-    did: 'did:plc:jcoy7v3a2t4rcfdh6i4kza25',
-    rkey: 'astro',
-  },
-  {
-    did: 'did:plc:tenurhgjptubkk5zf5qhi3og',
-    rkey: 'h-nba',
-  },
-  {
-    did: 'did:plc:vpkhqolt662uhesyj6nxm7ys',
-    rkey: 'devfeed',
-  },
-  {
-    did: 'did:plc:cndfx4udwgvpjaakvxvh7wm5',
-    rkey: 'flipboard-tech',
-  },
-  {
-    did: 'did:plc:w4xbfzo7kqfes5zb7r6qv3rw',
-    rkey: 'blacksky',
-  },
-  {
-    did: 'did:plc:lptjvw6ut224kwrj7ub3sqbe',
-    rkey: 'aaaotfjzjplna',
-  },
-  {
-    did: 'did:plc:gkvpokm7ec5j5yxls6xk4e3z',
-    rkey: 'formula-one',
-  },
-  {
-    did: 'did:plc:q6gjnaw2blty4crticxkmujt',
-    rkey: 'positivifeed',
-  },
-  {
-    did: 'did:plc:l72uci4styb4jucsgcrrj5ap',
-    rkey: 'aaao5dzfm36u4',
-  },
-  {
-    did: 'did:plc:k3jkadxv5kkjgs6boyon7m6n',
-    rkey: 'aaaavlyvqzst2',
-  },
-  {
-    did: 'did:plc:nkahctfdi6bxk72umytfwghw',
-    rkey: 'aaado2uvfsc6w',
-  },
-  {
-    did: 'did:plc:epihigio3d7un7u3gpqiy5gv',
-    rkey: 'aaaekwsc7zsvs',
-  },
-  {
-    did: 'did:plc:qiknc4t5rq7yngvz7g4aezq7',
-    rkey: 'aaaejxlobe474',
-  },
-  {
-    did: 'did:plc:mlq4aycufcuolr7ax6sezpc4',
-    rkey: 'aaaoudweck6uy',
-  },
-  {
-    did: 'did:plc:rcez5hcvq3vzlu5x7xrjyccg',
-    rkey: 'aaadzjxbcddzi',
-  },
-  {
-    did: 'did:plc:lnxbuzaenlwjrncx6sc4cfdr',
-    rkey: 'aaab2vesjtszc',
-  },
-  {
-    did: 'did:plc:x3cya3wkt4n6u4ihmvpsc5if',
-    rkey: 'aaacynbxwimok',
-  },
-  {
-    did: 'did:plc:abv47bjgzjgoh3yrygwoi36x',
-    rkey: 'aaagt6amuur5e',
-  },
-  {
-    did: 'did:plc:ffkgesg3jsv2j7aagkzrtcvt',
-    rkey: 'aaacjerk7gwek',
-  },
-  {
-    did: 'did:plc:geoqe3qls5mwezckxxsewys2',
-    rkey: 'aaai43yetqshu',
-  },
-  {
-    did: 'did:plc:2wqomm3tjqbgktbrfwgvrw34',
-    rkey: 'authors',
-  },
-]
diff --git a/src/lib/react-query.ts b/src/lib/react-query.ts
new file mode 100644
index 000000000..2a8f1d759
--- /dev/null
+++ b/src/lib/react-query.ts
@@ -0,0 +1,3 @@
+import {QueryClient} from '@tanstack/react-query'
+
+export const queryClient = new QueryClient()
diff --git a/src/view/com/auth/onboarding/RecommendedFeeds.tsx b/src/view/com/auth/onboarding/RecommendedFeeds.tsx
index 99cdcafd0..8e29a5895 100644
--- a/src/view/com/auth/onboarding/RecommendedFeeds.tsx
+++ b/src/view/com/auth/onboarding/RecommendedFeeds.tsx
@@ -1,5 +1,5 @@
 import React from 'react'
-import {FlatList, StyleSheet, View} from 'react-native'
+import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native'
 import {observer} from 'mobx-react-lite'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {TabletOrDesktop, Mobile} from 'view/com/util/layouts/Breakpoints'
@@ -10,7 +10,10 @@ import {Button} from 'view/com/util/forms/Button'
 import {RecommendedFeedsItem} from './RecommendedFeedsItem'
 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
 import {usePalette} from 'lib/hooks/usePalette'
-import {RECOMMENDED_FEEDS} from 'lib/constants'
+import {useQuery} from '@tanstack/react-query'
+import {useStores} from 'state/index'
+import {CustomFeedModel} from 'state/models/feeds/custom-feed'
+import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
 
 type Props = {
   next: () => void
@@ -18,8 +21,31 @@ type Props = {
 export const RecommendedFeeds = observer(function RecommendedFeedsImpl({
   next,
 }: Props) {
+  const store = useStores()
   const pal = usePalette('default')
   const {isTabletOrMobile} = useWebMediaQueries()
+  const {isLoading, data: recommendedFeeds} = useQuery({
+    staleTime: Infinity, // fixed list rn, never refetch
+    queryKey: ['onboarding', 'recommended_feeds'],
+    async queryFn() {
+      try {
+        const {
+          data: {feeds},
+          success,
+        } = await store.agent.app.bsky.feed.getSuggestedFeeds()
+
+        if (!success) return
+
+        return (feeds.length ? feeds : []).map(feed => {
+          return new CustomFeedModel(store, feed)
+        })
+      } catch (e) {
+        return
+      }
+    },
+  })
+
+  const hasFeeds = recommendedFeeds && recommendedFeeds.length
 
   const title = (
     <>
@@ -86,12 +112,20 @@ export const RecommendedFeeds = observer(function RecommendedFeedsImpl({
           horizontal
           titleStyle={isTabletOrMobile ? undefined : {minWidth: 470}}
           contentStyle={{paddingHorizontal: 0}}>
-          <FlatList
-            data={RECOMMENDED_FEEDS}
-            renderItem={({item}) => <RecommendedFeedsItem {...item} />}
-            keyExtractor={item => item.did + item.rkey}
-            style={{flex: 1}}
-          />
+          {hasFeeds ? (
+            <FlatList
+              data={recommendedFeeds}
+              renderItem={({item}) => <RecommendedFeedsItem item={item} />}
+              keyExtractor={item => item.uri}
+              style={{flex: 1}}
+            />
+          ) : isLoading ? (
+            <View>
+              <ActivityIndicator size="large" />
+            </View>
+          ) : (
+            <ErrorMessage message="Failed to load recommended feeds" />
+          )}
         </TitleColumnLayout>
       </TabletOrDesktop>
       <Mobile>
@@ -106,12 +140,20 @@ export const RecommendedFeeds = observer(function RecommendedFeedsImpl({
             pinned feeds.
           </Text>
 
-          <FlatList
-            data={RECOMMENDED_FEEDS}
-            renderItem={({item}) => <RecommendedFeedsItem {...item} />}
-            keyExtractor={item => item.did + item.rkey}
-            style={{flex: 1}}
-          />
+          {hasFeeds ? (
+            <FlatList
+              data={recommendedFeeds}
+              renderItem={({item}) => <RecommendedFeedsItem item={item} />}
+              keyExtractor={item => item.uri}
+              style={{flex: 1}}
+            />
+          ) : isLoading ? (
+            <View>
+              <ActivityIndicator size="large" />
+            </View>
+          ) : (
+            <ErrorMessage message="Failed to load recommended feeds" />
+          )}
 
           <Button
             onPress={next}
diff --git a/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx b/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx
index e5d12273a..d130dc138 100644
--- a/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx
+++ b/src/view/com/auth/onboarding/RecommendedFeedsItem.tsx
@@ -8,22 +8,17 @@ import {UserAvatar} from 'view/com/util/UserAvatar'
 import * as Toast from 'view/com/util/Toast'
 import {HeartIcon} from 'lib/icons'
 import {usePalette} from 'lib/hooks/usePalette'
-import {useCustomFeed} from 'lib/hooks/useCustomFeed'
 import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
-import {makeRecordUri} from 'lib/strings/url-helpers'
 import {sanitizeHandle} from 'lib/strings/handles'
+import {CustomFeedModel} from 'state/models/feeds/custom-feed'
 
 export const RecommendedFeedsItem = observer(function RecommendedFeedsItemImpl({
-  did,
-  rkey,
+  item,
 }: {
-  did: string
-  rkey: string
+  item: CustomFeedModel
 }) {
   const {isMobile} = useWebMediaQueries()
   const pal = usePalette('default')
-  const uri = makeRecordUri(did, 'app.bsky.feed.generator', rkey)
-  const item = useCustomFeed(uri)
   if (!item) return null
   const onToggle = async () => {
     if (item.isSaved) {
diff --git a/yarn.lock b/yarn.lock
index 308e3fd96..cba848b69 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6005,6 +6005,19 @@
     "@svgr/plugin-svgo" "^5.5.0"
     loader-utils "^2.0.0"
 
+"@tanstack/query-core@4.33.0":
+  version "4.33.0"
+  resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.33.0.tgz#7756da9a75a424e521622b1d84eb55b7a2b33715"
+  integrity sha512-qYu73ptvnzRh6se2nyBIDHGBQvPY1XXl3yR769B7B6mIDD7s+EZhdlWHQ67JI6UOTFRaI7wupnTnwJ3gE0Mr/g==
+
+"@tanstack/react-query@^4.33.0":
+  version "4.33.0"
+  resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.33.0.tgz#e927b0343a6ecaa948fee59e9ca98fe561062638"
+  integrity sha512-97nGbmDK0/m0B86BdiXzx3EW9RcDYKpnyL2+WwyuLHEgpfThYAnXFaMMmnTDuAO4bQJXEhflumIEUfKmP7ESGA==
+  dependencies:
+    "@tanstack/query-core" "4.33.0"
+    use-sync-external-store "^1.2.0"
+
 "@testing-library/jest-native@^5.4.1":
   version "5.4.2"
   resolved "https://registry.yarnpkg.com/@testing-library/jest-native/-/jest-native-5.4.2.tgz#6b0c987cc57f8d900763e763025d00d26ccbc85f"
@@ -19293,7 +19306,7 @@ use-sidecar@^1.1.2:
     detect-node-es "^1.1.0"
     tslib "^2.0.0"
 
-use-sync-external-store@^1.0.0:
+use-sync-external-store@^1.0.0, use-sync-external-store@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
   integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==