add new gated screen to onboarding
This commit is contained in:
@@ -7,6 +7,7 @@ export type Gate =
|
||||
| 'explore_show_suggested_feeds'
|
||||
| 'old_postonboarding'
|
||||
| 'onboarding_add_video_feed'
|
||||
| 'onboarding_suggested_accounts'
|
||||
| 'post_follow_profile_suggested_accounts'
|
||||
| 'post_threads_v2_unspecced'
|
||||
| 'remove_show_latest_button'
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
atoms as a,
|
||||
flatten,
|
||||
native,
|
||||
TextStyleProp,
|
||||
type TextStyleProp,
|
||||
useBreakpoints,
|
||||
useTheme,
|
||||
web,
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
import {useCallback, useContext, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {logger} from '#/logger'
|
||||
import {useOnboardingDispatch} from '#/state/shell'
|
||||
import {
|
||||
DescriptionText,
|
||||
OnboardingControls,
|
||||
TitleText,
|
||||
} from '#/screens/Onboarding/Layout'
|
||||
import {Context} from '#/screens/Onboarding/state'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import {IconCircle} from '#/components/IconCircle'
|
||||
import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as ArrowRotateCounterClockwise} from '#/components/icons/ArrowRotateCounterClockwise'
|
||||
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
|
||||
import {EmojiSad_Stroke2_Corner0_Rounded as EmojiSad} from '#/components/icons/Emoji'
|
||||
import {Hashtag_Stroke2_Corner0_Rounded as Hashtag} from '#/components/icons/Hashtag'
|
||||
import {Loader} from '#/components/Loader'
|
||||
|
||||
export function StepSuggestedAccounts() {
|
||||
const {_} = useLingui()
|
||||
const t = useTheme()
|
||||
const {gtMobile} = useBreakpoints()
|
||||
|
||||
const {dispatch} = useContext(Context)
|
||||
const [saving, setSaving] = useState(false)
|
||||
const onboardDispatch = useOnboardingDispatch()
|
||||
|
||||
const saveInterests = useCallback(async () => {
|
||||
setSaving(true)
|
||||
|
||||
try {
|
||||
setSaving(false)
|
||||
dispatch({type: 'next'})
|
||||
} catch (e: any) {
|
||||
logger.info(`onboading: error saving interests`)
|
||||
logger.error(e)
|
||||
}
|
||||
}, [setSaving, dispatch])
|
||||
|
||||
const skipOnboarding = useCallback(() => {
|
||||
onboardDispatch({type: 'finish'})
|
||||
dispatch({type: 'finish'})
|
||||
}, [onboardDispatch, dispatch])
|
||||
|
||||
const isError = false
|
||||
|
||||
const title = isError ? (
|
||||
<Trans>Oh no! Something went wrong.</Trans>
|
||||
) : (
|
||||
<Trans>Suggested Accounts</Trans>
|
||||
)
|
||||
const description = isError ? (
|
||||
<Trans>
|
||||
We weren't able to connect. Please try again to continue setting up your
|
||||
account. If it continues to fail, you can skip this flow.
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans>We'll use this to help customize your experience.</Trans>
|
||||
)
|
||||
|
||||
return (
|
||||
<View style={[a.align_start]} testID="onboardingInterests">
|
||||
<IconCircle
|
||||
icon={isError ? EmojiSad : Hashtag}
|
||||
style={[
|
||||
a.mb_2xl,
|
||||
isError
|
||||
? {
|
||||
backgroundColor: t.palette.negative_50,
|
||||
}
|
||||
: {},
|
||||
]}
|
||||
iconStyle={[
|
||||
isError
|
||||
? {
|
||||
color: t.palette.negative_900,
|
||||
}
|
||||
: {},
|
||||
]}
|
||||
/>
|
||||
|
||||
<TitleText>{title}</TitleText>
|
||||
<DescriptionText>{description}</DescriptionText>
|
||||
|
||||
<View style={[a.w_full, a.pt_2xl]} />
|
||||
|
||||
<OnboardingControls.Portal>
|
||||
{isError ? (
|
||||
<View style={[a.gap_md, gtMobile ? a.flex_row : a.flex_col]}>
|
||||
<Button
|
||||
// disabled={isFetching}
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
size="large"
|
||||
label={_(msg`Retry`)}
|
||||
// onPress={() => refetch()}
|
||||
>
|
||||
<ButtonText>
|
||||
<Trans>Retry</Trans>
|
||||
</ButtonText>
|
||||
<ButtonIcon icon={ArrowRotateCounterClockwise} position="right" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
color="secondary"
|
||||
size="large"
|
||||
label={_(msg`Skip this flow`)}
|
||||
onPress={skipOnboarding}>
|
||||
<ButtonText>
|
||||
<Trans>Skip</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
) : (
|
||||
<View style={[a.gap_md, gtMobile ? a.flex_row : a.flex_col]}>
|
||||
<Button
|
||||
disabled={saving}
|
||||
color="secondary"
|
||||
size="large"
|
||||
label={_(msg`Follow all accounts`)}>
|
||||
<ButtonText>
|
||||
<Trans>Follow all</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
<Button
|
||||
disabled={saving}
|
||||
variant="solid"
|
||||
color="primary"
|
||||
size="large"
|
||||
label={_(msg`Continue to next step`)}
|
||||
onPress={saveInterests}>
|
||||
<ButtonText>
|
||||
<Trans>Continue</Trans>
|
||||
</ButtonText>
|
||||
<ButtonIcon icon={saving ? Loader : ChevronRight} />
|
||||
</Button>
|
||||
</View>
|
||||
)}
|
||||
</OnboardingControls.Portal>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +1,29 @@
|
||||
import React from 'react'
|
||||
import {useMemo, useReducer} from 'react'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {useGate} from '#/lib/statsig/statsig'
|
||||
import {Layout, OnboardingControls} from '#/screens/Onboarding/Layout'
|
||||
import {Context, initialState, reducer} from '#/screens/Onboarding/state'
|
||||
import {StepFinished} from '#/screens/Onboarding/StepFinished'
|
||||
import {StepInterests} from '#/screens/Onboarding/StepInterests'
|
||||
import {StepProfile} from '#/screens/Onboarding/StepProfile'
|
||||
import {Portal} from '#/components/Portal'
|
||||
import {StepSuggestedAccounts} from './StepSuggestedAccounts'
|
||||
|
||||
export function Onboarding() {
|
||||
const {_} = useLingui()
|
||||
const [state, dispatch] = React.useReducer(reducer, {
|
||||
const gate = useGate()
|
||||
const showSuggestedAccounts = gate('onboarding_suggested_accounts')
|
||||
const [state, dispatch] = useReducer(reducer, {
|
||||
...initialState,
|
||||
totalSteps: showSuggestedAccounts ? 4 : 3,
|
||||
experiments: {
|
||||
onboarding_suggested_accounts: showSuggestedAccounts,
|
||||
},
|
||||
})
|
||||
|
||||
const interestsDisplayNames = React.useMemo(() => {
|
||||
const interestsDisplayNames = useMemo(() => {
|
||||
return {
|
||||
news: _(msg`News`),
|
||||
journalism: _(msg`Journalism`),
|
||||
@@ -46,13 +54,16 @@ export function Onboarding() {
|
||||
<Portal>
|
||||
<OnboardingControls.Provider>
|
||||
<Context.Provider
|
||||
value={React.useMemo(
|
||||
value={useMemo(
|
||||
() => ({state, dispatch, interestsDisplayNames}),
|
||||
[state, dispatch, interestsDisplayNames],
|
||||
)}>
|
||||
<Layout>
|
||||
{state.activeStep === 'profile' && <StepProfile />}
|
||||
{state.activeStep === 'interests' && <StepInterests />}
|
||||
{state.activeStep === 'suggested-accounts' && (
|
||||
<StepSuggestedAccounts />
|
||||
)}
|
||||
{state.activeStep === 'finished' && <StepFinished />}
|
||||
</Layout>
|
||||
</Context.Provider>
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
export type OnboardingState = {
|
||||
hasPrev: boolean
|
||||
totalSteps: number
|
||||
activeStep: 'profile' | 'interests' | 'finished'
|
||||
activeStep: 'profile' | 'interests' | 'suggested-accounts' | 'finished'
|
||||
activeStepIndex: number
|
||||
|
||||
interestsStepResults: {
|
||||
@@ -34,6 +34,10 @@ export type OnboardingState = {
|
||||
backgroundColor: AvatarColor
|
||||
}
|
||||
}
|
||||
|
||||
experiments?: {
|
||||
onboarding_suggested_accounts?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export type OnboardingAction =
|
||||
@@ -160,22 +164,49 @@ export function reducer(
|
||||
|
||||
switch (a.type) {
|
||||
case 'next': {
|
||||
if (s.activeStep === 'profile') {
|
||||
next.activeStep = 'interests'
|
||||
next.activeStepIndex = 2
|
||||
} else if (s.activeStep === 'interests') {
|
||||
next.activeStep = 'finished'
|
||||
next.activeStepIndex = 3
|
||||
if (s.experiments?.onboarding_suggested_accounts) {
|
||||
if (s.activeStep === 'profile') {
|
||||
next.activeStep = 'interests'
|
||||
next.activeStepIndex = 2
|
||||
} else if (s.activeStep === 'interests') {
|
||||
next.activeStep = 'suggested-accounts'
|
||||
next.activeStepIndex = 3
|
||||
}
|
||||
if (s.activeStep === 'suggested-accounts') {
|
||||
next.activeStep = 'finished'
|
||||
next.activeStepIndex = 4
|
||||
}
|
||||
} else {
|
||||
if (s.activeStep === 'profile') {
|
||||
next.activeStep = 'interests'
|
||||
next.activeStepIndex = 2
|
||||
} else if (s.activeStep === 'interests') {
|
||||
next.activeStep = 'finished'
|
||||
next.activeStepIndex = 3
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'prev': {
|
||||
if (s.activeStep === 'interests') {
|
||||
next.activeStep = 'profile'
|
||||
next.activeStepIndex = 1
|
||||
} else if (s.activeStep === 'finished') {
|
||||
next.activeStep = 'interests'
|
||||
next.activeStepIndex = 2
|
||||
if (s.experiments?.onboarding_suggested_accounts) {
|
||||
if (s.activeStep === 'interests') {
|
||||
next.activeStep = 'profile'
|
||||
next.activeStepIndex = 1
|
||||
} else if (s.activeStep === 'suggested-accounts') {
|
||||
next.activeStep = 'interests'
|
||||
next.activeStepIndex = 2
|
||||
} else if (s.activeStep === 'finished') {
|
||||
next.activeStep = 'suggested-accounts'
|
||||
next.activeStepIndex = 3
|
||||
}
|
||||
} else {
|
||||
if (s.activeStep === 'interests') {
|
||||
next.activeStep = 'profile'
|
||||
next.activeStepIndex = 1
|
||||
} else if (s.activeStep === 'finished') {
|
||||
next.activeStep = 'interests'
|
||||
next.activeStepIndex = 2
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user