diff options
author | Eric Bailey <git@esb.lol> | 2024-07-02 21:43:54 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-03 03:43:54 +0100 |
commit | 04cfd06639687012b59b52756015324cc622613b (patch) | |
tree | f4b4dda69d4e58b57d8e3bbcecff651f4f3df71f /src | |
parent | 0598fc2faa813486851f01451818220302f2f97a (diff) | |
download | voidsky-04cfd06639687012b59b52756015324cc622613b.tar.zst |
[D1X] Integrate interstitials (#4698)
* Use discriminated union * Integrate interstitials * Add gates and handling for variants * Only show interstitials for logged in accounts since flags are based on user ID * Nit --------- Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/components/FeedInterstitials.tsx | 8 | ||||
-rw-r--r-- | src/lib/statsig/gates.ts | 2 | ||||
-rw-r--r-- | src/view/com/posts/Feed.tsx | 223 |
3 files changed, 198 insertions, 35 deletions
diff --git a/src/components/FeedInterstitials.tsx b/src/components/FeedInterstitials.tsx index f1c4876a3..00342b39f 100644 --- a/src/components/FeedInterstitials.tsx +++ b/src/components/FeedInterstitials.tsx @@ -203,7 +203,7 @@ export function SuggestedFollows() { {content} <Button - label={_(msg`Browse more accounts on our explore page`)} + label={_(msg`Browse more accounts on the Explore page`)} onPress={() => { navigation.navigate('SearchTab') }}> @@ -211,7 +211,7 @@ export function SuggestedFollows() { <View style={[a.flex_1, a.justify_center]}> <View style={[a.flex_row, a.px_lg]}> <Text style={[a.pr_xl, a.flex_1, a.leading_snug]}> - <Trans>Browse more suggestions on our explore page</Trans> + <Trans>Browse more suggestions on the Explore page</Trans> </Text> <Arrow size="xl" /> @@ -329,7 +329,7 @@ export function SuggestedFeeds() { {content} <Button - label={_(msg`Browse more feeds on our explore page`)} + label={_(msg`Browse more feeds on the Explore page`)} onPress={() => { navigation.navigate('SearchTab') }} @@ -338,7 +338,7 @@ export function SuggestedFeeds() { <View style={[a.flex_1, a.justify_center]}> <View style={[a.flex_row, a.px_lg]}> <Text style={[a.pr_xl, a.flex_1, a.leading_snug]}> - <Trans>Browse more suggestions on our explore page</Trans> + <Trans>Browse more suggestions on the Explore page</Trans> </Text> <Arrow size="xl" /> diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index b667245dd..0b253b278 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -6,3 +6,5 @@ export type Gate = | 'request_notifications_permission_after_onboarding_v2' | 'show_avi_follow_button' | 'show_follow_back_label_v2' + | 'suggested_feeds_interstitial' + | 'suggested_follows_interstitial' diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx index 315286e72..3d90b8897 100644 --- a/src/view/com/posts/Feed.tsx +++ b/src/view/com/posts/Feed.tsx @@ -15,8 +15,8 @@ import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' import {FALLBACK_MARKER_POST} from '#/lib/api/feed/home' -import {KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants' -import {logEvent} from '#/lib/statsig/statsig' +import {DISCOVER_FEED_URI, KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants' +import {logEvent, useGate} from '#/lib/statsig/statsig' import {logger} from '#/logger' import {isWeb} from '#/platform/detection' import {listenPostCreated} from '#/state/events' @@ -25,6 +25,7 @@ import {STALE} from '#/state/queries' import { FeedDescriptor, FeedParams, + FeedPostSlice, pollLatest, RQKEY, usePostFeedQuery, @@ -33,6 +34,7 @@ import {useSession} from '#/state/session' import {useAnalytics} from 'lib/analytics/analytics' import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender' import {useTheme} from 'lib/ThemeContext' +import {SuggestedFeeds, SuggestedFollows} from '#/components/FeedInterstitials' import {List, ListRef} from '../util/List' import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder' import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' @@ -41,11 +43,92 @@ import {FeedErrorMessage} from './FeedErrorMessage' import {FeedShutdownMsg} from './FeedShutdownMsg' import {FeedSlice} from './FeedSlice' -const LOADING_ITEM = {_reactKey: '__loading__'} -const EMPTY_FEED_ITEM = {_reactKey: '__empty__'} -const ERROR_ITEM = {_reactKey: '__error__'} -const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} -const FEED_SHUTDOWN_MSG_ITEM = {_reactKey: '__feed_shutdown_msg_item__'} +type FeedItem = + | { + type: 'loading' + key: string + } + | { + type: 'empty' + key: string + } + | { + type: 'error' + key: string + } + | { + type: 'loadMoreError' + key: string + } + | { + type: 'feedShutdownMsg' + key: string + } + | { + type: 'slice' + key: string + slice: FeedPostSlice + } + | { + type: 'interstitialFeeds' + key: string + params: { + variant: 'default' | string + } + slot: number + } + | { + type: 'interstitialFollows' + key: string + params: { + variant: 'default' | string + } + slot: number + } + +const feedInterstitialType = 'interstitialFeeds' +const followInterstitialType = 'interstitialFollows' +const interstials: Record< + 'following' | 'discover', + (FeedItem & {type: 'interstitialFeeds' | 'interstitialFollows'})[] +> = { + following: [ + { + type: followInterstitialType, + params: { + variant: 'default', + }, + key: followInterstitialType, + slot: 20, + }, + { + type: feedInterstitialType, + params: { + variant: 'default', + }, + key: feedInterstitialType, + slot: 40, + }, + ], + discover: [ + { + type: feedInterstitialType, + params: { + variant: 'default', + }, + key: feedInterstitialType, + slot: 20, + }, + { + type: followInterstitialType, + params: { + variant: 'default', + }, + key: followInterstitialType, + slot: 40, + }, + ], +} // DISABLED need to check if this is causing random feed refreshes -prf // const REFRESH_AFTER = STALE.HOURS.ONE @@ -94,13 +177,16 @@ let Feed = ({ const {track} = useAnalytics() const {_} = useLingui() const queryClient = useQueryClient() - const {currentAccount} = useSession() + const {currentAccount, hasSession} = useSession() const initialNumToRender = useInitialNumToRender() const feedFeedback = useFeedFeedbackContext() const [isPTRing, setIsPTRing] = React.useState(false) const checkForNewRef = React.useRef<(() => void) | null>(null) const lastFetchRef = React.useRef<number>(Date.now()) const [feedType, feedUri] = feed.split('|') + const feedIsDiscover = feedUri === DISCOVER_FEED_URI + const feedIsFollowing = feedType === 'following' + const gate = useGate() const opts = React.useMemo( () => ({enabled, ignoreFilterFor}), @@ -198,29 +284,94 @@ let Feed = ({ } }, [pollInterval]) - const feedItems = React.useMemo(() => { - let arr: any[] = [] + const feedItems: FeedItem[] = React.useMemo(() => { + let arr: FeedItem[] = [] if (KNOWN_SHUTDOWN_FEEDS.includes(feedUri)) { - arr = arr.concat([FEED_SHUTDOWN_MSG_ITEM]) + arr.push({ + type: 'feedShutdownMsg', + key: 'feedShutdownMsg', + }) } if (isFetched) { if (isError && isEmpty) { - arr = arr.concat([ERROR_ITEM]) + arr.push({ + type: 'error', + key: 'error', + }) } else if (isEmpty) { - arr = arr.concat([EMPTY_FEED_ITEM]) + arr.push({ + type: 'empty', + key: 'empty', + }) } else if (data) { for (const page of data?.pages) { - arr = arr.concat(page.slices) + arr = arr.concat( + page.slices.map(s => ({ + type: 'slice', + slice: s, + key: s._reactKey, + })), + ) } } if (isError && !isEmpty) { - arr = arr.concat([LOAD_MORE_ERROR_ITEM]) + arr.push({ + type: 'loadMoreError', + key: 'loadMoreError', + }) } } else { - arr.push(LOADING_ITEM) + arr.push({ + type: 'loading', + key: 'loading', + }) } + + if (hasSession) { + const feedType = feedIsFollowing + ? 'following' + : feedIsDiscover + ? 'discover' + : undefined + + if (feedType) { + for (const interstitial of interstials[feedType]) { + const feedInterstitialEnabled = + interstitial.type === feedInterstitialType && + gate('suggested_feeds_interstitial') + const followInterstitialEnabled = + interstitial.type === followInterstitialType && + gate('suggested_follows_interstitial') + + if (feedInterstitialEnabled || followInterstitialEnabled) { + const variant = 'default' // replace with experiment variant + const int = { + ...interstitial, + params: {variant}, + // overwrite key with unique value + key: [interstitial.type, variant].join(':'), + } + + if (arr.length > interstitial.slot) { + arr.splice(interstitial.slot, 0, int) + } + } + } + } + } + return arr - }, [isFetched, isError, isEmpty, data, feedUri]) + }, [ + isFetched, + isError, + isEmpty, + data, + feedUri, + feedIsDiscover, + feedIsFollowing, + gate, + hasSession, + ]) // events // = @@ -280,10 +431,10 @@ let Feed = ({ // = const renderItem = React.useCallback( - ({item, index}: ListRenderItemInfo<any>) => { - if (item === EMPTY_FEED_ITEM) { + ({item, index}: ListRenderItemInfo<FeedItem>) => { + if (item.type === 'empty') { return renderEmptyState() - } else if (item === ERROR_ITEM) { + } else if (item.type === 'error') { return ( <FeedErrorMessage feedDesc={feed} @@ -292,7 +443,7 @@ let Feed = ({ savedFeedConfig={savedFeedConfig} /> ) - } else if (item === LOAD_MORE_ERROR_ITEM) { + } else if (item.type === 'loadMoreError') { return ( <LoadMoreRetryBtn label={_( @@ -301,18 +452,28 @@ let Feed = ({ onPress={onPressRetryLoadMore} /> ) - } else if (item === LOADING_ITEM) { + } else if (item.type === 'loading') { return <PostFeedLoadingPlaceholder /> - } else if (item === FEED_SHUTDOWN_MSG_ITEM) { + } else if (item.type === 'feedShutdownMsg') { return <FeedShutdownMsg feedUri={feedUri} /> - } else if (item.rootUri === FALLBACK_MARKER_POST.post.uri) { - // HACK - // tell the user we fell back to discover - // see home.ts (feed api) for more info - // -prf - return <DiscoverFallbackHeader /> + } else if (item.type === feedInterstitialType) { + return <SuggestedFeeds /> + } else if (item.type === followInterstitialType) { + return <SuggestedFollows /> + } else if (item.type === 'slice') { + if (item.slice.rootUri === FALLBACK_MARKER_POST.post.uri) { + // HACK + // tell the user we fell back to discover + // see home.ts (feed api) for more info + // -prf + return <DiscoverFallbackHeader /> + } + return ( + <FeedSlice slice={item.slice} hideTopBorder={index === 0 && !isWeb} /> + ) + } else { + return null } - return <FeedSlice slice={item} hideTopBorder={index === 0 && !isWeb} /> }, [ renderEmptyState, @@ -354,7 +515,7 @@ let Feed = ({ testID={testID ? `${testID}-flatlist` : undefined} ref={scrollElRef} data={feedItems} - keyExtractor={item => item._reactKey} + keyExtractor={item => item.key} renderItem={renderItem} ListFooterComponent={FeedFooter} ListHeaderComponent={ListHeaderComponent} |