Bluesky app fork with some witchin' additions 💫

create profile record before upload (#9547)

authored by samuel.fm and committed by GitHub 9971e91f 8cee40be

Changed files
+68 -8
src
components
contacts
screens
screens
Onboarding
StepFinished
+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 - &bull; <Trans>We never store plain phone numbers</Trans> 238 + &bull; <Trans>We never keep plain phone numbers</Trans> 210 239 </Text> 211 240 <Text style={style}> 212 241 &bull; <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
··· 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