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 samuel/patch-onpaste 208 lines 5.7 kB view raw
1import React from 'react' 2import { 3 ActivityIndicator, 4 FlatList as RNFlatList, 5 RefreshControl, 6 StyleProp, 7 View, 8 ViewStyle, 9} from 'react-native' 10import {AppBskyGraphDefs as GraphDefs} from '@atproto/api' 11import {msg} from '@lingui/macro' 12import {useLingui} from '@lingui/react' 13 14import {usePalette} from '#/lib/hooks/usePalette' 15import {cleanError} from '#/lib/strings/errors' 16import {s} from '#/lib/styles' 17import {logger} from '#/logger' 18import {useModerationOpts} from '#/state/preferences/moderation-opts' 19import {MyListsFilter, useMyListsQuery} from '#/state/queries/my-lists' 20import {atoms as a, useTheme} from '#/alf' 21import {BulletList_Stroke2_Corner0_Rounded as ListIcon} from '#/components/icons/BulletList' 22import * as ListCard from '#/components/ListCard' 23import {Text} from '#/components/Typography' 24import {ErrorMessage} from '../util/error/ErrorMessage' 25import {List} from '../util/List' 26 27const LOADING = {_reactKey: '__loading__'} 28const EMPTY = {_reactKey: '__empty__'} 29const ERROR_ITEM = {_reactKey: '__error__'} 30 31export function MyLists({ 32 filter, 33 inline, 34 style, 35 renderItem, 36 testID, 37}: { 38 filter: MyListsFilter 39 inline?: boolean 40 style?: StyleProp<ViewStyle> 41 renderItem?: (list: GraphDefs.ListView, index: number) => JSX.Element 42 testID?: string 43}) { 44 const pal = usePalette('default') 45 const t = useTheme() 46 const {_} = useLingui() 47 const moderationOpts = useModerationOpts() 48 const [isPTRing, setIsPTRing] = React.useState(false) 49 const {data, isFetching, isFetched, isError, error, refetch} = 50 useMyListsQuery(filter) 51 const isEmpty = !isFetching && !data?.length 52 53 const items = React.useMemo(() => { 54 let items: any[] = [] 55 if (isError && isEmpty) { 56 items = items.concat([ERROR_ITEM]) 57 } 58 if ((!isFetched && isFetching) || !moderationOpts) { 59 items = items.concat([LOADING]) 60 } else if (isEmpty) { 61 items = items.concat([EMPTY]) 62 } else { 63 items = items.concat(data) 64 } 65 return items 66 }, [isError, isEmpty, isFetched, isFetching, moderationOpts, data]) 67 68 let emptyText 69 switch (filter) { 70 case 'curate': 71 emptyText = _( 72 msg`Public, sharable lists which can be used to drive feeds.`, 73 ) 74 break 75 case 'mod': 76 emptyText = _( 77 msg`Public, sharable lists of users to mute or block in bulk.`, 78 ) 79 break 80 default: 81 emptyText = _(msg`You have no lists.`) 82 break 83 } 84 85 // events 86 // = 87 88 const onRefresh = React.useCallback(async () => { 89 setIsPTRing(true) 90 try { 91 await refetch() 92 } catch (err) { 93 logger.error('Failed to refresh lists', {message: err}) 94 } 95 setIsPTRing(false) 96 }, [refetch, setIsPTRing]) 97 98 // rendering 99 // = 100 101 const renderItemInner = React.useCallback( 102 ({item, index}: {item: any; index: number}) => { 103 if (item === EMPTY) { 104 return ( 105 <View style={[a.flex_1, a.align_center, a.gap_sm, a.px_xl, a.pt_xl]}> 106 <View 107 style={[ 108 a.align_center, 109 a.justify_center, 110 a.rounded_full, 111 t.atoms.bg_contrast_25, 112 { 113 width: 32, 114 height: 32, 115 }, 116 ]}> 117 <ListIcon size="md" fill={t.atoms.text_contrast_low.color} /> 118 </View> 119 <Text 120 style={[ 121 a.text_center, 122 a.flex_1, 123 a.text_sm, 124 a.leading_snug, 125 t.atoms.text_contrast_medium, 126 { 127 maxWidth: 200, 128 }, 129 ]}> 130 {emptyText} 131 </Text> 132 </View> 133 ) 134 } else if (item === ERROR_ITEM) { 135 return ( 136 <ErrorMessage 137 message={cleanError(error)} 138 onPressTryAgain={onRefresh} 139 /> 140 ) 141 } else if (item === LOADING) { 142 return ( 143 <View style={{padding: 20}}> 144 <ActivityIndicator /> 145 </View> 146 ) 147 } 148 return renderItem ? ( 149 renderItem(item, index) 150 ) : ( 151 <View 152 style={[ 153 index !== 0 && a.border_t, 154 t.atoms.border_contrast_low, 155 a.px_lg, 156 a.py_lg, 157 ]}> 158 <ListCard.Default view={item} /> 159 </View> 160 ) 161 }, 162 [t, renderItem, error, onRefresh, emptyText], 163 ) 164 165 if (inline) { 166 return ( 167 <View testID={testID} style={style}> 168 {items.length > 0 && ( 169 <RNFlatList 170 testID={testID ? `${testID}-flatlist` : undefined} 171 data={items} 172 keyExtractor={item => (item.uri ? item.uri : item._reactKey)} 173 renderItem={renderItemInner} 174 refreshControl={ 175 <RefreshControl 176 refreshing={isPTRing} 177 onRefresh={onRefresh} 178 tintColor={pal.colors.text} 179 titleColor={pal.colors.text} 180 /> 181 } 182 contentContainerStyle={[s.contentContainer]} 183 removeClippedSubviews={true} 184 /> 185 )} 186 </View> 187 ) 188 } else { 189 return ( 190 <View testID={testID} style={style}> 191 {items.length > 0 && ( 192 <List 193 testID={testID ? `${testID}-flatlist` : undefined} 194 data={items} 195 keyExtractor={item => (item.uri ? item.uri : item._reactKey)} 196 renderItem={renderItemInner} 197 refreshing={isPTRing} 198 onRefresh={onRefresh} 199 contentContainerStyle={[s.contentContainer]} 200 removeClippedSubviews={true} 201 desktopFixedHeight 202 sideBorders={false} 203 /> 204 )} 205 </View> 206 ) 207 } 208}