Bluesky app fork with some witchin' additions 馃挮
witchsky.app
bluesky
fork
client
1import React from 'react'
2import {type AppBskyFeedGetLikes as GetLikes} from '@atproto/api'
3import {msg} from '@lingui/core/macro'
4import {useLingui} from '@lingui/react'
5
6import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
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 {List} from '#/view/com/util/List'
13import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
14
15function renderItem({item, index}: {item: GetLikes.Like; index: number}) {
16 return (
17 <ProfileCardWithFollowBtn
18 key={item.actor.did}
19 profile={item.actor}
20 noBorder={index === 0}
21 />
22 )
23}
24
25function keyExtractor(item: GetLikes.Like) {
26 return item.actor.did
27}
28
29export function LikedByList({uri}: {uri: string}) {
30 const {_} = useLingui()
31 const initialNumToRender = useInitialNumToRender()
32 const [isPTRing, setIsPTRing] = React.useState(false)
33
34 const {
35 data: resolvedUri,
36 error: resolveError,
37 isLoading: isUriLoading,
38 } = useResolveUriQuery(uri)
39 const {
40 data,
41 isLoading: isLikedByLoading,
42 isFetchingNextPage,
43 hasNextPage,
44 fetchNextPage,
45 error: likedByError,
46 refetch,
47 } = useLikedByQuery(resolvedUri?.uri)
48
49 const error = resolveError || likedByError
50 const isError = !!resolveError || !!likedByError
51
52 const likes = React.useMemo(() => {
53 if (data?.pages) {
54 return data.pages.flatMap(page => page.likes)
55 }
56 return []
57 }, [data])
58
59 const onRefresh = React.useCallback(async () => {
60 setIsPTRing(true)
61 try {
62 await refetch()
63 } catch (err) {
64 logger.error('Failed to refresh likes', {message: err})
65 }
66 setIsPTRing(false)
67 }, [refetch, setIsPTRing])
68
69 const onEndReached = React.useCallback(async () => {
70 if (isFetchingNextPage || !hasNextPage || isError) return
71 try {
72 await fetchNextPage()
73 } catch (err) {
74 logger.error('Failed to load more likes', {message: err})
75 }
76 }, [isFetchingNextPage, hasNextPage, isError, fetchNextPage])
77
78 if (likes.length < 1) {
79 return (
80 <ListMaybePlaceholder
81 isLoading={isUriLoading || isLikedByLoading}
82 isError={isError}
83 emptyType="results"
84 emptyTitle={_(msg`No likes yet`)}
85 emptyMessage={_(
86 msg`Nobody has liked this yet. Maybe you should be the first!`,
87 )}
88 errorMessage={cleanError(resolveError || error)}
89 onRetry={isError ? refetch : undefined}
90 topBorder={false}
91 sideBorders={false}
92 />
93 )
94 }
95
96 return (
97 <List
98 data={likes}
99 renderItem={renderItem}
100 keyExtractor={keyExtractor}
101 refreshing={isPTRing}
102 onRefresh={onRefresh}
103 onEndReached={onEndReached}
104 ListFooterComponent={
105 <ListFooter
106 isFetchingNextPage={isFetchingNextPage}
107 error={cleanError(error)}
108 onRetry={fetchNextPage}
109 />
110 }
111 onEndReachedThreshold={3}
112 initialNumToRender={initialNumToRender}
113 windowSize={11}
114 sideBorders={false}
115 />
116 )
117}