diff --git a/src/components/FeedSuggestedFollows/index.tsx b/src/components/FeedSuggestedFollows/index.tsx new file mode 100644 index 0000000000..0c8993f8ce --- /dev/null +++ b/src/components/FeedSuggestedFollows/index.tsx @@ -0,0 +1,156 @@ +import React from 'react' +import {View} from 'react-native' +import {ScrollView} from 'react-native-gesture-handler' +import {AppBskyActorDefs, moderateProfile} from '@atproto/api' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {useProfileShadow} from '#/state/cache/profile-shadow' +import {useModerationOpts} from '#/state/queries/preferences' +import {useSuggestedFollowsQuery} from '#/state/queries/suggested-follows' +import {isJustAMute} from 'lib/moderation' +import {sanitizeDisplayName} from 'lib/strings/display-names' +import {sanitizeHandle} from 'lib/strings/handles' +import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' +import {atoms as a, useTheme} from '#/alf' +import {Button, ButtonIcon} from '#/components/Button' +import {useFollowMethods} from '#/components/hooks/useFollowMethods' +import {useRichText} from '#/components/hooks/useRichText' +import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check' +import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus' +import {RichText} from '#/components/RichText' +import {Text} from '#/components/Typography' + +export function SuggestedFollowCard({ + profile: profileUnshadowed, +}: { + profile: AppBskyActorDefs.ProfileViewBasic +}) { + const t = useTheme() + const {_} = useLingui() + const profile = useProfileShadow(profileUnshadowed) + const moderationOpts = useModerationOpts() + const {follow, unfollow} = useFollowMethods({ + profile, + // @ts-ignore TODO + logContext: 'FeedSuggestedFollowsCard', + }) + // @ts-ignore TODO + const [descriptionRT] = useRichText(profileUnshadowed?.description ?? '') + + if (!moderationOpts) return null + const moderation = moderateProfile(profile, moderationOpts) + const modui = moderation.ui('profileList') + if (modui.filter && !isJustAMute(modui)) return null + + return ( + + + + + + + + {sanitizeDisplayName( + profile.displayName || sanitizeHandle(profile.handle), + moderation.ui('displayName'), + )} + + + {sanitizeHandle(profile.handle, '@')} + + + + + + + + + + + + ) +} + +export function FeedSuggestedFollows() { + const t = useTheme() + const {isLoading, data: suggestions, error} = useSuggestedFollowsQuery() + + const profiles: AppBskyActorDefs.ProfileViewBasic[] = [] + if (suggestions) { + // Currently the responses contain duplicate items. + // Needs to be fixed on backend, but let's dedupe to be safe. + let seen = new Set() + for (const page of suggestions.pages) { + for (const actor of page.actors) { + if (!seen.has(actor.did)) { + seen.add(actor.did) + profiles.push(actor) + } + } + } + } + + return ( + + + + Suggested for you + + + + + + {isLoading + ? null + : error || !profiles.length + ? null + : profiles.map((profile, i) => ( + + ))} + + + + ) +}