diff options
Diffstat (limited to 'src/screens')
-rw-r--r-- | src/screens/Profile/Header/ProfileHeaderStandard.tsx | 14 | ||||
-rw-r--r-- | src/screens/Profile/Header/Shell.tsx | 2 | ||||
-rw-r--r-- | src/screens/Profile/KnownFollowers.tsx | 134 |
3 files changed, 149 insertions, 1 deletions
diff --git a/src/screens/Profile/Header/ProfileHeaderStandard.tsx b/src/screens/Profile/Header/ProfileHeaderStandard.tsx index f4b8d7705..f8a87a68e 100644 --- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx +++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx @@ -30,6 +30,10 @@ import {Button, ButtonIcon, ButtonText} from '#/components/Button' import {MessageProfileButton} from '#/components/dms/MessageProfileButton' import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' +import { + KnownFollowers, + shouldShowKnownFollowers, +} from '#/components/KnownFollowers' import * as Prompt from '#/components/Prompt' import {RichText} from '#/components/RichText' import {ProfileHeaderDisplayName} from './DisplayName' @@ -268,6 +272,16 @@ let ProfileHeaderStandard = ({ /> </View> ) : undefined} + + {!isMe && + shouldShowKnownFollowers(profile.viewer?.knownFollowers) && ( + <View style={[a.flex_row, a.align_center, a.gap_sm, a.pt_md]}> + <KnownFollowers + profile={profile} + moderationOpts={moderationOpts} + /> + </View> + )} </> )} </View> diff --git a/src/screens/Profile/Header/Shell.tsx b/src/screens/Profile/Header/Shell.tsx index 553b38a3b..82cba1704 100644 --- a/src/screens/Profile/Header/Shell.tsx +++ b/src/screens/Profile/Header/Shell.tsx @@ -83,7 +83,7 @@ let ProfileHeaderShell = ({ {!isPlaceholderProfile && ( <View - style={[a.px_lg, a.pb_sm]} + style={[a.px_lg, a.py_xs]} pointerEvents={isIOS ? 'auto' : 'box-none'}> {isMe ? ( <LabelsOnMe details={{did: profile.did}} labels={profile.labels} /> diff --git a/src/screens/Profile/KnownFollowers.tsx b/src/screens/Profile/KnownFollowers.tsx new file mode 100644 index 000000000..5cb45a11e --- /dev/null +++ b/src/screens/Profile/KnownFollowers.tsx @@ -0,0 +1,134 @@ +import React from 'react' +import {View} from 'react-native' +import {AppBskyActorDefs} from '@atproto/api' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {useFocusEffect} from '@react-navigation/native' + +import {cleanError} from '#/lib/strings/errors' +import {logger} from '#/logger' +import {useProfileKnownFollowersQuery} from '#/state/queries/known-followers' +import {useResolveDidQuery} from '#/state/queries/resolve-uri' +import {useSetMinimalShellMode} from '#/state/shell' +import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' +import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types' +import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard' +import {List} from '#/view/com/util/List' +import {ViewHeader} from '#/view/com/util/ViewHeader' +import { + ListFooter, + ListHeaderDesktop, + ListMaybePlaceholder, +} from '#/components/Lists' + +function renderItem({item}: {item: AppBskyActorDefs.ProfileViewBasic}) { + return <ProfileCardWithFollowBtn key={item.did} profile={item} /> +} + +function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic) { + return item.did +} + +type Props = NativeStackScreenProps< + CommonNavigatorParams, + 'ProfileKnownFollowers' +> +export const ProfileKnownFollowersScreen = ({route}: Props) => { + const {_} = useLingui() + const setMinimalShellMode = useSetMinimalShellMode() + const initialNumToRender = useInitialNumToRender() + + const {name} = route.params + + const [isPTRing, setIsPTRing] = React.useState(false) + const { + data: resolvedDid, + isLoading: isDidLoading, + error: resolveError, + } = useResolveDidQuery(route.params.name) + const { + data, + isLoading: isFollowersLoading, + isFetchingNextPage, + hasNextPage, + fetchNextPage, + error, + refetch, + } = useProfileKnownFollowersQuery(resolvedDid) + + const onRefresh = React.useCallback(async () => { + setIsPTRing(true) + try { + await refetch() + } catch (err) { + logger.error('Failed to refresh followers', {message: err}) + } + setIsPTRing(false) + }, [refetch, setIsPTRing]) + + const onEndReached = React.useCallback(async () => { + if (isFetchingNextPage || !hasNextPage || !!error) return + try { + await fetchNextPage() + } catch (err) { + logger.error('Failed to load more followers', {message: err}) + } + }, [isFetchingNextPage, hasNextPage, error, fetchNextPage]) + + const followers = React.useMemo(() => { + if (data?.pages) { + return data.pages.flatMap(page => page.followers) + } + return [] + }, [data]) + + const isError = Boolean(resolveError || error) + + useFocusEffect( + React.useCallback(() => { + setMinimalShellMode(false) + }, [setMinimalShellMode]), + ) + + if (followers.length < 1) { + return ( + <ListMaybePlaceholder + isLoading={isDidLoading || isFollowersLoading} + isError={isError} + emptyType="results" + emptyMessage={_(msg`You don't follow any users who follow @${name}.`)} + errorMessage={cleanError(resolveError || error)} + onRetry={isError ? refetch : undefined} + /> + ) + } + + return ( + <View style={{flex: 1}}> + <ViewHeader title={_(msg`Followers you know`)} /> + <List + data={followers} + renderItem={renderItem} + keyExtractor={keyExtractor} + refreshing={isPTRing} + onRefresh={onRefresh} + onEndReached={onEndReached} + onEndReachedThreshold={4} + ListHeaderComponent={ + <ListHeaderDesktop title={_(msg`Followers you know`)} /> + } + ListFooterComponent={ + <ListFooter + isFetchingNextPage={isFetchingNextPage} + error={cleanError(error)} + onRetry={fetchNextPage} + /> + } + // @ts-ignore our .web version only -prf + desktopFixedHeight + initialNumToRender={initialNumToRender} + windowSize={11} + /> + </View> + ) +} |