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 optimistic rendering of profile page (#7830)

* don't hold up rendering on starter packs

* add tab based on profile.associated

* move query into component, fix pending states

authored by samuel.fm and committed by

GitHub f9392d4a bdcddee7

+42 -50
+28 -29
src/components/StarterPack/ProfileStarterPacks.tsx
··· 1 - import React from 'react' 1 + import React, { 2 + useCallback, 3 + useEffect, 4 + useImperativeHandle, 5 + useState, 6 + } from 'react' 2 7 import { 3 8 findNodeHandle, 4 9 ListRenderItemInfo, ··· 6 11 View, 7 12 ViewStyle, 8 13 } from 'react-native' 9 - import {AppBskyGraphDefs, AppBskyGraphGetActorStarterPacks} from '@atproto/api' 14 + import {AppBskyGraphDefs} from '@atproto/api' 10 15 import {msg, Trans} from '@lingui/macro' 11 16 import {useLingui} from '@lingui/react' 12 17 import {useNavigation} from '@react-navigation/native' 13 - import {InfiniteData, UseInfiniteQueryResult} from '@tanstack/react-query' 14 18 15 19 import {useGenerateStarterPackMutation} from '#/lib/generate-starterpack' 16 20 import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset' ··· 19 23 import {NavigationProp} from '#/lib/routes/types' 20 24 import {parseStarterPackUri} from '#/lib/strings/starter-pack' 21 25 import {logger} from '#/logger' 26 + import {useActorStarterPacksQuery} from '#/state/queries/actor-starter-packs' 22 27 import {List, ListRef} from '#/view/com/util/List' 23 - import {Text} from '#/view/com/util/text/Text' 28 + import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' 24 29 import {atoms as a, ios, useTheme} from '#/alf' 25 30 import {Button, ButtonIcon, ButtonText} from '#/components/Button' 26 31 import {useDialogControl} from '#/components/Dialog' 32 + import {VerifyEmailDialog} from '#/components/dialogs/VerifyEmailDialog' 33 + import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' 27 34 import {LinearGradientBackground} from '#/components/LinearGradientBackground' 28 35 import {Loader} from '#/components/Loader' 29 36 import * as Prompt from '#/components/Prompt' 30 37 import {Default as StarterPackCard} from '#/components/StarterPack/StarterPackCard' 31 - import {VerifyEmailDialog} from '../dialogs/VerifyEmailDialog' 32 - import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '../icons/Plus' 38 + import {Text} from '#/components/Typography' 33 39 34 40 interface SectionRef { 35 41 scrollToTop: () => void 36 42 } 37 43 38 44 interface ProfileFeedgensProps { 39 - starterPacksQuery: UseInfiniteQueryResult< 40 - InfiniteData<AppBskyGraphGetActorStarterPacks.OutputSchema, unknown>, 41 - Error 42 - > 43 45 scrollElRef: ListRef 46 + did: string 44 47 headerOffset: number 45 48 enabled?: boolean 46 49 style?: StyleProp<ViewStyle> ··· 58 61 ProfileFeedgensProps 59 62 >(function ProfileFeedgensImpl( 60 63 { 61 - starterPacksQuery: query, 62 64 scrollElRef, 65 + did, 63 66 headerOffset, 64 67 enabled, 65 68 style, ··· 71 74 ) { 72 75 const t = useTheme() 73 76 const bottomBarOffset = useBottomBarOffset(100) 74 - const [isPTRing, setIsPTRing] = React.useState(false) 75 - const {data, refetch, isFetching, hasNextPage, fetchNextPage} = query 77 + const [isPTRing, setIsPTRing] = useState(false) 78 + const {data, refetch, isFetching, hasNextPage, fetchNextPage} = 79 + useActorStarterPacksQuery({did, enabled}) 76 80 const {isTabletOrDesktop} = useWebMediaQueries() 77 81 78 82 const items = data?.pages.flatMap(page => page.starterPacks) 79 83 80 - React.useImperativeHandle(ref, () => ({ 84 + useImperativeHandle(ref, () => ({ 81 85 scrollToTop: () => {}, 82 86 })) 83 87 84 - const onRefresh = React.useCallback(async () => { 88 + const onRefresh = useCallback(async () => { 85 89 setIsPTRing(true) 86 90 try { 87 91 await refetch() ··· 91 95 setIsPTRing(false) 92 96 }, [refetch, setIsPTRing]) 93 97 94 - const onEndReached = React.useCallback(async () => { 98 + const onEndReached = useCallback(async () => { 95 99 if (isFetching || !hasNextPage) return 96 100 97 101 try { ··· 101 105 } 102 106 }, [isFetching, hasNextPage, fetchNextPage]) 103 107 104 - React.useEffect(() => { 108 + useEffect(() => { 105 109 if (enabled && scrollElRef.current) { 106 110 const nativeTag = findNodeHandle(scrollElRef.current) 107 111 setScrollViewTag(nativeTag) ··· 140 144 desktopFixedHeight 141 145 onEndReached={onEndReached} 142 146 onRefresh={onRefresh} 143 - ListEmptyComponent={Empty} 147 + ListEmptyComponent={ 148 + data ? (isMe ? Empty : undefined) : FeedLoadingPlaceholder 149 + } 144 150 ListFooterComponent={ 145 - items?.length !== 0 && isMe ? CreateAnother : undefined 151 + !!data && items?.length !== 0 && isMe ? CreateAnother : undefined 146 152 } 147 153 /> 148 154 </View> ··· 181 187 182 188 function Empty() { 183 189 const {_} = useLingui() 184 - const t = useTheme() 185 190 const navigation = useNavigation<NavigationProp>() 186 191 const confirmDialogControl = useDialogControl() 187 192 const followersDialogControl = useDialogControl() ··· 190 195 const {needsEmailVerification} = useEmail() 191 196 const verifyEmailControl = useDialogControl() 192 197 193 - const [isGenerating, setIsGenerating] = React.useState(false) 198 + const [isGenerating, setIsGenerating] = useState(false) 194 199 195 200 const {mutate: generateStarterPack} = useGenerateStarterPackMutation({ 196 201 onSuccess: ({uri}) => { ··· 227 232 a.justify_between, 228 233 a.gap_lg, 229 234 a.shadow_lg, 230 - {marginTop: 1}, 235 + {marginTop: a.border.borderWidth}, 231 236 ]}> 232 237 <View style={[a.gap_xs]}> 233 - <Text 234 - style={[ 235 - a.font_bold, 236 - a.text_lg, 237 - t.atoms.text_contrast_medium, 238 - {color: 'white'}, 239 - ]}> 238 + <Text style={[a.font_bold, a.text_lg, {color: 'white'}]}> 240 239 <Trans>You haven't created a starter pack yet!</Trans> 241 240 </Text> 242 241 <Text style={[a.text_md, {color: 'white'}]}>
+8 -2
src/state/queries/actor-starter-packs.ts
··· 11 11 export const RQKEY_ROOT = 'actor-starter-packs' 12 12 export const RQKEY = (did?: string) => [RQKEY_ROOT, did] 13 13 14 - export function useActorStarterPacksQuery({did}: {did?: string}) { 14 + export function useActorStarterPacksQuery({ 15 + did, 16 + enabled = true, 17 + }: { 18 + did?: string 19 + enabled?: boolean 20 + }) { 15 21 const agent = useAgent() 16 22 17 23 return useInfiniteQuery< ··· 30 36 }) 31 37 return res.data 32 38 }, 33 - enabled: Boolean(did), 39 + enabled: Boolean(did) && enabled, 34 40 initialPageParam: undefined, 35 41 getNextPageParam: lastPage => lastPage.cursor, 36 42 })
+1 -1
src/view/com/feeds/ProfileFeedgens.tsx
··· 76 76 if (isError && isEmpty) { 77 77 items = items.concat([ERROR_ITEM]) 78 78 } 79 - if (!isFetched && isFetching) { 79 + if (!isFetched || isFetching) { 80 80 items = items.concat([LOADING]) 81 81 } else if (isEmpty) { 82 82 items = items.concat([EMPTY])
+1 -1
src/view/com/lists/ProfileLists.tsx
··· 72 72 if (isError && isEmpty) { 73 73 items = items.concat([ERROR_ITEM]) 74 74 } 75 - if (!isFetched && isFetching) { 75 + if (!isFetched || isFetching) { 76 76 items = items.concat([LOADING]) 77 77 } else if (isEmpty) { 78 78 items = items.concat([EMPTY])
+4 -17
src/view/screens/Profile.tsx
··· 3 3 import {SafeAreaView} from 'react-native-safe-area-context' 4 4 import { 5 5 AppBskyActorDefs, 6 - AppBskyGraphGetActorStarterPacks, 7 6 moderateProfile, 8 7 ModerationOpts, 9 8 RichText as RichTextAPI, ··· 11 10 import {msg} from '@lingui/macro' 12 11 import {useLingui} from '@lingui/react' 13 12 import {useFocusEffect} from '@react-navigation/native' 14 - import { 15 - InfiniteData, 16 - UseInfiniteQueryResult, 17 - useQueryClient, 18 - } from '@tanstack/react-query' 13 + import {useQueryClient} from '@tanstack/react-query' 19 14 20 15 import {useSetTitle} from '#/lib/hooks/useSetTitle' 21 16 import {ComposeIcon2} from '#/lib/icons' ··· 27 22 import {useProfileShadow} from '#/state/cache/profile-shadow' 28 23 import {listenSoftReset} from '#/state/events' 29 24 import {useModerationOpts} from '#/state/preferences/moderation-opts' 30 - import {useActorStarterPacksQuery} from '#/state/queries/actor-starter-packs' 31 25 import {useLabelerInfoQuery} from '#/state/queries/labeler' 32 26 import {resetProfilePostsQueries} from '#/state/queries/post-feed' 33 27 import {useProfileQuery} from '#/state/queries/profile' ··· 86 80 } = useProfileQuery({ 87 81 did: resolvedDid, 88 82 }) 89 - const starterPacksQuery = useActorStarterPacksQuery({did: resolvedDid}) 90 83 91 84 const onPressTryAgain = React.useCallback(() => { 92 85 if (resolveError) { ··· 114 107 }, [queryClient, profile?.viewer?.blockedBy, resolvedDid]) 115 108 116 109 // Most pushes will happen here, since we will have only placeholder data 117 - if (isLoadingDid || isLoadingProfile || starterPacksQuery.isLoading) { 110 + if (isLoadingDid || isLoadingProfile) { 118 111 return ( 119 112 <Layout.Content> 120 113 <ProfileHeaderLoading /> ··· 138 131 return ( 139 132 <ProfileScreenLoaded 140 133 profile={profile} 141 - starterPacksQuery={starterPacksQuery} 142 134 moderationOpts={moderationOpts} 143 135 isPlaceholderProfile={isPlaceholderProfile} 144 136 hideBackButton={!!route.params.hideBackButton} ··· 164 156 isPlaceholderProfile, 165 157 moderationOpts, 166 158 hideBackButton, 167 - starterPacksQuery, 168 159 }: { 169 160 profile: AppBskyActorDefs.ProfileViewDetailed 170 161 moderationOpts: ModerationOpts 171 162 hideBackButton: boolean 172 163 isPlaceholderProfile: boolean 173 - starterPacksQuery: UseInfiniteQueryResult< 174 - InfiniteData<AppBskyGraphGetActorStarterPacks.OutputSchema, unknown>, 175 - Error 176 - > 177 164 }) { 178 165 const profile = useProfileShadow(profileUnshadowed) 179 166 const {hasSession, currentAccount} = useSession() ··· 223 210 const showLikesTab = isMe 224 211 const showFeedsTab = isMe || (profile.associated?.feedgens || 0) > 0 225 212 const showStarterPacksTab = 226 - isMe || !!starterPacksQuery.data?.pages?.[0].starterPacks.length 213 + isMe || (profile.associated?.starterPacks || 0) > 0 227 214 const showListsTab = 228 215 hasSession && (isMe || (profile.associated?.lists || 0) > 0) 229 216 ··· 487 474 ? ({headerHeight, isFocused, scrollElRef}) => ( 488 475 <ProfileStarterPacks 489 476 ref={starterPacksSectionRef} 477 + did={profile.did} 490 478 isMe={isMe} 491 - starterPacksQuery={starterPacksQuery} 492 479 scrollElRef={scrollElRef as ListRef} 493 480 headerOffset={headerHeight} 494 481 enabled={isFocused}