my fork of the bluesky client
at main 129 lines 3.7 kB view raw
1import {useCallback, useMemo} from 'react' 2import {ActivityIndicator, FlatList, View} from 'react-native' 3import {AppBskyFeedGetLikes as GetLikes} from '@atproto/api' 4import {msg, Trans} from '@lingui/macro' 5import {useLingui} from '@lingui/react' 6 7import {cleanError} from '#/lib/strings/errors' 8import {logger} from '#/logger' 9import {useLikedByQuery} from '#/state/queries/post-liked-by' 10import {useResolveUriQuery} from '#/state/queries/resolve-uri' 11import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard' 12import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' 13import {atoms as a, useTheme} from '#/alf' 14import * as Dialog from '#/components/Dialog' 15import {Loader} from '#/components/Loader' 16import {Text} from '#/components/Typography' 17 18interface LikesDialogProps { 19 control: Dialog.DialogOuterProps['control'] 20 uri: string 21} 22 23export function LikesDialog(props: LikesDialogProps) { 24 return ( 25 <Dialog.Outer control={props.control}> 26 <Dialog.Handle /> 27 <LikesDialogInner {...props} /> 28 </Dialog.Outer> 29 ) 30} 31 32export function LikesDialogInner({control, uri}: LikesDialogProps) { 33 const {_} = useLingui() 34 const t = useTheme() 35 36 const { 37 data: resolvedUri, 38 error: resolveError, 39 isFetched: hasFetchedResolvedUri, 40 } = useResolveUriQuery(uri) 41 const { 42 data, 43 isFetching: isFetchingLikedBy, 44 isFetched: hasFetchedLikedBy, 45 isFetchingNextPage, 46 hasNextPage, 47 fetchNextPage, 48 isError, 49 error: likedByError, 50 } = useLikedByQuery(resolvedUri?.uri) 51 52 const isLoading = !hasFetchedResolvedUri || !hasFetchedLikedBy 53 const likes = useMemo(() => { 54 if (data?.pages) { 55 return data.pages.flatMap(page => page.likes) 56 } 57 return [] 58 }, [data]) 59 60 const onEndReached = useCallback(async () => { 61 if (isFetchingLikedBy || !hasNextPage || isError) return 62 try { 63 await fetchNextPage() 64 } catch (err) { 65 logger.error('Failed to load more likes', {message: err}) 66 } 67 }, [isFetchingLikedBy, hasNextPage, isError, fetchNextPage]) 68 69 const renderItem = useCallback( 70 ({item}: {item: GetLikes.Like}) => { 71 return ( 72 <ProfileCardWithFollowBtn 73 key={item.actor.did} 74 profile={item.actor} 75 onPress={() => control.close()} 76 /> 77 ) 78 }, 79 [control], 80 ) 81 82 return ( 83 <Dialog.Inner label={_(msg`Users that have liked this content or profile`)}> 84 <Text style={[a.text_2xl, a.font_bold, a.leading_tight, a.pb_lg]}> 85 <Trans>Liked by</Trans> 86 </Text> 87 88 {isLoading ? ( 89 <View style={{minHeight: 300}}> 90 <Loader size="xl" /> 91 </View> 92 ) : resolveError || likedByError || !data ? ( 93 <ErrorMessage message={cleanError(resolveError || likedByError)} /> 94 ) : likes.length === 0 ? ( 95 <View style={[t.atoms.bg_contrast_50, a.px_md, a.py_xl, a.rounded_md]}> 96 <Text style={[a.text_center]}> 97 <Trans> 98 Nobody has liked this yet. Maybe you should be the first! 99 </Trans> 100 </Text> 101 </View> 102 ) : ( 103 <FlatList 104 data={likes} 105 keyExtractor={item => item.actor.did} 106 onEndReached={onEndReached} 107 renderItem={renderItem} 108 initialNumToRender={15} 109 ListFooterComponent={ 110 <ListFooterComponent isFetching={isFetchingNextPage} /> 111 } 112 /> 113 )} 114 115 <Dialog.Close /> 116 </Dialog.Inner> 117 ) 118} 119 120function ListFooterComponent({isFetching}: {isFetching: boolean}) { 121 if (isFetching) { 122 return ( 123 <View style={a.pt_lg}> 124 <ActivityIndicator /> 125 </View> 126 ) 127 } 128 return null 129}