diff --git a/src/lib/statsig/events.ts b/src/lib/statsig/events.ts
index 159061eac9..b33dcfc44c 100644
--- a/src/lib/statsig/events.ts
+++ b/src/lib/statsig/events.ts
@@ -159,6 +159,7 @@ export type LogEvents = {
| 'AvatarButton'
| 'StarterPackProfilesList'
| 'FeedInterstitial'
+ | 'ProfileHeaderSuggestedFollows'
}
'profile:unfollow': {
logContext:
@@ -173,6 +174,7 @@ export type LogEvents = {
| 'AvatarButton'
| 'StarterPackProfilesList'
| 'FeedInterstitial'
+ | 'ProfileHeaderSuggestedFollows'
}
'chat:create': {
logContext: 'ProfileHeader' | 'NewChatDialog' | 'SendViaChatDialog'
diff --git a/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx b/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx
index c7df4d75be..dce36e3f7c 100644
--- a/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx
+++ b/src/view/com/profile/ProfileHeaderSuggestedFollows.tsx
@@ -1,32 +1,60 @@
import React from 'react'
-import {Pressable, ScrollView, StyleSheet, View} from 'react-native'
-import {AppBskyActorDefs, moderateProfile} from '@atproto/api'
-import {
- FontAwesomeIcon,
- FontAwesomeIconStyle,
-} from '@fortawesome/react-native-fontawesome'
+import {ScrollView, View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
-import {useProfileShadow} from '#/state/cache/profile-shadow'
+import {logEvent} from '#/lib/statsig/statsig'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
-import {useProfileFollowMutationQueue} from '#/state/queries/profile'
import {useSuggestedFollowsByActorQuery} from '#/state/queries/suggested-follows'
-import {useAnalytics} from 'lib/analytics/analytics'
-import {usePalette} from 'lib/hooks/usePalette'
-import {makeProfileLink} from 'lib/routes/links'
-import {sanitizeDisplayName} from 'lib/strings/display-names'
-import {sanitizeHandle} from 'lib/strings/handles'
import {isWeb} from 'platform/detection'
-import {Button} from 'view/com/util/forms/Button'
-import {Link} from 'view/com/util/Link'
-import {Text} from 'view/com/util/text/Text'
-import {PreviewableUserAvatar} from 'view/com/util/UserAvatar'
-import * as Toast from '../util/Toast'
+import {atoms as a, useTheme, ViewStyleProp} from '#/alf'
+import {Button, ButtonIcon} from '#/components/Button'
+import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
+import * as ProfileCard from '#/components/ProfileCard'
+import {Text} from '#/components/Typography'
-const OUTER_PADDING = 10
-const INNER_PADDING = 14
-const TOTAL_HEIGHT = 250
+const OUTER_PADDING = a.p_md.padding
+const INNER_PADDING = a.p_lg.padding
+const TOTAL_HEIGHT = 232
+const MOBILE_CARD_WIDTH = 300
+
+function CardOuter({
+ children,
+ style,
+}: {children: React.ReactNode | React.ReactNode[]} & ViewStyleProp) {
+ const t = useTheme()
+ return (
+
+ {children}
+
+ )
+}
+
+export function SuggestedFollowPlaceholder() {
+ const t = useTheme()
+ return (
+
+
+
+
+
+
+
+
+ )
+}
export function ProfileHeaderSuggestedFollows({
actorDid,
@@ -35,47 +63,55 @@ export function ProfileHeaderSuggestedFollows({
actorDid: string
requestDismiss: () => void
}) {
- const pal = usePalette('default')
- const {isLoading, data} = useSuggestedFollowsByActorQuery({
- did: actorDid,
- })
+ const t = useTheme()
+ const {_} = useLingui()
+ const {isLoading: isSuggestionsLoading, data} =
+ useSuggestedFollowsByActorQuery({
+ did: actorDid,
+ })
+ const moderationOpts = useModerationOpts()
+ const isLoading = isSuggestionsLoading || !moderationOpts
+
return (
+ style={[
+ t.atoms.bg_contrast_25,
+ {
+ height: '100%',
+ paddingTop: INNER_PADDING / 2,
+ },
+ ]}>
-
+ style={[
+ a.flex_row,
+ a.justify_between,
+ a.align_center,
+ a.pt_xs,
+ {
+ paddingBottom: INNER_PADDING / 2,
+ paddingLeft: INNER_PADDING,
+ paddingRight: INNER_PADDING / 2,
+ },
+ ]}>
+
Suggested for you
-
-
-
+ label={_(msg`Dismiss`)}
+ size="xsmall"
+ variant="ghost"
+ color="secondary"
+ shape="round">
+
+
- {isLoading ? (
- <>
-
-
-
-
-
-
- >
- ) : data ? (
- data.suggestions
- .filter(s => (s.associated?.labeler ? false : true))
- .map(profile => (
-
- ))
- ) : (
-
- )}
+ snapToInterval={MOBILE_CARD_WIDTH + a.gap_sm.gap}
+ decelerationRate="fast">
+
+ {isLoading ? (
+ <>
+
+
+
+
+
+ >
+ ) : data ? (
+ data.suggestions
+ .filter(s => (s.associated?.labeler ? false : true))
+ .map(profile => (
+ {
+ logEvent('profile:header:suggestedFollowsCard:press', {})
+ }}
+ style={[a.flex_1]}>
+ {({hovered, pressed}) => (
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ ))
+ ) : (
+
+ )}
+
)
}
-
-function SuggestedFollowSkeleton() {
- const pal = usePalette('default')
- return (
-
-
-
-
-
-
- )
-}
-
-function SuggestedFollow({
- profile: profileUnshadowed,
-}: {
- profile: AppBskyActorDefs.ProfileView
-}) {
- const {track} = useAnalytics()
- const pal = usePalette('default')
- const {_} = useLingui()
- const moderationOpts = useModerationOpts()
- const profile = useProfileShadow(profileUnshadowed)
- const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(
- profile,
- 'ProfileHeaderSuggestedFollows',
- )
-
- const onPressFollow = React.useCallback(async () => {
- try {
- track('ProfileHeader:SuggestedFollowFollowed')
- await queueFollow()
- } catch (e: any) {
- if (e?.name !== 'AbortError') {
- Toast.show(_(msg`An issue occurred, please try again.`), 'xmark')
- }
- }
- }, [queueFollow, track, _])
-
- const onPressUnfollow = React.useCallback(async () => {
- try {
- await queueUnfollow()
- } catch (e: any) {
- if (e?.name !== 'AbortError') {
- Toast.show(_(msg`An issue occurred, please try again.`), 'xmark')
- }
- }
- }, [queueUnfollow, _])
-
- if (!moderationOpts) {
- return null
- }
- const moderation = moderateProfile(profile, moderationOpts)
- const following = profile.viewer?.following
- return (
-
-
-
-
-
-
- {sanitizeDisplayName(
- profile.displayName || sanitizeHandle(profile.handle),
- moderation.ui('displayName'),
- )}
-
-
- {sanitizeHandle(profile.handle, '@')}
-
-
-
-
-
-
- )
-}
-
-const styles = StyleSheet.create({
- suggestedFollowCardOuter: {
- marginHorizontal: INNER_PADDING / 2,
- paddingTop: 10,
- paddingBottom: 12,
- paddingHorizontal: 10,
- borderRadius: 8,
- width: 130,
- alignItems: 'center',
- overflow: 'hidden',
- flexShrink: 1,
- },
-})