···11+import {useContext} from 'react'
12import {Alert, View} from 'react-native'
23import {useSafeAreaInsets} from 'react-native-safe-area-context'
34import * as Contacts from 'expo-contacts'
44-import {AppBskyContactImportContacts} from '@atproto/api'
55+import type AtpAgent from '@atproto/api'
66+import {
77+ type AppBskyActorProfile,
88+ AppBskyContactImportContacts,
99+ type Un$Typed,
1010+} from '@atproto/api'
511import {msg, t, Trans} from '@lingui/macro'
612import {useLingui} from '@lingui/react'
713import {useMutation, useQueryClient} from '@tanstack/react-query'
8141515+import {uploadBlob} from '#/lib/api'
916import {cleanError, isNetworkError} from '#/lib/strings/errors'
1017import {logger} from '#/logger'
1118import {findContactsStatusQueryKey} from '#/state/queries/find-contacts'
1219import {useAgent} from '#/state/session'
2020+import {
2121+ Context as OnboardingContext,
2222+ type OnboardingAction,
2323+ type OnboardingState,
2424+} from '#/screens/Onboarding/state'
1325import {atoms as a, ios, tokens, useGutters} from '#/alf'
1426import {Button, ButtonIcon, ButtonText} from '#/components/Button'
1527import * as Layout from '#/components/Layout'
···4355 const insets = useSafeAreaInsets()
4456 const gutters = useGutters([0, 'wide'])
4557 const queryClient = useQueryClient()
5858+ const maybeOnboardingContext = useContext(OnboardingContext)
46594760 const {mutate: uploadContacts, isPending: isUploadPending} = useMutation({
4861 mutationFn: async (contacts: Contacts.ExistingContact[]) => {
6262+ /**
6363+ * `importContacts` triggers a notification for the people you match with,
6464+ * however we prevent notifications coming from users without profiles.
6565+ * If you're using this as the onboarding flow, we need to create a profile
6666+ * record before this.
6767+ *
6868+ * When you finish onboarding, we'll upsert again - bit wasteful but fine.
6969+ */
7070+ if (context === 'Onboarding' && maybeOnboardingContext) {
7171+ try {
7272+ await createProfileRecord(agent, maybeOnboardingContext)
7373+ } catch (error) {
7474+ logger.debug('Error creating profile record:', {safeMessage: error})
7575+ }
7676+ }
7777+4978 const {phoneNumbers, indexToContactId} = normalizeContactBook(
5079 contacts,
5180 state.phoneCountryCode,
···202231 <Text style={style}>
203232 <Trans>
204233 Bluesky helps friends find each other by creating an encoded digital
205205- fingerprint, called a "hash," and then looking for matching hashes.
234234+ fingerprint, called a "hash", and then looking for matching hashes.
206235 </Trans>
207236 </Text>
208237 <Text style={style}>
209209- • <Trans>We never store plain phone numbers</Trans>
238238+ • <Trans>We never keep plain phone numbers</Trans>
210239 </Text>
211240 <Text style={style}>
212241 • <Trans>We delete hashes after matches are made</Trans>
···288317 ],
289318 )
290319}
320320+321321+/**
322322+ * Copied from `#/screens/Onboarding/StepFinished/index.tsx`
323323+ */
324324+async function createProfileRecord(
325325+ agent: AtpAgent,
326326+ onboardingContext: {
327327+ state: OnboardingState
328328+ dispatch: React.Dispatch<OnboardingAction>
329329+ },
330330+) {
331331+ const profileStepResults = onboardingContext.state.profileStepResults
332332+ const {imageUri, imageMime} = profileStepResults
333333+ const blobPromise =
334334+ imageUri && imageMime ? uploadBlob(agent, imageUri, imageMime) : undefined
335335+336336+ await agent.upsertProfile(async existing => {
337337+ let next: Un$Typed<AppBskyActorProfile.Record> = existing ?? {}
338338+339339+ if (blobPromise) {
340340+ const res = await blobPromise
341341+ if (res.data.blob) {
342342+ next.avatar = res.data.blob
343343+ }
344344+ }
345345+346346+ next.displayName = ''
347347+348348+ next.createdAt = new Date().toISOString()
349349+ return next
350350+ })
351351+}
+4-5
src/screens/Onboarding/StepFinished/index.tsx
···161161 }
162162163163 next.displayName = ''
164164- // HACKFIX
165165- // creating a bunch of identical profile objects is breaking the relay
166166- // tossing this unspecced field onto it to reduce the size of the problem
167167- // -prf
168168- next.createdAt = new Date().toISOString()
164164+165165+ if (!next.createdAt) {
166166+ next.createdAt = new Date().toISOString()
167167+ }
169168 return next
170169 })
171170