diff options
Diffstat (limited to 'src/view/com/posts/Feed.tsx')
-rw-r--r-- | src/view/com/posts/Feed.tsx | 144 |
1 files changed, 103 insertions, 41 deletions
diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx index 7e28712ee..50afc1950 100644 --- a/src/view/com/posts/Feed.tsx +++ b/src/view/com/posts/Feed.tsx @@ -1,5 +1,4 @@ import React, {MutableRefObject} from 'react' -import {observer} from 'mobx-react-lite' import { ActivityIndicator, Dimensions, @@ -12,7 +11,6 @@ import { import {FlatList} from '../util/Views' import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder' import {FeedErrorMessage} from './FeedErrorMessage' -import {PostsFeedModel} from 'state/models/feeds/posts' import {FeedSlice} from './FeedSlice' import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' import {OnScrollHandler} from 'lib/hooks/useOnMainScroll' @@ -21,17 +19,26 @@ import {usePalette} from 'lib/hooks/usePalette' import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED' import {useTheme} from 'lib/ThemeContext' import {logger} from '#/logger' +import { + FeedDescriptor, + FeedParams, + usePostFeedQuery, +} from '#/state/queries/post-feed' const LOADING_ITEM = {_reactKey: '__loading__'} const EMPTY_FEED_ITEM = {_reactKey: '__empty__'} const ERROR_ITEM = {_reactKey: '__error__'} const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} -export const Feed = observer(function Feed({ +export function Feed({ feed, + feedParams, style, + enabled, + pollInterval, scrollElRef, onScroll, + onHasNew, scrollEventThrottle, renderEmptyState, renderEndOfFeed, @@ -41,9 +48,13 @@ export const Feed = observer(function Feed({ ListHeaderComponent, extraData, }: { - feed: PostsFeedModel + feed: FeedDescriptor + feedParams?: FeedParams style?: StyleProp<ViewStyle> + enabled?: boolean + pollInterval?: number scrollElRef?: MutableRefObject<FlatList<any> | null> + onHasNew?: (v: boolean) => void onScroll?: OnScrollHandler scrollEventThrottle?: number renderEmptyState: () => JSX.Element @@ -58,32 +69,68 @@ export const Feed = observer(function Feed({ const theme = useTheme() const {track} = useAnalytics() const [isRefreshing, setIsRefreshing] = React.useState(false) + const checkForNewRef = React.useRef<(() => void) | null>(null) + + const opts = React.useMemo(() => ({enabled}), [enabled]) + const { + data, + dataUpdatedAt, + isFetching, + isFetched, + isError, + error, + refetch, + hasNextPage, + isFetchingNextPage, + fetchNextPage, + pollLatest, + } = usePostFeedQuery(feed, feedParams, opts) + const isEmpty = !isFetching && !data?.pages[0]?.slices.length - const data = React.useMemo(() => { - let feedItems: any[] = [] - if (feed.hasLoaded) { - if (feed.hasError) { - feedItems = feedItems.concat([ERROR_ITEM]) + const checkForNew = React.useCallback(async () => { + if (!isFetched || isFetching || !onHasNew) { + return + } + try { + if (await pollLatest()) { + onHasNew(true) } - if (feed.isEmpty) { - feedItems = feedItems.concat([EMPTY_FEED_ITEM]) - } else { - feedItems = feedItems.concat(feed.slices) + } catch (e) { + logger.error('Poll latest failed', {feed, error: String(e)}) + } + }, [feed, isFetched, isFetching, pollLatest, onHasNew]) + + React.useEffect(() => { + // we store the interval handler in a ref to avoid needless + // reassignments of the interval + checkForNewRef.current = checkForNew + }, [checkForNew]) + React.useEffect(() => { + const i = setInterval(() => checkForNewRef.current?.(), pollInterval) + return () => clearInterval(i) + }, [pollInterval]) + + const feedItems = React.useMemo(() => { + let arr: any[] = [] + if (isFetched) { + if (isError && isEmpty) { + arr = arr.concat([ERROR_ITEM]) + } + if (isEmpty) { + arr = arr.concat([EMPTY_FEED_ITEM]) + } else if (data) { + for (const page of data?.pages) { + arr = arr.concat(page.slices) + } } - if (feed.loadMoreError) { - feedItems = feedItems.concat([LOAD_MORE_ERROR_ITEM]) + if (isError && !isEmpty) { + arr = arr.concat([LOAD_MORE_ERROR_ITEM]) } } else { - feedItems.push(LOADING_ITEM) + arr.push(LOADING_ITEM) } - return feedItems - }, [ - feed.hasError, - feed.hasLoaded, - feed.isEmpty, - feed.slices, - feed.loadMoreError, - ]) + return arr + }, [isFetched, isError, isEmpty, data]) // events // = @@ -92,31 +139,33 @@ export const Feed = observer(function Feed({ track('Feed:onRefresh') setIsRefreshing(true) try { - await feed.refresh() + await refetch() + onHasNew?.(false) } catch (err) { logger.error('Failed to refresh posts feed', {error: err}) } setIsRefreshing(false) - }, [feed, track, setIsRefreshing]) + }, [refetch, track, setIsRefreshing, onHasNew]) const onEndReached = React.useCallback(async () => { - if (!feed.hasLoaded || !feed.hasMore) return + if (isFetching || !hasNextPage || isError) return track('Feed:onEndReached') try { - await feed.loadMore() + await fetchNextPage() } catch (err) { logger.error('Failed to load more posts', {error: err}) } - }, [feed, track]) + }, [isFetching, hasNextPage, isError, fetchNextPage, track]) const onPressTryAgain = React.useCallback(() => { - feed.refresh() - }, [feed]) + refetch() + onHasNew?.(false) + }, [refetch, onHasNew]) const onPressRetryLoadMore = React.useCallback(() => { - feed.retryLoadMore() - }, [feed]) + fetchNextPage() + }, [fetchNextPage]) // rendering // = @@ -127,7 +176,11 @@ export const Feed = observer(function Feed({ return renderEmptyState() } else if (item === ERROR_ITEM) { return ( - <FeedErrorMessage feed={feed} onPressTryAgain={onPressTryAgain} /> + <FeedErrorMessage + feedDesc={feed} + error={error} + onPressTryAgain={onPressTryAgain} + /> ) } else if (item === LOAD_MORE_ERROR_ITEM) { return ( @@ -139,23 +192,32 @@ export const Feed = observer(function Feed({ } else if (item === LOADING_ITEM) { return <PostFeedLoadingPlaceholder /> } - return <FeedSlice slice={item} /> + return <FeedSlice slice={item} dataUpdatedAt={dataUpdatedAt} /> }, - [feed, onPressTryAgain, onPressRetryLoadMore, renderEmptyState], + [ + feed, + dataUpdatedAt, + error, + onPressTryAgain, + onPressRetryLoadMore, + renderEmptyState, + ], ) + const shouldRenderEndOfFeed = + !hasNextPage && !isEmpty && !isFetching && !isError && !!renderEndOfFeed const FeedFooter = React.useCallback( () => - feed.isLoadingMore ? ( + isFetchingNextPage ? ( <View style={styles.feedFooter}> <ActivityIndicator /> </View> - ) : !feed.hasMore && !feed.isEmpty && renderEndOfFeed ? ( + ) : shouldRenderEndOfFeed ? ( renderEndOfFeed() ) : ( <View /> ), - [feed.isLoadingMore, feed.hasMore, feed.isEmpty, renderEndOfFeed], + [isFetchingNextPage, shouldRenderEndOfFeed, renderEndOfFeed], ) const scrollHandler = useAnimatedScrollHandler(onScroll || {}) @@ -164,7 +226,7 @@ export const Feed = observer(function Feed({ <FlatList testID={testID ? `${testID}-flatlist` : undefined} ref={scrollElRef} - data={data} + data={feedItems} keyExtractor={item => item._reactKey} renderItem={renderItem} ListFooterComponent={FeedFooter} @@ -197,7 +259,7 @@ export const Feed = observer(function Feed({ /> </View> ) -}) +} const styles = StyleSheet.create({ feedFooter: {paddingTop: 20}, |