Fix wrong feed being shown (#3015)

zio/stable
dan 2024-02-28 16:04:51 +00:00 committed by GitHub
parent 88c66c4bc5
commit 0dd3f9432b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 68 additions and 85 deletions

View File

@ -1,11 +1,9 @@
import React from 'react'
import { import {
useQuery, useQuery,
useInfiniteQuery, useInfiniteQuery,
InfiniteData, InfiniteData,
QueryKey, QueryKey,
useMutation, useMutation,
useQueryClient,
} from '@tanstack/react-query' } from '@tanstack/react-query'
import { import {
AtUri, AtUri,
@ -15,7 +13,6 @@ import {
AppBskyUnspeccedGetPopularFeedGenerators, AppBskyUnspeccedGetPopularFeedGenerators,
} from '@atproto/api' } from '@atproto/api'
import {logger} from '#/logger'
import {router} from '#/routes' import {router} from '#/routes'
import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles' import {sanitizeHandle} from '#/lib/strings/handles'
@ -219,83 +216,59 @@ const FOLLOWING_FEED_STUB: FeedSourceInfo = {
likeUri: '', likeUri: '',
} }
export function usePinnedFeedsInfos(): { export function usePinnedFeedsInfos() {
feeds: FeedSourceInfo[] const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery()
hasPinnedCustom: boolean const pinnedUris = preferences?.feeds?.pinned ?? []
isLoading: boolean
} {
const queryClient = useQueryClient()
const [tabs, setTabs] = React.useState<FeedSourceInfo[]>([
FOLLOWING_FEED_STUB,
])
const [isLoading, setLoading] = React.useState(true)
const {data: preferences} = usePreferencesQuery()
const hasPinnedCustom = React.useMemo<boolean>(() => { return useQuery({
return tabs.some(tab => tab !== FOLLOWING_FEED_STUB) staleTime: STALE.INFINITY,
}, [tabs]) enabled: !isLoadingPrefs,
queryKey: ['pinnedFeedsInfos', pinnedUris.join(',')],
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 () => { queryFn: async () => {
const type = getFeedTypeFromUri(uri) let resolved = new Map()
if (type === 'feed') { // Get all feeds. We can do this in a batch.
const res = const feedUris = pinnedUris.filter(
await getAgent().app.bsky.feed.getFeedGenerator({ uri => getFeedTypeFromUri(uri) === 'feed',
feed: uri, )
let feedsPromise = Promise.resolve()
if (feedUris.length > 0) {
feedsPromise = getAgent()
.app.bsky.feed.getFeedGenerators({
feeds: feedUris,
}) })
return hydrateFeedGenerator(res.data.view) .then(res => {
} else { for (let feedView of res.data.feeds) {
const res = await getAgent().app.bsky.graph.getList({ resolved.set(feedView.uri, hydrateFeedGenerator(feedView))
list: uri, }
})
}
// 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, limit: 1,
}) })
return hydrateList(res.data.list) .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))
} }
}
return result
}, },
}) })
} catch (e) {
// expected failure
logger.info(`usePinnedFeedsInfos: failed to fetch ${uri}`, {
error: e,
})
}
})(),
)
}
}
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}
} }

View File

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import {RenderTabBarFnProps} from 'view/com/pager/Pager' import {RenderTabBarFnProps} from 'view/com/pager/Pager'
import {HomeHeaderLayout} from './HomeHeaderLayout' import {HomeHeaderLayout} from './HomeHeaderLayout'
import {usePinnedFeedsInfos} from '#/state/queries/feed' import {FeedSourceInfo} from '#/state/queries/feed'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {NavigationProp} from 'lib/routes/types' import {NavigationProp} from 'lib/routes/types'
import {isWeb} from 'platform/detection' import {isWeb} from 'platform/detection'
@ -9,15 +9,22 @@ import {TabBar} from '../pager/TabBar'
import {usePalette} from '#/lib/hooks/usePalette' import {usePalette} from '#/lib/hooks/usePalette'
export function HomeHeader( 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 navigation = useNavigation<NavigationProp>()
const {feeds, hasPinnedCustom} = usePinnedFeedsInfos()
const pal = usePalette('default') const pal = usePalette('default')
const hasPinnedCustom = React.useMemo<boolean>(() => {
return feeds.some(tab => tab.uri !== '')
}, [feeds])
const items = React.useMemo(() => { const items = React.useMemo(() => {
const pinnedNames = feeds.map(f => f.displayName) const pinnedNames = feeds.map(f => f.displayName)
if (!hasPinnedCustom) { if (!hasPinnedCustom) {
return pinnedNames.concat('Feeds ✨') return pinnedNames.concat('Feeds ✨')
} }

View File

@ -21,7 +21,7 @@ import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed'
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'> type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
export function HomeScreen(props: Props) { export function HomeScreen(props: Props) {
const {data: preferences} = usePreferencesQuery() const {data: preferences} = usePreferencesQuery()
const {feeds: pinnedFeedInfos, isLoading: isPinnedFeedsLoading} = const {data: pinnedFeedInfos, isLoading: isPinnedFeedsLoading} =
usePinnedFeedsInfos() usePinnedFeedsInfos()
if (preferences && pinnedFeedInfos && !isPinnedFeedsLoading) { if (preferences && pinnedFeedInfos && !isPinnedFeedsLoading) {
return ( return (
@ -124,10 +124,11 @@ function HomeScreenReady({
onSelect={props.onSelect} onSelect={props.onSelect}
testID="homeScreenFeedTabs" testID="homeScreenFeedTabs"
onPressSelected={onPressSelected} onPressSelected={onPressSelected}
feeds={pinnedFeedInfos}
/> />
) )
}, },
[onPressSelected], [onPressSelected, pinnedFeedInfos],
) )
const renderFollowingEmptyState = React.useCallback(() => { const renderFollowingEmptyState = React.useCallback(() => {

View File

@ -15,7 +15,7 @@ import {emitSoftReset} from '#/state/events'
export function DesktopFeeds() { export function DesktopFeeds() {
const pal = usePalette('default') const pal = usePalette('default')
const {_} = useLingui() const {_} = useLingui()
const {feeds: pinnedFeedInfos} = usePinnedFeedsInfos() const {data: pinnedFeedInfos} = usePinnedFeedsInfos()
const selectedFeed = useSelectedFeed() const selectedFeed = useSelectedFeed()
const setSelectedFeed = useSetSelectedFeed() const setSelectedFeed = useSetSelectedFeed()
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()
@ -25,7 +25,9 @@ export function DesktopFeeds() {
} }
return getCurrentRoute(state) return getCurrentRoute(state)
}) })
if (!pinnedFeedInfos) {
return null
}
return ( return (
<View style={[styles.container, pal.view]}> <View style={[styles.container, pal.view]}>
{pinnedFeedInfos.map(feedInfo => { {pinnedFeedInfos.map(feedInfo => {