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 profile-init 194 lines 5.4 kB view raw
1import React, {MutableRefObject} from 'react' 2import {observer} from 'mobx-react-lite' 3import { 4 ActivityIndicator, 5 RefreshControl, 6 StyleProp, 7 StyleSheet, 8 View, 9 ViewStyle, 10} from 'react-native' 11import {FlatList} from '../util/Views' 12import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder' 13import {ErrorMessage} from '../util/error/ErrorMessage' 14import {PostsFeedModel} from 'state/models/feeds/posts' 15import {FeedSlice} from './FeedSlice' 16import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' 17import {OnScrollCb} from 'lib/hooks/useOnMainScroll' 18import {s} from 'lib/styles' 19import {useAnalytics} from 'lib/analytics/analytics' 20import {usePalette} from 'lib/hooks/usePalette' 21import {useTheme} from 'lib/ThemeContext' 22 23const LOADING_ITEM = {_reactKey: '__loading__'} 24const EMPTY_FEED_ITEM = {_reactKey: '__empty__'} 25const ERROR_ITEM = {_reactKey: '__error__'} 26const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} 27 28export const Feed = observer(function Feed({ 29 feed, 30 style, 31 scrollElRef, 32 onPressTryAgain, 33 onScroll, 34 scrollEventThrottle, 35 renderEmptyState, 36 renderEndOfFeed, 37 testID, 38 headerOffset = 0, 39 ListHeaderComponent, 40 extraData, 41}: { 42 feed: PostsFeedModel 43 style?: StyleProp<ViewStyle> 44 scrollElRef?: MutableRefObject<FlatList<any> | null> 45 onPressTryAgain?: () => void 46 onScroll?: OnScrollCb 47 scrollEventThrottle?: number 48 renderEmptyState: () => JSX.Element 49 renderEndOfFeed?: () => JSX.Element 50 testID?: string 51 headerOffset?: number 52 ListHeaderComponent?: () => JSX.Element 53 extraData?: any 54}) { 55 const pal = usePalette('default') 56 const theme = useTheme() 57 const {track} = useAnalytics() 58 const [isRefreshing, setIsRefreshing] = React.useState(false) 59 60 const data = React.useMemo(() => { 61 let feedItems: any[] = [] 62 if (feed.hasLoaded) { 63 if (feed.hasError) { 64 feedItems = feedItems.concat([ERROR_ITEM]) 65 } 66 if (feed.isEmpty) { 67 feedItems = feedItems.concat([EMPTY_FEED_ITEM]) 68 } else { 69 feedItems = feedItems.concat(feed.slices) 70 } 71 if (feed.loadMoreError) { 72 feedItems = feedItems.concat([LOAD_MORE_ERROR_ITEM]) 73 } 74 } 75 return feedItems 76 }, [ 77 feed.hasError, 78 feed.hasLoaded, 79 feed.isEmpty, 80 feed.slices, 81 feed.loadMoreError, 82 ]) 83 84 // events 85 // = 86 87 const onRefresh = React.useCallback(async () => { 88 track('Feed:onRefresh') 89 setIsRefreshing(true) 90 try { 91 await feed.refresh() 92 } catch (err) { 93 feed.rootStore.log.error('Failed to refresh posts feed', err) 94 } 95 setIsRefreshing(false) 96 }, [feed, track, setIsRefreshing]) 97 98 const onEndReached = React.useCallback(async () => { 99 if (!feed.hasLoaded || !feed.hasMore) return 100 101 track('Feed:onEndReached') 102 try { 103 await feed.loadMore() 104 } catch (err) { 105 feed.rootStore.log.error('Failed to load more posts', err) 106 } 107 }, [feed, track]) 108 109 const onPressRetryLoadMore = React.useCallback(() => { 110 feed.retryLoadMore() 111 }, [feed]) 112 113 // rendering 114 // = 115 116 const renderItem = React.useCallback( 117 ({item}: {item: any}) => { 118 if (item === EMPTY_FEED_ITEM) { 119 return renderEmptyState() 120 } else if (item === ERROR_ITEM) { 121 return ( 122 <ErrorMessage 123 message={feed.error} 124 onPressTryAgain={onPressTryAgain} 125 /> 126 ) 127 } else if (item === LOAD_MORE_ERROR_ITEM) { 128 return ( 129 <LoadMoreRetryBtn 130 label="There was an issue fetching posts. Tap here to try again." 131 onPress={onPressRetryLoadMore} 132 /> 133 ) 134 } else if (item === LOADING_ITEM) { 135 return <PostFeedLoadingPlaceholder /> 136 } 137 return <FeedSlice slice={item} /> 138 }, 139 [feed, onPressTryAgain, onPressRetryLoadMore, renderEmptyState], 140 ) 141 142 const FeedFooter = React.useCallback( 143 () => 144 feed.isLoadingMore ? ( 145 <View style={styles.feedFooter}> 146 <ActivityIndicator /> 147 </View> 148 ) : !feed.hasMore && !feed.isEmpty && renderEndOfFeed ? ( 149 renderEndOfFeed() 150 ) : ( 151 <View /> 152 ), 153 [feed.isLoadingMore, feed.hasMore, feed.isEmpty, renderEndOfFeed], 154 ) 155 156 return ( 157 <View testID={testID} style={style}> 158 <FlatList 159 testID={testID ? `${testID}-flatlist` : undefined} 160 ref={scrollElRef} 161 data={!feed.hasLoaded ? [LOADING_ITEM] : data} 162 keyExtractor={item => item._reactKey} 163 renderItem={renderItem} 164 ListFooterComponent={FeedFooter} 165 ListHeaderComponent={ListHeaderComponent} 166 refreshControl={ 167 <RefreshControl 168 refreshing={isRefreshing} 169 onRefresh={onRefresh} 170 tintColor={pal.colors.text} 171 titleColor={pal.colors.text} 172 progressViewOffset={headerOffset} 173 /> 174 } 175 contentContainerStyle={s.contentContainer} 176 style={{paddingTop: headerOffset}} 177 onScroll={onScroll} 178 scrollEventThrottle={scrollEventThrottle} 179 indicatorStyle={theme.colorScheme === 'dark' ? 'white' : 'black'} 180 onEndReached={onEndReached} 181 onEndReachedThreshold={2} 182 removeClippedSubviews={true} 183 contentOffset={{x: 0, y: headerOffset * -1}} 184 extraData={extraData} 185 // @ts-ignore our .web version only -prf 186 desktopFixedHeight 187 /> 188 </View> 189 ) 190}) 191 192const styles = StyleSheet.create({ 193 feedFooter: {paddingTop: 20}, 194})