Fix wrong feed being shown (#3015)
parent
88c66c4bc5
commit
0dd3f9432b
|
@ -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}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ✨')
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(() => {
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
Loading…
Reference in New Issue