forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import React from 'react'
2import {type AppBskyActorDefs} from '@atproto/api'
3import {msg} from '@lingui/core/macro'
4import {useLingui} from '@lingui/react'
5import {useFocusEffect} from '@react-navigation/native'
6
7import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
8import {useSetTitle} from '#/lib/hooks/useSetTitle'
9import {
10 type CommonNavigatorParams,
11 type NativeStackScreenProps,
12} from '#/lib/routes/types'
13import {cleanError} from '#/lib/strings/errors'
14import {logger} from '#/logger'
15import {useProfileKnownFollowersQuery} from '#/state/queries/known-followers'
16import {useProfileQuery} from '#/state/queries/profile'
17import {useResolveDidQuery} from '#/state/queries/resolve-uri'
18import {useSetMinimalShellMode} from '#/state/shell'
19import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
20import {List} from '#/view/com/util/List'
21import {ViewHeader} from '#/view/com/util/ViewHeader'
22import * as Layout from '#/components/Layout'
23import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
24
25function renderItem({
26 item,
27 index,
28}: {
29 item: AppBskyActorDefs.ProfileView
30 index: number
31}) {
32 return (
33 <ProfileCardWithFollowBtn
34 key={item.did}
35 profile={item}
36 noBorder={index === 0}
37 />
38 )
39}
40
41function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic) {
42 return item.did
43}
44
45type Props = NativeStackScreenProps<
46 CommonNavigatorParams,
47 'ProfileKnownFollowers'
48>
49export const ProfileKnownFollowersScreen = ({route}: Props) => {
50 const {_} = useLingui()
51 const setMinimalShellMode = useSetMinimalShellMode()
52 const initialNumToRender = useInitialNumToRender()
53
54 const {name} = route.params
55
56 const [isPTRing, setIsPTRing] = React.useState(false)
57 const {
58 data: resolvedDid,
59 isLoading: isDidLoading,
60 error: resolveError,
61 } = useResolveDidQuery(route.params.name)
62 const {data: profile} = useProfileQuery({
63 did: resolvedDid,
64 })
65 const {
66 data,
67 isLoading: isFollowersLoading,
68 isFetchingNextPage,
69 hasNextPage,
70 fetchNextPage,
71 error,
72 refetch,
73 } = useProfileKnownFollowersQuery(resolvedDid)
74
75 useSetTitle(
76 profile ? _(msg`Followers of @${profile.handle} that you know`) : undefined,
77 )
78
79 const onRefresh = React.useCallback(async () => {
80 setIsPTRing(true)
81 try {
82 await refetch()
83 } catch (err) {
84 logger.error('Failed to refresh followers', {message: err})
85 }
86 setIsPTRing(false)
87 }, [refetch, setIsPTRing])
88
89 const onEndReached = React.useCallback(async () => {
90 if (isFetchingNextPage || !hasNextPage || !!error) return
91 try {
92 await fetchNextPage()
93 } catch (err) {
94 logger.error('Failed to load more followers', {message: err})
95 }
96 }, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
97
98 const followers = React.useMemo(() => {
99 if (data?.pages) {
100 return data.pages.flatMap(page => page.followers)
101 }
102 return []
103 }, [data])
104
105 const isError = Boolean(resolveError || error)
106
107 useFocusEffect(
108 React.useCallback(() => {
109 setMinimalShellMode(false)
110 }, [setMinimalShellMode]),
111 )
112
113 if (followers.length < 1) {
114 return (
115 <Layout.Screen>
116 <ViewHeader title={_(msg`Followers you know`)} />
117 <ListMaybePlaceholder
118 isLoading={isDidLoading || isFollowersLoading}
119 isError={isError}
120 emptyType="results"
121 emptyMessage={_(msg`You don't follow any users who follow @${name}.`)}
122 errorMessage={cleanError(resolveError || error)}
123 onRetry={isError ? refetch : undefined}
124 topBorder={false}
125 sideBorders={false}
126 />
127 </Layout.Screen>
128 )
129 }
130
131 return (
132 <Layout.Screen>
133 <ViewHeader title={_(msg`Followers you know`)} />
134 <List
135 data={followers}
136 renderItem={renderItem}
137 keyExtractor={keyExtractor}
138 refreshing={isPTRing}
139 onRefresh={onRefresh}
140 onEndReached={onEndReached}
141 onEndReachedThreshold={4}
142 ListFooterComponent={
143 <ListFooter
144 isFetchingNextPage={isFetchingNextPage}
145 error={cleanError(error)}
146 onRetry={fetchNextPage}
147 />
148 }
149 // @ts-ignore our .web version only -prf
150 desktopFixedHeight
151 initialNumToRender={initialNumToRender}
152 windowSize={11}
153 sideBorders={false}
154 />
155 </Layout.Screen>
156 )
157}