From f55fbe73c7afb7f595ff88d928fa8d8b24835913 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Tue, 21 Feb 2023 10:32:29 -0600 Subject: [PATCH] A group of notifications fixes (#227) * Fix: don't group together notifications that can't visually be grouped (close #221) * Mark all notifications read on PTR * Small optimization: useCallback and useMemo in posts feed * Add loading spinner to footer of notifications (close #222) --- src/state/models/notifications-view.ts | 4 +- src/view/com/notifications/Feed.tsx | 71 +++++++++++------ src/view/com/posts/Feed.tsx | 106 +++++++++++++++---------- 3 files changed, 112 insertions(+), 69 deletions(-) diff --git a/src/state/models/notifications-view.ts b/src/state/models/notifications-view.ts index b6e3a34881..d74511139f 100644 --- a/src/state/models/notifications-view.ts +++ b/src/state/models/notifications-view.ts @@ -14,7 +14,7 @@ import {RootStoreModel} from './root-store' import {PostThreadViewModel} from './post-thread-view' import {cleanError} from '../../lib/strings' -const UNGROUPABLE_REASONS = ['assertion'] +const GROUPABLE_REASONS = ['vote', 'repost', 'follow'] const PAGE_SIZE = 30 const MS_1HR = 1e3 * 60 * 60 const MS_2DAY = MS_1HR * 48 @@ -539,7 +539,7 @@ function groupNotifications( for (const item of items) { const ts = +new Date(item.indexedAt) let grouped = false - if (!UNGROUPABLE_REASONS.includes(item.reason)) { + if (GROUPABLE_REASONS.includes(item.reason)) { for (const item2 of items2) { const ts2 = +new Date(item2.indexedAt) if ( diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx index 21f54e793f..81a38b95a4 100644 --- a/src/view/com/notifications/Feed.tsx +++ b/src/view/com/notifications/Feed.tsx @@ -1,6 +1,6 @@ import React, {MutableRefObject} from 'react' import {observer} from 'mobx-react-lite' -import {FlatList, StyleSheet, View} from 'react-native' +import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native' import {NotificationsViewModel} from '../../../state/models/notifications-view' import {FeedItem} from './FeedItem' import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder' @@ -22,11 +22,39 @@ export const Feed = observer(function Feed({ onPressTryAgain?: () => void onScroll?: OnScrollCb }) { + const data = React.useMemo(() => { + let feedItems + if (view.hasLoaded) { + if (view.isEmpty) { + feedItems = [EMPTY_FEED_ITEM] + } else { + feedItems = view.notifications + } + } + return feedItems + }, [view.hasLoaded, view.isEmpty, view.notifications]) + + const onRefresh = React.useCallback(async () => { + try { + await view.refresh() + await view.markAllRead() + } catch (err) { + view.rootStore.log.error('Failed to refresh notifications feed', err) + } + }, [view]) + const onEndReached = React.useCallback(async () => { + try { + await view.loadMore() + } catch (err) { + view.rootStore.log.error('Failed to load more notifications', err) + } + }, [view]) + // TODO optimize renderItem or FeedItem, we're getting this notice from RN: -prf // VirtualizedList: You have a large list that is slow to update - make sure your // renderItem function renders components that follow React performance best practices // like PureComponent, shouldComponentUpdate, etc - const renderItem = ({item}: {item: any}) => { + const renderItem = React.useCallback(({item}: {item: any}) => { if (item === EMPTY_FEED_ITEM) { return ( - } - const onRefresh = async () => { - try { - await view.refresh() - } catch (err) { - view.rootStore.log.error('Failed to refresh notifications feed', err) - } - } - const onEndReached = async () => { - try { - await view.loadMore() - } catch (err) { - view.rootStore.log.error('Failed to load more notifications', err) - } - } - let data - if (view.hasLoaded) { - if (view.isEmpty) { - data = [EMPTY_FEED_ITEM] - } else { - data = view.notifications - } - } + }, []) + + const FeedFooter = React.useCallback( + () => + view.isLoading ? ( + + + + ) : ( + + ), + [view], + ) + return ( {view.isLoading && !data && } @@ -72,6 +91,7 @@ export const Feed = observer(function Feed({ data={data} keyExtractor={item => item._reactKey} renderItem={renderItem} + ListFooterComponent={FeedFooter} refreshing={view.isRefreshing} onRefresh={onRefresh} onEndReached={onEndReached} @@ -84,5 +104,6 @@ export const Feed = observer(function Feed({ }) const styles = StyleSheet.create({ + feedFooter: {paddingTop: 20}, emptyState: {paddingVertical: 40}, }) diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx index 63fa14d2c9..b1d67315ac 100644 --- a/src/view/com/posts/Feed.tsx +++ b/src/view/com/posts/Feed.tsx @@ -40,27 +40,25 @@ export const Feed = observer(function Feed({ const {track} = useAnalytics() const [isRefreshing, setIsRefreshing] = React.useState(false) - // TODO optimize renderItem or FeedItem, we're getting this notice from RN: -prf - // VirtualizedList: You have a large list that is slow to update - make sure your - // renderItem function renders components that follow React performance best practices - // like PureComponent, shouldComponentUpdate, etc - const renderItem = ({item}: {item: any}) => { - if (item === EMPTY_FEED_ITEM) { - return ( - - ) - } else if (item === ERROR_FEED_ITEM) { - return ( - - ) + const data = React.useMemo(() => { + let feedItems: any[] = [] + if (feed.hasError) { + feedItems = feedItems.concat([ERROR_FEED_ITEM]) } - return - } - const onRefresh = async () => { + if (feed.hasLoaded) { + if (feed.isEmpty) { + feedItems = feedItems.concat([EMPTY_FEED_ITEM]) + } else { + feedItems = feedItems.concat(feed.feed) + } + } + return feedItems + }, [feed.hasError, feed.hasLoaded, feed.isEmpty, feed.feed]) + + // events + // = + + const onRefresh = React.useCallback(async () => { track('Feed:onRefresh') setIsRefreshing(true) try { @@ -69,34 +67,58 @@ export const Feed = observer(function Feed({ feed.rootStore.log.error('Failed to refresh posts feed', err) } setIsRefreshing(false) - } - const onEndReached = async () => { + }, [feed, track, setIsRefreshing]) + const onEndReached = React.useCallback(async () => { track('Feed:onEndReached') try { await feed.loadMore() } catch (err) { feed.rootStore.log.error('Failed to load more posts', err) } - } - let data: any[] = [] - if (feed.hasError) { - data = data.concat([ERROR_FEED_ITEM]) - } - if (feed.hasLoaded) { - if (feed.isEmpty) { - data = data.concat([EMPTY_FEED_ITEM]) - } else { - data = data.concat(feed.feed) - } - } - const FeedFooter = () => - feed.isLoading ? ( - - - - ) : ( - - ) + }, [feed, track]) + + // rendering + // = + + // TODO optimize renderItem or FeedItem, we're getting this notice from RN: -prf + // VirtualizedList: You have a large list that is slow to update - make sure your + // renderItem function renders components that follow React performance best practices + // like PureComponent, shouldComponentUpdate, etc + const renderItem = React.useCallback( + ({item}: {item: any}) => { + if (item === EMPTY_FEED_ITEM) { + return ( + + ) + } else if (item === ERROR_FEED_ITEM) { + return ( + + ) + } + return + }, + [feed, onPressTryAgain], + ) + + const FeedFooter = React.useCallback( + () => + feed.isLoading ? ( + + + + ) : ( + + ), + [feed], + ) + return ( {feed.isLoading && data.length === 0 && (