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>
This commit is contained in:
Ansh 2023-09-15 22:02:44 +05:30 committed by GitHub
parent 84b7edd9db
commit 188d4893f9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 108 additions and 153 deletions

View file

@ -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>
)
})

View file

@ -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>
)
})

View file

@ -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',
},
]

3
src/lib/react-query.ts Normal file
View file

@ -0,0 +1,3 @@
import {QueryClient} from '@tanstack/react-query'
export const queryClient = new QueryClient()

View file

@ -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}

View file

@ -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) {