Bluesky app fork with some witchin' additions 馃挮
at main 157 lines 4.4 kB view raw
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}