Merge branch 'charlesmchen/accountAndAddressSorting' into release/3.14.0

This commit is contained in:
Matthew Chen 2020-08-07 17:33:31 -03:00
commit 09bb503eb1
7 changed files with 122 additions and 73 deletions

View File

@ -214,6 +214,7 @@
3496957221A301A100DCFE74 /* OWSBackup.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496956921A301A100DCFE74 /* OWSBackup.m */; };
3496957321A301A100DCFE74 /* OWSBackupJob.m in Sources */ = {isa = PBXBuildFile; fileRef = 3496956A21A301A100DCFE74 /* OWSBackupJob.m */; };
3496957421A301A100DCFE74 /* OWSBackupAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496956B21A301A100DCFE74 /* OWSBackupAPI.swift */; };
3498A0A624DC81E100CA492C /* OWSContactsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3498A0A524DC81E100CA492C /* OWSContactsManager.swift */; };
34995F1B2411838D00C70546 /* NewGroupMembersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34995F172411838C00C70546 /* NewGroupMembersViewController.swift */; };
3499998122EF1E2100654932 /* SearcherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3499997E22EF1E2100654932 /* SearcherTest.swift */; };
3499998222EF1E2100654932 /* GRDBFullTextSearcherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3499997F22EF1E2100654932 /* GRDBFullTextSearcherTest.swift */; };
@ -1125,6 +1126,7 @@
3496956B21A301A100DCFE74 /* OWSBackupAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSBackupAPI.swift; sourceTree = "<group>"; };
3496956C21A301A100DCFE74 /* OWSBackupImportJob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackupImportJob.h; sourceTree = "<group>"; };
3496956D21A301A100DCFE74 /* OWSBackupIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackupIO.h; sourceTree = "<group>"; };
3498A0A524DC81E100CA492C /* OWSContactsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSContactsManager.swift; sourceTree = "<group>"; };
34995F172411838C00C70546 /* NewGroupMembersViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewGroupMembersViewController.swift; sourceTree = "<group>"; };
3499997E22EF1E2100654932 /* SearcherTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearcherTest.swift; sourceTree = "<group>"; };
3499997F22EF1E2100654932 /* GRDBFullTextSearcherTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GRDBFullTextSearcherTest.swift; sourceTree = "<group>"; };
@ -2318,15 +2320,16 @@
346129A11FD1F09100532771 /* contacts */ = {
isa = PBXGroup;
children = (
4C9D347E23689E06006A4307 /* IncomingContactSyncJobQueue.swift */,
4C6E446822AEDDEE007982E6 /* NewAccountDiscovery.swift */,
346129A21FD1F09100532771 /* OWSContactsManager.h */,
346129A31FD1F09100532771 /* OWSContactsManager.m */,
3498A0A524DC81E100CA492C /* OWSContactsManager.swift */,
4C046AA6236148880035B234 /* OWSGroupSyncProcessingJobQueue.swift */,
34612A041FD7238500532771 /* OWSSyncManager.h */,
34612A051FD7238500532771 /* OWSSyncManager.m */,
885C354F2370DFD50004BA35 /* OWSSyncManager.swift */,
346129AE1FD1F5D900532771 /* SystemContactsFetcher.swift */,
4C6E446822AEDDEE007982E6 /* NewAccountDiscovery.swift */,
4C046AA6236148880035B234 /* OWSGroupSyncProcessingJobQueue.swift */,
4C9D347E23689E06006A4307 /* IncomingContactSyncJobQueue.swift */,
);
path = contacts;
sourceTree = "<group>";
@ -4611,6 +4614,7 @@
34AC0A18211B39EA00997B47 /* TappableStackView.swift in Sources */,
346129A61FD1F09100532771 /* OWSContactsManager.m in Sources */,
349C3637233D198300D52012 /* LaunchJobs.swift in Sources */,
3498A0A624DC81E100CA492C /* OWSContactsManager.swift in Sources */,
8827004C232071C500F01C46 /* OWSWindow.swift in Sources */,
8809CE8122F534B200D38867 /* CustomKeyboard.swift in Sources */,
4541B71D209D3B7A0008608F /* ContactShareViewModel.swift in Sources */,

View File

@ -26,6 +26,8 @@ NS_ASSUME_NONNULL_BEGIN
@class CNContactViewController;
@class SignalServiceAddress;
// TODO: This class does some expensive work and isn't stateful.
// It probably makes sense to make it a singleton.
@interface ContactsViewHelper : NSObject
@property (nonatomic, readonly, weak) id<ContactsViewHelperDelegate> delegate;
@ -55,7 +57,6 @@ NS_ASSUME_NONNULL_BEGIN
// is only safe to be called on the main thread.
- (BOOL)isThreadBlocked:(TSThread *)thread;
// NOTE: This method uses a transaction.
- (SignalServiceAddress *)localAddress;
- (NSArray<SignalAccount *> *)signalAccountsMatchingSearchString:(NSString *)searchText

View File

@ -252,7 +252,7 @@ NS_ASSUME_NONNULL_BEGIN
self.phoneNumberSignalAccountMap = [phoneNumberSignalAccountMap copy];
self.uuidSignalAccountMap = [uuidSignalAccountMap copy];
self.signalAccounts = [signalAccounts sortedArrayUsingComparator:self.contactsManager.signalAccountComparator];
self.signalAccounts = [self.contactsManager sortSignalAccountsWithSneakyTransaction:signalAccounts];
self.nonSignalContacts = nil;
// Don't fire delegate "change" events during initialization.

View File

@ -87,7 +87,6 @@ extern NSString *const OWSContactsManagerSignalAccountsDidChangeNotification;
/**
* Used for sorting, respects system contacts name sort order preference.
*/
- (NSComparisonResult (^)(SignalAccount *left, SignalAccount *right))signalAccountComparator;
- (NSString *)comparableNameForSignalAccount:(SignalAccount *)signalAccount;
- (NSString *)comparableNameForSignalAccount:(SignalAccount *)signalAccount
transaction:(SDSAnyReadTransaction *)transaction;

View File

@ -743,7 +743,7 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
[allAddresses addObject:signalAccount.recipientAddress];
}
self.signalAccounts = [signalAccounts sortedArrayUsingComparator:self.signalAccountComparator];
self.signalAccounts = [self sortSignalAccountsWithSneakyTransaction:signalAccounts];
[self.profileManager setContactAddresses:allAddresses];
@ -1292,66 +1292,6 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
return [self.profileManager profileAvatarForAddress:address transaction:transaction];
}
- (NSComparisonResult)compareSignalAccount:(SignalAccount *)left withSignalAccount:(SignalAccount *)right
{
return self.signalAccountComparator(left, right);
}
- (NSComparisonResult (^)(SignalAccount *left, SignalAccount *right))signalAccountComparator
{
return ^NSComparisonResult(SignalAccount *left, SignalAccount *right) {
NSString *leftName = [self comparableNameForSignalAccount:left];
NSString *rightName = [self comparableNameForSignalAccount:right];
NSComparisonResult nameComparison = [leftName caseInsensitiveCompare:rightName];
if (nameComparison == NSOrderedSame) {
return [left.recipientAddress.stringForDisplay compare:right.recipientAddress.stringForDisplay];
}
return nameComparison;
};
}
- (NSArray<SignalServiceAddress *> *)sortSignalServiceAddresses:(NSArray<SignalServiceAddress *> *)addresses
transaction:(SDSAnyReadTransaction *)transaction
{
return [addresses sortedArrayUsingComparator:[self signalServiceAddressComparatorWithTransaction:transaction]];
}
- (NSComparisonResult (^)(SignalServiceAddress *left,
SignalServiceAddress *right))signalServiceAddressComparatorWithTransaction:(SDSAnyReadTransaction *)transaction
{
// We want to sort E164 phone numbers _after_ names.
NSCharacterSet *e164PrefixSet = [NSCharacterSet characterSetWithCharactersInString:@"+"];
BOOL (^hasE164Prefix)(NSString *) = ^(NSString *string) {
if (string.length < 1) {
return NO;
}
unichar firstChar = [string characterAtIndex:0];
return [e164PrefixSet characterIsMember:firstChar];
};
return ^NSComparisonResult(SignalServiceAddress *left, SignalServiceAddress *right) {
NSString *leftName = [self comparableNameForAddress:left transaction:transaction];
NSString *rightName = [self comparableNameForAddress:right transaction:transaction];
BOOL leftHasE164Prefix = hasE164Prefix(leftName);
BOOL rightHasE164Prefix = hasE164Prefix(rightName);
if (leftHasE164Prefix && !rightHasE164Prefix) {
return NSOrderedDescending;
} else if (!leftHasE164Prefix && rightHasE164Prefix) {
return NSOrderedAscending;
}
NSComparisonResult nameComparison = [leftName caseInsensitiveCompare:rightName];
if (nameComparison == NSOrderedSame) {
return [left.stringForDisplay compare:right.stringForDisplay];
}
return nameComparison;
};
}
- (BOOL)shouldSortByGivenName
{
return [[CNContactsUserDefaults sharedDefaults] sortOrder] == CNContactSortOrderGivenName;

View File

@ -0,0 +1,111 @@
//
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
//
import Foundation
@objc
public extension OWSContactsManager {
// MARK: - Dependencies
private var databaseStorage: SDSDatabaseStorage {
return SDSDatabaseStorage.shared
}
// MARK: -
func sortSignalAccountsWithSneakyTransaction(_ signalAccounts: [SignalAccount]) -> [SignalAccount] {
databaseStorage.read { transaction in
self.sortSignalAccounts(signalAccounts, transaction: transaction)
}
}
func sortSignalAccounts(_ signalAccounts: [SignalAccount],
transaction: SDSAnyReadTransaction) -> [SignalAccount] {
typealias SortableAccount = SortableValue<SignalAccount>
let sortableAccounts = signalAccounts.map { signalAccount in
return SortableAccount(value: signalAccount,
comparableName: self.comparableName(for: signalAccount,
transaction: transaction),
comparableAddress: signalAccount.recipientAddress.stringForDisplay)
}
return sort(sortableAccounts)
}
// MARK: -
func sortSignalServiceAddressesWithSneakyTransaction(_ addresses: [SignalServiceAddress]) -> [SignalServiceAddress] {
databaseStorage.read { transaction in
self.sortSignalServiceAddresses(addresses, transaction: transaction)
}
}
func sortSignalServiceAddresses(_ addresses: [SignalServiceAddress],
transaction: SDSAnyReadTransaction) -> [SignalServiceAddress] {
typealias SortableAddress = SortableValue<SignalServiceAddress>
let sortableAddresses = addresses.map { address in
return SortableAddress(value: address,
comparableName: self.comparableName(for: address,
transaction: transaction),
comparableAddress: address.stringForDisplay)
}
return sort(sortableAddresses)
}
}
// MARK: -
private struct SortableValue<T> {
let value: T
let comparableName: String
let comparableAddress: String
init (value: T, comparableName: String, comparableAddress: String) {
self.value = value
self.comparableName = comparableName.lowercased()
self.comparableAddress = comparableAddress.lowercased()
}
}
// MARK: -
extension SortableValue: Equatable, Comparable {
// MARK: - Equatable
public static func == (lhs: SortableValue<T>, rhs: SortableValue<T>) -> Bool {
return (lhs.comparableName == rhs.comparableName &&
lhs.comparableAddress == rhs.comparableAddress)
}
// MARK: - Comparable
public static func < (left: SortableValue<T>, right: SortableValue<T>) -> Bool {
// We want to sort E164 phone numbers _after_ names.
let leftHasE164Prefix = left.comparableName.hasPrefix("+")
let rightHasE164Prefix = right.comparableName.hasPrefix("+")
if leftHasE164Prefix && !rightHasE164Prefix {
return false
} else if !leftHasE164Prefix && rightHasE164Prefix {
return true
}
if left.comparableName != right.comparableName {
return left.comparableName < right.comparableName
} else {
return left.comparableAddress < right.comparableAddress
}
}
}
// MARK: -
fileprivate extension OWSContactsManager {
func sort<T>(_ values: [SortableValue<T>]) -> [T] {
return values.sorted().map { $0.value }
}
}

View File

@ -60,12 +60,6 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)isSystemContactWithSignalAccount:(NSString *)phoneNumber;
- (BOOL)hasNameInSystemContactsForAddress:(SignalServiceAddress *)address;
- (NSArray<SignalServiceAddress *> *)sortSignalServiceAddresses:(NSArray<SignalServiceAddress *> *)addresses
transaction:(SDSAnyReadTransaction *)transaction;
- (NSComparisonResult)compareSignalAccount:(SignalAccount *)left
withSignalAccount:(SignalAccount *)right NS_SWIFT_NAME(compare(signalAccount:with:));
- (NSString *)comparableNameForSignalAccount:(SignalAccount *)signalAccount
transaction:(SDSAnyReadTransaction *)transaction;