[Contacts] Contacts matching flow (#9486)

* add expo-contacts

* add expo-sms

* update copy

* add basic settings screen

* state machine, flow screen

* phone input screen

* otp screen

* tweak spacing

* resend code logic

* add layoutanimationconfig

* check availablility in settings screen

* get contacts

* matches screen

* search, temp setup for matches UI

* add a bunch of number parsing logic with libphonenumber

* cast to looser type

* rename sync to find

FCF (Find Contacts Flow)

* update geolocation hook

* nicer design for settings screen

* add completed state

* up border contrast

* update expo deps

* add pending spinner

* add country whitelist

* add empty state screen

* drop add more functionality

* upload -> import

* fix typo

* fix permission string

* copy updates

* rm envelope icon

* update sms copy

* add inviteinfo component

* woke is back

* [Contacts] NUXes (#9515)

* add a bunch of number parsing logic with libphonenumber

* add banner nux

* add announcement nux

* native only nux

* rm shitty animation

* move isNative check

* [Contacts] Onboarding step (#9489)

* add a bunch of number parsing logic with libphonenumber

* restructure onboarding to better support dynamic screens

* integrate existing flow into onboarding

* add intro step

* lift state up to allow going back freely

* gate onboarding by geo if unsupported country

* add done button to standalone flow

* [Contacts] Add `contact-match` notification type to feed (#9519)

* add a bunch of number parsing logic with libphonenumber

* add contact-joined notif type

* update string, api package

* Update NotificationFeedItem.tsx

* Update NotificationFeedItem.tsx

* Delete SyncContactsFlow.web.tsx

* fix follow back btn for this case

* [Contacts] API integration (#9487)

* api integration for flow

* copy tweak

* tweaks after running it

* wire up status page

* rename toast

* use api lib

* rm temp code

* maybe fix otp error

* clear code on error/resend

* add 1s delay to verify success

* update package versions

* Delete SyncContactsFlow.web.tsx

* try and fix yarn.lock lint

* even woker

* fix uppercase friends

* surfdude feedback

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* rm log

* allow resend on invalid code

* Update GetContacts.tsx

* overwrite country code if possible

* fix Apple's Best Feature

* devenv latest

* disable bounces

* interactive keyboard dismiss

* copy changes

* national format

* allow resending immediately

* move success state down a bit

* Update FindContactsSettings.tsx

* [Contacts] More onboarding changes (#9491)

* integrate existing flow into onboarding

* refreshed onboarding styles, rm stepper

* center content on web

* Add back dismiss button for internal onboarding

* Import sort

---------

Co-authored-by: Eric Bailey <git@esb.lol>

* add matches query to shadow

* add clockwise arrow

* update status design, fix dismiss, fix queries

* add metrics

* add more comments

* Update metrics.ts

* refetch both queries on PTR

* fix shadow state in matches page

* reduce empty space at bottom

* show contact info on matches

* filter out contacts without numbers at an earlier stage

* Error handling ✨

* get notifs working

* filter out businesses

* rm log

* remove TODO from learn more links

* try and exclude from web bundle

---------

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>
Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
Samuel Newman
2025-12-12 20:32:06 +02:00
committed by GitHub
parent d3a6db4594
commit ad7d49ef8f
98 changed files with 4906 additions and 1079 deletions
+8 -106
View File
@@ -1,4 +1,4 @@
import {useCallback, useContext, useState} from 'react'
import {useCallback, useState} from 'react'
import {View} from 'react-native'
import {
type AppBskyActorDefs,
@@ -35,28 +35,23 @@ import {
useSetActiveStarterPack,
} from '#/state/shell/starter-pack'
import {
DescriptionText,
OnboardingControls,
OnboardingHeaderSlot,
TitleText,
} from '#/screens/Onboarding/Layout'
import {Context, type OnboardingState} from '#/screens/Onboarding/state'
import {
type OnboardingState,
useOnboardingInternalState,
} from '#/screens/Onboarding/state'
import {bulkWriteFollows} from '#/screens/Onboarding/util'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {atoms as a, useBreakpoints} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {IconCircle} from '#/components/IconCircle'
import {ArrowRight_Stroke2_Corner0_Rounded as ArrowRight} from '#/components/icons/Arrow'
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
import {Growth_Stroke2_Corner0_Rounded as Growth} from '#/components/icons/Growth'
import {News2_Stroke2_Corner0_Rounded as News} from '#/components/icons/News2'
import {Trending2_Stroke2_Corner2_Rounded as Trending} from '#/components/icons/Trending'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
import * as bsky from '#/types/bsky'
import {ValuePropositionPager} from './ValuePropositionPager'
export function StepFinished() {
const {state, dispatch} = useContext(Context)
const {state, dispatch} = useOnboardingInternalState()
const onboardDispatch = useOnboardingDispatch()
const [saving, setSaving] = useState(false)
const queryClient = useQueryClient()
@@ -247,18 +242,12 @@ export function StepFinished() {
gate,
])
return state.experiments?.onboarding_value_prop ? (
return (
<ValueProposition
finishOnboarding={finishOnboarding}
saving={saving}
state={state}
/>
) : (
<LegacyFinalStep
finishOnboarding={finishOnboarding}
saving={saving}
state={state}
/>
)
}
@@ -359,90 +348,3 @@ function ValueProposition({
</>
)
}
function LegacyFinalStep({
finishOnboarding,
saving,
state,
}: {
finishOnboarding: () => void
saving: boolean
state: OnboardingState
}) {
const t = useTheme()
const {_} = useLingui()
return (
<View style={[a.align_start]}>
<IconCircle icon={Check} style={[a.mb_2xl]} />
<TitleText>
<Trans>You're ready to go!</Trans>
</TitleText>
<DescriptionText>
<Trans>We hope you have a wonderful time. Remember, Bluesky is:</Trans>
</DescriptionText>
<View style={[a.pt_5xl, a.gap_3xl]}>
<View style={[a.flex_row, a.align_center, a.w_full, a.gap_lg]}>
<IconCircle icon={Growth} size="lg" style={{width: 48, height: 48}} />
<View style={[a.flex_1, a.gap_xs]}>
<Text style={[a.font_semi_bold, a.text_lg]}>
<Trans>Public</Trans>
</Text>
<Text
style={[t.atoms.text_contrast_medium, a.text_md, a.leading_snug]}>
<Trans>
Your posts, likes, and blocks are public. Mutes are private.
</Trans>
</Text>
</View>
</View>
<View style={[a.flex_row, a.align_center, a.w_full, a.gap_lg]}>
<IconCircle icon={News} size="lg" style={{width: 48, height: 48}} />
<View style={[a.flex_1, a.gap_xs]}>
<Text style={[a.font_semi_bold, a.text_lg]}>
<Trans>Open</Trans>
</Text>
<Text
style={[t.atoms.text_contrast_medium, a.text_md, a.leading_snug]}>
<Trans>Never lose access to your followers or data.</Trans>
</Text>
</View>
</View>
<View style={[a.flex_row, a.align_center, a.w_full, a.gap_lg]}>
<IconCircle
icon={Trending}
size="lg"
style={{width: 48, height: 48}}
/>
<View style={[a.flex_1, a.gap_xs]}>
<Text style={[a.font_semi_bold, a.text_lg]}>
<Trans>Flexible</Trans>
</Text>
<Text
style={[t.atoms.text_contrast_medium, a.text_md, a.leading_snug]}>
<Trans>Choose the algorithms that power your custom feeds.</Trans>
</Text>
</View>
</View>
</View>
<OnboardingControls.Portal>
<Button
testID="onboardingFinish"
disabled={saving}
key={state.activeStep} // remove focus state on nav
color="primary"
size="large"
label={_(msg`Complete onboarding and start using your account`)}
onPress={finishOnboarding}>
<ButtonText>
{saving ? <Trans>Finalizing</Trans> : <Trans>Let's go!</Trans>}
</ButtonText>
{saving && <ButtonIcon icon={Loader} position="right" />}
</Button>
</OnboardingControls.Portal>
</View>
)
}