mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix wrong feed being shown (#3015)

authored by danabra.mov and committed by

GitHub 0dd3f943 88c66c4b

+67 -84
+49 -76
src/state/queries/feed.ts
··· 1 - import React from 'react' 2 1 import { 3 2 useQuery, 4 3 useInfiniteQuery, 5 4 InfiniteData, 6 5 QueryKey, 7 6 useMutation, 8 - useQueryClient, 9 7 } from '@tanstack/react-query' 10 8 import { 11 9 AtUri, ··· 15 13 AppBskyUnspeccedGetPopularFeedGenerators, 16 14 } from '@atproto/api' 17 15 18 - import {logger} from '#/logger' 19 16 import {router} from '#/routes' 20 17 import {sanitizeDisplayName} from '#/lib/strings/display-names' 21 18 import {sanitizeHandle} from '#/lib/strings/handles' ··· 219 216 likeUri: '', 220 217 } 221 218 222 - export function usePinnedFeedsInfos(): { 223 - feeds: FeedSourceInfo[] 224 - hasPinnedCustom: boolean 225 - isLoading: boolean 226 - } { 227 - const queryClient = useQueryClient() 228 - const [tabs, setTabs] = React.useState<FeedSourceInfo[]>([ 229 - FOLLOWING_FEED_STUB, 230 - ]) 231 - const [isLoading, setLoading] = React.useState(true) 232 - const {data: preferences} = usePreferencesQuery() 219 + export function usePinnedFeedsInfos() { 220 + const {data: preferences, isLoading: isLoadingPrefs} = usePreferencesQuery() 221 + const pinnedUris = preferences?.feeds?.pinned ?? [] 233 222 234 - const hasPinnedCustom = React.useMemo<boolean>(() => { 235 - return tabs.some(tab => tab !== FOLLOWING_FEED_STUB) 236 - }, [tabs]) 223 + return useQuery({ 224 + staleTime: STALE.INFINITY, 225 + enabled: !isLoadingPrefs, 226 + queryKey: ['pinnedFeedsInfos', pinnedUris.join(',')], 227 + queryFn: async () => { 228 + let resolved = new Map() 237 229 238 - React.useEffect(() => { 239 - if (!preferences?.feeds?.pinned) return 240 - const uris = preferences.feeds.pinned 241 - 242 - async function fetchFeedInfo() { 243 - const reqs = [] 244 - 245 - for (const uri of uris) { 246 - const cached = queryClient.getQueryData<FeedSourceInfo>( 247 - feedSourceInfoQueryKey({uri}), 248 - ) 230 + // Get all feeds. We can do this in a batch. 231 + const feedUris = pinnedUris.filter( 232 + uri => getFeedTypeFromUri(uri) === 'feed', 233 + ) 234 + let feedsPromise = Promise.resolve() 235 + if (feedUris.length > 0) { 236 + feedsPromise = getAgent() 237 + .app.bsky.feed.getFeedGenerators({ 238 + feeds: feedUris, 239 + }) 240 + .then(res => { 241 + for (let feedView of res.data.feeds) { 242 + resolved.set(feedView.uri, hydrateFeedGenerator(feedView)) 243 + } 244 + }) 245 + } 249 246 250 - if (cached) { 251 - reqs.push(cached) 252 - } else { 253 - reqs.push( 254 - (async () => { 255 - // these requests can fail, need to filter those out 256 - try { 257 - return await queryClient.fetchQuery({ 258 - staleTime: STALE.SECONDS.FIFTEEN, 259 - queryKey: feedSourceInfoQueryKey({uri}), 260 - queryFn: async () => { 261 - const type = getFeedTypeFromUri(uri) 247 + // Get all lists. This currently has to be done individually. 248 + const listUris = pinnedUris.filter( 249 + uri => getFeedTypeFromUri(uri) === 'list', 250 + ) 251 + const listsPromises = listUris.map(listUri => 252 + getAgent() 253 + .app.bsky.graph.getList({ 254 + list: listUri, 255 + limit: 1, 256 + }) 257 + .then(res => { 258 + const listView = res.data.list 259 + resolved.set(listView.uri, hydrateList(listView)) 260 + }), 261 + ) 262 262 263 - if (type === 'feed') { 264 - const res = 265 - await getAgent().app.bsky.feed.getFeedGenerator({ 266 - feed: uri, 267 - }) 268 - return hydrateFeedGenerator(res.data.view) 269 - } else { 270 - const res = await getAgent().app.bsky.graph.getList({ 271 - list: uri, 272 - limit: 1, 273 - }) 274 - return hydrateList(res.data.list) 275 - } 276 - }, 277 - }) 278 - } catch (e) { 279 - // expected failure 280 - logger.info(`usePinnedFeedsInfos: failed to fetch ${uri}`, { 281 - error: e, 282 - }) 283 - } 284 - })(), 285 - ) 263 + // The returned result will have the original order. 264 + const result = [FOLLOWING_FEED_STUB] 265 + await Promise.allSettled([feedsPromise, ...listsPromises]) 266 + for (let pinnedUri of pinnedUris) { 267 + if (resolved.has(pinnedUri)) { 268 + result.push(resolved.get(pinnedUri)) 286 269 } 287 270 } 288 - 289 - const views = (await Promise.all(reqs)).filter( 290 - Boolean, 291 - ) as FeedSourceInfo[] 292 - 293 - setTabs([FOLLOWING_FEED_STUB].concat(views)) 294 - setLoading(false) 295 - } 296 - 297 - fetchFeedInfo() 298 - }, [queryClient, setTabs, preferences?.feeds?.pinned]) 299 - 300 - return {feeds: tabs, hasPinnedCustom, isLoading} 271 + return result 272 + }, 273 + }) 301 274 }
+11 -4
src/view/com/home/HomeHeader.tsx
··· 1 1 import React from 'react' 2 2 import {RenderTabBarFnProps} from 'view/com/pager/Pager' 3 3 import {HomeHeaderLayout} from './HomeHeaderLayout' 4 - import {usePinnedFeedsInfos} from '#/state/queries/feed' 4 + import {FeedSourceInfo} from '#/state/queries/feed' 5 5 import {useNavigation} from '@react-navigation/native' 6 6 import {NavigationProp} from 'lib/routes/types' 7 7 import {isWeb} from 'platform/detection' ··· 9 9 import {usePalette} from '#/lib/hooks/usePalette' 10 10 11 11 export function HomeHeader( 12 - props: RenderTabBarFnProps & {testID?: string; onPressSelected: () => void}, 12 + props: RenderTabBarFnProps & { 13 + testID?: string 14 + onPressSelected: () => void 15 + feeds: FeedSourceInfo[] 16 + }, 13 17 ) { 18 + const {feeds} = props 14 19 const navigation = useNavigation<NavigationProp>() 15 - const {feeds, hasPinnedCustom} = usePinnedFeedsInfos() 16 20 const pal = usePalette('default') 17 21 22 + const hasPinnedCustom = React.useMemo<boolean>(() => { 23 + return feeds.some(tab => tab.uri !== '') 24 + }, [feeds]) 25 + 18 26 const items = React.useMemo(() => { 19 27 const pinnedNames = feeds.map(f => f.displayName) 20 - 21 28 if (!hasPinnedCustom) { 22 29 return pinnedNames.concat('Feeds ✨') 23 30 }
+3 -2
src/view/screens/Home.tsx
··· 21 21 type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'> 22 22 export function HomeScreen(props: Props) { 23 23 const {data: preferences} = usePreferencesQuery() 24 - const {feeds: pinnedFeedInfos, isLoading: isPinnedFeedsLoading} = 24 + const {data: pinnedFeedInfos, isLoading: isPinnedFeedsLoading} = 25 25 usePinnedFeedsInfos() 26 26 if (preferences && pinnedFeedInfos && !isPinnedFeedsLoading) { 27 27 return ( ··· 124 124 onSelect={props.onSelect} 125 125 testID="homeScreenFeedTabs" 126 126 onPressSelected={onPressSelected} 127 + feeds={pinnedFeedInfos} 127 128 /> 128 129 ) 129 130 }, 130 - [onPressSelected], 131 + [onPressSelected, pinnedFeedInfos], 131 132 ) 132 133 133 134 const renderFollowingEmptyState = React.useCallback(() => {
+4 -2
src/view/shell/desktop/Feeds.tsx
··· 15 15 export function DesktopFeeds() { 16 16 const pal = usePalette('default') 17 17 const {_} = useLingui() 18 - const {feeds: pinnedFeedInfos} = usePinnedFeedsInfos() 18 + const {data: pinnedFeedInfos} = usePinnedFeedsInfos() 19 19 const selectedFeed = useSelectedFeed() 20 20 const setSelectedFeed = useSetSelectedFeed() 21 21 const navigation = useNavigation<NavigationProp>() ··· 25 25 } 26 26 return getCurrentRoute(state) 27 27 }) 28 - 28 + if (!pinnedFeedInfos) { 29 + return null 30 + } 29 31 return ( 30 32 <View style={[styles.container, pal.view]}> 31 33 {pinnedFeedInfos.map(feedInfo => {