diff --git a/src/components/FeedInterstitials.tsx b/src/components/FeedInterstitials.tsx index e37d2c3e..65e981f7 100644 --- a/src/components/FeedInterstitials.tsx +++ b/src/components/FeedInterstitials.tsx @@ -1,18 +1,21 @@ import React from 'react' import {View} from 'react-native' import {ScrollView} from 'react-native-gesture-handler' -import {AppBskyFeedDefs, AtUri} from '@atproto/api' +import {AppBskyActorDefs, AppBskyFeedDefs, AtUri} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useNavigation} from '@react-navigation/native' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {NavigationProp} from '#/lib/routes/types' +import {useGate} from '#/lib/statsig/statsig' import {logEvent} from '#/lib/statsig/statsig' import {logger} from '#/logger' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useGetPopularFeedsQuery} from '#/state/queries/feed' +import {FeedDescriptor} from '#/state/queries/post-feed' import {useProfilesQuery} from '#/state/queries/profile' +import {useSuggestedFollowsByActorQuery} from '#/state/queries/suggested-follows' import {useSession} from '#/state/session' import {useProgressGuide} from '#/state/shell/progress-guide' import * as userActionHistory from '#/state/userActionHistory' @@ -173,14 +176,63 @@ function useExperimentalSuggestedUsersQuery() { } } -export function SuggestedFollows() { - const t = useTheme() - const {_} = useLingui() +export function SuggestedFollows({feed}: {feed: FeedDescriptor}) { + const gate = useGate() + const [feedType, feedUri] = feed.split('|') + if (feedType === 'author') { + if (gate('show_follow_suggestions_in_profile')) { + return + } else { + return null + } + } else { + return + } +} + +export function SuggestedFollowsProfile({did}: {did: string}) { + const { + isLoading: isSuggestionsLoading, + data, + error, + } = useSuggestedFollowsByActorQuery({ + did, + }) + return ( + + ) +} + +export function SuggestedFollowsHome() { const { isLoading: isSuggestionsLoading, profiles, error, } = useExperimentalSuggestedUsersQuery() + return ( + + ) +} + +export function ProfileGrid({ + isSuggestionsLoading, + error, + profiles, +}: { + isSuggestionsLoading: boolean + profiles: AppBskyActorDefs.ProfileViewDetailed[] + error: Error | null +}) { + const t = useTheme() + const {_} = useLingui() const moderationOpts = useModerationOpts() const navigation = useNavigation() const {gtMobile} = useBreakpoints() diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index d4478477..f45be0b7 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -4,5 +4,6 @@ export type Gate = | 'fixed_bottom_bar' | 'onboarding_minimum_interests' | 'suggested_feeds_interstitial' + | 'show_follow_suggestions_in_profile' | 'video_debug' | 'videos' diff --git a/src/screens/Profile/Header/ProfileHeaderStandard.tsx b/src/screens/Profile/Header/ProfileHeaderStandard.tsx index 2b6353b2..2036023c 100644 --- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx +++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx @@ -10,6 +10,7 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {useGate} from '#/lib/statsig/statsig' import {logger} from '#/logger' import {isIOS} from '#/platform/detection' import {Shadow} from '#/state/cache/types' @@ -59,6 +60,7 @@ let ProfileHeaderStandard = ({ const profile: Shadow = useProfileShadow(profileUnshadowed) const t = useTheme() + const gate = useGate() const {currentAccount, hasSession} = useSession() const {_} = useLingui() const {openModal} = useModalControls() @@ -203,27 +205,29 @@ let ProfileHeaderStandard = ({ {hasSession && ( <> - + label={_(msg`Show follows similar to ${profile.handle}`)} + style={{width: 36, height: 36}}> + + + )} )} diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx index f0709a3e..bad6ccfe 100644 --- a/src/view/com/posts/Feed.tsx +++ b/src/view/com/posts/Feed.tsx @@ -101,7 +101,7 @@ const feedInterstitialType = 'interstitialFeeds' const followInterstitialType = 'interstitialFollows' const progressGuideInterstitialType = 'interstitialProgressGuide' const interstials: Record< - 'following' | 'discover', + 'following' | 'discover' | 'profile', (FeedItem & { type: | 'interstitialFeeds' @@ -128,6 +128,16 @@ const interstials: Record< slot: 20, }, ], + profile: [ + { + type: followInterstitialType, + params: { + variant: 'default', + }, + key: followInterstitialType, + slot: 5, + }, + ], } export function getFeedPostSlice(feedItem: FeedItem): FeedPostSlice | null { @@ -193,9 +203,7 @@ let Feed = ({ const [isPTRing, setIsPTRing] = React.useState(false) const checkForNewRef = React.useRef<(() => void) | null>(null) const lastFetchRef = React.useRef(Date.now()) - const [feedType, feedUri] = feed.split('|') - const feedIsDiscover = feedUri === DISCOVER_FEED_URI - const feedIsFollowing = feedType === 'following' + const [feedType, feedUri, feedTab] = feed.split('|') const gate = useGate() const opts = React.useMemo( @@ -339,14 +347,21 @@ let Feed = ({ } if (hasSession) { - const feedType = feedIsFollowing - ? 'following' - : feedIsDiscover - ? 'discover' - : undefined + let feedKind: 'following' | 'discover' | 'profile' | undefined + if (feedType === 'following') { + feedKind = 'following' + } else if (feedUri === DISCOVER_FEED_URI) { + feedKind = 'discover' + } else if ( + feedType === 'author' && + (feedTab === 'posts_and_author_threads' || + feedTab === 'posts_with_replies') + ) { + feedKind = 'profile' + } - if (feedType) { - for (const interstitial of interstials[feedType]) { + if (feedKind) { + for (const interstitial of interstials[feedKind]) { const shouldShow = (interstitial.type === feedInterstitialType && gate('suggested_feeds_interstitial')) || @@ -377,9 +392,9 @@ let Feed = ({ isEmpty, lastFetchedAt, data, + feedType, feedUri, - feedIsDiscover, - feedIsFollowing, + feedTab, gate, hasSession, ]) @@ -470,7 +485,7 @@ let Feed = ({ } else if (item.type === feedInterstitialType) { return } else if (item.type === followInterstitialType) { - return + return } else if (item.type === progressGuideInterstitialType) { return } else if (item.type === 'slice') {