diff options
author | Samuel Newman <mozzius@protonmail.com> | 2025-04-02 00:19:59 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-04-01 14:19:59 -0700 |
commit | 318b29d3fd7d22895ed6ba9a8daff6f296d7dd58 (patch) | |
tree | b8bc10dc22c9cafc8b06531019c9045446552b9b | |
parent | 4db3ccbec7bbe9659911ce09db23320d3f0ab2eb (diff) | |
download | voidsky-318b29d3fd7d22895ed6ba9a8daff6f296d7dd58.tar.zst |
Fix loading jumps and footer on feeds tab (#8063)
* Fix loading jumps and footer on feeds tab * same for lists/starter packs
-rw-r--r-- | src/components/StarterPack/ProfileStarterPacks.tsx | 59 | ||||
-rw-r--r-- | src/view/com/feeds/ProfileFeedgens.tsx | 53 | ||||
-rw-r--r-- | src/view/com/lists/ProfileLists.tsx | 55 |
3 files changed, 86 insertions, 81 deletions
diff --git a/src/components/StarterPack/ProfileStarterPacks.tsx b/src/components/StarterPack/ProfileStarterPacks.tsx index da5f0dc6a..d56789506 100644 --- a/src/components/StarterPack/ProfileStarterPacks.tsx +++ b/src/components/StarterPack/ProfileStarterPacks.tsx @@ -6,12 +6,12 @@ import React, { } from 'react' import { findNodeHandle, - ListRenderItemInfo, - StyleProp, + type ListRenderItemInfo, + type StyleProp, View, - ViewStyle, + type ViewStyle, } from 'react-native' -import {AppBskyGraphDefs} from '@atproto/api' +import {type AppBskyGraphDefs} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useNavigation} from '@react-navigation/native' @@ -20,11 +20,11 @@ import {useGenerateStarterPackMutation} from '#/lib/generate-starterpack' import {useBottomBarOffset} from '#/lib/hooks/useBottomBarOffset' import {useEmail} from '#/lib/hooks/useEmail' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' -import {NavigationProp} from '#/lib/routes/types' +import {type NavigationProp} from '#/lib/routes/types' import {parseStarterPackUri} from '#/lib/strings/starter-pack' import {logger} from '#/logger' import {useActorStarterPacksQuery} from '#/state/queries/actor-starter-packs' -import {List, ListRef} from '#/view/com/util/List' +import {List, type ListRef} from '#/view/com/util/List' import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {atoms as a, ios, useTheme} from '#/alf' import {Button, ButtonIcon, ButtonText} from '#/components/Button' @@ -75,8 +75,14 @@ export const ProfileStarterPacks = React.forwardRef< const t = useTheme() const bottomBarOffset = useBottomBarOffset(100) const [isPTRing, setIsPTRing] = useState(false) - const {data, refetch, isFetching, hasNextPage, fetchNextPage} = - useActorStarterPacksQuery({did, enabled}) + const { + data, + refetch, + isError, + hasNextPage, + isFetchingNextPage, + fetchNextPage, + } = useActorStarterPacksQuery({did, enabled}) const {isTabletOrDesktop} = useWebMediaQueries() const items = data?.pages.flatMap(page => page.starterPacks) @@ -95,15 +101,14 @@ export const ProfileStarterPacks = React.forwardRef< setIsPTRing(false) }, [refetch, setIsPTRing]) - const onEndReached = useCallback(async () => { - if (isFetching || !hasNextPage) return - + const onEndReached = React.useCallback(async () => { + if (isFetchingNextPage || !hasNextPage || isError) return try { await fetchNextPage() } catch (err) { logger.error('Failed to load more starter packs', {message: err}) } - }, [isFetching, hasNextPage, fetchNextPage]) + }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage]) useEffect(() => { if (enabled && scrollElRef.current) { @@ -112,21 +117,21 @@ export const ProfileStarterPacks = React.forwardRef< } }, [enabled, scrollElRef, setScrollViewTag]) - const renderItem = ({ - item, - index, - }: ListRenderItemInfo<AppBskyGraphDefs.StarterPackView>) => { - return ( - <View - style={[ - a.p_lg, - (isTabletOrDesktop || index !== 0) && a.border_t, - t.atoms.border_contrast_low, - ]}> - <StarterPackCard starterPack={item} /> - </View> - ) - } + const renderItem = useCallback( + ({item, index}: ListRenderItemInfo<AppBskyGraphDefs.StarterPackView>) => { + return ( + <View + style={[ + a.p_lg, + (isTabletOrDesktop || index !== 0) && a.border_t, + t.atoms.border_contrast_low, + ]}> + <StarterPackCard starterPack={item} /> + </View> + ) + }, + [isTabletOrDesktop, t.atoms.border_contrast_low], + ) return ( <View testID={testID} style={style}> diff --git a/src/view/com/feeds/ProfileFeedgens.tsx b/src/view/com/feeds/ProfileFeedgens.tsx index b55c6b9bd..2bf95de48 100644 --- a/src/view/com/feeds/ProfileFeedgens.tsx +++ b/src/view/com/feeds/ProfileFeedgens.tsx @@ -1,30 +1,28 @@ import React from 'react' import { - ActivityIndicator, findNodeHandle, - ListRenderItemInfo, - StyleProp, - StyleSheet, + type ListRenderItemInfo, + type StyleProp, View, - ViewStyle, + type ViewStyle, } from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {cleanError} from '#/lib/strings/errors' import {logger} from '#/logger' import {isNative, isWeb} from '#/platform/detection' import {usePreferencesQuery} from '#/state/queries/preferences' import {RQKEY, useProfileFeedgensQuery} from '#/state/queries/profile-feedgens' import {EmptyState} from '#/view/com/util/EmptyState' +import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' +import {List, type ListRef} from '#/view/com/util/List' import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' +import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn' import {atoms as a, ios, useTheme} from '#/alf' import * as FeedCard from '#/components/FeedCard' -import {ErrorMessage} from '../util/error/ErrorMessage' -import {List, ListRef} from '../util/List' -import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' +import {ListFooter} from '#/components/Lists' const LOADING = {_reactKey: '__loading__'} const EMPTY = {_reactKey: '__empty__'} @@ -58,8 +56,7 @@ export const ProfileFeedgens = React.forwardRef< const opts = React.useMemo(() => ({enabled}), [enabled]) const { data, - isFetching, - isFetched, + isPending, isFetchingNextPage, hasNextPage, fetchNextPage, @@ -67,16 +64,15 @@ export const ProfileFeedgens = React.forwardRef< error, refetch, } = useProfileFeedgensQuery(did, opts) - const isEmpty = !isFetching && !data?.pages[0]?.feeds.length + const isEmpty = !isPending && !data?.pages[0]?.feeds.length const {data: preferences} = usePreferencesQuery() - const {isMobile} = useWebMediaQueries() const items = React.useMemo(() => { let items: any[] = [] if (isError && isEmpty) { items = items.concat([ERROR_ITEM]) } - if (!isFetched || isFetching) { + if (isPending) { items = items.concat([LOADING]) } else if (isEmpty) { items = items.concat([EMPTY]) @@ -88,7 +84,7 @@ export const ProfileFeedgens = React.forwardRef< items = items.concat([LOAD_MORE_ERROR_ITEM]) } return items - }, [isError, isEmpty, isFetched, isFetching, data]) + }, [isError, isEmpty, isPending, data]) // events // = @@ -118,14 +114,14 @@ export const ProfileFeedgens = React.forwardRef< }, [refetch, setIsPTRing]) const onEndReached = React.useCallback(async () => { - if (isFetching || !hasNextPage || isError) return + if (isFetchingNextPage || !hasNextPage || isError) return try { await fetchNextPage() } catch (err) { logger.error('Failed to load more feeds', {message: err}) } - }, [isFetching, hasNextPage, isError, fetchNextPage]) + }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage]) const onPressRetryLoadMore = React.useCallback(() => { fetchNextPage() @@ -186,10 +182,16 @@ export const ProfileFeedgens = React.forwardRef< }, [enabled, scrollElRef, setScrollViewTag]) const ProfileFeedgensFooter = React.useCallback(() => { - return isFetchingNextPage ? ( - <ActivityIndicator style={[styles.footer]} /> - ) : null - }, [isFetchingNextPage]) + return ( + <ListFooter + hasNextPage={hasNextPage} + isFetchingNextPage={isFetchingNextPage} + onRetry={fetchNextPage} + error={cleanError(error)} + height={180 + headerOffset} + /> + ) + }, [hasNextPage, error, isFetchingNextPage, headerOffset, fetchNextPage]) return ( <View testID={testID} style={style}> @@ -197,14 +199,13 @@ export const ProfileFeedgens = React.forwardRef< testID={testID ? `${testID}-flatlist` : undefined} ref={scrollElRef} data={items} - keyExtractor={(item: any) => item._reactKey || item.uri} + keyExtractor={keyExtractor} renderItem={renderItem} ListFooterComponent={ProfileFeedgensFooter} refreshing={isPTRing} onRefresh={onRefresh} headerOffset={headerOffset} progressViewOffset={ios(0)} - contentContainerStyle={isMobile && {paddingBottom: headerOffset + 100}} removeClippedSubviews={true} desktopFixedHeight onEndReached={onEndReached} @@ -213,6 +214,6 @@ export const ProfileFeedgens = React.forwardRef< ) }) -const styles = StyleSheet.create({ - footer: {paddingTop: 20}, -}) +function keyExtractor(item: any) { + return item._reactKey || item.uri +} diff --git a/src/view/com/lists/ProfileLists.tsx b/src/view/com/lists/ProfileLists.tsx index 3a0b0b198..437648c62 100644 --- a/src/view/com/lists/ProfileLists.tsx +++ b/src/view/com/lists/ProfileLists.tsx @@ -1,29 +1,27 @@ import React from 'react' import { - ActivityIndicator, findNodeHandle, - ListRenderItemInfo, - StyleProp, - StyleSheet, + type ListRenderItemInfo, + type StyleProp, View, - ViewStyle, + type ViewStyle, } from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {cleanError} from '#/lib/strings/errors' import {logger} from '#/logger' import {isNative, isWeb} from '#/platform/detection' import {RQKEY, useProfileListsQuery} from '#/state/queries/profile-lists' import {EmptyState} from '#/view/com/util/EmptyState' +import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' +import {List, type ListRef} from '#/view/com/util/List' import {FeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' +import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn' import {atoms as a, ios, useTheme} from '#/alf' import * as ListCard from '#/components/ListCard' -import {ErrorMessage} from '../util/error/ErrorMessage' -import {List, ListRef} from '../util/List' -import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' +import {ListFooter} from '#/components/Lists' const LOADING = {_reactKey: '__loading__'} const EMPTY = {_reactKey: '__empty__'} @@ -55,8 +53,7 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>( const opts = React.useMemo(() => ({enabled}), [enabled]) const { data, - isFetching, - isFetched, + isPending, hasNextPage, fetchNextPage, isFetchingNextPage, @@ -64,15 +61,14 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>( error, refetch, } = useProfileListsQuery(did, opts) - const {isMobile} = useWebMediaQueries() - const isEmpty = !isFetching && !data?.pages[0]?.lists.length + const isEmpty = !isPending && !data?.pages[0]?.lists.length const items = React.useMemo(() => { let items: any[] = [] if (isError && isEmpty) { items = items.concat([ERROR_ITEM]) } - if (!isFetched || isFetching) { + if (isPending) { items = items.concat([LOADING]) } else if (isEmpty) { items = items.concat([EMPTY]) @@ -85,7 +81,7 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>( items = items.concat([LOAD_MORE_ERROR_ITEM]) } return items - }, [isError, isEmpty, isFetched, isFetching, data]) + }, [isError, isEmpty, isPending, data]) // events // = @@ -115,13 +111,13 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>( }, [refetch, setIsPTRing]) const onEndReached = React.useCallback(async () => { - if (isFetching || !hasNextPage || isError) return + if (isFetchingNextPage || !hasNextPage || isError) return try { await fetchNextPage() } catch (err) { logger.error('Failed to load more lists', {message: err}) } - }, [isFetching, hasNextPage, isError, fetchNextPage]) + }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage]) const onPressRetryLoadMore = React.useCallback(() => { fetchNextPage() @@ -182,10 +178,16 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>( }, [enabled, scrollElRef, setScrollViewTag]) const ProfileListsFooter = React.useCallback(() => { - return isFetchingNextPage ? ( - <ActivityIndicator style={[styles.footer]} /> - ) : null - }, [isFetchingNextPage]) + return ( + <ListFooter + hasNextPage={hasNextPage} + isFetchingNextPage={isFetchingNextPage} + onRetry={fetchNextPage} + error={cleanError(error)} + height={180 + headerOffset} + /> + ) + }, [hasNextPage, error, isFetchingNextPage, headerOffset, fetchNextPage]) return ( <View testID={testID} style={style}> @@ -193,16 +195,13 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>( testID={testID ? `${testID}-flatlist` : undefined} ref={scrollElRef} data={items} - keyExtractor={(item: any) => item._reactKey || item.uri} + keyExtractor={keyExtractor} renderItem={renderItemInner} ListFooterComponent={ProfileListsFooter} refreshing={isPTRing} onRefresh={onRefresh} headerOffset={headerOffset} progressViewOffset={ios(0)} - contentContainerStyle={ - isMobile && {paddingBottom: headerOffset + 100} - } removeClippedSubviews={true} desktopFixedHeight onEndReached={onEndReached} @@ -212,6 +211,6 @@ export const ProfileLists = React.forwardRef<SectionRef, ProfileListsProps>( }, ) -const styles = StyleSheet.create({ - footer: {paddingTop: 20}, -}) +function keyExtractor(item: any) { + return item._reactKey || item.uri +} |