import { useCallback, useEffect, useImperativeHandle, useMemo, useState, } from 'react' import { findNodeHandle, type ListRenderItemInfo, type StyleProp, useWindowDimensions, View, type ViewStyle, } from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' import {cleanError} from '#/lib/strings/errors' import {logger} from '#/logger' import {isIOS, 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 {ListFooter} from '#/components/Lists' const LOADING = {_reactKey: '__loading__'} const EMPTY = {_reactKey: '__empty__'} const ERROR_ITEM = {_reactKey: '__error__'} const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} interface SectionRef { scrollToTop: () => void } interface ProfileFeedgensProps { ref?: React.Ref did: string scrollElRef: ListRef headerOffset: number enabled?: boolean style?: StyleProp testID?: string setScrollViewTag: (tag: number | null) => void } export function ProfileFeedgens({ ref, did, scrollElRef, headerOffset, enabled, style, testID, setScrollViewTag, }: ProfileFeedgensProps) { const {_} = useLingui() const t = useTheme() const [isPTRing, setIsPTRing] = useState(false) const {height} = useWindowDimensions() const opts = useMemo(() => ({enabled}), [enabled]) const { data, isPending, isFetchingNextPage, hasNextPage, fetchNextPage, isError, error, refetch, } = useProfileFeedgensQuery(did, opts) const isEmpty = !isPending && !data?.pages[0]?.feeds.length const {data: preferences} = usePreferencesQuery() 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.feeds) } } else 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 feeds', {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 feeds', {message: err}) } }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage]) const onPressRetryLoadMore = useCallback(() => { fetchNextPage() }, [fetchNextPage]) // rendering // = const renderItem = 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 (preferences) { return ( ) } return null }, [_, t, error, refetch, onPressRetryLoadMore, preferences], ) useEffect(() => { if (isIOS && enabled && scrollElRef.current) { const nativeTag = findNodeHandle(scrollElRef.current) setScrollViewTag(nativeTag) } }, [enabled, scrollElRef, setScrollViewTag]) const ProfileFeedgensFooter = useCallback(() => { if (isEmpty) return null return ( ) }, [ hasNextPage, error, isFetchingNextPage, headerOffset, fetchNextPage, isEmpty, ]) return ( ) } function keyExtractor(item: any) { return item._reactKey || item.uri }