about summary refs log tree commit diff
path: root/src/view/com/notifications/Feed.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/notifications/Feed.tsx')
-rw-r--r--src/view/com/notifications/Feed.tsx181
1 files changed, 97 insertions, 84 deletions
diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx
index 4794a9867..e82c654be 100644
--- a/src/view/com/notifications/Feed.tsx
+++ b/src/view/com/notifications/Feed.tsx
@@ -1,8 +1,6 @@
 import React, {MutableRefObject} from 'react'
-import {observer} from 'mobx-react-lite'
 import {CenteredView, FlatList} from '../util/Views'
 import {ActivityIndicator, RefreshControl, StyleSheet, View} from 'react-native'
-import {NotificationsFeedModel} from 'state/models/feeds/notifications'
 import {FeedItem} from './FeedItem'
 import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
 import {ErrorMessage} from '../util/error/ErrorMessage'
@@ -12,20 +10,22 @@ import {OnScrollHandler} from 'lib/hooks/useOnMainScroll'
 import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIXED'
 import {s} from 'lib/styles'
 import {usePalette} from 'lib/hooks/usePalette'
+import {useNotificationFeedQuery} from '#/state/queries/notifications/feed'
+import {useUnreadNotificationsApi} from '#/state/queries/notifications/unread'
 import {logger} from '#/logger'
+import {cleanError} from '#/lib/strings/errors'
+import {useModerationOpts} from '#/state/queries/preferences'
 
 const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
 const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
-const LOADING_SPINNER = {_reactKey: '__loading_spinner__'}
+const LOADING_ITEM = {_reactKey: '__loading__'}
 
-export const Feed = observer(function Feed({
-  view,
+export function Feed({
   scrollElRef,
   onPressTryAgain,
   onScroll,
   ListHeaderComponent,
 }: {
-  view: NotificationsFeedModel
   scrollElRef?: MutableRefObject<FlatList<any> | null>
   onPressTryAgain?: () => void
   onScroll?: OnScrollHandler
@@ -33,35 +33,54 @@ export const Feed = observer(function Feed({
 }) {
   const pal = usePalette('default')
   const [isPTRing, setIsPTRing] = React.useState(false)
-  const data = React.useMemo(() => {
-    let feedItems: any[] = []
-    if (view.isRefreshing && !isPTRing) {
-      feedItems = [LOADING_SPINNER]
+
+  const moderationOpts = useModerationOpts()
+  const {markAllRead} = useUnreadNotificationsApi()
+  const {
+    data,
+    dataUpdatedAt,
+    isFetching,
+    isFetched,
+    isError,
+    error,
+    refetch,
+    hasNextPage,
+    isFetchingNextPage,
+    fetchNextPage,
+  } = useNotificationFeedQuery({enabled: !!moderationOpts})
+  const isEmpty = !isFetching && !data?.pages[0]?.items.length
+  const firstItem = data?.pages[0]?.items[0]
+
+  // mark all read on fresh data
+  React.useEffect(() => {
+    if (firstItem) {
+      markAllRead()
     }
-    if (view.hasLoaded) {
-      if (view.isEmpty) {
-        feedItems = feedItems.concat([EMPTY_FEED_ITEM])
-      } else {
-        feedItems = feedItems.concat(view.notifications)
+  }, [firstItem, markAllRead])
+
+  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)
     }
-    if (view.loadMoreError) {
-      feedItems = (feedItems || []).concat([LOAD_MORE_ERROR_ITEM])
-    }
-    return feedItems
-  }, [
-    view.hasLoaded,
-    view.isEmpty,
-    view.notifications,
-    view.loadMoreError,
-    view.isRefreshing,
-    isPTRing,
-  ])
+    return arr
+  }, [isFetched, isError, isEmpty, data])
 
   const onRefresh = React.useCallback(async () => {
     try {
       setIsPTRing(true)
-      await view.refresh()
+      await refetch()
     } catch (err) {
       logger.error('Failed to refresh notifications feed', {
         error: err,
@@ -69,21 +88,21 @@ export const Feed = observer(function Feed({
     } finally {
       setIsPTRing(false)
     }
-  }, [view, setIsPTRing])
+  }, [refetch, setIsPTRing])
 
   const onEndReached = React.useCallback(async () => {
+    if (isFetching || !hasNextPage || isError) return
+
     try {
-      await view.loadMore()
+      await fetchNextPage()
     } catch (err) {
-      logger.error('Failed to load more notifications', {
-        error: err,
-      })
+      logger.error('Failed to load more notifications', {error: err})
     }
-  }, [view])
+  }, [isFetching, hasNextPage, isError, fetchNextPage])
 
   const onPressRetryLoadMore = React.useCallback(() => {
-    view.retryLoadMore()
-  }, [view])
+    fetchNextPage()
+  }, [fetchNextPage])
 
   // 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
@@ -106,78 +125,72 @@ export const Feed = observer(function Feed({
             onPress={onPressRetryLoadMore}
           />
         )
-      } else if (item === LOADING_SPINNER) {
-        return (
-          <View style={styles.loading}>
-            <ActivityIndicator size="small" />
-          </View>
-        )
+      } else if (item === LOADING_ITEM) {
+        return <NotificationFeedLoadingPlaceholder />
       }
-      return <FeedItem item={item} />
+      return (
+        <FeedItem
+          item={item}
+          dataUpdatedAt={dataUpdatedAt}
+          moderationOpts={moderationOpts!}
+        />
+      )
     },
-    [onPressRetryLoadMore],
+    [onPressRetryLoadMore, dataUpdatedAt, moderationOpts],
   )
 
   const FeedFooter = React.useCallback(
     () =>
-      view.isLoading ? (
+      isFetchingNextPage ? (
         <View style={styles.feedFooter}>
           <ActivityIndicator />
         </View>
       ) : (
         <View />
       ),
-    [view],
+    [isFetchingNextPage],
   )
 
   const scrollHandler = useAnimatedScrollHandler(onScroll || {})
   return (
     <View style={s.hContentRegion}>
-      <CenteredView>
-        {view.isLoading && !data.length && (
-          <NotificationFeedLoadingPlaceholder />
-        )}
-        {view.hasError && (
+      {error && (
+        <CenteredView>
           <ErrorMessage
-            message={view.error}
+            message={cleanError(error)}
             onPressTryAgain={onPressTryAgain}
           />
-        )}
-      </CenteredView>
-      {data.length ? (
-        <FlatList
-          testID="notifsFeed"
-          ref={scrollElRef}
-          data={data}
-          keyExtractor={item => item._reactKey}
-          renderItem={renderItem}
-          ListHeaderComponent={ListHeaderComponent}
-          ListFooterComponent={FeedFooter}
-          refreshControl={
-            <RefreshControl
-              refreshing={isPTRing}
-              onRefresh={onRefresh}
-              tintColor={pal.colors.text}
-              titleColor={pal.colors.text}
-            />
-          }
-          onEndReached={onEndReached}
-          onEndReachedThreshold={0.6}
-          onScroll={scrollHandler}
-          scrollEventThrottle={1}
-          contentContainerStyle={s.contentContainer}
-          // @ts-ignore our .web version only -prf
-          desktopFixedHeight
-        />
-      ) : null}
+        </CenteredView>
+      )}
+      <FlatList
+        testID="notifsFeed"
+        ref={scrollElRef}
+        data={items}
+        keyExtractor={item => item._reactKey}
+        renderItem={renderItem}
+        ListHeaderComponent={ListHeaderComponent}
+        ListFooterComponent={FeedFooter}
+        refreshControl={
+          <RefreshControl
+            refreshing={isPTRing}
+            onRefresh={onRefresh}
+            tintColor={pal.colors.text}
+            titleColor={pal.colors.text}
+          />
+        }
+        onEndReached={onEndReached}
+        onEndReachedThreshold={0.6}
+        onScroll={scrollHandler}
+        scrollEventThrottle={1}
+        contentContainerStyle={s.contentContainer}
+        // @ts-ignore our .web version only -prf
+        desktopFixedHeight
+      />
     </View>
   )
-})
+}
 
 const styles = StyleSheet.create({
-  loading: {
-    paddingVertical: 20,
-  },
   feedFooter: {paddingTop: 20},
   emptyState: {paddingVertical: 40},
 })