about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAnsh <anshnanda10@gmail.com>2023-09-15 22:02:44 +0530
committerGitHub <noreply@github.com>2023-09-15 09:32:44 -0700
commit188d4893f9f209aae10294bf72fe9f23ed399c28 (patch)
treeb87f291ff3f796d51578fd9f2e68e3311c8e74c2 /src
parent84b7edd9db4bb08f03b5c882da5542fc29a72232 (diff)
downloadvoidsky-188d4893f9f209aae10294bf72fe9f23ed399c28.tar.zst
add React Query and hook up to existing functionality (#1358)
* add React Query and hook up to existing functionality

* wire in remote data, add error message

* remove hard-coded feeds

* oops fix logic

* add loading state

* fix loading on mobile

---------

Co-authored-by: Eric Bailey <git@esb.lol>
Diffstat (limited to 'src')
-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
6 files changed, 93 insertions, 152 deletions
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) {