TM-SGNL-iOS/SignalServiceKit/Contacts/FetchedSystemContacts.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

125 lines
4.8 KiB
Swift

//
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
public struct FetchedSystemContacts {
struct SystemContactRef {
let cnContactId: String
let userProvidedLabel: String
}
let phoneNumberToContactRef: [CanonicalPhoneNumber: SystemContactRef]
let cnContactIdToContact: [String: SystemContact]
private init(
phoneNumberToContactRef: [CanonicalPhoneNumber: SystemContactRef],
cnContactIdToContact: [String: SystemContact]
) {
self.phoneNumberToContactRef = phoneNumberToContactRef
self.cnContactIdToContact = cnContactIdToContact
}
static func parseContacts(
_ orderedContacts: [SystemContact],
phoneNumberUtil: PhoneNumberUtil,
localPhoneNumber: String?
) -> FetchedSystemContacts {
// A given Contact may have multiple phone numbers.
var phoneNumberToContactRef = [CanonicalPhoneNumber: SystemContactRef]()
var cnContactIdToContact = [String: SystemContact]()
let localPhoneNumber = E164(localPhoneNumber).map(CanonicalPhoneNumber.init(nonCanonicalPhoneNumber:))
for systemContact in orderedContacts {
var parsedPhoneNumbers = Self._parsePhoneNumbers(
for: systemContact,
phoneNumberUtil: phoneNumberUtil,
localPhoneNumber: localPhoneNumber
)
// Ignore any system contact records for the local contact. For the local
// user we never want to show the avatar / name that you have entered for
// yourself in your system contacts. Instead, we always want to display
// your profile name and avatar.
parsedPhoneNumbers.removeAll(where: { $0.canonicalValue == localPhoneNumber })
if parsedPhoneNumbers.isEmpty {
continue
}
cnContactIdToContact[systemContact.cnContactId] = systemContact
for parsedPhoneNumber in parsedPhoneNumbers {
let phoneNumber = parsedPhoneNumber.canonicalValue
if phoneNumberToContactRef[phoneNumber] != nil {
// We've already picked a Contact for this number.
continue
}
phoneNumberToContactRef[phoneNumber] = SystemContactRef(
cnContactId: systemContact.cnContactId,
userProvidedLabel: parsedPhoneNumber.userProvidedLabel
)
}
}
return FetchedSystemContacts(
phoneNumberToContactRef: phoneNumberToContactRef,
cnContactIdToContact: cnContactIdToContact
)
}
public static func parsePhoneNumbers(
for systemContact: SystemContact,
phoneNumberUtil: PhoneNumberUtil,
localPhoneNumber: CanonicalPhoneNumber?
) -> [CanonicalPhoneNumber] {
return _parsePhoneNumbers(
for: systemContact,
phoneNumberUtil: phoneNumberUtil,
localPhoneNumber: localPhoneNumber
).map { $0.canonicalValue }
}
private struct ParsedPhoneNumber {
let canonicalValue: CanonicalPhoneNumber
let userProvidedLabel: String
}
private static func _parsePhoneNumbers(
for systemContact: SystemContact,
phoneNumberUtil: PhoneNumberUtil,
localPhoneNumber: CanonicalPhoneNumber?
) -> [ParsedPhoneNumber] {
var results = [ParsedPhoneNumber]()
for (phoneNumber, phoneNumberLabel) in systemContact.phoneNumbers {
let parsedPhoneNumbers = parsePhoneNumber(
phoneNumber,
phoneNumberUtil: phoneNumberUtil,
localPhoneNumber: localPhoneNumber
)
for parsedPhoneNumber in parsedPhoneNumbers {
results.append(ParsedPhoneNumber(
canonicalValue: parsedPhoneNumber,
userProvidedLabel: phoneNumberLabel ?? ""
))
}
}
return results
}
public static func parsePhoneNumber(
_ userTextPhoneNumber: String,
phoneNumberUtil: PhoneNumberUtil,
localPhoneNumber: CanonicalPhoneNumber?
) -> [CanonicalPhoneNumber] {
let phoneNumbers = phoneNumberUtil.parsePhoneNumbers(
userSpecifiedText: userTextPhoneNumber,
localPhoneNumber: localPhoneNumber?.rawValue.stringValue
)
var results = [CanonicalPhoneNumber]()
for phoneNumberObj in phoneNumbers {
guard let phoneNumber = E164(phoneNumberObj.e164) else {
owsFailDebug("Couldn't convert parsed phone number to E164")
continue
}
results.append(CanonicalPhoneNumber(nonCanonicalPhoneNumber: phoneNumber))
}
return results
}
}