mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React from 'react'
2import {
3 ActivityIndicator,
4 FlatList as RNFlatList,
5 RefreshControl,
6 StyleProp,
7 StyleSheet,
8 View,
9 ViewStyle,
10} from 'react-native'
11import {AppBskyGraphDefs as GraphDefs} from '@atproto/api'
12import {ListCard} from './ListCard'
13import {MyListsFilter, useMyListsQuery} from '#/state/queries/my-lists'
14import {ErrorMessage} from '../util/error/ErrorMessage'
15import {Text} from '../util/text/Text'
16import {useAnalytics} from 'lib/analytics/analytics'
17import {usePalette} from 'lib/hooks/usePalette'
18import {List} from '../util/List'
19import {s} from 'lib/styles'
20import {logger} from '#/logger'
21import {Trans} from '@lingui/macro'
22import {cleanError} from '#/lib/strings/errors'
23
24const LOADING = {_reactKey: '__loading__'}
25const EMPTY = {_reactKey: '__empty__'}
26const ERROR_ITEM = {_reactKey: '__error__'}
27
28export function MyLists({
29 filter,
30 inline,
31 style,
32 renderItem,
33 testID,
34}: {
35 filter: MyListsFilter
36 inline?: boolean
37 style?: StyleProp<ViewStyle>
38 renderItem?: (list: GraphDefs.ListView, index: number) => JSX.Element
39 testID?: string
40}) {
41 const pal = usePalette('default')
42 const {track} = useAnalytics()
43 const [isPTRing, setIsPTRing] = React.useState(false)
44 const {data, isFetching, isFetched, isError, error, refetch} =
45 useMyListsQuery(filter)
46 const isEmpty = !isFetching && !data?.length
47
48 const items = React.useMemo(() => {
49 let items: any[] = []
50 if (isError && isEmpty) {
51 items = items.concat([ERROR_ITEM])
52 }
53 if (!isFetched && isFetching) {
54 items = items.concat([LOADING])
55 } else if (isEmpty) {
56 items = items.concat([EMPTY])
57 } else {
58 items = items.concat(data)
59 }
60 return items
61 }, [isError, isEmpty, isFetched, isFetching, data])
62
63 // events
64 // =
65
66 const onRefresh = React.useCallback(async () => {
67 track('Lists:onRefresh')
68 setIsPTRing(true)
69 try {
70 await refetch()
71 } catch (err) {
72 logger.error('Failed to refresh lists', {message: err})
73 }
74 setIsPTRing(false)
75 }, [refetch, track, setIsPTRing])
76
77 // rendering
78 // =
79
80 const renderItemInner = React.useCallback(
81 ({item, index}: {item: any; index: number}) => {
82 if (item === EMPTY) {
83 return (
84 <View
85 key={item._reactKey}
86 testID="listsEmpty"
87 style={[{padding: 18, borderTopWidth: 1}, pal.border]}>
88 <Text style={pal.textLight}>
89 <Trans>You have no lists.</Trans>
90 </Text>
91 </View>
92 )
93 } else if (item === ERROR_ITEM) {
94 return (
95 <ErrorMessage
96 key={item._reactKey}
97 message={cleanError(error)}
98 onPressTryAgain={onRefresh}
99 />
100 )
101 } else if (item === LOADING) {
102 return (
103 <View key={item._reactKey} style={{padding: 20}}>
104 <ActivityIndicator />
105 </View>
106 )
107 }
108 return renderItem ? (
109 renderItem(item, index)
110 ) : (
111 <ListCard
112 key={item.uri}
113 list={item}
114 testID={`list-${item.name}`}
115 style={styles.item}
116 />
117 )
118 },
119 [error, onRefresh, renderItem, pal],
120 )
121
122 if (inline) {
123 return (
124 <View testID={testID} style={style}>
125 {items.length > 0 && (
126 <RNFlatList
127 testID={testID ? `${testID}-flatlist` : undefined}
128 data={items}
129 keyExtractor={item => (item.uri ? item.uri : item._reactKey)}
130 renderItem={renderItemInner}
131 refreshControl={
132 <RefreshControl
133 refreshing={isPTRing}
134 onRefresh={onRefresh}
135 tintColor={pal.colors.text}
136 titleColor={pal.colors.text}
137 />
138 }
139 contentContainerStyle={[s.contentContainer]}
140 removeClippedSubviews={true}
141 // @ts-ignore our .web version only -prf
142 desktopFixedHeight
143 />
144 )}
145 </View>
146 )
147 } else {
148 return (
149 <View testID={testID} style={style}>
150 {items.length > 0 && (
151 <List
152 testID={testID ? `${testID}-flatlist` : undefined}
153 data={items}
154 keyExtractor={item => (item.uri ? item.uri : item._reactKey)}
155 renderItem={renderItemInner}
156 refreshing={isPTRing}
157 onRefresh={onRefresh}
158 contentContainerStyle={[s.contentContainer]}
159 removeClippedSubviews={true}
160 // @ts-ignore our .web version only -prf
161 desktopFixedHeight
162 />
163 )}
164 </View>
165 )
166 }
167}
168
169const styles = StyleSheet.create({
170 item: {
171 paddingHorizontal: 18,
172 paddingVertical: 4,
173 },
174})