ad7d49ef8f
* 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>
101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
import {type AppBskyContactDefs} from '@atproto/api'
|
|
|
|
import {type CountryCode} from '#/lib/international-telephone-codes'
|
|
import {normalizePhoneNumber} from './phone-number'
|
|
import {type Contact, type Match} from './state'
|
|
|
|
/**
|
|
* Filters out contacts that do not have any associated phone numbers,
|
|
* as well as businesses
|
|
*/
|
|
export function contactsWithPhoneNumbersOnly(contacts: Contact[]) {
|
|
return contacts.filter(
|
|
contact =>
|
|
contact.phoneNumbers &&
|
|
contact.phoneNumbers.length > 0 &&
|
|
contact.contactType !== 'company',
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Takes the raw contact book and returns a plain list of numbers in E.164 format, along
|
|
* with a mapping to retrieve the contact ID when we get the results back.
|
|
*
|
|
* `countryCode` is used as a fallback for local numbers that don't have a country code associated with them.
|
|
* I'm making the assumption that most local numbers in someone's phone book will be the same as theirs.
|
|
*/
|
|
export function normalizeContactBook(
|
|
contacts: Contact[],
|
|
countryCode: CountryCode,
|
|
ownNumber: string,
|
|
): {
|
|
phoneNumbers: string[]
|
|
indexToContactId: Map<number, Contact['id']>
|
|
} {
|
|
const phoneNumbers: string[] = []
|
|
const indexToContactId = new Map<number, Contact['id']>()
|
|
|
|
for (const contact of contacts) {
|
|
for (const number of contact.phoneNumbers ?? []) {
|
|
let rawNumber: string
|
|
|
|
if (number.number) {
|
|
rawNumber = number.number
|
|
} else if (number.digits) {
|
|
rawNumber = number.digits
|
|
} else {
|
|
continue
|
|
}
|
|
|
|
const normalized = normalizePhoneNumber(
|
|
rawNumber,
|
|
number.countryCode,
|
|
countryCode,
|
|
)
|
|
if (normalized === null) continue
|
|
|
|
// skip if it's your own number
|
|
if (normalized === ownNumber) continue
|
|
|
|
phoneNumbers.push(normalized)
|
|
indexToContactId.set(phoneNumbers.length - 1, contact.id)
|
|
}
|
|
}
|
|
|
|
return {
|
|
phoneNumbers,
|
|
indexToContactId,
|
|
}
|
|
}
|
|
|
|
export function filterMatchedNumbers(
|
|
contacts: Contact[],
|
|
results: AppBskyContactDefs.MatchAndContactIndex[],
|
|
mapping: Map<number, Contact['id']>,
|
|
) {
|
|
const filteredIds = new Set<Contact['id']>()
|
|
|
|
for (const result of results) {
|
|
const id = mapping.get(result.contactIndex)
|
|
if (id !== undefined) {
|
|
filteredIds.add(id)
|
|
}
|
|
}
|
|
|
|
return contacts.filter(contact => !filteredIds.has(contact.id))
|
|
}
|
|
|
|
export function getMatchedContacts(
|
|
contacts: Contact[],
|
|
results: AppBskyContactDefs.MatchAndContactIndex[],
|
|
mapping: Map<number, Contact['id']>,
|
|
): Array<Match> {
|
|
const contactsById = new Map(contacts.map(c => [c.id, c]))
|
|
|
|
return results.map(result => {
|
|
const id = mapping.get(result.contactIndex)
|
|
const contact = id !== undefined ? contactsById.get(id) : undefined
|
|
return {profile: result.match, contact}
|
|
})
|
|
}
|