add intro step

This commit is contained in:
Samuel Newman
2025-12-05 17:54:08 +02:00
parent d7a228e96f
commit 430453cbff
3 changed files with 103 additions and 1 deletions
@@ -0,0 +1,78 @@
import {View} from 'react-native'
import * as Contacts from 'expo-contacts'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useQuery} from '@tanstack/react-query'
import {atoms as a, useTheme} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {Button, ButtonText} from '#/components/Button'
import {ContactsHeroImage} from '#/components/contacts/components/HeroImage'
import {InlineLinkText} from '#/components/Link'
import {Text} from '#/components/Typography'
import {OnboardingControls} from '../Layout'
import {useOnboardingInternalState} from '../state'
export function StepFindContactsIntro() {
const {_} = useLingui()
const t = useTheme()
const {dispatch} = useOnboardingInternalState()
const {data: isAvailable, isSuccess} = useQuery({
queryKey: ['contacts-available'],
queryFn: async () => await Contacts.isAvailableAsync(),
})
return (
<View style={[a.w_full, a.gap_lg]}>
<ContactsHeroImage />
<Text style={[a.text_3xl, a.leading_snug, a.font_bold]}>
<Trans>Bluesky is more fun with Friends</Trans>
</Text>
<Text style={[a.text_md, a.leading_snug, t.atoms.text_contrast_medium]}>
<Trans>
Find your friends on Bluesky by verifying your phone number and
matching with your contacts. We protect your information and you
control what happens next.{' '}
<InlineLinkText
to="#"
label={_(msg`Learn more`)}
style={[a.text_md, a.leading_snug]}>
TODO: Learn more
</InlineLinkText>
</Trans>
</Text>
{!isAvailable && isSuccess && (
<Admonition type="error">
<Trans>
Contact sync is not available on this device, as the app is unable
to access your contacts.
</Trans>
</Admonition>
)}
<OnboardingControls.Portal>
<View style={[a.gap_md]}>
<Button
onPress={() => dispatch({type: 'next'})}
label={_(msg`Upload contacts`)}
size="large"
color="primary">
<ButtonText>
<Trans>Upload contacts</Trans>
</ButtonText>
</Button>
<Button
onPress={() => dispatch({type: 'skip-contacts'})}
label={_(msg`Skip`)}
size="large"
color="secondary">
<ButtonText>
<Trans>Skip</Trans>
</ButtonText>
</Button>
</View>
</OnboardingControls.Portal>
</View>
)
}
+5
View File
@@ -24,6 +24,7 @@ import {Portal} from '#/components/Portal'
import {ScreenTransition} from '#/components/ScreenTransition'
import {ENV} from '#/env'
import {StepFindContacts} from './StepFindContacts'
import {StepFindContactsIntro} from './StepFindContactsIntro'
import {StepSuggestedAccounts} from './StepSuggestedAccounts'
import {StepSuggestedStarterpacks} from './StepSuggestedStarterpacks'
@@ -67,6 +68,7 @@ export function Onboarding() {
key={state.activeStep}
direction={state.stepTransitionDirection}
style={a.flex_1}>
{/* FindContactsFlow cannot be nested in Layout */}
{state.activeStep === 'find-contacts' ? (
<StepFindContacts />
) : (
@@ -79,6 +81,9 @@ export function Onboarding() {
{state.activeStep === 'suggested-starterpacks' && (
<StepSuggestedStarterpacks />
)}
{state.activeStep === 'find-contacts-intro' && (
<StepFindContactsIntro />
)}
{state.activeStep === 'finished' && <StepFinished />}
</Layout>
)}
+20 -1
View File
@@ -11,6 +11,7 @@ type OnboardingScreen =
| 'interests'
| 'suggested-accounts'
| 'suggested-starterpacks'
| 'find-contacts-intro'
| 'find-contacts'
| 'finished'
@@ -47,6 +48,9 @@ export type OnboardingAction =
| {
type: 'prev'
}
| {
type: 'skip-contacts'
}
| {
type: 'finish'
}
@@ -82,6 +86,7 @@ export function createInitialOnboardingState(
interests: true,
'suggested-accounts': true,
'suggested-starterpacks': starterPacksStepEnabled,
'find-contacts-intro': findContactsStepEnabled,
'find-contacts': findContactsStepEnabled,
finished: true,
}
@@ -130,11 +135,24 @@ export function reducer(
const prevIndex = stepOrder.indexOf(next.activeStep) - 1
const prevStep = stepOrder[prevIndex]
if (prevStep) {
next.activeStep = prevStep
// override back behaviour for the find contacts screen
// so returning to the flow takes you to the intro screen
if (prevStep === 'find-contacts') {
next.activeStep = 'find-contacts-intro'
} else {
next.activeStep = prevStep
}
}
next.stepTransitionDirection = 'Backward'
break
}
case 'skip-contacts': {
const nextIndex = stepOrder.indexOf('find-contacts') + 1
const nextStep = stepOrder[nextIndex] ?? 'finished'
next.activeStep = nextStep
next.stepTransitionDirection = 'Forward'
break
}
case 'finish': {
next = createInitialOnboardingState({
starterPacksStepEnabled: s.screens['suggested-starterpacks'],
@@ -187,6 +205,7 @@ function getStepOrder(s: OnboardingState): OnboardingScreen[] {
s.screens.interests && ('interests' as const),
s.screens['suggested-accounts'] && ('suggested-accounts' as const),
s.screens['suggested-starterpacks'] && ('suggested-starterpacks' as const),
s.screens['find-contacts-intro'] && ('find-contacts-intro' as const),
s.screens['find-contacts'] && ('find-contacts' as const),
s.screens.finished && ('finished' as const),
].filter(x => !!x)