Add progress guide interstitial, put behind gate
This commit is contained in:
@@ -6,6 +6,7 @@ import {msg, Trans} from '@lingui/macro'
|
|||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
|
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||||
import {NavigationProp} from '#/lib/routes/types'
|
import {NavigationProp} from '#/lib/routes/types'
|
||||||
import {logEvent} from '#/lib/statsig/statsig'
|
import {logEvent} from '#/lib/statsig/statsig'
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
@@ -20,6 +21,7 @@ import {PersonPlus_Stroke2_Corner0_Rounded as Person} from '#/components/icons/P
|
|||||||
import {InlineLinkText} from '#/components/Link'
|
import {InlineLinkText} from '#/components/Link'
|
||||||
import * as ProfileCard from '#/components/ProfileCard'
|
import * as ProfileCard from '#/components/ProfileCard'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
import {ProgressGuideList} from './ProgressGuide/List'
|
||||||
|
|
||||||
function CardOuter({
|
function CardOuter({
|
||||||
children,
|
children,
|
||||||
@@ -352,3 +354,25 @@ export function SuggestedFeeds() {
|
|||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ProgressGuide() {
|
||||||
|
const t = useTheme()
|
||||||
|
const {isDesktop} = useWebMediaQueries()
|
||||||
|
|
||||||
|
if (isDesktop) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.border_t,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
a.px_lg,
|
||||||
|
a.py_lg,
|
||||||
|
a.pb_lg,
|
||||||
|
]}>
|
||||||
|
<ProgressGuideList />
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {TimesLarge_Stroke2_Corner0_Rounded as Times} from '#/components/icons/Ti
|
|||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {ProgressGuideTask} from './Task'
|
import {ProgressGuideTask} from './Task'
|
||||||
|
|
||||||
export function ProgressGuideList({style}: {style: StyleProp<ViewStyle>}) {
|
export function ProgressGuideList({style}: {style?: StyleProp<ViewStyle>}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const guide = useProgressGuide('like-10-and-follow-7')
|
const guide = useProgressGuide('like-10-and-follow-7')
|
||||||
@@ -27,7 +27,7 @@ export function ProgressGuideList({style}: {style: StyleProp<ViewStyle>}) {
|
|||||||
style={[
|
style={[
|
||||||
t.atoms.text_contrast_medium,
|
t.atoms.text_contrast_medium,
|
||||||
a.font_semibold,
|
a.font_semibold,
|
||||||
a.text_xs,
|
a.text_sm,
|
||||||
{textTransform: 'uppercase'},
|
{textTransform: 'uppercase'},
|
||||||
]}>
|
]}>
|
||||||
<Trans>Get started</Trans>
|
<Trans>Get started</Trans>
|
||||||
|
|||||||
@@ -6,44 +6,41 @@ import {atoms as a, useTheme} from '#/alf'
|
|||||||
import {AnimatedCheck} from '../anim/AnimatedCheck'
|
import {AnimatedCheck} from '../anim/AnimatedCheck'
|
||||||
import {Text} from '../Typography'
|
import {Text} from '../Typography'
|
||||||
|
|
||||||
export interface ProgressGuideTaskProps {
|
|
||||||
current: number
|
|
||||||
total: number
|
|
||||||
title: string
|
|
||||||
subtitle?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ProgressGuideTask({
|
export function ProgressGuideTask({
|
||||||
current,
|
current,
|
||||||
total,
|
total,
|
||||||
title,
|
title,
|
||||||
subtitle,
|
subtitle,
|
||||||
}: ProgressGuideTaskProps) {
|
}: {
|
||||||
|
current: number
|
||||||
|
total: number
|
||||||
|
title: string
|
||||||
|
subtitle?: string
|
||||||
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.flex_row, a.gap_sm, !subtitle && a.align_center]}>
|
<View style={[a.flex_row, a.gap_md, !subtitle && a.align_center]}>
|
||||||
{current === total ? (
|
{current === total ? (
|
||||||
<AnimatedCheck playOnMount fill={t.palette.primary_500} width={16} />
|
<AnimatedCheck playOnMount fill={t.palette.primary_500} width={24} />
|
||||||
) : (
|
) : (
|
||||||
<Progress.Circle
|
<Progress.Circle
|
||||||
progress={current / total}
|
progress={current / total}
|
||||||
color={t.palette.primary_400}
|
color={t.palette.primary_400}
|
||||||
size={16}
|
size={24}
|
||||||
thickness={2}
|
thickness={2}
|
||||||
borderWidth={0}
|
borderWidth={0}
|
||||||
unfilledColor={t.palette.contrast_50}
|
unfilledColor={t.palette.contrast_50}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<View style={[a.flex_col, a.gap_md]}>
|
|
||||||
<View style={[a.flex_col, a.gap_xs]}>
|
<View style={[a.flex_col, a.gap_xs]}>
|
||||||
<Text style={[a.text_sm, a.font_semibold]}>{title}</Text>
|
<Text style={[a.text_md, a.font_semibold]}>{title}</Text>
|
||||||
{subtitle && (
|
{subtitle && (
|
||||||
<Text style={[a.text_xs, t.atoms.text_contrast_medium]}>
|
<Text style={[a.text_sm, t.atoms.text_contrast_medium]}>
|
||||||
{subtitle}
|
{subtitle}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,5 +7,6 @@ export type Gate =
|
|||||||
| 'show_avi_follow_button'
|
| 'show_avi_follow_button'
|
||||||
| 'show_follow_back_label_v2'
|
| 'show_follow_back_label_v2'
|
||||||
| 'new_user_guided_tour'
|
| 'new_user_guided_tour'
|
||||||
|
| 'new_user_progress_guide'
|
||||||
| 'suggested_feeds_interstitial'
|
| 'suggested_feeds_interstitial'
|
||||||
| 'suggested_follows_interstitial'
|
| 'suggested_follows_interstitial'
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React from 'react'
|
|||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
|
import {useGate} from '#/lib/statsig/statsig'
|
||||||
import {
|
import {
|
||||||
ProgressGuideToast,
|
ProgressGuideToast,
|
||||||
ProgressGuideToastRef,
|
ProgressGuideToastRef,
|
||||||
@@ -55,14 +56,10 @@ export function useProgressGuideControls() {
|
|||||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {mutate} = useSetActiveProgressGuideMutation()
|
const {mutate} = useSetActiveProgressGuideMutation()
|
||||||
|
const gate = useGate()
|
||||||
|
|
||||||
const [activeProgressGuide, setActiveProgressGuide] =
|
const [activeProgressGuide, setActiveProgressGuide] =
|
||||||
React.useState<ProgressGuide>({
|
React.useState<ProgressGuide>(undefined)
|
||||||
guide: 'like-10-and-follow-7',
|
|
||||||
numLikes: 0,
|
|
||||||
numFollows: 0,
|
|
||||||
isComplete: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const firstLikeToastRef = React.useRef<ProgressGuideToastRef | null>(null)
|
const firstLikeToastRef = React.useRef<ProgressGuideToastRef | null>(null)
|
||||||
const fifthLikeToastRef = React.useRef<ProgressGuideToastRef | null>(null)
|
const fifthLikeToastRef = React.useRef<ProgressGuideToastRef | null>(null)
|
||||||
@@ -71,6 +68,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
const controls = React.useMemo(() => {
|
const controls = React.useMemo(() => {
|
||||||
return {
|
return {
|
||||||
startProgressGuide(guide: ProgressGuideName) {
|
startProgressGuide(guide: ProgressGuideName) {
|
||||||
|
if (!gate('new_user_progress_guide')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (guide === 'like-10-and-follow-7') {
|
if (guide === 'like-10-and-follow-7') {
|
||||||
const guideObj = {
|
const guideObj = {
|
||||||
guide: 'like-10-and-follow-7',
|
guide: 'like-10-and-follow-7',
|
||||||
@@ -127,7 +127,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
setActiveProgressGuide(guide)
|
setActiveProgressGuide(guide)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [activeProgressGuide, setActiveProgressGuide, mutate])
|
}, [activeProgressGuide, setActiveProgressGuide, mutate, gate])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProgressGuideContext.Provider value={activeProgressGuide}>
|
<ProgressGuideContext.Provider value={activeProgressGuide}>
|
||||||
|
|||||||
@@ -34,7 +34,11 @@ import {useSession} from '#/state/session'
|
|||||||
import {useAnalytics} from 'lib/analytics/analytics'
|
import {useAnalytics} from 'lib/analytics/analytics'
|
||||||
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||||
import {useTheme} from 'lib/ThemeContext'
|
import {useTheme} from 'lib/ThemeContext'
|
||||||
import {SuggestedFeeds, SuggestedFollows} from '#/components/FeedInterstitials'
|
import {
|
||||||
|
ProgressGuide,
|
||||||
|
SuggestedFeeds,
|
||||||
|
SuggestedFollows,
|
||||||
|
} from '#/components/FeedInterstitials'
|
||||||
import {List, ListRef} from '../util/List'
|
import {List, ListRef} from '../util/List'
|
||||||
import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||||
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
|
import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn'
|
||||||
@@ -85,14 +89,36 @@ type FeedItem =
|
|||||||
}
|
}
|
||||||
slot: number
|
slot: number
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: 'interstitialProgressGuide'
|
||||||
|
key: string
|
||||||
|
params: {
|
||||||
|
variant: 'default' | string
|
||||||
|
}
|
||||||
|
slot: number
|
||||||
|
}
|
||||||
|
|
||||||
const feedInterstitialType = 'interstitialFeeds'
|
const feedInterstitialType = 'interstitialFeeds'
|
||||||
const followInterstitialType = 'interstitialFollows'
|
const followInterstitialType = 'interstitialFollows'
|
||||||
|
const progressGuideInterstitialType = 'interstitialProgressGuide'
|
||||||
const interstials: Record<
|
const interstials: Record<
|
||||||
'following' | 'discover',
|
'following' | 'discover',
|
||||||
(FeedItem & {type: 'interstitialFeeds' | 'interstitialFollows'})[]
|
(FeedItem & {
|
||||||
|
type:
|
||||||
|
| 'interstitialFeeds'
|
||||||
|
| 'interstitialFollows'
|
||||||
|
| 'interstitialProgressGuide'
|
||||||
|
})[]
|
||||||
> = {
|
> = {
|
||||||
following: [
|
following: [
|
||||||
|
{
|
||||||
|
type: progressGuideInterstitialType,
|
||||||
|
params: {
|
||||||
|
variant: 'default',
|
||||||
|
},
|
||||||
|
key: progressGuideInterstitialType,
|
||||||
|
slot: 0,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: followInterstitialType,
|
type: followInterstitialType,
|
||||||
params: {
|
params: {
|
||||||
@@ -111,6 +137,14 @@ const interstials: Record<
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
discover: [
|
discover: [
|
||||||
|
{
|
||||||
|
type: progressGuideInterstitialType,
|
||||||
|
params: {
|
||||||
|
variant: 'default',
|
||||||
|
},
|
||||||
|
key: progressGuideInterstitialType,
|
||||||
|
slot: 0,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: feedInterstitialType,
|
type: feedInterstitialType,
|
||||||
params: {
|
params: {
|
||||||
@@ -336,14 +370,14 @@ let Feed = ({
|
|||||||
|
|
||||||
if (feedType) {
|
if (feedType) {
|
||||||
for (const interstitial of interstials[feedType]) {
|
for (const interstitial of interstials[feedType]) {
|
||||||
const feedInterstitialEnabled =
|
const shouldShow =
|
||||||
interstitial.type === feedInterstitialType &&
|
(interstitial.type === feedInterstitialType &&
|
||||||
gate('suggested_feeds_interstitial')
|
gate('suggested_feeds_interstitial')) ||
|
||||||
const followInterstitialEnabled =
|
(interstitial.type === followInterstitialType &&
|
||||||
interstitial.type === followInterstitialType &&
|
gate('suggested_follows_interstitial')) ||
|
||||||
gate('suggested_follows_interstitial')
|
interstitial.type === progressGuideInterstitialType
|
||||||
|
|
||||||
if (feedInterstitialEnabled || followInterstitialEnabled) {
|
if (shouldShow) {
|
||||||
const variant = 'default' // replace with experiment variant
|
const variant = 'default' // replace with experiment variant
|
||||||
const int = {
|
const int = {
|
||||||
...interstitial,
|
...interstitial,
|
||||||
@@ -460,6 +494,8 @@ let Feed = ({
|
|||||||
return <SuggestedFeeds />
|
return <SuggestedFeeds />
|
||||||
} else if (item.type === followInterstitialType) {
|
} else if (item.type === followInterstitialType) {
|
||||||
return <SuggestedFollows />
|
return <SuggestedFollows />
|
||||||
|
} else if (item.type === progressGuideInterstitialType) {
|
||||||
|
return <ProgressGuide />
|
||||||
} else if (item.type === 'slice') {
|
} else if (item.type === 'slice') {
|
||||||
if (item.slice.rootUri === FALLBACK_MARKER_POST.post.uri) {
|
if (item.slice.rootUri === FALLBACK_MARKER_POST.post.uri) {
|
||||||
// HACK
|
// HACK
|
||||||
|
|||||||
Reference in New Issue
Block a user