+64
-3
src/components/contacts/screens/GetContacts.tsx
+64
-3
src/components/contacts/screens/GetContacts.tsx
···
1
+
import {useContext} from 'react'
1
2
import {Alert, View} from 'react-native'
2
3
import {useSafeAreaInsets} from 'react-native-safe-area-context'
3
4
import * as Contacts from 'expo-contacts'
4
-
import {AppBskyContactImportContacts} from '@atproto/api'
5
+
import type AtpAgent from '@atproto/api'
6
+
import {
7
+
type AppBskyActorProfile,
8
+
AppBskyContactImportContacts,
9
+
type Un$Typed,
10
+
} from '@atproto/api'
5
11
import {msg, t, Trans} from '@lingui/macro'
6
12
import {useLingui} from '@lingui/react'
7
13
import {useMutation, useQueryClient} from '@tanstack/react-query'
8
14
15
+
import {uploadBlob} from '#/lib/api'
9
16
import {cleanError, isNetworkError} from '#/lib/strings/errors'
10
17
import {logger} from '#/logger'
11
18
import {findContactsStatusQueryKey} from '#/state/queries/find-contacts'
12
19
import {useAgent} from '#/state/session'
20
+
import {
21
+
Context as OnboardingContext,
22
+
type OnboardingAction,
23
+
type OnboardingState,
24
+
} from '#/screens/Onboarding/state'
13
25
import {atoms as a, ios, tokens, useGutters} from '#/alf'
14
26
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
15
27
import * as Layout from '#/components/Layout'
···
43
55
const insets = useSafeAreaInsets()
44
56
const gutters = useGutters([0, 'wide'])
45
57
const queryClient = useQueryClient()
58
+
const maybeOnboardingContext = useContext(OnboardingContext)
46
59
47
60
const {mutate: uploadContacts, isPending: isUploadPending} = useMutation({
48
61
mutationFn: async (contacts: Contacts.ExistingContact[]) => {
62
+
/**
63
+
* `importContacts` triggers a notification for the people you match with,
64
+
* however we prevent notifications coming from users without profiles.
65
+
* If you're using this as the onboarding flow, we need to create a profile
66
+
* record before this.
67
+
*
68
+
* When you finish onboarding, we'll upsert again - bit wasteful but fine.
69
+
*/
70
+
if (context === 'Onboarding' && maybeOnboardingContext) {
71
+
try {
72
+
await createProfileRecord(agent, maybeOnboardingContext)
73
+
} catch (error) {
74
+
logger.debug('Error creating profile record:', {safeMessage: error})
75
+
}
76
+
}
77
+
49
78
const {phoneNumbers, indexToContactId} = normalizeContactBook(
50
79
contacts,
51
80
state.phoneCountryCode,
···
202
231
<Text style={style}>
203
232
<Trans>
204
233
Bluesky helps friends find each other by creating an encoded digital
205
-
fingerprint, called a "hash," and then looking for matching hashes.
234
+
fingerprint, called a "hash", and then looking for matching hashes.
206
235
</Trans>
207
236
</Text>
208
237
<Text style={style}>
209
-
• <Trans>We never store plain phone numbers</Trans>
238
+
• <Trans>We never keep plain phone numbers</Trans>
210
239
</Text>
211
240
<Text style={style}>
212
241
• <Trans>We delete hashes after matches are made</Trans>
···
288
317
],
289
318
)
290
319
}
320
+
321
+
/**
322
+
* Copied from `#/screens/Onboarding/StepFinished/index.tsx`
323
+
*/
324
+
async function createProfileRecord(
325
+
agent: AtpAgent,
326
+
onboardingContext: {
327
+
state: OnboardingState
328
+
dispatch: React.Dispatch<OnboardingAction>
329
+
},
330
+
) {
331
+
const profileStepResults = onboardingContext.state.profileStepResults
332
+
const {imageUri, imageMime} = profileStepResults
333
+
const blobPromise =
334
+
imageUri && imageMime ? uploadBlob(agent, imageUri, imageMime) : undefined
335
+
336
+
await agent.upsertProfile(async existing => {
337
+
let next: Un$Typed<AppBskyActorProfile.Record> = existing ?? {}
338
+
339
+
if (blobPromise) {
340
+
const res = await blobPromise
341
+
if (res.data.blob) {
342
+
next.avatar = res.data.blob
343
+
}
344
+
}
345
+
346
+
next.displayName = ''
347
+
348
+
next.createdAt = new Date().toISOString()
349
+
return next
350
+
})
351
+
}
+4
-5
src/screens/Onboarding/StepFinished/index.tsx
+4
-5
src/screens/Onboarding/StepFinished/index.tsx
···
161
161
}
162
162
163
163
next.displayName = ''
164
-
// HACKFIX
165
-
// creating a bunch of identical profile objects is breaking the relay
166
-
// tossing this unspecced field onto it to reduce the size of the problem
167
-
// -prf
168
-
next.createdAt = new Date().toISOString()
164
+
165
+
if (!next.createdAt) {
166
+
next.createdAt = new Date().toISOString()
167
+
}
169
168
return next
170
169
})
171
170