import React from 'react' import { ActivityIndicator, ListRenderItemInfo, StyleSheet, View, } from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {usePalette} from '#/lib/hooks/usePalette' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {cleanError} from '#/lib/strings/errors' import {s} from '#/lib/styles' import {logger} from '#/logger' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useNotificationFeedQuery} from '#/state/queries/notifications/feed' import {useUnreadNotificationsApi} from '#/state/queries/notifications/unread' import {EmptyState} from '#/view/com/util/EmptyState' import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' import {List, ListRef} from '#/view/com/util/List' import {NotificationFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn' import {CenteredView} from '#/view/com/util/Views' import {FeedItem} from './FeedItem' import hairlineWidth = StyleSheet.hairlineWidth const EMPTY_FEED_ITEM = {_reactKey: '__empty__'} const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} const LOADING_ITEM = {_reactKey: '__loading__'} export function Feed({ scrollElRef, onPressTryAgain, onScrolledDownChange, ListHeaderComponent, }: { scrollElRef?: ListRef onPressTryAgain?: () => void onScrolledDownChange: (isScrolledDown: boolean) => void ListHeaderComponent?: () => JSX.Element }) { const initialNumToRender = useInitialNumToRender() const [isPTRing, setIsPTRing] = React.useState(false) const pal = usePalette('default') const {isTabletOrMobile} = useWebMediaQueries() const {_} = useLingui() const moderationOpts = useModerationOpts() const {checkUnread} = useUnreadNotificationsApi() const { data, isFetching, isFetched, isError, error, hasNextPage, isFetchingNextPage, fetchNextPage, } = useNotificationFeedQuery({enabled: !!moderationOpts}) const isEmpty = !isFetching && !data?.pages[0]?.items.length const items = React.useMemo(() => { let arr: any[] = [] if (isFetched) { if (isEmpty) { arr = arr.concat([EMPTY_FEED_ITEM]) } else if (data) { for (const page of data?.pages) { arr = arr.concat(page.items) } } if (isError && !isEmpty) { arr = arr.concat([LOAD_MORE_ERROR_ITEM]) } } else { arr.push(LOADING_ITEM) } return arr }, [isFetched, isError, isEmpty, data]) const onRefresh = React.useCallback(async () => { try { setIsPTRing(true) await checkUnread({invalidate: true}) } catch (err) { logger.error('Failed to refresh notifications feed', { message: err, }) } finally { setIsPTRing(false) } }, [checkUnread, setIsPTRing]) const onEndReached = React.useCallback(async () => { if (isFetching || !hasNextPage || isError) return try { await fetchNextPage() } catch (err) { logger.error('Failed to load more notifications', {message: err}) } }, [isFetching, hasNextPage, isError, fetchNextPage]) const onPressRetryLoadMore = React.useCallback(() => { fetchNextPage() }, [fetchNextPage]) const renderItem = React.useCallback( ({item, index}: ListRenderItemInfo) => { if (item === EMPTY_FEED_ITEM) { return ( ) } else if (item === LOAD_MORE_ERROR_ITEM) { return ( ) } else if (item === LOADING_ITEM) { return ( ) } return ( ) }, [moderationOpts, isTabletOrMobile, _, onPressRetryLoadMore, pal.border], ) const FeedFooter = React.useCallback( () => isFetchingNextPage ? ( ) : ( ), [isFetchingNextPage], ) return ( {error && ( )} item._reactKey} renderItem={renderItem} ListHeaderComponent={ListHeaderComponent} ListFooterComponent={FeedFooter} refreshing={isPTRing} onRefresh={onRefresh} onEndReached={onEndReached} onEndReachedThreshold={2} onScrolledDownChange={onScrolledDownChange} contentContainerStyle={s.contentContainer} // @ts-ignore our .web version only -prf desktopFixedHeight initialNumToRender={initialNumToRender} windowSize={11} sideBorders={false} /> ) } const styles = StyleSheet.create({ feedFooter: {paddingTop: 20}, emptyState: {paddingVertical: 40}, })