You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-ios/Signal/src/ViewControllers/ContactsViewHelper.m

283 lines
8.5 KiB
Matlab

//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
#import "ContactsViewHelper.h"
#import "ContactTableViewCell.h"
#import "Environment.h"
#import <SignalServiceKit/Contact.h>
#import <SignalServiceKit/OWSBlockingManager.h>
#import <SignalServiceKit/PhoneNumber.h>
#import <SignalServiceKit/SignalAccount.h>
#import <SignalServiceKit/TSAccountManager.h>
NS_ASSUME_NONNULL_BEGIN
@interface ContactsViewHelper ()
// This property is a cached value that is lazy-populated.
@property (nonatomic, nullable) NSArray<Contact *> *nonSignalContacts;
@property (nonatomic) NSDictionary<NSString *, SignalAccount *> *signalAccountMap;
@property (nonatomic) NSArray<SignalAccount *> *signalAccounts;
@property (nonatomic) NSArray<NSString *> *blockedPhoneNumbers;
@end
#pragma mark -
@implementation ContactsViewHelper
- (instancetype)init
{
self = [super init];
if (!self) {
return self;
}
_blockingManager = [OWSBlockingManager sharedManager];
_blockedPhoneNumbers = [_blockingManager blockedPhoneNumbers];
_contactsManager = [Environment getCurrent].contactsManager;
self.signalAccountMap = self.contactsManager.signalAccountMap;
self.signalAccounts = self.contactsManager.signalAccounts;
[self observeNotifications];
return self;
}
- (void)observeNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(signalAccountsDidChange:)
name:OWSContactsManagerSignalAccountsDidChangeNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(blockedPhoneNumbersDidChange:)
name:kNSNotificationName_BlockedPhoneNumbersDidChange
object:nil];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)signalAccountsDidChange:(NSNotification *)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
[self updateContacts];
});
}
- (void)blockedPhoneNumbersDidChange:(id)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
self.blockedPhoneNumbers = [_blockingManager blockedPhoneNumbers];
[self updateContacts];
});
}
#pragma mark - Contacts
- (nullable SignalAccount *)signalAccountForRecipientId:(NSString *)recipientId
{
OWSAssert([NSThread isMainThread]);
OWSAssert(recipientId.length > 0);
return self.signalAccountMap[recipientId];
}
- (BOOL)isSignalAccountHidden:(SignalAccount *)signalAccount
{
OWSAssert([NSThread isMainThread]);
if ([self.delegate shouldHideLocalNumber] && [self isCurrentUser:signalAccount]) {
return YES;
}
return NO;
}
- (BOOL)isCurrentUser:(SignalAccount *)signalAccount
{
OWSAssert([NSThread isMainThread]);
NSString *localNumber = [TSAccountManager localNumber];
if ([signalAccount.recipientId isEqualToString:localNumber]) {
return YES;
}
for (PhoneNumber *phoneNumber in signalAccount.contact.parsedPhoneNumbers) {
if ([[phoneNumber toE164] isEqualToString:localNumber]) {
return YES;
}
}
return NO;
}
- (NSString *)localNumber
{
return [TSAccountManager localNumber];
}
- (BOOL)isRecipientIdBlocked:(NSString *)recipientId
{
AssertIsOnMainThread();
return [_blockedPhoneNumbers containsObject:recipientId];
}
- (void)updateContacts
{
AssertIsOnMainThread();
NSMutableDictionary<NSString *, SignalAccount *> *signalAccountMap = [NSMutableDictionary new];
NSMutableArray<SignalAccount *> *signalAccounts = [NSMutableArray new];
for (SignalAccount *signalAccount in self.contactsManager.signalAccounts) {
if (![self isSignalAccountHidden:signalAccount]) {
signalAccountMap[signalAccount.recipientId] = signalAccount;
[signalAccounts addObject:signalAccount];
}
}
self.signalAccountMap = [signalAccountMap copy];
self.signalAccounts = [signalAccounts copy];
self.nonSignalContacts = nil;
[self.delegate contactsViewHelperDidUpdateContacts];
}
- (BOOL)doesSignalAccount:(SignalAccount *)signalAccount matchSearchTerm:(NSString *)searchTerm
{
OWSAssert(signalAccount);
OWSAssert(searchTerm.length > 0);
if ([signalAccount.contact.fullName.lowercaseString containsString:searchTerm.lowercaseString]) {
return YES;
}
NSString *asPhoneNumber = [PhoneNumber removeFormattingCharacters:searchTerm];
if (asPhoneNumber.length > 0 && [signalAccount.recipientId containsString:asPhoneNumber]) {
return YES;
}
return NO;
}
- (BOOL)doesSignalAccount:(SignalAccount *)signalAccount matchSearchTerms:(NSArray<NSString *> *)searchTerms
{
OWSAssert(signalAccount);
OWSAssert(searchTerms.count > 0);
for (NSString *searchTerm in searchTerms) {
if (![self doesSignalAccount:signalAccount matchSearchTerm:searchTerm]) {
return NO;
}
}
return YES;
}
- (NSArray<NSString *> *)searchTermsForSearchString:(NSString *)searchText
{
return [[[searchText stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]
componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *_Nullable searchTerm,
NSDictionary<NSString *, id> *_Nullable bindings) {
return searchTerm.length > 0;
}]];
}
- (NSArray<SignalAccount *> *)signalAccountsMatchingSearchString:(NSString *)searchText
{
NSArray<NSString *> *searchTerms = [self searchTermsForSearchString:searchText];
if (searchTerms.count < 1) {
return self.signalAccounts;
}
return [self.signalAccounts
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(SignalAccount *signalAccount,
NSDictionary<NSString *, id> *_Nullable bindings) {
return [self doesSignalAccount:signalAccount matchSearchTerms:searchTerms];
}]];
}
- (BOOL)doesContact:(Contact *)contact matchSearchTerm:(NSString *)searchTerm
{
OWSAssert(contact);
OWSAssert(searchTerm.length > 0);
if ([contact.fullName.lowercaseString containsString:searchTerm.lowercaseString]) {
return YES;
}
NSString *asPhoneNumber = [PhoneNumber removeFormattingCharacters:searchTerm];
if (asPhoneNumber.length > 0) {
for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) {
if ([phoneNumber.toE164 containsString:asPhoneNumber]) {
return YES;
}
}
}
return NO;
}
- (BOOL)doesContact:(Contact *)contact matchSearchTerms:(NSArray<NSString *> *)searchTerms
{
OWSAssert(contact);
OWSAssert(searchTerms.count > 0);
for (NSString *searchTerm in searchTerms) {
if (![self doesContact:contact matchSearchTerm:searchTerm]) {
return NO;
}
}
return YES;
}
- (NSArray<Contact *> *)nonSignalContactsMatchingSearchString:(NSString *)searchText
{
NSArray<NSString *> *searchTerms = [self searchTermsForSearchString:searchText];
if (searchTerms.count < 1) {
return [NSArray new];
}
return [self.nonSignalContacts filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(Contact *contact,
NSDictionary<NSString *, id> *_Nullable bindings) {
return [self doesContact:contact matchSearchTerms:searchTerms];
}]];
}
- (nullable NSArray<Contact *> *)nonSignalContacts
{
if (!_nonSignalContacts) {
NSMutableSet<Contact *> *nonSignalContacts = [NSMutableSet new];
[[TSStorageManager sharedManager].dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
for (Contact *contact in self.contactsManager.allContactsMap.allValues) {
NSArray<SignalRecipient *> *signalRecipients = [contact signalRecipientsWithTransaction:transaction];
if (signalRecipients.count < 1) {
[nonSignalContacts addObject:contact];
}
}
}];
_nonSignalContacts = [nonSignalContacts.allObjects
sortedArrayUsingComparator:^NSComparisonResult(Contact *_Nonnull left, Contact *_Nonnull right) {
return [left.fullName compare:right.fullName];
}];
}
return _nonSignalContacts;
}
@end
NS_ASSUME_NONNULL_END