From 2bec1db541e204a273d6a33763ec43ab217b04c0 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 2 May 2017 10:54:07 -0400 Subject: [PATCH] Respond to CR. // FREEBIE --- Signal.xcodeproj/project.pbxproj | 4 - .../AddToBlockListViewController.m | 33 ++--- .../AddToGroupViewController.h | 6 + .../AddToGroupViewController.m | 24 +++- .../src/ViewControllers/ContactsViewHelper.h | 3 +- .../src/ViewControllers/ContactsViewHelper.m | 7 +- Signal/src/ViewControllers/GroupViewHelper.h | 10 -- Signal/src/ViewControllers/GroupViewHelper.m | 76 ----------- .../ViewControllers/MessagesViewController.m | 2 +- .../ViewControllers/NewGroupViewController.m | 124 ++++++++---------- ...SConversationSettingsTableViewController.h | 2 +- ...SConversationSettingsTableViewController.m | 35 +++-- .../SelectRecipientViewController.h | 4 + .../SelectRecipientViewController.m | 10 +- .../UpdateGroupViewController.h | 11 +- .../UpdateGroupViewController.m | 117 ++++++++--------- Signal/test/contact/OWSContactsSearcherTest.m | 69 ---------- .../translations/en.lproj/Localizable.strings | 12 +- 18 files changed, 202 insertions(+), 347 deletions(-) delete mode 100644 Signal/test/contact/OWSContactsSearcherTest.m diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 79cecbd9c..62444cd25 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -128,7 +128,6 @@ 45794E861E00620000066731 /* CallUIAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45794E851E00620000066731 /* CallUIAdapter.swift */; }; 45843D1F1D2236B30013E85A /* OWSContactsSearcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */; }; 45843D201D2236B30013E85A /* OWSContactsSearcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */; }; - 45843D221D223BA10013E85A /* OWSContactsSearcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */; }; 45847E871E4283C30080EAB3 /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 45847E861E4283C30080EAB3 /* Intents.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 45855F371D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */; }; 45855F381D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */; }; @@ -528,7 +527,6 @@ 45794E851E00620000066731 /* CallUIAdapter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CallUIAdapter.swift; path = UserInterface/CallUIAdapter.swift; sourceTree = ""; }; 45843D1D1D2236B30013E85A /* OWSContactsSearcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactsSearcher.h; sourceTree = ""; }; 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSearcher.m; sourceTree = ""; }; - 45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSearcherTest.m; sourceTree = ""; }; 45847E861E4283C30080EAB3 /* Intents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Intents.framework; path = System/Library/Frameworks/Intents.framework; sourceTree = SDKROOT; }; 45855F351D9498A40084F340 /* OWSContactAvatarBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactAvatarBuilder.h; sourceTree = ""; }; 45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactAvatarBuilder.m; sourceTree = ""; }; @@ -1434,7 +1432,6 @@ isa = PBXGroup; children = ( B660F6761C29867F00687D6E /* OWSContactsManagerTest.m */, - 45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */, 954AEE681DF33D32002E5410 /* ContactsPickerTest.swift */, ); path = contact; @@ -2196,7 +2193,6 @@ 45AE48521E0732D6004D96C2 /* TurnServerInfo.swift in Sources */, 450873C41D9D5149006B54F2 /* OWSExpirationTimerView.m in Sources */, 453D28BB1D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m in Sources */, - 45843D221D223BA10013E85A /* OWSContactsSearcherTest.m in Sources */, B660F7561C29988E00687D6E /* PushManager.m in Sources */, 45FBC5D21DF8592E00E9B410 /* SignalCall.swift in Sources */, 451A13B21E13DED2000A50FD /* CallNotificationsAdapter.swift in Sources */, diff --git a/Signal/src/ViewControllers/AddToBlockListViewController.m b/Signal/src/ViewControllers/AddToBlockListViewController.m index d9072d9f9..18d4968cb 100644 --- a/Signal/src/ViewControllers/AddToBlockListViewController.m +++ b/Signal/src/ViewControllers/AddToBlockListViewController.m @@ -60,6 +60,14 @@ NS_ASSUME_NONNULL_BEGIN }]; } +- (BOOL)canSignalAccountBeSelected:(SignalAccount *)signalAccount +{ + OWSAssert(signalAccount); + + ContactsViewHelper *helper = self.contactsViewHelper; + return ![helper isRecipientIdBlocked:signalAccount.recipientId]; +} + - (void)signalAccountWasSelected:(SignalAccount *)signalAccount { OWSAssert(signalAccount); @@ -67,25 +75,7 @@ NS_ASSUME_NONNULL_BEGIN __weak AddToBlockListViewController *weakSelf = self; ContactsViewHelper *helper = self.contactsViewHelper; if ([helper isRecipientIdBlocked:signalAccount.recipientId]) { - NSString *displayName = [helper.contactsManager displayNameForSignalAccount:signalAccount]; - UIAlertController *controller = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_ALREADY_BLOCKED_ALERT_TITLE", - @"A title of the alert if user tries to block a " - @"user who is already blocked.") - message:[NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_VIEW_ALREADY_" - @"BLOCKED_ALERT_MESSAGE_" - @"FORMAT", - @"A format for the message of the alert " - @"if user tries to " - @"block a user who is already blocked. " - @"Embeds {{the " - @"blocked user's name or phone number}}."), - displayName] - preferredStyle:UIAlertControllerStyleAlert]; - [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) - style:UIAlertActionStyleDefault - handler:nil]]; - [self presentViewController:controller animated:YES completion:nil]; + OWSAssert(0); return; } [BlockListUIUtils showBlockSignalAccountActionSheet:signalAccount @@ -114,6 +104,11 @@ NS_ASSUME_NONNULL_BEGIN return NO; } +- (nullable NSString *)accessoryMessageForSignalAccount:(SignalAccount *)signalAccount +{ + return nil; +} + #pragma mark - Logging + (NSString *)tag diff --git a/Signal/src/ViewControllers/AddToGroupViewController.h b/Signal/src/ViewControllers/AddToGroupViewController.h index a0f5bcef5..071318d67 100644 --- a/Signal/src/ViewControllers/AddToGroupViewController.h +++ b/Signal/src/ViewControllers/AddToGroupViewController.h @@ -4,10 +4,14 @@ #import "SelectRecipientViewController.h" +NS_ASSUME_NONNULL_BEGIN + @protocol AddToGroupViewControllerDelegate - (void)recipientIdWasAdded:(NSString *)recipientId; +- (BOOL)isRecipientGroupMember:(NSString *)recipientId; + @end #pragma mark - @@ -19,3 +23,5 @@ @property (nonatomic) BOOL hideContacts; @end + +NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/AddToGroupViewController.m b/Signal/src/ViewControllers/AddToGroupViewController.m index a9caca929..f335efd7a 100644 --- a/Signal/src/ViewControllers/AddToGroupViewController.m +++ b/Signal/src/ViewControllers/AddToGroupViewController.m @@ -66,13 +66,24 @@ NS_ASSUME_NONNULL_BEGIN } } +- (BOOL)canSignalAccountBeSelected:(SignalAccount *)signalAccount +{ + OWSAssert(signalAccount); + + return ![self.addToGroupDelegate isRecipientGroupMember:signalAccount.recipientId]; +} + - (void)signalAccountWasSelected:(SignalAccount *)signalAccount { OWSAssert(signalAccount); __weak AddToGroupViewController *weakSelf = self; ContactsViewHelper *helper = self.contactsViewHelper; - if ([helper isRecipientIdBlocked:signalAccount.recipientId]) { + if ([self.addToGroupDelegate isRecipientGroupMember:signalAccount.recipientId]) { + OWSAssert(0); + + return; + } else if ([helper isRecipientIdBlocked:signalAccount.recipientId]) { [BlockListUIUtils showUnblockSignalAccountActionSheet:signalAccount fromViewController:self blockingManager:helper.blockingManager @@ -110,6 +121,17 @@ NS_ASSUME_NONNULL_BEGIN return YES; } +- (nullable NSString *)accessoryMessageForSignalAccount:(SignalAccount *)signalAccount +{ + OWSAssert(signalAccount); + + if ([self.addToGroupDelegate isRecipientGroupMember:signalAccount.recipientId]) { + return NSLocalizedString(@"NEW_GROUP_MEMBER_LABEL", @"An indicator that a user is a member of the new group."); + } + + return nil; +} + #pragma mark - Logging + (NSString *)tag diff --git a/Signal/src/ViewControllers/ContactsViewHelper.h b/Signal/src/ViewControllers/ContactsViewHelper.h index 32249e8e5..6180cef4d 100644 --- a/Signal/src/ViewControllers/ContactsViewHelper.h +++ b/Signal/src/ViewControllers/ContactsViewHelper.h @@ -35,8 +35,6 @@ NS_ASSUME_NONNULL_BEGIN - (nullable SignalAccount *)signalAccountForRecipientId:(NSString *)recipientId; -- (nullable NSArray *)blockedPhoneNumbers; - // This method is faster than OWSBlockingManager but // is only safe to be called on the main thread. // @@ -50,6 +48,7 @@ NS_ASSUME_NONNULL_BEGIN // is only safe to be called on the main thread. - (BOOL)isRecipientIdBlocked:(NSString *)recipientId; +// NOTE: This method uses a transaction. - (NSString *)localNumber; - (NSArray *)signalAccountsMatchingSearchString:(NSString *)searchText; diff --git a/Signal/src/ViewControllers/ContactsViewHelper.m b/Signal/src/ViewControllers/ContactsViewHelper.m index 7cbacfcf6..c553297dc 100644 --- a/Signal/src/ViewControllers/ContactsViewHelper.m +++ b/Signal/src/ViewControllers/ContactsViewHelper.m @@ -33,7 +33,7 @@ NS_ASSUME_NONNULL_BEGIN } _blockingManager = [OWSBlockingManager sharedManager]; - self.blockedPhoneNumbers = [_blockingManager blockedPhoneNumbers]; + _blockedPhoneNumbers = [_blockingManager blockedPhoneNumbers]; _contactsManager = [Environment getCurrent].contactsManager; self.signalAccountMap = self.contactsManager.signalAccountMap; @@ -92,7 +92,6 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert([NSThread isMainThread]); if ([self.delegate shouldHideLocalNumber] && [self isCurrentUser:signalAccount]) { - // We never want to add ourselves to a group. return YES; } @@ -159,8 +158,8 @@ NS_ASSUME_NONNULL_BEGIN [signalAccounts addObject:signalAccount]; } } - self.signalAccountMap = signalAccountMap; - self.signalAccounts = signalAccounts; + self.signalAccountMap = [signalAccountMap copy]; + self.signalAccounts = [signalAccounts copy]; [self.delegate contactsViewHelperDidUpdateContacts]; } diff --git a/Signal/src/ViewControllers/GroupViewHelper.h b/Signal/src/ViewControllers/GroupViewHelper.h index 57543f155..932f6d543 100644 --- a/Signal/src/ViewControllers/GroupViewHelper.h +++ b/Signal/src/ViewControllers/GroupViewHelper.h @@ -27,16 +27,6 @@ typedef void (^GroupViewSuccessBlock)(); @property (nonatomic, weak) id delegate; -- (void)showRemoveFromGroupAlertForSignalAccount:(SignalAccount *)signalAccount - fromViewController:(UIViewController *)fromViewController - contactsManager:(OWSContactsManager *)contactsManager - successBlock:(GroupViewSuccessBlock)successBlock; - -- (void)showRemoveFromGroupAlertForRecipientId:(NSString *)recipientId - fromViewController:(UIViewController *)fromViewController - contactsManager:(OWSContactsManager *)contactsManager - successBlock:(GroupViewSuccessBlock)successBlock; - - (void)showChangeGroupAvatarUI; @end diff --git a/Signal/src/ViewControllers/GroupViewHelper.m b/Signal/src/ViewControllers/GroupViewHelper.m index 4e70df7e6..60a689928 100644 --- a/Signal/src/ViewControllers/GroupViewHelper.m +++ b/Signal/src/ViewControllers/GroupViewHelper.m @@ -21,82 +21,6 @@ NS_ASSUME_NONNULL_BEGIN @implementation GroupViewHelper -#pragma mark - Alerts - -- (void)showRemoveFromGroupAlertForSignalAccount:(SignalAccount *)signalAccount - fromViewController:(UIViewController *)fromViewController - contactsManager:(OWSContactsManager *)contactsManager - successBlock:(GroupViewSuccessBlock)successBlock -{ - OWSAssert(signalAccount); - OWSAssert(fromViewController); - OWSAssert(contactsManager); - OWSAssert(successBlock); - - NSString *displayName = [contactsManager displayNameForSignalAccount:signalAccount]; - UIAlertController *controller = [UIAlertController - alertControllerWithTitle: - NSLocalizedString(@"EDIT_GROUP_REMOVE_MEMBER_ALERT_TITLE", - @"A title of the alert confirming whether user wants to remove a user from a group.") - message:[NSString - stringWithFormat:NSLocalizedString( - @"EDIT_GROUP_REMOVE_MEMBER_ALERT_MESSAGE_FORMAT", - @"A format for the message of the alert confirming whether " - @"user wants to remove a user from a group. Embeds {{the " - @"user's name or phone number}}."), - displayName] - preferredStyle:UIAlertControllerStyleAlert]; - [controller addAction:[UIAlertAction - actionWithTitle: - NSLocalizedString(@"EDIT_GROUP_REMOVE_MEMBER_BUTTON", - @"A title of the button that confirms user wants to remove a user from a group.") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - successBlock(); - }]]; - [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil) - style:UIAlertActionStyleDefault - handler:nil]]; - [fromViewController presentViewController:controller animated:YES completion:nil]; -} - -- (void)showRemoveFromGroupAlertForRecipientId:(NSString *)recipientId - fromViewController:(UIViewController *)fromViewController - contactsManager:(OWSContactsManager *)contactsManager - successBlock:(GroupViewSuccessBlock)successBlock -{ - OWSAssert(recipientId.length > 0); - OWSAssert(fromViewController); - OWSAssert(contactsManager); - OWSAssert(successBlock); - - NSString *displayName = [contactsManager displayNameForPhoneIdentifier:recipientId]; - UIAlertController *controller = [UIAlertController - alertControllerWithTitle: - NSLocalizedString(@"EDIT_GROUP_REMOVE_MEMBER_ALERT_TITLE", - @"A title of the alert confirming whether user wants to remove a user from a group.") - message:[NSString - stringWithFormat:NSLocalizedString( - @"EDIT_GROUP_REMOVE_MEMBER_ALERT_MESSAGE_FORMAT", - @"A format for the message of the alert confirming whether " - @"user wants to remove a user from a group. Embeds {{the " - @"user's name or phone number}}."), - displayName] - preferredStyle:UIAlertControllerStyleAlert]; - [controller addAction:[UIAlertAction - actionWithTitle: - NSLocalizedString(@"EDIT_GROUP_REMOVE_MEMBER_BUTTON", - @"A title of the button that confirms user wants to remove a user from a group.") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - successBlock(); - }]]; - [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil) - style:UIAlertActionStyleDefault - handler:nil]]; - [fromViewController presentViewController:controller animated:YES completion:nil]; -} - #pragma mark - Group Avatar - (void)showChangeGroupAvatarUI diff --git a/Signal/src/ViewControllers/MessagesViewController.m b/Signal/src/ViewControllers/MessagesViewController.m index 32acd161a..eaca2abb6 100644 --- a/Signal/src/ViewControllers/MessagesViewController.m +++ b/Signal/src/ViewControllers/MessagesViewController.m @@ -1652,7 +1652,7 @@ typedef enum : NSUInteger { OWSConversationSettingsTableViewController *settingsVC = [[UIStoryboard main] instantiateViewControllerWithIdentifier:@"OWSConversationSettingsTableViewController"]; - settingsVC.delegate = self; + settingsVC.conversationSettingsViewDelegate = self; [settingsVC configureWithThread:self.thread]; [self.navigationController pushViewController:settingsVC animated:YES]; } diff --git a/Signal/src/ViewControllers/NewGroupViewController.m b/Signal/src/ViewControllers/NewGroupViewController.m index b1e601e53..f431ef8ee 100644 --- a/Signal/src/ViewControllers/NewGroupViewController.m +++ b/Signal/src/ViewControllers/NewGroupViewController.m @@ -42,7 +42,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) UITextField *groupNameTextField; @property (nonatomic, nullable) UIImage *groupAvatar; -@property (nonatomic, nullable) NSMutableSet *memberRecipientIds; +@property (nonatomic) NSMutableSet *memberRecipientIds; @property (nonatomic) BOOL hasUnsavedChanges; @@ -172,7 +172,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)avatarTouched:(UIGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateRecognized) { - [self showChangeGroupAvatarUI:nil]; + [self showChangeGroupAvatarUI]; } } @@ -183,7 +183,7 @@ NS_ASSUME_NONNULL_BEGIN OWSTableContents *contents = [OWSTableContents new]; __weak NewGroupViewController *weakSelf = self; - ContactsViewHelper *helper = self.contactsViewHelper; + ContactsViewHelper *contactsViewHelper = self.contactsViewHelper; NSArray *signalAccounts = self.contactsViewHelper.signalAccounts; NSMutableSet *nonContactMemberRecipientIds = [self.memberRecipientIds mutableCopy]; @@ -204,57 +204,39 @@ NS_ASSUME_NONNULL_BEGIN for (NSString *recipientId in [nonContactMemberRecipientIds.allObjects sortedArrayUsingSelector:@selector(compare:)]) { - [nonContactsSection - addItem:[OWSTableItem itemWithCustomCellBlock:^{ - NewGroupViewController *strongSelf = weakSelf; - if (!strongSelf) { - return (ContactTableViewCell *)nil; - } - - ContactTableViewCell *cell = [ContactTableViewCell new]; - SignalAccount *signalAccount = [helper signalAccountForRecipientId:recipientId]; - BOOL isCurrentMember = [weakSelf.memberRecipientIds containsObject:recipientId]; - BOOL isBlocked = [helper isRecipientIdBlocked:recipientId]; - if (isCurrentMember) { - // In the "contacts" section, we label members as such when editing an existing group. - cell.accessoryMessage = NSLocalizedString( - @"NEW_GROUP_MEMBER_LABEL", @"An indicator that a user is a member of the new group."); - } else if (isBlocked) { - cell.accessoryMessage = NSLocalizedString( - @"CONTACT_CELL_IS_BLOCKED", @"An indicator that a contact has been blocked."); - } else { - OWSAssert(cell.accessoryMessage == nil); - } + [nonContactsSection addItem:[OWSTableItem itemWithCustomCellBlock:^{ + NewGroupViewController *strongSelf = weakSelf; + if (!strongSelf) { + return (ContactTableViewCell *)nil; + } - if (signalAccount) { - [cell configureWithSignalAccount:signalAccount contactsManager:helper.contactsManager]; - } else { - [cell configureWithRecipientId:recipientId contactsManager:helper.contactsManager]; - } + ContactTableViewCell *cell = [ContactTableViewCell new]; + SignalAccount *signalAccount = [contactsViewHelper signalAccountForRecipientId:recipientId]; + BOOL isCurrentMember = [strongSelf.memberRecipientIds containsObject:recipientId]; + BOOL isBlocked = [contactsViewHelper isRecipientIdBlocked:recipientId]; + if (isCurrentMember) { + // In the "contacts" section, we label members as such when editing an existing group. + cell.accessoryMessage = NSLocalizedString( + @"NEW_GROUP_MEMBER_LABEL", @"An indicator that a user is a member of the new group."); + } else if (isBlocked) { + cell.accessoryMessage = NSLocalizedString( + @"CONTACT_CELL_IS_BLOCKED", @"An indicator that a contact has been blocked."); + } else { + OWSAssert(cell.accessoryMessage == nil); + } - return cell; + if (signalAccount) { + [cell configureWithSignalAccount:signalAccount contactsManager:contactsViewHelper.contactsManager]; + } else { + [cell configureWithRecipientId:recipientId contactsManager:contactsViewHelper.contactsManager]; } - customRowHeight:[ContactTableViewCell rowHeight] - actionBlock:^{ - SignalAccount *signalAccount = [helper signalAccountForRecipientId:recipientId]; - if (signalAccount) { - [weakSelf.groupViewHelper - showRemoveFromGroupAlertForSignalAccount:signalAccount - fromViewController:weakSelf - contactsManager:helper.contactsManager - successBlock:^{ - [weakSelf removeSignalAccount:signalAccount]; - }]; - } else { - [weakSelf.groupViewHelper - showRemoveFromGroupAlertForRecipientId:recipientId - fromViewController:weakSelf - contactsManager:helper.contactsManager - successBlock:^{ - [weakSelf removeRecipientId:recipientId]; - }]; - } - }]]; + + return cell; + } + customRowHeight:[ContactTableViewCell rowHeight] + actionBlock:^{ + [weakSelf removeRecipientId:recipientId]; + }]]; } [contents addSection:nonContactsSection]; } @@ -267,7 +249,12 @@ NS_ASSUME_NONNULL_BEGIN if (signalAccounts.count > 0) { if (nonContactMemberRecipientIds.count < 1) { - // We always want to offer a way to add non-contacts. + // If the group contains any non-contacts or has not contacts, + // the "add non-contact user" will show up in the previous section + // of the table. However, it's more attractive to hide that section + // for the common case where people want to create a group from just + // their contacts. Therefore, when that section is hidden, we want + // to allow people to add non-contacts. [signalAccountSection addItem:[self createAddNonContactItem]]; } @@ -282,8 +269,8 @@ NS_ASSUME_NONNULL_BEGIN ContactTableViewCell *cell = [ContactTableViewCell new]; NSString *recipientId = signalAccount.recipientId; - BOOL isCurrentMember = [weakSelf.memberRecipientIds containsObject:recipientId]; - BOOL isBlocked = [helper isRecipientIdBlocked:recipientId]; + BOOL isCurrentMember = [strongSelf.memberRecipientIds containsObject:recipientId]; + BOOL isBlocked = [contactsViewHelper isRecipientIdBlocked:recipientId]; if (isCurrentMember) { // In the "contacts" section, we label members as such when editing an existing group. cell.accessoryMessage = NSLocalizedString( @@ -295,7 +282,7 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(cell.accessoryMessage == nil); } - [cell configureWithSignalAccount:signalAccount contactsManager:helper.contactsManager]; + [cell configureWithSignalAccount:signalAccount contactsManager:contactsViewHelper.contactsManager]; return cell; } @@ -304,13 +291,7 @@ NS_ASSUME_NONNULL_BEGIN NSString *recipientId = signalAccount.recipientId; BOOL isCurrentMember = [weakSelf.memberRecipientIds containsObject:recipientId]; if (isCurrentMember) { - [weakSelf.groupViewHelper - showRemoveFromGroupAlertForSignalAccount:signalAccount - fromViewController:weakSelf - contactsManager:helper.contactsManager - successBlock:^{ - [weakSelf removeSignalAccount:signalAccount]; - }]; + [weakSelf removeRecipientId:recipientId]; } else { [weakSelf addRecipientId:recipientId]; } @@ -354,14 +335,6 @@ NS_ASSUME_NONNULL_BEGIN }]; } -- (void)removeSignalAccount:(SignalAccount *)signalAccount -{ - OWSAssert(signalAccount); - - [self.memberRecipientIds removeObject:signalAccount.recipientId]; - [self updateTableContents]; -} - - (void)removeRecipientId:(NSString *)recipientId { OWSAssert(recipientId.length > 0); @@ -395,7 +368,7 @@ NS_ASSUME_NONNULL_BEGIN if (self.shouldEditGroupNameOnAppear) { [self.groupNameTextField becomeFirstResponder]; } else if (self.shouldEditAvatarOnAppear) { - [self showChangeGroupAvatarUI:nil]; + [self showChangeGroupAvatarUI]; } self.shouldEditGroupNameOnAppear = NO; self.shouldEditAvatarOnAppear = NO; @@ -475,7 +448,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Group Avatar -- (void)showChangeGroupAvatarUI:(nullable id)sender +- (void)showChangeGroupAvatarUI { [self.groupViewHelper showChangeGroupAvatarUI]; } @@ -588,6 +561,13 @@ NS_ASSUME_NONNULL_BEGIN [self addRecipientId:recipientId]; } +- (BOOL)isRecipientGroupMember:(NSString *)recipientId +{ + OWSAssert(recipientId.length > 0); + + return [self.memberRecipientIds containsObject:recipientId]; +} + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h index 3a2fcec30..6735e11fb 100644 --- a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h +++ b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h @@ -12,7 +12,7 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSConversationSettingsTableViewController : OWSTableViewController -@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id conversationSettingsViewDelegate; - (void)configureWithThread:(TSThread *)thread; - (void)presentedModalWasDismissed; diff --git a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m index a3ad5b485..8f6843513 100644 --- a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m +++ b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m @@ -318,15 +318,7 @@ NS_ASSUME_NONNULL_BEGIN return cell; } actionBlock:^{ - OWSConversationSettingsTableViewController *strongSelf = weakSelf; - if (!strongSelf) { - return; - } - OWSAssert(strongSelf.delegate); - UpdateGroupViewController *updateGroupViewController = [UpdateGroupViewController new]; - updateGroupViewController.delegate = strongSelf.delegate; - updateGroupViewController.thread = (TSGroupThread *)strongSelf.thread; - [strongSelf.navigationController pushViewController:updateGroupViewController animated:YES]; + [weakSelf showUpdateGroupView:UpdateGroupMode_Default]; }], [OWSTableItem itemWithCustomCellBlock:^{ UITableViewCell *cell = [UITableViewCell new]; @@ -548,19 +540,12 @@ NS_ASSUME_NONNULL_BEGIN { if (sender.state == UIGestureRecognizerStateRecognized) { if (self.isGroupThread) { - OWSAssert(self.delegate); - UpdateGroupViewController *updateGroupViewController = [UpdateGroupViewController new]; - updateGroupViewController.delegate = self.delegate; - updateGroupViewController.thread = (TSGroupThread *)self.thread; - CGPoint location = [sender locationInView:self.avatarView]; if (CGRectContainsPoint(self.avatarView.bounds, location)) { - updateGroupViewController.shouldEditAvatarOnAppear = YES; + [self showUpdateGroupView:UpdateGroupMode_EditGroupAvatar]; } else { - updateGroupViewController.shouldEditGroupNameOnAppear = YES; + [self showUpdateGroupView:UpdateGroupMode_EditGroupName]; } - - [self.navigationController pushViewController:updateGroupViewController animated:YES]; } else { // TODO: Edit 1:1 contact. } @@ -616,6 +601,20 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Actions +- (void)showUpdateGroupView:(UpdateGroupMode)mode +{ + OWSAssert(self.conversationSettingsViewDelegate); + + UpdateGroupViewController *updateGroupViewController = [UpdateGroupViewController new]; + updateGroupViewController.conversationSettingsViewDelegate = self.conversationSettingsViewDelegate; + updateGroupViewController.thread = (TSGroupThread *)self.thread; + updateGroupViewController.mode = mode; + + UINavigationController *navigationController = + [[UINavigationController alloc] initWithRootViewController:updateGroupViewController]; + [self presentViewController:navigationController animated:YES completion:nil]; +} + - (void)didTapLeaveGroup { UIAlertController *alertController = diff --git a/Signal/src/ViewControllers/SelectRecipientViewController.h b/Signal/src/ViewControllers/SelectRecipientViewController.h index 519fcda6d..090493bcf 100644 --- a/Signal/src/ViewControllers/SelectRecipientViewController.h +++ b/Signal/src/ViewControllers/SelectRecipientViewController.h @@ -14,8 +14,12 @@ - (void)phoneNumberWasSelected:(NSString *)phoneNumber; +- (BOOL)canSignalAccountBeSelected:(SignalAccount *)signalAccount; + - (void)signalAccountWasSelected:(SignalAccount *)signalAccount; +- (nullable NSString *)accessoryMessageForSignalAccount:(SignalAccount *)signalAccount; + - (BOOL)shouldHideLocalNumber; - (BOOL)shouldHideContacts; diff --git a/Signal/src/ViewControllers/SelectRecipientViewController.m b/Signal/src/ViewControllers/SelectRecipientViewController.m index 83894c85c..6ab7ba914 100644 --- a/Signal/src/ViewControllers/SelectRecipientViewController.m +++ b/Signal/src/ViewControllers/SelectRecipientViewController.m @@ -486,13 +486,21 @@ NSString *const kSelectRecipientViewControllerCellIdentifier = @"kSelectRecipien cell.accessoryMessage = NSLocalizedString( @"CONTACT_CELL_IS_BLOCKED", @"An indicator that a contact has been blocked."); } else { - OWSAssert(cell.accessoryMessage == nil); + cell.accessoryMessage = [weakSelf.delegate accessoryMessageForSignalAccount:signalAccount]; } [cell configureWithSignalAccount:signalAccount contactsManager:helper.contactsManager]; + + if (![weakSelf.delegate canSignalAccountBeSelected:signalAccount]) { + cell.selectionStyle = UITableViewCellSelectionStyleNone; + } + return cell; } customRowHeight:[ContactTableViewCell rowHeight] actionBlock:^{ + if (![weakSelf.delegate canSignalAccountBeSelected:signalAccount]) { + return; + } [weakSelf.delegate signalAccountWasSelected:signalAccount]; }]]; } diff --git a/Signal/src/ViewControllers/UpdateGroupViewController.h b/Signal/src/ViewControllers/UpdateGroupViewController.h index 7eff3671b..9403cdf9b 100644 --- a/Signal/src/ViewControllers/UpdateGroupViewController.h +++ b/Signal/src/ViewControllers/UpdateGroupViewController.h @@ -8,15 +8,20 @@ NS_ASSUME_NONNULL_BEGIN @class TSGroupThread; +typedef NS_ENUM(NSUInteger, UpdateGroupMode) { + UpdateGroupMode_Default = 0, + UpdateGroupMode_EditGroupName, + UpdateGroupMode_EditGroupAvatar, +}; + @interface UpdateGroupViewController : UIViewController -@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) id conversationSettingsViewDelegate; // This property _must_ be set before the view is presented. @property (nonatomic) TSGroupThread *thread; -@property (nonatomic) BOOL shouldEditGroupNameOnAppear; -@property (nonatomic) BOOL shouldEditAvatarOnAppear; +@property (nonatomic) UpdateGroupMode mode; @end diff --git a/Signal/src/ViewControllers/UpdateGroupViewController.m b/Signal/src/ViewControllers/UpdateGroupViewController.m index 241b5cfd8..7cf3494c9 100644 --- a/Signal/src/ViewControllers/UpdateGroupViewController.m +++ b/Signal/src/ViewControllers/UpdateGroupViewController.m @@ -19,6 +19,7 @@ #import "UIUtil.h" #import "UIView+OWS.h" #import "UIViewController+OWS.h" +#import "ViewControllerUtils.h" #import #import #import @@ -44,7 +45,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, nullable) UIImage *groupAvatar; @property (nonatomic, nullable) NSSet *previousMemberRecipientIds; -@property (nonatomic, nullable) NSMutableSet *memberRecipientIds; +@property (nonatomic) NSMutableSet *memberRecipientIds; @property (nonatomic) BOOL hasUnsavedChanges; @@ -135,13 +136,18 @@ NS_ASSUME_NONNULL_BEGIN { [super viewDidAppear:animated]; - if (self.shouldEditGroupNameOnAppear) { - [self.groupNameTextField becomeFirstResponder]; - } else if (self.shouldEditAvatarOnAppear) { - [self showChangeGroupAvatarUI:nil]; + switch (self.mode) { + case UpdateGroupMode_EditGroupName: + [self.groupNameTextField becomeFirstResponder]; + break; + case UpdateGroupMode_EditGroupAvatar: + [self showChangeGroupAvatarUI]; + break; + default: + break; } - self.shouldEditGroupNameOnAppear = NO; - self.shouldEditAvatarOnAppear = NO; + // Only perform these actions the first time the view appears. + self.mode = UpdateGroupMode_Default; } - (UIView *)firstSectionHeader @@ -197,7 +203,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)avatarTouched:(UIGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateRecognized) { - [self showChangeGroupAvatarUI:nil]; + [self showChangeGroupAvatarUI]; } } @@ -210,7 +216,7 @@ NS_ASSUME_NONNULL_BEGIN OWSTableContents *contents = [OWSTableContents new]; __weak UpdateGroupViewController *weakSelf = self; - ContactsViewHelper *helper = self.contactsViewHelper; + ContactsViewHelper *contactsViewHelper = self.contactsViewHelper; // Group Members @@ -235,7 +241,7 @@ NS_ASSUME_NONNULL_BEGIN }]]; NSMutableSet *memberRecipientIds = [self.memberRecipientIds mutableCopy]; - [memberRecipientIds removeObject:[helper localNumber]]; + [memberRecipientIds removeObject:[contactsViewHelper localNumber]]; for (NSString *recipientId in [memberRecipientIds.allObjects sortedArrayUsingSelector:@selector(compare:)]) { [section addItem:[OWSTableItem itemWithCustomCellBlock:^{ @@ -245,9 +251,9 @@ NS_ASSUME_NONNULL_BEGIN } ContactTableViewCell *cell = [ContactTableViewCell new]; - SignalAccount *signalAccount = [helper signalAccountForRecipientId:recipientId]; + SignalAccount *signalAccount = [contactsViewHelper signalAccountForRecipientId:recipientId]; BOOL isPreviousMember = [strongSelf.previousMemberRecipientIds containsObject:recipientId]; - BOOL isBlocked = [helper isRecipientIdBlocked:recipientId]; + BOOL isBlocked = [contactsViewHelper isRecipientIdBlocked:recipientId]; if (isPreviousMember) { if (isBlocked) { cell.accessoryMessage = NSLocalizedString( @@ -265,18 +271,18 @@ NS_ASSUME_NONNULL_BEGIN } if (signalAccount) { - [cell configureWithSignalAccount:signalAccount contactsManager:helper.contactsManager]; + [cell configureWithSignalAccount:signalAccount contactsManager:contactsViewHelper.contactsManager]; } else { - [cell configureWithRecipientId:recipientId contactsManager:helper.contactsManager]; + [cell configureWithRecipientId:recipientId contactsManager:contactsViewHelper.contactsManager]; } return cell; } customRowHeight:[ContactTableViewCell rowHeight] actionBlock:^{ - SignalAccount *signalAccount = [helper signalAccountForRecipientId:recipientId]; + SignalAccount *signalAccount = [contactsViewHelper signalAccountForRecipientId:recipientId]; BOOL isPreviousMember = [weakSelf.previousMemberRecipientIds containsObject:recipientId]; - BOOL isBlocked = [helper isRecipientIdBlocked:recipientId]; + BOOL isBlocked = [contactsViewHelper isRecipientIdBlocked:recipientId]; if (isPreviousMember) { if (isBlocked) { if (signalAccount) { @@ -284,25 +290,18 @@ NS_ASSUME_NONNULL_BEGIN } else { [weakSelf showUnblockAlertForRecipientId:recipientId]; } - } - } else { - if (signalAccount) { - [weakSelf.groupViewHelper - showRemoveFromGroupAlertForSignalAccount:signalAccount - fromViewController:weakSelf - contactsManager:helper.contactsManager - successBlock:^{ - [weakSelf removeSignalAccount:signalAccount]; - }]; } else { - [weakSelf.groupViewHelper - showRemoveFromGroupAlertForRecipientId:recipientId - fromViewController:weakSelf - contactsManager:helper.contactsManager - successBlock:^{ - [weakSelf removeRecipientId:recipientId]; - }]; + [ViewControllerUtils + showAlertWithTitle: + NSLocalizedString(@"UPDATE_GROUP_CANT_REMOVE_MEMBERS_ALERT_TITLE", + @"Title for alert indicating that group members can't be removed.") + message:NSLocalizedString( + @"UPDATE_GROUP_CANT_REMOVE_MEMBERS_ALERT_MESSAGE", + @"Title for alert indicating that group members can't " + @"be removed.")]; } + } else { + [weakSelf removeRecipientId:recipientId]; } }]]; } @@ -316,11 +315,10 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(signalAccount); __weak UpdateGroupViewController *weakSelf = self; - ContactsViewHelper *helper = self.contactsViewHelper; [BlockListUIUtils showUnblockSignalAccountActionSheet:signalAccount fromViewController:self - blockingManager:helper.blockingManager - contactsManager:helper.contactsManager + blockingManager:self.contactsViewHelper.blockingManager + contactsManager:self.contactsViewHelper.contactsManager completionBlock:^(BOOL isBlocked) { if (!isBlocked) { [weakSelf updateTableContents]; @@ -333,11 +331,10 @@ NS_ASSUME_NONNULL_BEGIN OWSAssert(recipientId.length > 0); __weak UpdateGroupViewController *weakSelf = self; - ContactsViewHelper *helper = self.contactsViewHelper; [BlockListUIUtils showUnblockPhoneNumberActionSheet:recipientId fromViewController:self - blockingManager:helper.blockingManager - contactsManager:helper.contactsManager + blockingManager:self.contactsViewHelper.blockingManager + contactsManager:self.contactsViewHelper.contactsManager completionBlock:^(BOOL isBlocked) { if (!isBlocked) { [weakSelf updateTableContents]; @@ -345,14 +342,6 @@ NS_ASSUME_NONNULL_BEGIN }]; } -- (void)removeSignalAccount:(SignalAccount *)signalAccount -{ - OWSAssert(signalAccount); - - [self.memberRecipientIds removeObject:signalAccount.recipientId]; - [self updateTableContents]; -} - - (void)removeRecipientId:(NSString *)recipientId { OWSAssert(recipientId.length > 0); @@ -365,18 +354,18 @@ NS_ASSUME_NONNULL_BEGIN - (void)updateGroup { - OWSAssert(self.delegate); + OWSAssert(self.conversationSettingsViewDelegate); TSGroupModel *groupModel = [[TSGroupModel alloc] initWithTitle:self.groupNameTextField.text memberIds:[self.memberRecipientIds.allObjects mutableCopy] image:self.groupAvatar groupId:self.thread.groupModel.groupId]; - [self.delegate groupWasUpdated:groupModel]; + [self.conversationSettingsViewDelegate groupWasUpdated:groupModel]; } #pragma mark - Group Avatar -- (void)showChangeGroupAvatarUI:(nullable id)sender +- (void)showChangeGroupAvatarUI { [self.groupNameTextField resignFirstResponder]; @@ -413,7 +402,7 @@ NS_ASSUME_NONNULL_BEGIN if (!self.hasUnsavedChanges) { // If user made no changes, return to conversation settings view. - [self.navigationController popViewControllerAnimated:YES]; + [self dismissViewControllerAnimated:YES completion:nil]; return; } @@ -424,21 +413,22 @@ NS_ASSUME_NONNULL_BEGIN NSLocalizedString(@"EDIT_GROUP_VIEW_UNSAVED_CHANGES_MESSAGE", @"The alert message if user tries to exit update group view without saving changes.") preferredStyle:UIAlertControllerStyleAlert]; - [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"ALERT_SAVE", - @"The label for the 'save' button in action sheets.") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *action) { - OWSAssert(self.delegate); + [controller + addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"ALERT_SAVE", + @"The label for the 'save' button in action sheets.") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *action) { + OWSAssert(self.conversationSettingsViewDelegate); - [self updateGroup]; + [self updateGroup]; - [self.delegate popAllConversationSettingsViews]; - }]]; + [self.conversationSettingsViewDelegate popAllConversationSettingsViews]; + }]]; [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"ALERT_DONT_SAVE", @"The label for the 'don't save' button in action sheets.") style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) { - [self.navigationController popViewControllerAnimated:YES]; + [self dismissViewControllerAnimated:YES completion:nil]; }]]; [self presentViewController:controller animated:YES completion:nil]; } @@ -499,6 +489,13 @@ NS_ASSUME_NONNULL_BEGIN [self updateTableContents]; } +- (BOOL)isRecipientGroupMember:(NSString *)recipientId +{ + OWSAssert(recipientId.length > 0); + + return [self.memberRecipientIds containsObject:recipientId]; +} + @end NS_ASSUME_NONNULL_END diff --git a/Signal/test/contact/OWSContactsSearcherTest.m b/Signal/test/contact/OWSContactsSearcherTest.m deleted file mode 100644 index 1223992ef..000000000 --- a/Signal/test/contact/OWSContactsSearcherTest.m +++ /dev/null @@ -1,69 +0,0 @@ -// -// OWSContactSearcherTest.m -// Signal -// -// Created by Michael Kirk on 6/27/16. -// Copyright © 2016 Open Whisper Systems. All rights reserved. -// - -#import - -#import "OWSContactsSearcher.h" - -@interface OWSContactsSearcherTest : XCTestCase - -@property Contact *meow; -@property Contact *clement; -@property OWSContactsSearcher *contactsSearcher; - -@end - -@implementation OWSContactsSearcherTest - -- (void)setUp { - [super setUp]; - - self.meow = [[Contact alloc] initWithContactWithFirstName:@"Chairman" - andLastName:@"Meow" - andUserTextPhoneNumbers:@[ @"1-323-555-1234", @"+86 10 1111 2222" ] - andImage:nil - andContactID:1]; - - self.clement = [[Contact alloc] initWithContactWithFirstName:@"Clément" - andLastName:@"Duval" - andUserTextPhoneNumbers:@[ @"33 123456789" ] - andImage:nil - andContactID:2]; - - self.contactsSearcher = [[OWSContactsSearcher alloc] initWithContacts:@[self.meow, self.clement]]; -} - -- (void)testFilterWithStringMatchAllOnEmtpy { - XCTAssertEqualObjects((@[self.meow, self.clement]), [self.contactsSearcher filterWithString:@""]); - XCTAssertEqualObjects((@[self.meow, self.clement]), [self.contactsSearcher filterWithString:@" "]); -} - -- (void)testFilterWithStringMatchByName { - XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"Chairman"]); - XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"Chair"]); - XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"Meow"]); - XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"Chairman Meow"]); - XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@" Chairman Meow "]); - XCTAssertEqualObjects((@[self.meow, self.clement]), ([self.contactsSearcher filterWithString:@"C"])); - XCTAssertEqualObjects(@[], [self.contactsSearcher filterWithString:@"Chairman Meowww"]); -} - -- (void)testFilterWithStringByNumber { - XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"1-323-555-1234"]); - XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"+86 10 1111 2222"]); - XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"323-555-1234"]); - XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"323.555.1234"]); - XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"3235551234"]); - XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"323"]); - XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"323 555 1234"]); - XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"+1 323 555 1234"]); - XCTAssertEqualObjects(@[self.meow], [self.contactsSearcher filterWithString:@"+13235551234"]); - XCTAssertEqualObjects((@[self.meow, self.clement]), [self.contactsSearcher filterWithString:@"1234"]); -} - -@end diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index d0fcbaf70..8756f7853 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -154,12 +154,6 @@ /* A format for the 'unblock user' action sheet title. Embeds {{the blocked user's name or phone number}}. */ "BLOCK_LIST_UNBLOCK_TITLE_FORMAT" = "Unblock %@?"; -/* A format for the message of the alert if user tries to block a user who is already blocked. Embeds {{the blocked user's name or phone number}}. */ -"BLOCK_LIST_VIEW_ALREADY_BLOCKED_ALERT_MESSAGE_FORMAT" = "%@ is already blocked."; - -/* A title of the alert if user tries to block a user who is already blocked. */ -"BLOCK_LIST_VIEW_ALREADY_BLOCKED_ALERT_TITLE" = "Already Blocked"; - /* A label for the block button in the block list view */ "BLOCK_LIST_VIEW_BLOCK_BUTTON" = "Block"; @@ -1231,6 +1225,12 @@ /* No comment provided by engineer. */ "UNSUPPORTED_FEATURE_ERROR" = "Your device doesn't support this feature."; +/* Title for alert indicating that group members can't be removed. */ +"UPDATE_GROUP_CANT_REMOVE_MEMBERS_ALERT_MESSAGE" = "You cannot remove group members. They will either have to leave, or you can create a new group without this member."; + +/* Title for alert indicating that group members can't be removed. */ +"UPDATE_GROUP_CANT_REMOVE_MEMBERS_ALERT_TITLE" = "Not Allowed"; + /* Description of CallKit to upgrading (existing) users */ "UPGRADE_EXPERIENCE_CALLKIT_DESCRIPTION" = "Answering calls from your lock screen is easy with iOS call integration. We anonymize your caller by default, so it's private too.";