mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at patch-version 191 lines 5.3 kB view raw
1import React from 'react' 2import { 3 ActivityIndicator, 4 ListRenderItemInfo, 5 StyleSheet, 6 View, 7} from 'react-native' 8import {msg} from '@lingui/macro' 9import {useLingui} from '@lingui/react' 10 11import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' 12import {cleanError} from '#/lib/strings/errors' 13import {s} from '#/lib/styles' 14import {logger} from '#/logger' 15import {useModerationOpts} from '#/state/preferences/moderation-opts' 16import {useNotificationFeedQuery} from '#/state/queries/notifications/feed' 17import {EmptyState} from '#/view/com/util/EmptyState' 18import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' 19import {List, ListRef} from '#/view/com/util/List' 20import {NotificationFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' 21import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn' 22import {NotificationFeedItem} from './NotificationFeedItem' 23 24const EMPTY_FEED_ITEM = {_reactKey: '__empty__'} 25const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} 26const LOADING_ITEM = {_reactKey: '__loading__'} 27 28export function NotificationFeed({ 29 filter, 30 enabled, 31 scrollElRef, 32 onPressTryAgain, 33 onScrolledDownChange, 34 ListHeaderComponent, 35 refreshNotifications, 36}: { 37 filter: 'all' | 'mentions' 38 enabled: boolean 39 scrollElRef?: ListRef 40 onPressTryAgain?: () => void 41 onScrolledDownChange: (isScrolledDown: boolean) => void 42 ListHeaderComponent?: () => JSX.Element 43 refreshNotifications: () => Promise<void> 44}) { 45 const initialNumToRender = useInitialNumToRender() 46 const [isPTRing, setIsPTRing] = React.useState(false) 47 const {_} = useLingui() 48 const moderationOpts = useModerationOpts() 49 const { 50 data, 51 isFetching, 52 isFetched, 53 isError, 54 error, 55 hasNextPage, 56 isFetchingNextPage, 57 fetchNextPage, 58 } = useNotificationFeedQuery({ 59 enabled: enabled && !!moderationOpts, 60 filter, 61 }) 62 const isEmpty = !isFetching && !data?.pages[0]?.items.length 63 64 const items = React.useMemo(() => { 65 let arr: any[] = [] 66 if (isFetched) { 67 if (isEmpty) { 68 arr = arr.concat([EMPTY_FEED_ITEM]) 69 } else if (data) { 70 for (const page of data?.pages) { 71 arr = arr.concat(page.items) 72 } 73 } 74 if (isError && !isEmpty) { 75 arr = arr.concat([LOAD_MORE_ERROR_ITEM]) 76 } 77 } else { 78 arr.push(LOADING_ITEM) 79 } 80 return arr 81 }, [isFetched, isError, isEmpty, data]) 82 83 const onRefresh = React.useCallback(async () => { 84 try { 85 setIsPTRing(true) 86 await refreshNotifications() 87 } catch (err) { 88 logger.error('Failed to refresh notifications feed', { 89 message: err, 90 }) 91 } finally { 92 setIsPTRing(false) 93 } 94 }, [refreshNotifications, setIsPTRing]) 95 96 const onEndReached = React.useCallback(async () => { 97 if (isFetching || !hasNextPage || isError) return 98 99 try { 100 await fetchNextPage() 101 } catch (err) { 102 logger.error('Failed to load more notifications', {message: err}) 103 } 104 }, [isFetching, hasNextPage, isError, fetchNextPage]) 105 106 const onPressRetryLoadMore = React.useCallback(() => { 107 fetchNextPage() 108 }, [fetchNextPage]) 109 110 const renderItem = React.useCallback( 111 ({item, index}: ListRenderItemInfo<any>) => { 112 if (item === EMPTY_FEED_ITEM) { 113 return ( 114 <EmptyState 115 icon="bell" 116 message={_(msg`No notifications yet!`)} 117 style={styles.emptyState} 118 /> 119 ) 120 } else if (item === LOAD_MORE_ERROR_ITEM) { 121 return ( 122 <LoadMoreRetryBtn 123 label={_( 124 msg`There was an issue fetching notifications. Tap here to try again.`, 125 )} 126 onPress={onPressRetryLoadMore} 127 /> 128 ) 129 } else if (item === LOADING_ITEM) { 130 return <NotificationFeedLoadingPlaceholder /> 131 } 132 return ( 133 <NotificationFeedItem 134 highlightUnread={filter === 'all'} 135 item={item} 136 moderationOpts={moderationOpts!} 137 hideTopBorder={index === 0} 138 /> 139 ) 140 }, 141 [moderationOpts, _, onPressRetryLoadMore, filter], 142 ) 143 144 const FeedFooter = React.useCallback( 145 () => 146 isFetchingNextPage ? ( 147 <View style={styles.feedFooter}> 148 <ActivityIndicator /> 149 </View> 150 ) : ( 151 <View /> 152 ), 153 [isFetchingNextPage], 154 ) 155 156 return ( 157 <View style={s.hContentRegion}> 158 {error && ( 159 <ErrorMessage 160 message={cleanError(error)} 161 onPressTryAgain={onPressTryAgain} 162 /> 163 )} 164 <List 165 testID="notifsFeed" 166 ref={scrollElRef} 167 data={items} 168 keyExtractor={item => item._reactKey} 169 renderItem={renderItem} 170 ListHeaderComponent={ListHeaderComponent} 171 ListFooterComponent={FeedFooter} 172 refreshing={isPTRing} 173 onRefresh={onRefresh} 174 onEndReached={onEndReached} 175 onEndReachedThreshold={2} 176 onScrolledDownChange={onScrolledDownChange} 177 contentContainerStyle={s.contentContainer} 178 desktopFixedHeight 179 initialNumToRender={initialNumToRender} 180 windowSize={11} 181 sideBorders={false} 182 removeClippedSubviews={true} 183 /> 184 </View> 185 ) 186} 187 188const styles = StyleSheet.create({ 189 feedFooter: {paddingTop: 20}, 190 emptyState: {paddingVertical: 40}, 191})