Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
at main 100 lines 2.8 kB view raw
1import {type AppBskyContactDefs} from '@atproto/api' 2 3import {type CountryCode} from '#/lib/international-telephone-codes' 4import {normalizePhoneNumber} from './phone-number' 5import {type Contact, type Match} from './state' 6 7/** 8 * Filters out contacts that do not have any associated phone numbers, 9 * as well as businesses 10 */ 11export function contactsWithPhoneNumbersOnly(contacts: Contact[]) { 12 return contacts.filter( 13 contact => 14 contact.phoneNumbers && 15 contact.phoneNumbers.length > 0 && 16 contact.contactType !== 'company', 17 ) 18} 19 20/** 21 * Takes the raw contact book and returns a plain list of numbers in E.164 format, along 22 * with a mapping to retrieve the contact ID when we get the results back. 23 * 24 * `countryCode` is used as a fallback for local numbers that don't have a country code associated with them. 25 * I'm making the assumption that most local numbers in someone's phone book will be the same as theirs. 26 */ 27export function normalizeContactBook( 28 contacts: Contact[], 29 countryCode: CountryCode, 30 ownNumber: string, 31): { 32 phoneNumbers: string[] 33 indexToContactId: Map<number, Contact['id']> 34} { 35 const phoneNumbers: string[] = [] 36 const indexToContactId = new Map<number, Contact['id']>() 37 38 for (const contact of contacts) { 39 for (const number of contact.phoneNumbers ?? []) { 40 let rawNumber: string 41 42 if (number.number) { 43 rawNumber = number.number 44 } else if (number.digits) { 45 rawNumber = number.digits 46 } else { 47 continue 48 } 49 50 const normalized = normalizePhoneNumber( 51 rawNumber, 52 number.countryCode, 53 countryCode, 54 ) 55 if (normalized === null) continue 56 57 // skip if it's your own number 58 if (normalized === ownNumber) continue 59 60 phoneNumbers.push(normalized) 61 indexToContactId.set(phoneNumbers.length - 1, contact.id) 62 } 63 } 64 65 return { 66 phoneNumbers, 67 indexToContactId, 68 } 69} 70 71export function filterMatchedNumbers( 72 contacts: Contact[], 73 results: AppBskyContactDefs.MatchAndContactIndex[], 74 mapping: Map<number, Contact['id']>, 75) { 76 const filteredIds = new Set<Contact['id']>() 77 78 for (const result of results) { 79 const id = mapping.get(result.contactIndex) 80 if (id !== undefined) { 81 filteredIds.add(id) 82 } 83 } 84 85 return contacts.filter(contact => !filteredIds.has(contact.id)) 86} 87 88export function getMatchedContacts( 89 contacts: Contact[], 90 results: AppBskyContactDefs.MatchAndContactIndex[], 91 mapping: Map<number, Contact['id']>, 92): Array<Match> { 93 const contactsById = new Map(contacts.map(c => [c.id, c])) 94 95 return results.map(result => { 96 const id = mapping.get(result.contactIndex) 97 const contact = id !== undefined ? contactsById.get(id) : undefined 98 return {profile: result.match, contact} 99 }) 100}