diff --git a/Signal/src/ViewControllers/OWSTableViewController.m b/Signal/src/ViewControllers/OWSTableViewController.m index 018b0256e..1ad21c1fd 100644 --- a/Signal/src/ViewControllers/OWSTableViewController.m +++ b/Signal/src/ViewControllers/OWSTableViewController.m @@ -277,6 +277,7 @@ NSString * const kOWSTableCellIdentifier = @"kOWSTableCellIdentifier"; - (void)setContents:(OWSTableContents *)contents { OWSAssert(contents); + AssertIsOnMainThread(); _contents = contents; diff --git a/Signal/src/ViewControllers/ShowGroupMembersViewController.m b/Signal/src/ViewControllers/ShowGroupMembersViewController.m index b827b79f8..dd8511c84 100644 --- a/Signal/src/ViewControllers/ShowGroupMembersViewController.m +++ b/Signal/src/ViewControllers/ShowGroupMembersViewController.m @@ -18,9 +18,11 @@ #import #import +@import ContactsUI; + NS_ASSUME_NONNULL_BEGIN -@interface ShowGroupMembersViewController () +@interface ShowGroupMembersViewController () @property (nonatomic, readonly) TSGroupThread *thread; @property (nonatomic, readonly) ContactsViewHelper *contactsViewHelper; @@ -78,13 +80,25 @@ NS_ASSUME_NONNULL_BEGIN - (void)viewDidLoad { [super viewDidLoad]; - [self.navigationController.navigationBar setTranslucent:NO]; + + // HACK otherwise CNContactViewController Navbar is shown as black. + // RADAR rdar://28433898 http://www.openradar.me/28433898 + // CNContactViewController incompatible with opaque navigation bar + [self.navigationController.navigationBar setTranslucent:YES]; self.title = _thread.groupModel.groupName; [self updateTableContents]; } +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + // In case we're dismissing a CNContactViewController which requires default system appearance + [UIUtil applySignalAppearence]; +} + #pragma mark - Table Contents - (void)updateTableContents @@ -143,13 +157,15 @@ NS_ASSUME_NONNULL_BEGIN UIAlertController *actionSheetController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - [actionSheetController - addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"GROUP_MEMBERS_VIEW_CONTACT_INFO", - @"Button label for the 'show contact info' button") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [self showContactInfoViewForRecipientId:recipientId]; - }]]; + NSString *contactInfoTitle = signalAccount + ? NSLocalizedString(@"GROUP_MEMBERS_VIEW_CONTACT_INFO", @"Button label for the 'show contact info' button") + : NSLocalizedString( + @"GROUP_MEMBERS_ADD_CONTACT_INFO", @"Button label to add information to an unknown contact"); + [actionSheetController addAction:[UIAlertAction actionWithTitle:contactInfoTitle + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *_Nonnull action) { + [self showContactInfoViewForRecipientId:recipientId]; + }]]; BOOL isBlocked; if (signalAccount) { @@ -280,37 +296,43 @@ NS_ASSUME_NONNULL_BEGIN [self presentViewController:alertController animated:YES completion:nil]; return; } - + + CNContactViewController *_Nullable contactViewController; if (signalAccount) { - // FIXME This is broken until converted to Contacts framework. - ABPersonViewController *view = [[ABPersonViewController alloc] init]; - - ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, nil); - // Assume person is already defined. - view.displayedPerson = ABAddressBookGetPersonWithRecordID(addressBookRef, signalAccount.contact.recordID); - view.allowsActions = NO; - view.allowsEditing = YES; + CNContact *_Nullable cnContact = signalAccount.contact.cnContact; + if (cnContact) { + contactViewController = [CNContactViewController viewControllerForContact:cnContact]; + } + } - [self.navigationController pushViewController:view animated:YES]; - } else { - // FIXME This is broken until converted to Contacts framework. - ABUnknownPersonViewController *view = [[ABUnknownPersonViewController alloc] init]; + if (!contactViewController) { + CNMutableContact *newContact = [CNMutableContact new]; + CNPhoneNumber *phoneNumber = [CNPhoneNumber phoneNumberWithStringValue:recipientId]; + CNLabeledValue *labeledPhoneNumber = [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMain + value:phoneNumber]; + newContact.phoneNumbers = @[labeledPhoneNumber]; + + contactViewController = [CNContactViewController viewControllerForNewContact:newContact]; + } - ABRecordRef aContact = ABPersonCreate(); - CFErrorRef anError = NULL; + contactViewController.delegate = self; + contactViewController.allowsActions = NO; + contactViewController.allowsEditing = YES; + contactViewController.navigationItem.leftBarButtonItem = + [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil) + style:UIBarButtonItemStylePlain + target:self + action:@selector(dismissPressed)]; - ABMultiValueRef phone = ABMultiValueCreateMutable(kABMultiStringPropertyType); - ABMultiValueAddValueAndLabel(phone, (__bridge CFTypeRef)recipientId, kABPersonPhoneMainLabel, NULL); + UINavigationController *navigationController = + [[UINavigationController alloc] initWithRootViewController:contactViewController]; + [self presentViewController:navigationController animated:YES completion:nil]; - ABRecordSetValue(aContact, kABPersonPhoneProperty, phone, &anError); - CFRelease(phone); - if (!anError && aContact) { - view.displayedPerson = aContact; // Assume person is already defined. - view.allowsAddingToAddressBook = YES; - [self.navigationController pushViewController:view animated:YES]; - } - } + // HACK otherwise CNContactViewController Navbar is shown as black. + // RADAR rdar://28433898 http://www.openradar.me/28433898 + // CNContactViewController incompatible with opaque navigation bar + [UIUtil applyDefaultSystemAppearence]; } - (void)showConversationViewForRecipientId:(NSString *)recipientId @@ -325,10 +347,20 @@ NS_ASSUME_NONNULL_BEGIN [Environment callUserWithIdentifier:recipientId]; } +- (void)dismissPressed +{ + DDLogDebug(@"%@ %s", self.tag, __PRETTY_FUNCTION__); + [self dismissViewControllerAnimated:YES completion:nil]; +} + #pragma mark - ContactsViewHelperDelegate - (void)contactsViewHelperDidUpdateContacts { + // FIXME new names are not immediately chown. + // doing some debugging, it seems that even though [self.tableView reloadData] is called + // we don't see subsequent invocations of `cellForIndexpath:` unless the items are scrolled off screen. + [self updateTableContents]; } @@ -337,6 +369,27 @@ NS_ASSUME_NONNULL_BEGIN return YES; } +#pragma mark - CNContactViewControllerDelegate + +- (void)contactViewController:(CNContactViewController *)viewController + didCompleteWithContact:(nullable CNContact *)contact +{ + DDLogDebug(@"%@ done editing contact.", self.tag); + [self dismissViewControllerAnimated:YES completion:nil]; +} + +#pragma mark - Logging + ++ (NSString *)tag +{ + return [NSString stringWithFormat:@"[%@]", self.class]; +} + +- (NSString *)tag +{ + return self.class.tag; +} + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/contact/SystemContactsFetcher.swift b/Signal/src/contact/SystemContactsFetcher.swift index c3a67c90b..2eda10aa1 100644 --- a/Signal/src/contact/SystemContactsFetcher.swift +++ b/Signal/src/contact/SystemContactsFetcher.swift @@ -4,6 +4,7 @@ import Foundation import Contacts +import ContactsUI @objc protocol SystemContactsFetcherDelegate: class { func systemContactsFetcher(_ systemContactsFetcher: SystemContactsFetcher, updatedContacts contacts: [Contact]) @@ -36,7 +37,8 @@ class SystemContactsFetcher: NSObject { CNContactFormatter.descriptorForRequiredKeys(for: .fullName), CNContactThumbnailImageDataKey as CNKeyDescriptor, // TODO full image instead of thumbnail? CNContactPhoneNumbersKey as CNKeyDescriptor, - CNContactEmailAddressesKey as CNKeyDescriptor + CNContactEmailAddressesKey as CNKeyDescriptor, + CNContactViewController.descriptorForRequiredKeys() ] /** @@ -106,14 +108,11 @@ class SystemContactsFetcher: NSObject { systemContactsHaveBeenRequestedAtLeastOnce = true - let contactStore = self.contactStore - let allowedContactKeys = self.allowedContactKeys - DispatchQueue.global().async { var systemContacts = [CNContact]() do { - let contactFetchRequest = CNContactFetchRequest(keysToFetch: allowedContactKeys) - try contactStore.enumerateContacts(with: contactFetchRequest) { (contact, _) -> Void in + let contactFetchRequest = CNContactFetchRequest(keysToFetch: self.allowedContactKeys) + try self.contactStore.enumerateContacts(with: contactFetchRequest) { (contact, _) -> Void in systemContacts.append(contact) } } catch let error as NSError { diff --git a/Signal/src/util/UIUtil.m b/Signal/src/util/UIUtil.m index b90f0b979..c4138f364 100644 --- a/Signal/src/util/UIUtil.m +++ b/Signal/src/util/UIUtil.m @@ -33,6 +33,7 @@ { [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; [[UINavigationBar appearance] setBarStyle:UIBarStyleDefault]; + [[UINavigationBar appearance] setTintColor:[UIColor blackColor]]; [[UIBarButtonItem appearance] setTintColor:[UIColor blackColor]]; [[UINavigationBar appearance] setTitleTextAttributes:@{ NSForegroundColorAttributeName : [UIColor blackColor], diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index f986e986a..acd93a650 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -520,6 +520,9 @@ /* No comment provided by engineer. */ "GROUP_MEMBER_LEFT" = " %@ left the group. "; +/* Button label to add information to an unknown contact */ +"GROUP_MEMBERS_ADD_CONTACT_INFO" = "Add Contact"; + /* Button label for the 'call group member' button */ "GROUP_MEMBERS_CALL" = "Call";