From 5b8631d1887a08aa746a2b832688873e8ce3b1f2 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 10 Sep 2025 18:49:04 +0300 Subject: Fix profile lists/feeds/starterpacks tabs position issue (#8935) --- src/view/com/lists/ProfileLists.tsx | 349 +++++++++++++++++++----------------- 1 file changed, 180 insertions(+), 169 deletions(-) (limited to 'src/view/com/lists/ProfileLists.tsx') diff --git a/src/view/com/lists/ProfileLists.tsx b/src/view/com/lists/ProfileLists.tsx index 8fea51081..ce51cb337 100644 --- a/src/view/com/lists/ProfileLists.tsx +++ b/src/view/com/lists/ProfileLists.tsx @@ -1,8 +1,15 @@ -import React from 'react' +import { + useCallback, + useEffect, + useImperativeHandle, + useMemo, + useState, +} from 'react' import { findNodeHandle, type ListRenderItemInfo, type StyleProp, + useWindowDimensions, View, type ViewStyle, } from 'react-native' @@ -33,6 +40,7 @@ interface SectionRef { } interface ProfileListsProps { + ref?: React.Ref did: string scrollElRef: ListRef headerOffset: number @@ -42,182 +50,185 @@ interface ProfileListsProps { setScrollViewTag: (tag: number | null) => void } -export const ProfileLists = React.forwardRef( - function ProfileListsImpl( - {did, scrollElRef, headerOffset, enabled, style, testID, setScrollViewTag}, - ref, - ) { - const t = useTheme() - const {_} = useLingui() - const [isPTRing, setIsPTRing] = React.useState(false) - const opts = React.useMemo(() => ({enabled}), [enabled]) - const { - data, - isPending, - hasNextPage, - fetchNextPage, - isFetchingNextPage, - isError, - error, - refetch, - } = useProfileListsQuery(did, opts) - const isEmpty = !isPending && !data?.pages[0]?.lists.length - - const items = React.useMemo(() => { - let items: any[] = [] - if (isError && isEmpty) { - items = items.concat([ERROR_ITEM]) - } - if (isPending) { - items = items.concat([LOADING]) - } else if (isEmpty) { - items = items.concat([EMPTY]) - } else if (data?.pages) { - for (const page of data?.pages) { - items = items.concat(page.lists) - } - } - if (isError && !isEmpty) { - items = items.concat([LOAD_MORE_ERROR_ITEM]) - } - return items - }, [isError, isEmpty, isPending, data]) - - // events - // = - - const queryClient = useQueryClient() - - const onScrollToTop = React.useCallback(() => { - scrollElRef.current?.scrollToOffset({ - animated: isNative, - offset: -headerOffset, - }) - queryClient.invalidateQueries({queryKey: RQKEY(did)}) - }, [scrollElRef, queryClient, headerOffset, did]) - - React.useImperativeHandle(ref, () => ({ - scrollToTop: onScrollToTop, - })) - - const onRefresh = React.useCallback(async () => { - setIsPTRing(true) - try { - await refetch() - } catch (err) { - logger.error('Failed to refresh lists', {message: err}) +export function ProfileLists({ + ref, + did, + scrollElRef, + headerOffset, + enabled, + style, + testID, + setScrollViewTag, +}: ProfileListsProps) { + const t = useTheme() + const {_} = useLingui() + const {height} = useWindowDimensions() + const [isPTRing, setIsPTRing] = useState(false) + const opts = useMemo(() => ({enabled}), [enabled]) + const { + data, + isPending, + hasNextPage, + fetchNextPage, + isFetchingNextPage, + isError, + error, + refetch, + } = useProfileListsQuery(did, opts) + const isEmpty = !isPending && !data?.pages[0]?.lists.length + + const items = useMemo(() => { + let items: any[] = [] + if (isError && isEmpty) { + items = items.concat([ERROR_ITEM]) + } + if (isPending) { + items = items.concat([LOADING]) + } else if (isEmpty) { + items = items.concat([EMPTY]) + } else if (data?.pages) { + for (const page of data?.pages) { + items = items.concat(page.lists) } - setIsPTRing(false) - }, [refetch, setIsPTRing]) - - const onEndReached = React.useCallback(async () => { - if (isFetchingNextPage || !hasNextPage || isError) return - try { - await fetchNextPage() - } catch (err) { - logger.error('Failed to load more lists', {message: err}) - } - }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage]) - - const onPressRetryLoadMore = React.useCallback(() => { - fetchNextPage() - }, [fetchNextPage]) - - // rendering - // = - - const renderItemInner = React.useCallback( - ({item, index}: ListRenderItemInfo) => { - if (item === EMPTY) { - return ( - - ) - } else if (item === ERROR_ITEM) { - return ( - - ) - } else if (item === LOAD_MORE_ERROR_ITEM) { - return ( - - ) - } else if (item === LOADING) { - return - } + } + if (isError && !isEmpty) { + items = items.concat([LOAD_MORE_ERROR_ITEM]) + } + return items + }, [isError, isEmpty, isPending, data]) + + // events + // = + + const queryClient = useQueryClient() + + const onScrollToTop = useCallback(() => { + scrollElRef.current?.scrollToOffset({ + animated: isNative, + offset: -headerOffset, + }) + queryClient.invalidateQueries({queryKey: RQKEY(did)}) + }, [scrollElRef, queryClient, headerOffset, did]) + + useImperativeHandle(ref, () => ({ + scrollToTop: onScrollToTop, + })) + + const onRefresh = useCallback(async () => { + setIsPTRing(true) + try { + await refetch() + } catch (err) { + logger.error('Failed to refresh lists', {message: err}) + } + setIsPTRing(false) + }, [refetch, setIsPTRing]) + + const onEndReached = useCallback(async () => { + if (isFetchingNextPage || !hasNextPage || isError) return + try { + await fetchNextPage() + } catch (err) { + logger.error('Failed to load more lists', {message: err}) + } + }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage]) + + const onPressRetryLoadMore = useCallback(() => { + fetchNextPage() + }, [fetchNextPage]) + + // rendering + // = + + const renderItemInner = useCallback( + ({item, index}: ListRenderItemInfo) => { + if (item === EMPTY) { return ( - - - + ) - }, - [error, refetch, onPressRetryLoadMore, _, t.atoms.border_contrast_low], - ) - - React.useEffect(() => { - if (isIOS && enabled && scrollElRef.current) { - const nativeTag = findNodeHandle(scrollElRef.current) - setScrollViewTag(nativeTag) + } else if (item === ERROR_ITEM) { + return ( + + ) + } else if (item === LOAD_MORE_ERROR_ITEM) { + return ( + + ) + } else if (item === LOADING) { + return } - }, [enabled, scrollElRef, setScrollViewTag]) - - const ProfileListsFooter = React.useCallback(() => { - if (isEmpty) return null return ( - + + + ) - }, [ - hasNextPage, - error, - isFetchingNextPage, - headerOffset, - fetchNextPage, - isEmpty, - ]) - + }, + [error, refetch, onPressRetryLoadMore, _, t.atoms.border_contrast_low], + ) + + useEffect(() => { + if (isIOS && enabled && scrollElRef.current) { + const nativeTag = findNodeHandle(scrollElRef.current) + setScrollViewTag(nativeTag) + } + }, [enabled, scrollElRef, setScrollViewTag]) + + const ProfileListsFooter = useCallback(() => { + if (isEmpty) return null return ( - - - + ) - }, -) + }, [ + hasNextPage, + error, + isFetchingNextPage, + headerOffset, + fetchNextPage, + isEmpty, + ]) + + return ( + + + + ) +} function keyExtractor(item: any) { return item._reactKey || item.uri -- cgit 1.4.1