Merge branch 'charlesmchen/nse2021.10.21.2'

This commit is contained in:
Matthew Chen 2021-10-22 19:12:34 -03:00
parent 749b4b9cae
commit 631633b76a
40 changed files with 810 additions and 338 deletions

2
Pods

@ -1 +1 @@
Subproject commit 5eb2e77c4768eed576f576a9eaf0b07fbfa865a2
Subproject commit f6c13f81b9571440b2cf512c44089e36333854ed

View File

@ -240,6 +240,7 @@
342FFE7A271EF581000AC89F /* UIResponder+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 342FFE73271EF580000AC89F /* UIResponder+OWS.swift */; };
342FFE7B271EF581000AC89F /* UIStoryboard+OWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 342FFE74271EF580000AC89F /* UIStoryboard+OWS.swift */; };
342FFE7E271EF5B2000AC89F /* ReturnToCallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 342FFE7D271EF5B1000AC89F /* ReturnToCallViewController.swift */; };
342FFE832721D4B6000AC89F /* ContactsState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 342FFE822721D4B6000AC89F /* ContactsState.swift */; };
34330A5A1E7875FB00DF2FB9 /* fontawesome-webfont.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A591E7875FB00DF2FB9 /* fontawesome-webfont.ttf */; };
343417F12530A7480034FE0C /* CVComponentReactions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 343417F02530A7480034FE0C /* CVComponentReactions.swift */; };
3434AE1C22AEDE7D002EE04E /* ViewOnceMessageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3434AE1B22AEDE7D002EE04E /* ViewOnceMessageViewController.swift */; };
@ -1235,6 +1236,7 @@
342FFE73271EF580000AC89F /* UIResponder+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIResponder+OWS.swift"; sourceTree = "<group>"; };
342FFE74271EF580000AC89F /* UIStoryboard+OWS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard+OWS.swift"; sourceTree = "<group>"; };
342FFE7D271EF5B1000AC89F /* ReturnToCallViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReturnToCallViewController.swift; sourceTree = "<group>"; };
342FFE822721D4B6000AC89F /* ContactsState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactsState.swift; sourceTree = "<group>"; };
34330A591E7875FB00DF2FB9 /* fontawesome-webfont.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "fontawesome-webfont.ttf"; sourceTree = "<group>"; };
343417F02530A7480034FE0C /* CVComponentReactions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CVComponentReactions.swift; sourceTree = "<group>"; };
3434AE1B22AEDE7D002EE04E /* ViewOnceMessageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewOnceMessageViewController.swift; sourceTree = "<group>"; };
@ -2747,6 +2749,7 @@
346129A11FD1F09100532771 /* contacts */ = {
isa = PBXGroup;
children = (
342FFE822721D4B6000AC89F /* ContactsState.swift */,
4C9D347E23689E06006A4307 /* IncomingContactSyncJobQueue.swift */,
4C6E446822AEDDEE007982E6 /* NewAccountDiscovery.swift */,
346129A21FD1F09100532771 /* OWSContactsManager.h */,
@ -5721,6 +5724,7 @@
files = (
88D23D0C23CEBF4400B0E74B /* AppNotifications.swift in Sources */,
4CB93DC22180FF07004B9764 /* ProximityMonitoringManager.swift in Sources */,
342FFE832721D4B6000AC89F /* ContactsState.swift in Sources */,
88D7BA9E266809F50088D1C2 /* CallMessageRelay.swift in Sources */,
4C046AA7236148880035B234 /* OWSGroupSyncProcessingJobQueue.swift in Sources */,
4C9D347F23689E06006A4307 /* IncomingContactSyncJobQueue.swift in Sources */,

View File

@ -198,7 +198,8 @@ import SignalMessaging
// * The thread belongs to someone in our system contacts
// * The thread existed before messages requests
return (self.profileManager.isThread(inProfileWhitelist: thread, transaction: transaction)
|| self.contactsManager.isSystemContact(address: thread.contactAddress)
|| self.contactsManager.isSystemContact(address: thread.contactAddress,
transaction: transaction)
|| GRDBThreadFinder.isPreMessageRequestsThread(thread, transaction: transaction.unwrapGrdbRead))
}

View File

@ -138,7 +138,9 @@ class ContactViewController: OWSViewController, ContactShareViewHelperDelegate {
private func systemContactsForContact() -> [String] {
AssertIsOnMainThread()
return contactShare.systemContactPhoneNumbers()
return databaseStorage.read { transaction in
contactShare.systemContactPhoneNumbers(transaction: transaction)
}
}
private func updateContent() {

View File

@ -883,7 +883,7 @@ fileprivate extension CVComponentState.Builder {
self.contactShare = ContactShare(state: state)
let hasSendTextButton = !contactShare.systemContactsWithSignalAccountPhoneNumbers(transaction: transaction).isEmpty
let hasInviteButton = !contactShare.systemContactPhoneNumbers().isEmpty
let hasInviteButton = !contactShare.systemContactPhoneNumbers(transaction: transaction).isEmpty
let hasAddToContactsButton = !contactShare.e164PhoneNumbers().isEmpty
if hasSendTextButton {

View File

@ -1084,7 +1084,8 @@ extension CVComponentSystemMessage {
guard let profileChangeNewNameComponents = infoMessage.profileChangeNewNameComponents else {
return nil
}
guard Self.contactsManager.isSystemContact(address: profileChangeAddress) else {
guard Self.contactsManager.isSystemContact(address: profileChangeAddress,
transaction: transaction) else {
return nil
}
let systemContactName = Self.contactsManagerImpl.nameFromSystemContacts(for: profileChangeAddress,

View File

@ -21,7 +21,10 @@ extension ConversationViewController {
}
// If the user is in the system contacts, show a badge
if self.contactsManagerImpl.isSystemContact(address: contactThread.contactAddress) {
let isSystemContact = databaseStorage.read { transaction in
contactsManagerImpl.isSystemContact(address: contactThread.contactAddress, transaction: transaction)
}
if isSystemContact {
icon = UIImage(named: "contact-outline-16")?.withRenderingMode(.alwaysTemplate)
}
} else if let groupThread = thread as? TSGroupThread {

View File

@ -153,10 +153,12 @@ class DebugUIGroupsV2: DebugUIPage {
let prefix = prefix + " "
let signalAccounts = self.contactsManagerImpl.unsortedSignalAccounts(transaction: transaction)
// These will fail if you aren't registered or
// don't have some Signal users in your contacts.
let localAddress = tsAccountManager.localAddress!
var allAddresses = contactsManagerImpl.signalAccounts.map { $0.recipientAddress }
var allAddresses = signalAccounts.map { $0.recipientAddress }
if groupsVersion == .V2 {
// V2 group members must have a uuid.
allAddresses = allAddresses.filter { $0.uuid != nil }

View File

@ -277,9 +277,12 @@ NSString *const kArchiveButtonPseudoGroup = @"kArchiveButtonPseudoGroup";
- (NSArray<SignalAccount *> *)suggestedAccountsForFirstContact
{
// Load all signal accounts even though we only need the first N;
// we want the returned value to be stable so we need to sort.
NSArray<SignalAccount *> *sortedSignalAccounts = [self.contactsManagerImpl sortedSignalAccountsWithSneakyTransaction];
NSMutableArray<SignalAccount *> *accounts = [NSMutableArray new];
for (SignalAccount *account in self.contactsManager.signalAccounts) {
for (SignalAccount *account in sortedSignalAccounts) {
if (account.recipientAddress.isLocalAddress) {
continue;
}
@ -294,33 +297,41 @@ NSString *const kArchiveButtonPseudoGroup = @"kArchiveButtonPseudoGroup";
- (void)updateFirstConversationLabel
{
NSArray<SignalAccount *> *signalAccounts = self.suggestedAccountsForFirstContact;
NSString *formatString = @"";
__block NSString *formatString = @"";
NSMutableArray<NSString *> *contactNames = [NSMutableArray new];
if (signalAccounts.count >= 3) {
[contactNames addObject:[self.contactsManager displayNameForSignalAccount:signalAccounts[0]]];
[contactNames addObject:[self.contactsManager displayNameForSignalAccount:signalAccounts[1]]];
[contactNames addObject:[self.contactsManager displayNameForSignalAccount:signalAccounts[2]]];
formatString = NSLocalizedString(@"HOME_VIEW_FIRST_CONVERSATION_OFFER_3_CONTACTS_FORMAT",
@"Format string for a label offering to start a new conversation with your contacts, if you have at least "
@"3 Signal contacts. Embeds {{The names of 3 of your Signal contacts}}.");
} else if (signalAccounts.count == 2) {
[contactNames addObject:[self.contactsManager displayNameForSignalAccount:signalAccounts[0]]];
[contactNames addObject:[self.contactsManager displayNameForSignalAccount:signalAccounts[1]]];
formatString = NSLocalizedString(@"HOME_VIEW_FIRST_CONVERSATION_OFFER_2_CONTACTS_FORMAT",
@"Format string for a label offering to start a new conversation with your contacts, if you have 2 Signal "
@"contacts. Embeds {{The names of 2 of your Signal contacts}}.");
} else if (signalAccounts.count == 1) {
[contactNames addObject:[self.contactsManager displayNameForSignalAccount:signalAccounts[0]]];
formatString = NSLocalizedString(@"HOME_VIEW_FIRST_CONVERSATION_OFFER_1_CONTACT_FORMAT",
@"Format string for a label offering to start a new conversation with your contacts, if you have 1 Signal "
@"contact. Embeds {{The name of 1 of your Signal contacts}}.");
}
[self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) {
if (signalAccounts.count >= 3) {
[contactNames addObject:[self.contactsManagerImpl displayNameForSignalAccount:signalAccounts[0]
transaction:transaction]];
[contactNames addObject:[self.contactsManagerImpl displayNameForSignalAccount:signalAccounts[1]
transaction:transaction]];
[contactNames addObject:[self.contactsManagerImpl displayNameForSignalAccount:signalAccounts[2]
transaction:transaction]];
formatString = NSLocalizedString(@"HOME_VIEW_FIRST_CONVERSATION_OFFER_3_CONTACTS_FORMAT",
@"Format string for a label offering to start a new conversation with your contacts, if you have at least "
@"3 Signal contacts. Embeds {{The names of 3 of your Signal contacts}}.");
} else if (signalAccounts.count == 2) {
[contactNames addObject:[self.contactsManagerImpl displayNameForSignalAccount:signalAccounts[0]
transaction:transaction]];
[contactNames addObject:[self.contactsManagerImpl displayNameForSignalAccount:signalAccounts[1]
transaction:transaction]];
formatString = NSLocalizedString(@"HOME_VIEW_FIRST_CONVERSATION_OFFER_2_CONTACTS_FORMAT",
@"Format string for a label offering to start a new conversation with your contacts, if you have 2 Signal "
@"contacts. Embeds {{The names of 2 of your Signal contacts}}.");
} else if (signalAccounts.count == 1) {
[contactNames addObject:[self.contactsManagerImpl displayNameForSignalAccount:signalAccounts[0]
transaction:transaction]];
formatString = NSLocalizedString(@"HOME_VIEW_FIRST_CONVERSATION_OFFER_1_CONTACT_FORMAT",
@"Format string for a label offering to start a new conversation with your contacts, if you have 1 Signal "
@"contact. Embeds {{The name of 1 of your Signal contacts}}.");
}
}];
NSString *embedToken = @"%@";
NSArray<NSString *> *formatSplits = [formatString componentsSeparatedByString:embedToken];

View File

@ -31,8 +31,7 @@ NS_ASSUME_NONNULL_BEGIN
- (NSString *)stringForCollation
{
OWSContactsManager *contactsManager = Environment.shared.contactsManager;
return [contactsManager comparableNameForSignalAccount:self];
return [self.contactsManagerImpl comparableNameForSignalAccountWithSneakyTransaction:self];
}
@end

View File

@ -26,7 +26,9 @@ extension ConversationSettingsViewController {
owsFailDebug("Invalid thread.")
return false
}
return contactsManagerImpl.isSystemContact(address: contactThread.contactAddress)
return databaseStorage.read { transaction in
contactsManagerImpl.isSystemContact(address: contactThread.contactAddress, transaction: transaction)
}
}
// MARK: - Table

View File

@ -257,7 +257,10 @@ class MemberActionSheet: InteractiveSheetViewController {
))
if contactsManagerImpl.supportsContactEditing {
if contactsManager.isSystemContact(address: address) {
let isSystemContact = databaseStorage.read { transaction in
contactsManager.isSystemContact(address: address, transaction: transaction)
}
if isSystemContact {
section.add(.actionItem(
icon: .settingsUserInContacts,
name: NSLocalizedString(

View File

@ -300,7 +300,9 @@ class NameCollisionResolutionViewController: OWSTableViewController2 {
extension NameCollisionResolutionViewController: CNContactViewControllerDelegate, ContactsViewHelperObserver {
func shouldShowContactUpdateAction(for address: SignalServiceAddress) -> Bool {
return contactsManager.isSystemContact(address: address) && contactsManagerImpl.supportsContactEditing
databaseStorage.read { transaction in
contactsManager.isSystemContact(address: address, transaction: transaction) && contactsManagerImpl.supportsContactEditing
}
}
func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) {

View File

@ -84,7 +84,9 @@ public class OWSAddToContactViewController: OWSViewController {
}
private func updateData() {
contacts = contactsManagerImpl.allContacts
contacts = databaseStorage.read { transaction in
contactsManagerImpl.allSortedContacts(transaction: transaction)
}
}
}

View File

@ -80,7 +80,7 @@ extension NameCollision {
commonGroupsString: commonGroupsString(for: $0.address, thread: thread, transaction: transaction),
avatar: avatar(for: $0.address, transaction: transaction),
isBlocked: blockingManager.isAddressBlocked($0.address),
isSystemContact: Self.contactsManager.isSystemContact(address: $0.address)
isSystemContact: Self.contactsManager.isSystemContact(address: $0.address, transaction: transaction)
)
}
}

View File

@ -62,10 +62,6 @@ class GRDBFullTextSearcherContactsManager: NSObject, ContactsManagerProtocol {
"Fake name"
}
func nameComponents(for address: SignalServiceAddress) -> PersonNameComponents? {
PersonNameComponents()
}
func nameComponents(for address: SignalServiceAddress, transaction: SDSAnyReadTransaction) -> PersonNameComponents? {
PersonNameComponents()
}
@ -74,12 +70,20 @@ class GRDBFullTextSearcherContactsManager: NSObject, ContactsManagerProtocol {
[]
}
func isSystemContact(phoneNumber: String) -> Bool {
true
func isSystemContactWithSneakyTransaction(phoneNumber: String) -> Bool {
return true
}
func isSystemContact(address: SignalServiceAddress) -> Bool {
true
func isSystemContact(phoneNumber: String, transaction: SDSAnyReadTransaction) -> Bool {
return true
}
func isSystemContactWithSneakyTransaction(address: SignalServiceAddress) -> Bool {
return true
}
func isSystemContact(address: SignalServiceAddress, transaction: SDSAnyReadTransaction) -> Bool {
return true
}
func isSystemContact(withSignalAccount recipientId: String) -> Bool {
@ -90,10 +94,6 @@ class GRDBFullTextSearcherContactsManager: NSObject, ContactsManagerProtocol {
true
}
func hasNameInSystemContacts(for address: SignalServiceAddress) -> Bool {
(mockDisplayNameMap[address] ?? "").count > 0
}
func compare(signalAccount left: SignalAccount, with right: SignalAccount) -> ComparisonResult {
owsFailDebug("if this method ends up being used by the tests, we should provide a better implementation.")

View File

@ -361,10 +361,6 @@ class TestContactsManager: NSObject, ContactsManagerProtocol {
address.stringForDisplay
}
func nameComponents(for address: SignalServiceAddress) -> PersonNameComponents? {
PersonNameComponents()
}
func nameComponents(for address: SignalServiceAddress, transaction: SDSAnyReadTransaction) -> PersonNameComponents? {
PersonNameComponents()
}
@ -373,12 +369,20 @@ class TestContactsManager: NSObject, ContactsManagerProtocol {
[]
}
func isSystemContact(phoneNumber: String) -> Bool {
true
func isSystemContactWithSneakyTransaction(phoneNumber: String) -> Bool {
return true
}
func isSystemContact(address: SignalServiceAddress) -> Bool {
true
func isSystemContact(phoneNumber: String, transaction: SDSAnyReadTransaction) -> Bool {
return true
}
func isSystemContactWithSneakyTransaction(address: SignalServiceAddress) -> Bool {
return true
}
func isSystemContact(address: SignalServiceAddress, transaction: SDSAnyReadTransaction) -> Bool {
return true
}
func isSystemContact(withSignalAccount phoneNumber: String) -> Bool {
@ -389,10 +393,6 @@ class TestContactsManager: NSObject, ContactsManagerProtocol {
true
}
func hasNameInSystemContacts(for address: SignalServiceAddress) -> Bool {
false
}
func compare(signalAccount left: SignalAccount, with right: SignalAccount) -> ComparisonResult {
.orderedSame
}

View File

@ -0,0 +1,382 @@
//
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
//
import Foundation
import SignalServiceKit
@objc
public protocol ContactsState: AnyObject {
func unsortedSignalAccounts(transaction: SDSAnyReadTransaction) -> [SignalAccount]
// Order respects the systems contact sorting preference.
func sortedSignalAccounts(transaction: SDSAnyReadTransaction) -> [SignalAccount]
func setSortedSignalAccounts(_ signalAccounts: [SignalAccount])
func contact(forPhoneNumber phoneNumber: String, transaction: SDSAnyReadTransaction) -> Contact?
func allContacts(transaction: SDSAnyReadTransaction) -> [Contact]
func contactsMaps(transaction: SDSAnyReadTransaction) -> ContactsMaps
func setContactsMaps(_ contactsMaps: ContactsMaps,
localNumber: String?,
transaction: SDSAnyWriteTransaction)
func warmCaches(transaction: SDSAnyReadTransaction) -> ContactsStateSummary
}
// MARK: -
@objc
public class ContactsStateSummary: NSObject {
@objc
public let contactCount: UInt
@objc
public let signalAccountCount: UInt
required init(contactCount: UInt, signalAccountCount: UInt) {
self.contactCount = contactCount
self.signalAccountCount = signalAccountCount
}
}
// MARK: -
@objc
public class ContactsMaps: NSObject {
@objc
public let uniqueIdToContactMap: [String: Contact]
@objc
public let phoneNumberToContactMap: [String: Contact]
@objc
public var allContacts: [Contact] { Array(uniqueIdToContactMap.values) }
required init(uniqueIdToContactMap: [String: Contact],
phoneNumberToContactMap: [String: Contact]) {
self.uniqueIdToContactMap = uniqueIdToContactMap
self.phoneNumberToContactMap = phoneNumberToContactMap
}
static let empty: ContactsMaps = ContactsMaps(uniqueIdToContactMap: [:], phoneNumberToContactMap: [:])
// Builds a map of phone number-to-Contact.
// A given Contact may have multiple phone numbers.
@objc
public static func build(contacts: [Contact],
localNumber: String?) -> ContactsMaps {
var uniqueIdToContactMap = [String: Contact]()
var phoneNumberToContactMap = [String: Contact]()
for contact in contacts {
let phoneNumbers = Self.phoneNumbers(forContact: contact, localNumber: localNumber)
guard !phoneNumbers.isEmpty else {
continue
}
uniqueIdToContactMap[contact.uniqueId] = contact
for phoneNumber in phoneNumbers {
phoneNumberToContactMap[phoneNumber] = contact
}
}
return ContactsMaps(uniqueIdToContactMap: uniqueIdToContactMap,
phoneNumberToContactMap: phoneNumberToContactMap)
}
fileprivate static func phoneNumbers(forContact contact: Contact, localNumber: String?) -> [String] {
return contact.parsedPhoneNumbers.compactMap { phoneNumber in
guard let phoneNumberE164 = phoneNumber.toE164().nilIfEmpty else {
return nil
}
// 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.
let isLocalContact = phoneNumberE164 == localNumber
guard !isLocalContact else {
return nil
}
return phoneNumberE164
}
}
@objc
public func isSystemContact(address: SignalServiceAddress) -> Bool {
guard let phoneNumber = address.phoneNumber?.nilIfEmpty else {
return false
}
return isSystemContact(phoneNumber: phoneNumber)
}
@objc
public func isSystemContact(phoneNumber: String) -> Bool {
phoneNumberToContactMap[phoneNumber] != nil
}
public func isEqualForCache(_ other: ContactsMaps) -> Bool {
let mapSelf = self.uniqueIdToContactMap
let mapOther = other.uniqueIdToContactMap
let keysSelf = Set(mapSelf.keys)
let keysOther = Set(mapOther.keys)
guard keysSelf == keysOther else {
return false
}
for key in keysSelf {
guard let valueSelf = mapSelf[key],
let valueOther = mapOther[key],
valueSelf.isEqualForCache(valueOther) else {
return false
}
}
return true
}
}
// MARK: -
@objc
public class ContactsStateInDatabase: NSObject, ContactsState {
@objc
public func unsortedSignalAccounts(transaction: SDSAnyReadTransaction) -> [SignalAccount] {
SignalAccount.anyFetchAll(transaction: transaction)
}
// Order respects the systems contact sorting preference.
@objc
public func sortedSignalAccounts(transaction: SDSAnyReadTransaction) -> [SignalAccount] {
contactsManagerImpl.sortSignalAccounts(unsortedSignalAccounts(transaction: transaction),
transaction: transaction)
}
@objc
public func setSortedSignalAccounts(_ signalAccounts: [SignalAccount]) {
// Ignore.
}
private static let uniqueIdStore = SDSKeyValueStore(collection: "ContactsState.uniqueIdStore")
private static let phoneNumberStore = SDSKeyValueStore(collection: "ContactsState.phoneNumberStore")
private static func serializeContact(_ contact: Contact, label: String) -> Data? {
let data = NSKeyedArchiver.archivedData(withRootObject: contact)
guard !data.isEmpty else {
owsFailDebug("Could not serialize contact: \(label).")
return nil
}
return data
}
private static func deserializeContact(data: Data, label: String) -> Contact? {
do {
guard let contact = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Contact else {
owsFailDebug("Invalid value: \(label).")
return nil
}
return contact
} catch {
owsFailDebug("Deserialize failed[\(label)]: \(error).")
return nil
}
}
@objc
public func contact(forPhoneNumber phoneNumber: String, transaction: SDSAnyReadTransaction) -> Contact? {
guard let data = Self.phoneNumberStore.getData(phoneNumber, transaction: transaction) else {
return nil
}
return Self.deserializeContact(data: data, label: phoneNumber)
}
@objc
public func allContacts(transaction: SDSAnyReadTransaction) -> [Contact] {
var contacts = [Contact]()
Self.uniqueIdStore.enumerateKeys(transaction: transaction) { (key, _) in
guard let data = Self.uniqueIdStore.getData(key, transaction: transaction) else {
owsFailDebug("Missing data for key: \(key).")
return
}
guard let contact = Self.deserializeContact(data: data, label: key) else {
return
}
contacts.append(contact)
}
return contacts
}
@objc
public func contactsMaps(transaction: SDSAnyReadTransaction) -> ContactsMaps {
let contacts = allContacts(transaction: transaction)
let localNumber: String? = tsAccountManager.localNumber(with: transaction)
return ContactsMaps.build(contacts: contacts, localNumber: localNumber)
}
@objc
public func setContactsMaps(_ newContactsMaps: ContactsMaps,
localNumber: String?,
transaction: SDSAnyWriteTransaction) {
let oldContactsMaps = self.contactsMaps(transaction: transaction)
if oldContactsMaps.isEqualForCache(newContactsMaps) {
Logger.verbose("Ignoring redundant contactsMap update.")
return
}
Self.uniqueIdStore.removeAll(transaction: transaction)
Self.phoneNumberStore.removeAll(transaction: transaction)
for contact in newContactsMaps.uniqueIdToContactMap.values {
let phoneNumbers = ContactsMaps.phoneNumbers(forContact: contact, localNumber: localNumber)
guard !phoneNumbers.isEmpty else {
continue
}
guard let contactData = Self.serializeContact(contact, label: contact.uniqueId) else {
continue
}
Self.uniqueIdStore.setData(contactData, key: contact.uniqueId, transaction: transaction)
for phoneNumber in phoneNumbers {
Self.phoneNumberStore.setData(contactData, key: phoneNumber, transaction: transaction)
}
}
}
@objc
public func warmCaches(transaction: SDSAnyReadTransaction) -> ContactsStateSummary {
// Only load the summary.
let contactCount = Self.uniqueIdStore.numberOfKeys(transaction: transaction)
let signalAccountCount = SignalAccount.anyCount(transaction: transaction)
return ContactsStateSummary(contactCount: contactCount, signalAccountCount: signalAccountCount)
}
}
// MARK: -
@objc
public class ContactsStateInMemory: NSObject, ContactsState {
private let unfairLock = UnfairLock()
private let contactsStateInDatabase = ContactsStateInDatabase()
private var sortedSignalAccountsCache: [SignalAccount]?
private var contactsMapsCache: ContactsMaps?
@objc
public func unsortedSignalAccounts(transaction: SDSAnyReadTransaction) -> [SignalAccount] {
// Prefer cache.
if let cachedValue = (unfairLock.withLock { sortedSignalAccountsCache }) {
return cachedValue
}
// Fail over.
// NOTE: We use the "sorted" getter because our cache should be sorted.
let result = contactsStateInDatabase.sortedSignalAccounts(transaction: transaction)
unfairLock.withLock { sortedSignalAccountsCache = result }
return result
}
// Order respects the systems contact sorting preference.
@objc
public func sortedSignalAccounts(transaction: SDSAnyReadTransaction) -> [SignalAccount] {
// Prefer cache.
if let cachedValue = (unfairLock.withLock { sortedSignalAccountsCache }) {
return cachedValue
}
// Fail over.
let result = contactsStateInDatabase.sortedSignalAccounts(transaction: transaction)
unfairLock.withLock { sortedSignalAccountsCache = result }
return result
}
@objc
public func setSortedSignalAccounts(_ signalAccounts: [SignalAccount]) {
unfairLock.withLock {
// Update cache.
sortedSignalAccountsCache = signalAccounts
}
return contactsStateInDatabase.setSortedSignalAccounts(signalAccounts)
}
@objc
public func contact(forPhoneNumber phoneNumber: String, transaction: SDSAnyReadTransaction) -> Contact? {
unfairLock.withLock {
guard let contactsMaps = contactsMapsCache else {
owsFailDebug("Missing contactsMaps.")
// Don't bother failing over to contactsStateInDatabase.
return nil
}
return contactsMaps.phoneNumberToContactMap[phoneNumber]
}
}
@objc
public func allContacts(transaction: SDSAnyReadTransaction) -> [Contact] {
unfairLock.withLock {
guard let contactsMaps = contactsMapsCache else {
owsFailDebug("Missing contactsMaps.")
// Don't bother failing over to contactsStateInDatabase.
return []
}
return Array(contactsMaps.phoneNumberToContactMap.values)
}
}
@objc
public func contactsMaps(transaction: SDSAnyReadTransaction) -> ContactsMaps {
unfairLock.withLock {
guard let contactsMaps = contactsMapsCache else {
owsFailDebug("Missing contactsMaps.")
// Don't bother failing over to contactsStateInDatabase.
return .empty
}
return contactsMaps
}
}
@objc
public func setContactsMaps(_ contactsMaps: ContactsMaps,
localNumber: String?,
transaction: SDSAnyWriteTransaction) {
unfairLock.withLock {
// Update cache.
contactsMapsCache = contactsMaps
}
return contactsStateInDatabase.setContactsMaps(contactsMaps,
localNumber: localNumber,
transaction: transaction)
}
@objc
public func warmCaches(transaction: SDSAnyReadTransaction) -> ContactsStateSummary {
let sortedSignalAccounts = contactsStateInDatabase.sortedSignalAccounts(transaction: transaction)
let contactsMaps = contactsStateInDatabase.contactsMaps(transaction: transaction)
unfairLock.withLock {
sortedSignalAccountsCache = sortedSignalAccounts
contactsMapsCache = contactsMaps
}
// Don't call contactsStateInDatabase.warmCaches().
return ContactsStateSummary(contactCount: UInt(contactsMaps.uniqueIdToContactMap.count),
signalAccountCount: UInt(sortedSignalAccounts.count))
}
}
// MARK: -
@objc
extension OWSContactsManager {
public func contact(forPhoneNumber phoneNumber: String, transaction: SDSAnyReadTransaction) -> Contact? {
contactsState.contact(forPhoneNumber: phoneNumber, transaction: transaction)
}
public func contactsMaps(transaction: SDSAnyReadTransaction) -> ContactsMaps {
contactsState.contactsMaps(transaction: transaction)
}
}

View File

@ -17,29 +17,27 @@ extern NSNotificationName const OWSContactsManagerContactsDidChangeNotification;
@class SignalServiceAddress;
@class UIFont;
@protocol ContactsState;
/**
* Get latest Signal contacts, and be notified when they change.
*/
@interface OWSContactsManager : NSObject <ContactsManagerProtocol>
@property (nonatomic, readonly) BOOL shouldSortByGivenName;
@property (nonatomic, readonly) id<ContactsState> contactsState;
#pragma mark - Accessors
@property (nonatomic, readonly) SDSKeyValueStore *keyValueStore;
@property (atomic, readonly) NSArray<Contact *> *allContacts;
@property (atomic, readonly) NSDictionary<NSString *, Contact *> *allContactsMap;
// order of the signalAccounts array respects the systems contact sorting preference
@property (atomic, readonly) NSArray<SignalAccount *> *signalAccounts;
// This will return an instance of SignalAccount for _known_ signal accounts.
- (nullable SignalAccount *)fetchSignalAccountForAddress:(SignalServiceAddress *)address;
- (nullable SignalAccount *)fetchSignalAccountForAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction;
- (nullable NSString *)nameFromSystemContactsForAddress:(SignalServiceAddress *)address;
- (nullable NSString *)nameFromSystemContactsForAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction;
@ -59,7 +57,7 @@ extern NSNotificationName const OWSContactsManagerContactsDidChangeNotification;
// Not set until a contact fetch has completed.
// Set even if no contacts are found.
@property (nonatomic, readonly) BOOL hasLoadedContacts;
@property (nonatomic, readonly) BOOL hasLoadedSystemContacts;
// Request systems contacts and start syncing changes. The user will see an alert
// if they haven't previously.
@ -77,14 +75,9 @@ extern NSNotificationName const OWSContactsManagerContactsDidChangeNotification;
#pragma mark - Util
- (BOOL)isSystemContactWithPhoneNumber:(NSString *)phoneNumber;
- (BOOL)isSystemContactWithAddress:(SignalServiceAddress *)address;
- (BOOL)hasNameInSystemContactsForAddress:(SignalServiceAddress *)address;
/**
* Used for sorting, respects system contacts name sort order preference.
*/
- (NSString *)comparableNameForSignalAccount:(SignalAccount *)signalAccount;
- (NSString *)comparableNameForSignalAccount:(SignalAccount *)signalAccount
transaction:(SDSAnyReadTransaction *)transaction;
- (NSString *)comparableNameForAddress:(SignalServiceAddress *)address transaction:(SDSAnyReadTransaction *)transaction;

View File

@ -31,11 +31,6 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
@interface OWSContactsManager () <SystemContactsFetcherDelegate>
@property (nonatomic) BOOL isContactsUpdateInFlight;
// This reflects the contents of the device phone book and includes
// contacts that do not correspond to any signal account.
@property (atomic) NSArray<Contact *> *allContacts;
@property (atomic) NSDictionary<NSString *, Contact *> *allContactsMap;
@property (atomic) NSArray<SignalAccount *> *signalAccounts;
@property (nonatomic, readonly) SystemContactsFetcher *systemContactsFetcher;
@property (nonatomic, readonly) AnyLRUCache *cnContactCache;
@ -56,14 +51,16 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
_keyValueStore = [[SDSKeyValueStore alloc] initWithCollection:OWSContactsManagerCollection];
_allContacts = @[];
_allContactsMap = @{};
_signalAccounts = @[];
_systemContactsFetcher = [SystemContactsFetcher new];
_systemContactsFetcher.delegate = self;
_cnContactCache = [[AnyLRUCache alloc] initWithMaxSize:50
nseMaxSize:0
shouldEvacuateInBackground:YES];
if (CurrentAppContext().isMainApp) {
_contactsState = [ContactsStateInMemory new];
} else {
_contactsState = [ContactsStateInDatabase new];
}
OWSSingletonAssert();
@ -75,19 +72,14 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
}
- (void)setup {
__block NSMutableArray<SignalAccount *> *signalAccounts;
[self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) {
NSUInteger signalAccountCount = [SignalAccount anyCountWithTransaction:transaction];
OWSLogInfo(@"loading %lu signal accounts from cache.", (unsigned long)signalAccountCount);
signalAccounts = [[NSMutableArray alloc] initWithCapacity:signalAccountCount];
[SignalAccount anyEnumerateWithTransaction:transaction
block:^(SignalAccount *signalAccount, BOOL *stop) {
[signalAccounts addObject:signalAccount];
}];
ContactsStateSummary *contactsStateSummary = [self.contactsState warmCachesWithTransaction:transaction];
OWSLogInfo(@"Loaded %lu contacts, %lu signal accounts from cache.",
(unsigned long)contactsStateSummary.contactCount,
(unsigned long)contactsStateSummary.signalAccountCount);
}];
[self updateSignalAccounts:signalAccounts shouldSetHasLoadedContacts:NO];
self.isSetup = YES;
}
#pragma mark - System Contact Fetching
@ -240,8 +232,6 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
{
OWSAssertDebug(contacts);
OWSAssertDebug(completion);
OWSAssertIsOnMainThread();
dispatch_async(self.intersectionQueue, ^{
__block BOOL isFullIntersection = YES;
@ -436,7 +426,8 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
// Retry with exponential backoff.
//
// TODO: Abort if another contact intersection succeeds in the meantime.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)),
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self intersectContacts:phoneNumbers
retryDelaySeconds:retryDelaySeconds * 2.0
success:successParameter
@ -445,7 +436,7 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
};
OWSContactDiscoveryTask *discoveryTask = [[OWSContactDiscoveryTask alloc] initWithPhoneNumbers:phoneNumbers];
[discoveryTask performAtQoS:QOS_CLASS_USER_INITIATED
callbackQueue:dispatch_get_main_queue()
callbackQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
success:success
failure:failure];
}
@ -456,107 +447,86 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
shouldClearStaleCache:(BOOL)shouldClearStaleCache
{
dispatch_async(self.intersectionQueue, ^{
NSMutableArray<Contact *> *allContacts = [contacts mutableCopy];
NSMutableDictionary<NSString *, Contact *> *allContactsMap = [NSMutableDictionary new];
for (Contact *contact in contacts) {
for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) {
NSString *phoneNumberE164 = phoneNumber.toE164;
__block ContactsMaps *contactsMaps;
DatabaseStorageWrite(self.databaseStorage, ^(SDSAnyWriteTransaction *transaction) {
NSString *_Nullable localNumber = [self.tsAccountManager localNumberWithTransaction:transaction];
contactsMaps = [ContactsMaps buildWithContacts:contacts localNumber:localNumber];
[self.contactsState setContactsMaps:contactsMaps localNumber:localNumber transaction:transaction];
});
// 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.
BOOL isLocalContact = [phoneNumberE164 isEqualToString:TSAccountManager.localNumber];
if (isLocalContact) {
[allContacts removeObject:contact];
} else if (phoneNumberE164.length > 0) {
allContactsMap[phoneNumberE164] = contact;
}
}
if (SSKDebugFlags.internalLogging) {
OWSLogInfo(@"Updating contacts: %lu, phoneNumberToContactMap: %lu",
(unsigned long)contactsMaps.uniqueIdToContactMap.count,
(unsigned long)contactsMaps.phoneNumberToContactMap.count);
}
NSArray<Contact *> *sortedContacts = [allContacts
sortedArrayUsingComparator:[Contact comparatorSortingNamesByFirstThenLast:self.shouldSortByGivenName]];
[self.cnContactCache removeAllObjects];
[[NSNotificationCenter defaultCenter] postNotificationNameAsync:OWSContactsManagerContactsDidChangeNotification
object:nil];
dispatch_async(dispatch_get_main_queue(), ^{
if (SSKDebugFlags.internalLogging) {
OWSLogInfo(@"sortedContacts: %lu, allContactsMap: %lu",
(unsigned long)sortedContacts.count,
(unsigned long)allContactsMap.count);
[self intersectContacts:contactsMaps.allContacts
isUserRequested:isUserRequested
completion:^(NSError *_Nullable error) {
if (error != nil) {
OWSFailDebug(@"Error: %@", error);
return;
}
self.allContacts = sortedContacts;
self.allContactsMap = [allContactsMap copy];
[self.cnContactCache removeAllObjects];
[[NSNotificationCenter defaultCenter]
postNotificationNameAsync:OWSContactsManagerContactsDidChangeNotification
object:nil];
[self intersectContacts:allContacts
isUserRequested:isUserRequested
completion:^(NSError *_Nullable error) {
if (error != nil) {
OWSFailDebug(@"Error: %@", error);
return;
}
[OWSContactsManager
buildSignalAccountsForContacts:sortedContacts
shouldClearStaleCache:shouldClearStaleCache
completion:^(NSArray<SignalAccount *> *signalAccounts) {
[self updateSignalAccounts:signalAccounts
shouldSetHasLoadedContacts:didLoad];
}];
}];
});
[OWSContactsManager buildSignalAccountsForContacts:contactsMaps.allContacts
shouldClearStaleCache:shouldClearStaleCache
completion:^(NSArray<SignalAccount *> *signalAccounts) {
[self updateSignalAccountsForSystemContactsFetch:signalAccounts
shouldSetHasLoadedSystemContacts:didLoad];
}];
}];
});
}
- (void)updateSignalAccounts:(NSArray<SignalAccount *> *)signalAccounts
shouldSetHasLoadedContacts:(BOOL)shouldSetHasLoadedContacts
- (void)updateSignalAccountsForSystemContactsFetch:(NSArray<SignalAccount *> *)newSignalAccounts
shouldSetHasLoadedSystemContacts:(BOOL)shouldSetHasLoadedSystemContacts
{
OWSAssertIsOnMainThread();
BOOL hadLoadedContacts = self.hasLoadedContacts;
if (shouldSetHasLoadedContacts) {
_hasLoadedContacts = YES;
BOOL hadLoadedContacts = self.hasLoadedSystemContacts;
if (shouldSetHasLoadedSystemContacts) {
_hasLoadedSystemContacts = YES;
}
if ([signalAccounts isEqual:self.signalAccounts]) {
__block NSArray<SignalAccount *> *oldSortedSignalAccounts;
__block NSArray<SignalAccount *> *newSortedSignalAccounts;
[self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) {
oldSortedSignalAccounts = [self sortedSignalAccountsWithTransaction:transaction];
newSortedSignalAccounts = [self sortSignalAccounts:newSignalAccounts transaction:transaction];
}];
if ([oldSortedSignalAccounts isEqual:newSortedSignalAccounts]) {
OWSLogDebug(@"SignalAccounts unchanged.");
self.isSetup = YES;
if (hadLoadedContacts != self.hasLoadedContacts) {
if (hadLoadedContacts != self.hasLoadedSystemContacts) {
[[NSNotificationCenter defaultCenter]
postNotificationNameAsync:OWSContactsManagerSignalAccountsDidChangeNotification
object:nil];
postNotificationNameAsync:OWSContactsManagerSignalAccountsDidChangeNotification
object:nil];
}
return;
}
NSMutableArray<SignalServiceAddress *> *allAddresses = [NSMutableArray new];
for (SignalAccount *signalAccount in signalAccounts) {
[allAddresses addObject:signalAccount.recipientAddress];
NSArray<SignalAccount *> *sortedSignalAccounts = [self sortSignalAccountsWithSneakyTransaction:newSignalAccounts];
[self.contactsState setSortedSignalAccounts:sortedSignalAccounts];
if (CurrentAppContext().isMainApp) {
// Ensure all system contacts are in the profile whitelist.
NSMutableArray<SignalServiceAddress *> *allAddresses = [NSMutableArray new];
for (SignalAccount *signalAccount in newSignalAccounts) {
[allAddresses addObject:signalAccount.recipientAddress];
}
[self.profileManagerImpl setContactAddresses:allAddresses];
}
self.signalAccounts = [self sortSignalAccountsWithSneakyTransaction:signalAccounts];
[self.profileManagerImpl setContactAddresses:allAddresses];
self.isSetup = YES;
[[NSNotificationCenter defaultCenter]
postNotificationNameAsync:OWSContactsManagerSignalAccountsDidChangeNotification
object:nil];
}
- (nullable NSString *)cachedContactNameForAddress:(SignalServiceAddress *)address
{
SignalAccount *_Nullable signalAccount = [self fetchSignalAccountForAddress:address];
return [self cachedContactNameForAddress:address signalAccount:signalAccount];
}
- (nullable NSString *)cachedContactNameForAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction
{
@ -570,30 +540,23 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
// if signalAccount is nil.
phoneNumber = [self phoneNumberForAddress:address transaction:transaction];
}
return [self cachedContactNameForAddress:address signalAccount:signalAccount phoneNumber:phoneNumber];
}
- (nullable NSString *)cachedContactNameForAddress:(SignalServiceAddress *)address
signalAccount:(nullable SignalAccount *)signalAccount
{
NSString *_Nullable phoneNumber = nil;
if (signalAccount == nil) {
// cachedContactNameForAddress only needs the phone number
// if signalAccount is nil.
phoneNumber = [self phoneNumberForAddress:address];
}
return [self cachedContactNameForAddress:address signalAccount:signalAccount phoneNumber:phoneNumber];
return [self cachedContactNameForAddress:address
signalAccount:signalAccount
phoneNumber:phoneNumber
transaction:transaction];
}
- (nullable NSString *)cachedContactNameForAddress:(SignalServiceAddress *)address
signalAccount:(nullable SignalAccount *)signalAccount
phoneNumber:(nullable NSString *)phoneNumber
transaction:(SDSAnyReadTransaction *)transaction
{
OWSAssertDebug(address);
if (!signalAccount) {
// search system contacts for no-longer-registered signal users, for which there will be no SignalAccount
Contact *_Nullable nonSignalContact = self.allContactsMap[phoneNumber];
Contact *_Nullable nonSignalContact = [self contactForPhoneNumber:phoneNumber
transaction:transaction];
if (!nonSignalContact) {
return nil;
}
@ -624,27 +587,19 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
phoneNumber = [self phoneNumberForAddress:address transaction:transaction];
}
return [self cachedContactNameComponentsForSignalAccount:signalAccount phoneNumber:phoneNumber];
}
- (nullable NSPersonNameComponents *)cachedContactNameComponentsForAddress:(SignalServiceAddress *)address
{
SignalAccount *_Nullable signalAccount = [self fetchSignalAccountForAddress:address];
NSString *_Nullable phoneNumber = nil;
if (signalAccount == nil) {
// We only need the phone number if signalAccount is nil.
phoneNumber = [self phoneNumberForAddress:address];
}
return [self cachedContactNameComponentsForSignalAccount:signalAccount phoneNumber:phoneNumber];
return [self cachedContactNameComponentsForSignalAccount:signalAccount
phoneNumber:phoneNumber
transaction:transaction];
}
- (nullable NSPersonNameComponents *)cachedContactNameComponentsForSignalAccount:(nullable SignalAccount *)signalAccount
phoneNumber:(nullable NSString *)phoneNumber
transaction:(SDSAnyReadTransaction *)transaction
{
if (!signalAccount) {
// search system contacts for no-longer-registered signal users, for which there will be no SignalAccount
Contact *_Nullable nonSignalContact = self.allContactsMap[phoneNumber];
Contact *_Nullable nonSignalContact = [self.contactsState contactForPhoneNumber:phoneNumber
transaction:transaction];
if (!nonSignalContact) {
return nil;
}
@ -688,20 +643,41 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
#pragma mark - Whisper User Management
- (BOOL)isSystemContactWithPhoneNumberWithSneakyTransaction:(NSString *)phoneNumber
{
__block BOOL result;
[self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) {
result = [self isSystemContactWithPhoneNumber:phoneNumber transaction:transaction];
}];
return result;
}
- (BOOL)isSystemContactWithPhoneNumber:(NSString *)phoneNumber
transaction:(SDSAnyReadTransaction *)transaction
{
OWSAssertDebug(phoneNumber.length > 0);
return self.allContactsMap[phoneNumber] != nil;
return [self.contactsState contactForPhoneNumber:phoneNumber
transaction:transaction] != nil;
}
- (BOOL)isSystemContactWithAddressWithSneakyTransaction:(SignalServiceAddress *)address
{
__block BOOL result;
[self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) {
result = [self isSystemContactWithAddress:address transaction:transaction];
}];
return result;
}
- (BOOL)isSystemContactWithAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction
{
NSString *phoneNumber = address.phoneNumber;
NSString *_Nullable phoneNumber = address.phoneNumber;
if (phoneNumber.length == 0) {
return NO;
}
return [self isSystemContactWithPhoneNumber:phoneNumber];
return [self isSystemContactWithPhoneNumber:phoneNumber transaction:transaction];
}
- (BOOL)isSystemContactWithSignalAccount:(SignalServiceAddress *)address
@ -719,11 +695,6 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
return [self hasSignalAccountForAddress:address transaction:transaction];
}
- (BOOL)hasNameInSystemContactsForAddress:(SignalServiceAddress *)address
{
return [self cachedContactNameForAddress:address].length > 0;
}
- (BOOL)hasNameInSystemContactsForAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction
{
@ -772,11 +743,6 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
@"UNKNOWN_CONTACT_NAME", @"Displayed if for some reason we can't determine a contacts phone number *or* name");
}
- (nullable NSString *)nameFromSystemContactsForAddress:(SignalServiceAddress *)address
{
return [self cachedContactNameForAddress:address];
}
- (nullable NSString *)nameFromSystemContactsForAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction
{
@ -823,7 +789,8 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
return self.unknownUserLabel;
}
// TODO: Remove?
- (NSString *)displayNameForAddress:(SignalServiceAddress *)address
{
OWSAssertDebug(address.isValid);
@ -842,13 +809,6 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
return NSLocalizedString(@"UNKNOWN_USER", @"Label indicating an unknown user.");
}
- (NSString *_Nonnull)displayNameForSignalAccount:(SignalAccount *)signalAccount
{
OWSAssertDebug(signalAccount);
return [self displayNameForAddress:signalAccount.recipientAddress];
}
- (NSString *)shortDisplayNameForAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction
{
@ -870,22 +830,6 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
return [OWSFormat formatNameComponentsShort:nameComponents];
}
- (nullable NSPersonNameComponents *)nameComponentsForAddress:(SignalServiceAddress *)address
{
OWSAssertDebug(address.isValid);
NSPersonNameComponents *_Nullable savedContactNameComponents = [self cachedContactNameComponentsForAddress:address];
if (savedContactNameComponents) {
return savedContactNameComponents;
}
__block NSPersonNameComponents *_Nullable profileNameComponents;
[self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) {
profileNameComponents = [self.profileManagerImpl nameComponentsForAddress:address transaction:transaction];
}];
return profileNameComponents;
}
- (nullable NSPersonNameComponents *)nameComponentsForAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction
{
@ -900,6 +844,7 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
return [self.profileManagerImpl nameComponentsForAddress:address transaction:transaction];
}
// TODO: Remove?
- (nullable SignalAccount *)fetchSignalAccountForAddress:(SignalServiceAddress *)address
{
OWSAssertDebug(address);
@ -939,6 +884,7 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
return [self fetchSignalAccountForAddress:address transaction:transaction] != nil;
}
// TODO: Remove?
- (nullable NSData *)profileImageDataForAddressWithSneakyTransaction:(nullable SignalServiceAddress *)address
{
if (address == nil) {
@ -981,38 +927,6 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
return contact.comparableNameLastFirst;
}
- (NSString *)comparableNameForSignalAccount:(SignalAccount *)signalAccount
{
NSString *_Nullable name = [self comparableNameForContact:signalAccount.contact];
if (name.length > 0) {
return name;
}
NSString *_Nullable phoneNumber = signalAccount.recipientPhoneNumber;
if (phoneNumber != nil) {
Contact *_Nullable contact = self.allContactsMap[phoneNumber];
NSString *_Nullable comparableContactName = [self comparableNameForContact:contact];
if (comparableContactName.length > 0) {
return comparableContactName;
}
}
__block NSPersonNameComponents *_Nullable nameComponents;
[self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) {
nameComponents = [self nameComponentsForAddress:signalAccount.recipientAddress transaction:transaction];
}];
if (nameComponents != nil && nameComponents.givenName.length > 0 && nameComponents.familyName.length > 0) {
NSString *leftName = self.shouldSortByGivenName ? nameComponents.givenName : nameComponents.familyName;
NSString *rightName = self.shouldSortByGivenName ? nameComponents.familyName : nameComponents.givenName;
return [NSString stringWithFormat:@"%@\t%@", leftName, rightName];
}
// Fall back to non-contact display name.
return [self displayNameForSignalAccount:signalAccount];
}
- (NSString *)comparableNameForSignalAccount:(SignalAccount *)signalAccount
transaction:(SDSAnyReadTransaction *)transaction
{
@ -1024,7 +938,7 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
NSString *_Nullable phoneNumber = signalAccount.recipientPhoneNumber;
if (phoneNumber != nil) {
Contact *_Nullable contact = self.allContactsMap[phoneNumber];
Contact *_Nullable contact = [self contactForPhoneNumber:phoneNumber transaction:transaction];
NSString *_Nullable comparableContactName = [self comparableNameForContact:contact];
if (comparableContactName.length > 0) {
return comparableContactName;
@ -1044,6 +958,7 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
return [self displayNameForAddress:signalAccount.recipientAddress transaction:transaction];
}
// TODO: Remove?
- (BOOL)isKnownRegisteredUserWithSneakyTransaction:(SignalServiceAddress *)address
{
__block BOOL result;

View File

@ -3,6 +3,7 @@
//
import Foundation
import SignalServiceKit
// TODO: Should we use these caches in NSE?
fileprivate extension OWSContactsManager {
@ -566,7 +567,7 @@ extension OWSContactsManager {
}
if let phoneNumber = self.phoneNumber(for: address, transaction: transaction),
let contact = self.allContactsMap[phoneNumber],
let contact = contactsState.contact(forPhoneNumber: phoneNumber, transaction: transaction),
let cnContactId = contact.cnContactId,
let avatarData = self.avatarData(forCNContactId: cnContactId),
let validData = validateIfNecessary(avatarData) {
@ -884,3 +885,93 @@ public extension Array where Element == SignalAccount {
}
}
}
// MARK: -
extension OWSContactsManager {
@objc
public func unsortedSignalAccounts(transaction: SDSAnyReadTransaction) -> [SignalAccount] {
contactsState.unsortedSignalAccounts(transaction: transaction)
}
@objc
public func unsortedSignalAccountsWithSneakyTransaction() -> [SignalAccount] {
databaseStorage.read { transaction in
unsortedSignalAccounts(transaction: transaction)
}
}
// Order respects the systems contact sorting preference.
@objc
public func sortedSignalAccounts(transaction: SDSAnyReadTransaction) -> [SignalAccount] {
contactsState.sortedSignalAccounts(transaction: transaction)
}
@objc
public func sortedSignalAccountsWithSneakyTransaction() -> [SignalAccount] {
databaseStorage.read { transaction in
sortedSignalAccounts(transaction: transaction)
}
}
private static func removeLocalContact(contacts: [Contact],
transaction: SDSAnyReadTransaction) -> [Contact] {
let localNumber: String? = tsAccountManager.localNumber(with: transaction)
var contactIdSet = Set<String>()
return contacts.compactMap { contact in
// Skip local contacts.
func isLocalContact() -> Bool {
for phoneNumber in contact.parsedPhoneNumbers {
if phoneNumber.toE164() == localNumber {
return true
}
}
return false
}
guard !isLocalContact() else {
return nil
}
// De-deduplicate.
guard !contactIdSet.contains(contact.uniqueId) else {
return nil
}
contactIdSet.insert(contact.uniqueId)
return contact
}
}
@objc
public func allUnsortedContacts(transaction: SDSAnyReadTransaction) -> [Contact] {
Self.removeLocalContact(contacts: contactsState.allContacts(transaction: transaction),
transaction: transaction)
}
@objc
public func allSortedContacts(transaction: SDSAnyReadTransaction) -> [Contact] {
let contacts = (allUnsortedContacts(transaction: transaction) as NSArray)
let comparator = Contact.comparatorSortingNames(byFirstThenLast: self.shouldSortByGivenName)
return contacts.sortedArray(options: [], usingComparator: comparator) as! [Contact]
}
@objc
public func comparableNameForSignalAccountWithSneakyTransaction(_ signalAccount: SignalAccount) -> String {
databaseStorage.read { transaction in
self.comparableName(for: signalAccount, transaction: transaction)
}
}
@objc
public func displayName(forSignalAccount signalAccount: SignalAccount,
transaction: SDSAnyReadTransaction) -> String {
self.displayName(for: signalAccount.recipientAddress, transaction: transaction)
}
@objc
public func displayNameForSignalAccountWithSneakyTransaction(_ signalAccount: SignalAccount) -> String {
databaseStorage.read { transaction in
self.displayName(forSignalAccount: signalAccount, transaction: transaction)
}
}
}

View File

@ -287,7 +287,8 @@ NSString *const kSyncManagerLastContactSyncKey = @"kTSStorageManagerOWSSyncManag
- (AnyPromise *)syncAllContacts
{
return [self syncContactsForSignalAccounts:self.contactsManager.signalAccounts
NSArray<SignalAccount *> *allSignalAccounts = [self.contactsManagerImpl unsortedSignalAccountsWithSneakyTransaction];
return [self syncContactsForSignalAccounts:allSignalAccounts
skipIfRedundant:NO
debounce:NO
isDurableSend:NO];
@ -301,7 +302,9 @@ NSString *const kSyncManagerLastContactSyncKey = @"kTSStorageManagerOWSSyncManag
- (void)sendSyncContactsMessageIfNecessary
{
OWSAssertDebug(self.tsAccountManager.isRegisteredPrimaryDevice);
[self syncContactsForSignalAccounts:self.contactsManager.signalAccounts
NSArray<SignalAccount *> *allSignalAccounts = [self.contactsManagerImpl unsortedSignalAccountsWithSneakyTransaction];
[self syncContactsForSignalAccounts:allSignalAccounts
skipIfRedundant:YES
debounce:YES
isDurableSend:NO];

View File

@ -33,6 +33,9 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly, nullable) NSString *cnContactId;
@property (nonatomic, readonly) BOOL isFromContactSync;
// This property should only be accessed from Swift.
@property (nonatomic, readonly) NSDictionary<NSString *, NSString *> *phoneNumberNameMap;
#if TARGET_OS_IOS
- (instancetype)initWithSystemContact:(CNContact *)cnContact;

View File

@ -14,14 +14,6 @@
NS_ASSUME_NONNULL_BEGIN
@interface Contact ()
@property (nonatomic, readonly) NSDictionary<NSString *, NSString *> *phoneNumberNameMap;
@end
#pragma mark -
@implementation Contact
@synthesize comparableNameFirstLast = _comparableNameFirstLast;

View File

@ -0,0 +1,38 @@
//
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
//
import Foundation
extension Contact {
public func isEqualForCache(_ other: Contact) -> Bool {
guard self.firstName == other.firstName,
self.lastName == other.lastName,
self.nickname == other.nickname,
self.fullName == other.fullName,
self.comparableNameFirstLast == other.comparableNameFirstLast,
self.comparableNameLastFirst == other.comparableNameLastFirst,
self.uniqueId == other.uniqueId,
self.cnContactId == other.cnContactId,
self.isFromContactSync == other.isFromContactSync,
self.phoneNumberNameMap == other.phoneNumberNameMap else {
return false
}
// Ignore ordering of these properties.
guard Set(self.userTextPhoneNumbers) == Set(other.userTextPhoneNumbers),
Set(self.emails) == Set(other.emails) else {
return false
}
// Ignore ordering of this property,
// Don't rely on equality of PhoneNumber.
let parsedPhoneNumbersSelf = Set(self.parsedPhoneNumbers.compactMap { $0.toE164() })
let parsedPhoneNumbersOther = Set(other.parsedPhoneNumbers.compactMap { $0.toE164() })
guard parsedPhoneNumbersSelf == parsedPhoneNumbersOther else {
return false
}
return true
}
}

View File

@ -155,7 +155,7 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value);
- (NSArray<NSString *> *)systemContactsWithSignalAccountPhoneNumbers;
- (NSArray<NSString *> *)systemContactsWithSignalAccountPhoneNumbersWithTransaction:
(SDSAnyReadTransaction *)transaction;
- (NSArray<NSString *> *)systemContactPhoneNumbers;
- (NSArray<NSString *> *)systemContactPhoneNumbersWithTransaction:(SDSAnyReadTransaction *)transaction;
- (NSArray<NSString *> *)e164PhoneNumbers NS_SWIFT_NAME(e164PhoneNumbers());
@end

View File

@ -561,12 +561,13 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
}]];
}
- (NSArray<NSString *> *)systemContactPhoneNumbers
- (NSArray<NSString *> *)systemContactPhoneNumbersWithTransaction:(SDSAnyReadTransaction *)transaction
{
return [self.e164PhoneNumbers
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *_Nullable recipientId,
NSDictionary<NSString *, id> *_Nullable bindings) {
return [OWSContact.contactsManager isSystemContactWithPhoneNumber:recipientId];
return [OWSContact.contactsManager isSystemContactWithPhoneNumber:recipientId
transaction:transaction];
}]];
}

View File

@ -616,7 +616,7 @@ NSString *const MessageSenderSpamChallengeResolvedException = @"SpamChallengeRes
});
OWSLogDebug(@"built message: %@ plainTextData.length: %lu", [message class], (unsigned long)plaintext.length);
// 2. gather "ud sending access" using a single write transaction.
// 2. Gather "ud sending access".
NSMutableDictionary<SignalServiceAddress *, OWSUDSendingAccess *> *sendingAccessMap = [NSMutableDictionary new];
if (senderCertificates != nil) {
for (SignalServiceAddress *address in addresses) {

View File

@ -493,7 +493,10 @@ public class OWSUDManagerImpl: NSObject, OWSUDManager {
case .everybody:
senderCertificate = senderCertificates.defaultCert
case .contactsOnly:
if Self.contactsManager.isSystemContact(address: address) {
let isSystemContact = databaseStorage.read { transaction in
Self.contactsManager.isSystemContact(address: address, transaction: transaction)
}
if isSystemContact {
senderCertificate = senderCertificates.defaultCert
} else {
senderCertificate = senderCertificates.uuidOnlyCert

View File

@ -25,7 +25,6 @@ NS_ASSUME_NONNULL_BEGIN
/// - The address' UUID
- (NSString *)displayNameForAddress:(SignalServiceAddress *)address;
- (NSString *)displayNameForAddress:(SignalServiceAddress *)address transaction:(SDSAnyReadTransaction *)transaction;
- (NSString *)displayNameForSignalAccount:(SignalAccount *)signalAccount;
/// Returns the user's nickname / first name, if supported by the name's locale.
/// If we don't know the user's name components, falls back to displayNameForAddress:
@ -41,7 +40,6 @@ NS_ASSUME_NONNULL_BEGIN
- (NSString *)shortDisplayNameForAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction;
- (nullable NSPersonNameComponents *)nameComponentsForAddress:(SignalServiceAddress *)address;
- (nullable NSPersonNameComponents *)nameComponentsForAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction;
@ -49,16 +47,18 @@ NS_ASSUME_NONNULL_BEGIN
- (NSString *)displayNameForThreadWithSneakyTransaction:(TSThread *)thread
NS_SWIFT_NAME(displayNameWithSneakyTransaction(thread:));
- (NSArray<SignalAccount *> *)signalAccounts;
- (BOOL)isSystemContactWithPhoneNumber:(NSString *)phoneNumber NS_SWIFT_NAME(isSystemContact(phoneNumber:));
- (BOOL)isSystemContactWithAddress:(SignalServiceAddress *)address NS_SWIFT_NAME(isSystemContact(address:));
- (BOOL)isSystemContactWithPhoneNumberWithSneakyTransaction:(NSString *)phoneNumber NS_SWIFT_NAME(isSystemContactWithSneakyTransaction(phoneNumber:));
- (BOOL)isSystemContactWithPhoneNumber:(NSString *)phoneNumber
transaction:(SDSAnyReadTransaction *)transaction NS_SWIFT_NAME(isSystemContact(phoneNumber:transaction:));
- (BOOL)isSystemContactWithAddressWithSneakyTransaction:(SignalServiceAddress *)address
NS_SWIFT_NAME(isSystemContactWithSneakyTransaction(address:));
- (BOOL)isSystemContactWithAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction NS_SWIFT_NAME(isSystemContact(address:transaction:));
- (BOOL)isSystemContactWithSignalAccount:(SignalServiceAddress *)address
NS_SWIFT_NAME(isSystemContactWithSignalAccount(_:));
- (BOOL)isSystemContactWithSignalAccount:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction
NS_SWIFT_NAME(isSystemContactWithSignalAccount(_:transaction:));
- (BOOL)hasNameInSystemContactsForAddress:(SignalServiceAddress *)address;
- (BOOL)hasNameInSystemContactsForAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction;

View File

@ -303,6 +303,8 @@ public class SDSKeyValueStore: NSObject {
write(codingValue, forKey: key, transaction: transaction)
}
// MARK: -
@objc
public func removeValue(forKey key: String, transaction: SDSAnyWriteTransaction) {
write(nil, forKey: key, transaction: transaction)

View File

@ -26,10 +26,6 @@ public class FakeContactsManager: NSObject, ContactsManagerProtocol {
return "Short fake name"
}
public func nameComponents(for address: SignalServiceAddress) -> PersonNameComponents? {
return PersonNameComponents()
}
public func nameComponents(for address: SignalServiceAddress, transaction: SDSAnyReadTransaction) -> PersonNameComponents? {
return PersonNameComponents()
}
@ -42,11 +38,19 @@ public class FakeContactsManager: NSObject, ContactsManagerProtocol {
return []
}
public func isSystemContact(phoneNumber: String) -> Bool {
public func isSystemContactWithSneakyTransaction(phoneNumber: String) -> Bool {
return true
}
public func isSystemContact(address: SignalServiceAddress) -> Bool {
public func isSystemContact(phoneNumber: String, transaction: SDSAnyReadTransaction) -> Bool {
return true
}
public func isSystemContactWithSneakyTransaction(address: SignalServiceAddress) -> Bool {
return true
}
public func isSystemContact(address: SignalServiceAddress, transaction: SDSAnyReadTransaction) -> Bool {
return true
}
@ -58,10 +62,6 @@ public class FakeContactsManager: NSObject, ContactsManagerProtocol {
return true
}
public func hasNameInSystemContacts(for address: SignalServiceAddress) -> Bool {
return true
}
public func hasNameInSystemContacts(for address: SignalServiceAddress, transaction: SDSAnyReadTransaction) -> Bool {
return true
}

View File

@ -117,7 +117,7 @@ public class LRUCache<KeyType: Hashable & Equatable, ValueType> {
return
}
guard maxSize > 0 else {
Logger.warn("Using disabled cache.")
Logger.verbose("Using disabled cache.")
return
}
cache.setObject(value as AnyObject, forKey: key as AnyObject)

View File

@ -19,8 +19,7 @@ public class LocalDevice: NSObject {
ProcessInfo.processInfo.activeProcessorCount
}
@objc
public static var memoryUsage: String {
public static var memoryUsageUInt64: UInt64? {
let vmInfoExpectedSize = MemoryLayout<task_vm_info_data_t>.size / MemoryLayout<natural_t>.size
var vmInfo = task_vm_info_data_t()
var vmInfoSize = mach_msg_type_number_t(vmInfoExpectedSize)
@ -37,9 +36,17 @@ public class LocalDevice: NSObject {
guard kern == KERN_SUCCESS else {
let errorString = String(cString: mach_error_string(kern), encoding: .ascii) ?? "Unknown error"
owsFailDebug(errorString)
return "Unknown"
return nil
}
return "\(vmInfo.phys_footprint)"
return vmInfo.phys_footprint
}
@objc
public static var memoryUsage: String {
guard let memoryUsageUInt64 = self.memoryUsageUInt64 else {
return "Unknown"
}
return "\(memoryUsageUInt64)"
}
}

View File

@ -2352,7 +2352,8 @@ public class GroupManager: NSObject {
}
let isLocalAddress = groupUpdateSourceAddress.isLocalAddress
let isSystemContact = contactsManager.isSystemContact(address: groupUpdateSourceAddress)
let isSystemContact = contactsManager.isSystemContact(address: groupUpdateSourceAddress,
transaction: transaction)
let isUserInProfileWhitelist = profileManager.isUser(inProfileWhitelist: groupUpdateSourceAddress,
transaction: transaction)
let shouldAddToWhitelist = (isLocalAddress || isSystemContact || isUserInProfileWhitelist)

View File

@ -237,9 +237,13 @@ class OWSUDManagerTest: SSKBaseTestSwift {
}
let completed = self.expectation(description: "completed")
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict).done { senderCertificates in
firstly {
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
}.done { senderCertificates in
do {
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress, requireSyncAccess: false, senderCertificates: senderCertificates)!
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
requireSyncAccess: false,
senderCertificates: senderCertificates)!
XCTAssertEqual(.unknown, sendingAccess.udAccess.udAccessMode)
XCTAssertFalse(sendingAccess.udAccess.isRandomKey)
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),

View File

@ -54,7 +54,7 @@ typedef void (^BlockAlertCompletionBlock)(ActionSheetAction *action);
fromViewController:(UIViewController *)fromViewController
completionBlock:(nullable BlockActionCompletionBlock)completionBlock
{
NSString *displayName = [self.contactsManager displayNameForSignalAccount:signalAccount];
NSString *displayName = [self.contactsManagerImpl displayNameForSignalAccountWithSneakyTransaction:signalAccount];
[self showBlockAddressesActionSheet:@[ signalAccount.recipientAddress ]
displayName:displayName
fromViewController:fromViewController
@ -289,7 +289,7 @@ typedef void (^BlockAlertCompletionBlock)(ActionSheetAction *action);
fromViewController:(UIViewController *)fromViewController
completionBlock:(nullable BlockActionCompletionBlock)completionBlock
{
NSString *displayName = [self.contactsManager displayNameForSignalAccount:signalAccount];
NSString *displayName = [self.contactsManagerImpl displayNameForSignalAccountWithSneakyTransaction:signalAccount];
[self showUnblockAddressesActionSheet:@[ signalAccount.recipientAddress ]
displayName:displayName
fromViewController:fromViewController

View File

@ -421,7 +421,7 @@ public class FullTextSearcher: NSObject {
if address.isLocalAddress {
return omitLocalUser
}
if self.contactsManager.isSystemContact(address: address) {
if self.contactsManager.isSystemContact(address: address, transaction: transaction) {
return false
}
guard let thread = TSContactThread.getWithContactAddress(address, transaction: transaction) else {

View File

@ -124,8 +124,8 @@ public class ContactShareViewModel: NSObject {
}
@objc
public func systemContactPhoneNumbers() -> [String] {
return dbRecord.systemContactPhoneNumbers()
public func systemContactPhoneNumbers(transaction: SDSAnyReadTransaction) -> [String] {
return dbRecord.systemContactPhoneNumbers(with: transaction)
}
@objc

View File

@ -8,6 +8,7 @@
#import <SignalCoreKit/NSString+OWS.h>
#import <SignalMessaging/Environment.h>
#import <SignalMessaging/OWSProfileManager.h>
#import <SignalMessaging/SignalMessaging-Swift.h>
#import <SignalServiceKit/AppContext.h>
#import <SignalServiceKit/Contact.h>
#import <SignalServiceKit/PhoneNumber.h>
@ -217,7 +218,7 @@ NS_ASSUME_NONNULL_BEGIN
{
OWSAssertDebug(!CurrentAppContext().isNSE);
return self.contactsManagerImpl.hasLoadedContacts;
return self.contactsManagerImpl.hasLoadedSystemContacts;
}
- (void)updateContacts
@ -227,24 +228,27 @@ NS_ASSUME_NONNULL_BEGIN
NSMutableDictionary<NSString *, SignalAccount *> *phoneNumberSignalAccountMap = [NSMutableDictionary new];
NSMutableDictionary<NSUUID *, SignalAccount *> *uuidSignalAccountMap = [NSMutableDictionary new];
NSMutableArray<SignalAccount *> *signalAccounts = [NSMutableArray new];
NSMutableArray<SignalAccount *> *accountsToProcess = [self.contactsManager.signalAccounts mutableCopy];
__block NSArray<SignalAccount *> *allSignalAccounts;
__block NSArray<SignalServiceAddress *> *whitelistedAddresses;
__block ContactsMaps *contactsMaps;
[self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) {
allSignalAccounts = [self.contactsManagerImpl unsortedSignalAccountsWithTransaction:transaction];
whitelistedAddresses = [self.profileManagerImpl allWhitelistedRegisteredAddressesWithTransaction:transaction];
contactsMaps = [self.contactsManagerImpl contactsMapsWithTransaction:transaction];
}];
NSMutableArray<SignalAccount *> *accountsToProcess = [allSignalAccounts mutableCopy];
for (SignalServiceAddress *address in whitelistedAddresses) {
if ([self.contactsManager isSystemContactWithAddress:address]) {
if ([contactsMaps isSystemContactWithAddress:address]) {
continue;
}
[accountsToProcess addObject:[[SignalAccount alloc] initWithSignalServiceAddress:address]];
}
NSMutableSet<SignalServiceAddress *> *addressSet = [NSMutableSet new];
NSMutableArray<SignalAccount *> *signalAccounts = [NSMutableArray new];
for (SignalAccount *signalAccount in accountsToProcess) {
if ([addressSet containsObject:signalAccount.recipientAddress]) {
OWSLogVerbose(@"Ignoring duplicate: %@", signalAccount.recipientAddress);
@ -361,18 +365,18 @@ NS_ASSUME_NONNULL_BEGIN
NSMutableSet<Contact *> *nonSignalContactSet = [NSMutableSet new];
__block NSArray<Contact *> *nonSignalContacts;
[self.databaseStorage
asyncReadWithBlock:^(SDSAnyReadTransaction *transaction) {
for (Contact *contact in self.contactsManagerImpl.allContactsMap.allValues) {
NSArray<SignalRecipient *> *signalRecipients = [contact signalRecipientsWithTransaction:transaction];
if (signalRecipients.count < 1) {
[nonSignalContactSet addObject:contact];
}
[self.databaseStorage asyncReadWithBlock:^(SDSAnyReadTransaction *transaction) {
NSArray<Contact *> *contacts = [self.contactsManagerImpl allSortedContactsWithTransaction:transaction];
for (Contact *contact in contacts) {
NSArray<SignalRecipient *> *signalRecipients = [contact signalRecipientsWithTransaction:transaction];
if (signalRecipients.count < 1) {
[nonSignalContactSet addObject:contact];
}
nonSignalContacts = [nonSignalContactSet.allObjects sortedArrayUsingComparator:^NSComparisonResult(
Contact *_Nonnull left, Contact *_Nonnull right) { return [left.fullName compare:right.fullName]; }];
}
completion:^{ self.nonSignalContacts = nonSignalContacts; }];
nonSignalContacts = [nonSignalContactSet.allObjects sortedArrayUsingComparator:^NSComparisonResult(
Contact *_Nonnull left, Contact *_Nonnull right) { return [left.fullName compare:right.fullName]; }];
}
completion:^{ self.nonSignalContacts = nonSignalContacts; }];
}
- (nullable NSArray<Contact *> *)nonSignalContacts
@ -383,7 +387,8 @@ NS_ASSUME_NONNULL_BEGIN
if (!_nonSignalContacts) {
NSMutableSet<Contact *> *nonSignalContacts = [NSMutableSet new];
[self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) {
for (Contact *contact in self.contactsManagerImpl.allContactsMap.allValues) {
NSArray<Contact *> *contacts = [self.contactsManagerImpl allSortedContactsWithTransaction:transaction];
for (Contact *contact in contacts) {
NSArray<SignalRecipient *> *signalRecipients = [contact signalRecipientsWithTransaction:transaction];
if (signalRecipients.count < 1) {
[nonSignalContacts addObject:contact];