From b6eb1476cbf38f0f78ab34ab3f53c30404290faf Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 10 Sep 2018 12:25:38 -0500 Subject: [PATCH] Leave group when blocking it --- .../HomeView/HomeViewController.m | 46 ++++++-------- .../OWSConversationSettingsViewController.m | 14 +++-- SignalMessaging/utils/BlockListUIUtils.h | 2 + SignalMessaging/utils/BlockListUIUtils.m | 63 ++++++++++++------- SignalMessaging/utils/ThreadUtil.h | 8 +++ SignalMessaging/utils/ThreadUtil.m | 42 +++++++++++++ .../src/Contacts/Threads/TSGroupThread.h | 3 + .../src/Contacts/Threads/TSGroupThread.m | 18 ++++++ 8 files changed, 141 insertions(+), 55 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 89958fe9c..1cd5ea5cc 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -1076,41 +1076,31 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations } TSThread *thread = [self threadForIndexPath:indexPath]; - if ([thread isKindOfClass:[TSGroupThread class]]) { + if ([thread isKindOfClass:[TSGroupThread class]]) { TSGroupThread *gThread = (TSGroupThread *)thread; if ([gThread.groupModel.groupMemberIds containsObject:[TSAccountManager localNumber]]) { - UIAlertController *removingFromGroup = [UIAlertController - alertControllerWithTitle:[NSString - stringWithFormat:NSLocalizedString(@"GROUP_REMOVING", nil), [thread name]] - message:nil - preferredStyle:UIAlertControllerStyleAlert]; - [self presentViewController:removingFromGroup animated:YES completion:nil]; - - TSOutgoingMessage *message = [TSOutgoingMessage outgoingMessageInThread:thread - groupMetaMessage:TSGroupMessageQuit - expiresInSeconds:0]; - [self.messageSender enqueueMessage:message - success:^{ - [self dismissViewControllerAnimated:YES - completion:^{ - [self deleteThread:thread]; - }]; - } - failure:^(NSError *error) { - [self dismissViewControllerAnimated:YES - completion:^{ - [OWSAlerts - showAlertWithTitle: - NSLocalizedString(@"GROUP_REMOVING_FAILED", - @"Title of alert indicating that group deletion failed.") - message:error.localizedRecoverySuggestion]; - }]; - }]; + [ThreadUtil sendLeaveGroupMessageInThread:gThread + presentingViewController:self + messageSender:self.messageSender + completion:^(NSError *_Nullable error) { + if (error) { + NSString *title = NSLocalizedString(@"GROUP_REMOVING_FAILED", + @"Title of alert indicating that group deletion failed."); + + [OWSAlerts showAlertWithTitle:title + message:error.localizedRecoverySuggestion]; + return; + } + + [self deleteThread:thread]; + }]; } else { + // MJK - turn these trailing elses into guards [self deleteThread:thread]; } } else { + // MJK - turn these trailing elses into guards [self deleteThread:thread]; } } diff --git a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m index 65dacd486..315814dd1 100644 --- a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m +++ b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m @@ -1009,10 +1009,10 @@ const CGFloat kIconViewLength = 24; DDLogWarn(@"%@ Failed to leave group with error: %@", self.logTag, error); }]; - NSMutableArray *newGroupMemberIds = [NSMutableArray arrayWithArray:gThread.groupModel.groupMemberIds]; - [newGroupMemberIds removeObject:[self.accountManager localNumber]]; - gThread.groupModel.groupMemberIds = newGroupMemberIds; - [gThread save]; + + [self.editingDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { + [gThread leaveGroupWithTransaction:transaction]; + }]; [self.navigationController popViewControllerAnimated:YES]; } @@ -1042,12 +1042,14 @@ const CGFloat kIconViewLength = 24; } [BlockListUIUtils showBlockThreadActionSheet:self.thread fromViewController:self - blockingManager:_blockingManager - contactsManager:_contactsManager + blockingManager:self.blockingManager + contactsManager:self.contactsManager + messageSender:self.messageSender completionBlock:^(BOOL isBlocked) { // Update switch state if user cancels action. blockConversationSwitch.on = isBlocked; }]; + } else { OWSAssert(isCurrentlyBlocked); if (!isCurrentlyBlocked) { diff --git a/SignalMessaging/utils/BlockListUIUtils.h b/SignalMessaging/utils/BlockListUIUtils.h index ecd22a2d7..23e46ebf6 100644 --- a/SignalMessaging/utils/BlockListUIUtils.h +++ b/SignalMessaging/utils/BlockListUIUtils.h @@ -9,6 +9,7 @@ NS_ASSUME_NONNULL_BEGIN @class Contact; @class OWSBlockingManager; @class OWSContactsManager; +@class OWSMessageSender; @class SignalAccount; @class TSThread; @@ -24,6 +25,7 @@ typedef void (^BlockActionCompletionBlock)(BOOL isBlocked); fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager contactsManager:(OWSContactsManager *)contactsManager + messageSender:(OWSMessageSender *)messageSender completionBlock:(nullable BlockActionCompletionBlock)completionBlock; + (void)showBlockPhoneNumberActionSheet:(NSString *)phoneNumber diff --git a/SignalMessaging/utils/BlockListUIUtils.m b/SignalMessaging/utils/BlockListUIUtils.m index 061c610a7..bb03ea3fe 100644 --- a/SignalMessaging/utils/BlockListUIUtils.m +++ b/SignalMessaging/utils/BlockListUIUtils.m @@ -10,6 +10,7 @@ #import #import #import +#import NS_ASSUME_NONNULL_BEGIN @@ -23,6 +24,7 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager contactsManager:(OWSContactsManager *)contactsManager + messageSender:(OWSMessageSender *)messageSender completionBlock:(nullable BlockActionCompletionBlock)completionBlock { if ([thread isKindOfClass:[TSContactThread class]]) { @@ -34,10 +36,10 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); completionBlock:completionBlock]; } else if ([thread isKindOfClass:[TSGroupThread class]]) { TSGroupThread *groupThread = (TSGroupThread *)thread; - [self showBlockGroupActionSheet:groupThread.groupModel - displayName:groupThread.name + [self showBlockGroupActionSheet:groupThread fromViewController:fromViewController blockingManager:blockingManager + messageSender:messageSender completionBlock:completionBlock]; } else { OWSFail(@"unexpected thread type: %@", thread.class); @@ -142,20 +144,20 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); [fromViewController presentViewController:actionSheetController animated:YES completion:nil]; } -+ (void)showBlockGroupActionSheet:(TSGroupModel *)groupModel - displayName:(NSString *)displayName ++ (void)showBlockGroupActionSheet:(TSGroupThread *)groupThread fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager + messageSender:(OWSMessageSender *)messageSender completionBlock:(nullable BlockActionCompletionBlock)completionBlock { - OWSAssert(displayName.length > 0); + OWSAssert(groupThread); OWSAssert(fromViewController); OWSAssert(blockingManager); NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_BLOCK_GROUP_TITLE_FORMAT", @"A format for the 'block group' action sheet title. Embeds the {{group name}}."), - [self formatDisplayNameForAlertTitle:displayName]]; + [self formatDisplayNameForAlertTitle:groupThread.name]]; UIAlertController *actionSheetController = [UIAlertController alertControllerWithTitle:title @@ -167,10 +169,10 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); actionWithTitle:NSLocalizedString(@"BLOCK_LIST_BLOCK_BUTTON", @"Button label for the 'block' button") style:UIAlertActionStyleDestructive handler:^(UIAlertAction *_Nonnull action) { - [self blockGroup:groupModel - displayName:displayName + [self blockGroup:groupThread fromViewController:fromViewController blockingManager:blockingManager + messageSender:messageSender completionBlock:^(UIAlertAction *ignore) { if (completionBlock) { completionBlock(YES); @@ -218,27 +220,46 @@ typedef void (^BlockAlertCompletionBlock)(UIAlertAction *action); completionBlock:completionBlock]; } -+ (void)blockGroup:(TSGroupModel *)groupModel - displayName:(NSString *)displayName ++ (void)blockGroup:(TSGroupThread *)groupThread fromViewController:(UIViewController *)fromViewController blockingManager:(OWSBlockingManager *)blockingManager + messageSender:(OWSMessageSender *)messageSender completionBlock:(BlockAlertCompletionBlock)completionBlock { - OWSAssert(displayName.length > 0); + OWSAssert(groupThread); OWSAssert(fromViewController); OWSAssert(blockingManager); - [blockingManager addBlockedGroupId:groupModel.groupId]; + // block the group regardless of the ability to deliver the "leave group" message. + [blockingManager addBlockedGroupId:groupThread.groupModel.groupId]; - [self showOkAlertWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_BLOCKED_GROUP_ALERT_TITLE", - @"The title of the 'group blocked' alert.") - message:[NSString - stringWithFormat:NSLocalizedString(@"BLOCK_LIST_VIEW_BLOCKED_ALERT_MESSAGE_FORMAT", - @"The message format of the 'conversation blocked' alert. " - @"Embeds the {{conversation title}}."), - [self formatDisplayNameForAlertMessage:displayName]] - fromViewController:fromViewController - completionBlock:completionBlock]; + // blockingManager.addBlocked* creates sneaky transactions, so we can't pass in a transaction + // via params and instead have to create our own sneaky transaction here. + [groupThread leaveGroupWithSneakyTransaction]; + + [ThreadUtil + sendLeaveGroupMessageInThread:groupThread + presentingViewController:fromViewController + messageSender:messageSender + completion:^(NSError *_Nullable error) { + if (error) { + DDLogError(@"Failed to leave blocked group with error: %@", error); + } + + [self + showOkAlertWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_BLOCKED_GROUP_ALERT_TITLE", + @"The title of the 'group blocked' alert.") + message:[NSString + stringWithFormat: + NSLocalizedString( + @"BLOCK_LIST_VIEW_BLOCKED_ALERT_MESSAGE_FORMAT", + @"The message format of the 'conversation blocked' " + @"alert. " + @"Embeds the {{conversation title}}."), + [self formatDisplayNameForAlertMessage:groupThread.name]] + fromViewController:fromViewController + completionBlock:completionBlock]; + }]; } #pragma mark - Unblock diff --git a/SignalMessaging/utils/ThreadUtil.h b/SignalMessaging/utils/ThreadUtil.h index c2216697f..05b9bf69c 100644 --- a/SignalMessaging/utils/ThreadUtil.h +++ b/SignalMessaging/utils/ThreadUtil.h @@ -10,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN @class OWSUnreadIndicator; @class SignalAttachment; @class TSContactThread; +@class TSGroupThread; @class TSInteraction; @class TSThread; @class YapDatabaseConnection; @@ -71,6 +72,13 @@ NS_ASSUME_NONNULL_BEGIN messageSender:(OWSMessageSender *)messageSender completion:(void (^_Nullable)(NSError *_Nullable error))completion; ++ (void)sendLeaveGroupMessageInThread:(TSGroupThread *)thread + presentingViewController:(UIViewController *)presentingViewController + messageSender:(OWSMessageSender *)messageSender + completion:(void (^_Nullable)(NSError *_Nullable error))completion; + +#pragma mark - dynamic interactions + // This method will create and/or remove any offers and indicators // necessary for this thread. This includes: // diff --git a/SignalMessaging/utils/ThreadUtil.m b/SignalMessaging/utils/ThreadUtil.m index 30fc8b826..b711ed71d 100644 --- a/SignalMessaging/utils/ThreadUtil.m +++ b/SignalMessaging/utils/ThreadUtil.m @@ -212,6 +212,48 @@ NS_ASSUME_NONNULL_BEGIN return message; } ++ (void)sendLeaveGroupMessageInThread:(TSGroupThread *)thread + presentingViewController:(UIViewController *)presentingViewController + messageSender:(OWSMessageSender *)messageSender + completion:(void (^_Nullable)(NSError *_Nullable error))completion +{ + OWSAssert([thread isKindOfClass:[TSGroupThread class]]); + OWSAssert(presentingViewController); + OWSAssert(messageSender); + + NSString *title = [NSString + stringWithFormat:NSLocalizedString(@"GROUP_REMOVING", @"Modal text when removing a group"), thread.name]; + UIAlertController *removingFromGroup = + [UIAlertController alertControllerWithTitle:title message:title preferredStyle:UIAlertControllerStyleAlert]; + [presentingViewController presentViewController:removingFromGroup animated:YES completion:nil]; + + TSOutgoingMessage *message = + [TSOutgoingMessage outgoingMessageInThread:thread groupMetaMessage:TSGroupMessageQuit expiresInSeconds:0]; + [messageSender enqueueMessage:message + success:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + [presentingViewController dismissViewControllerAnimated:YES + completion:^{ + if (completion) { + completion(nil); + } + }]; + }); + } + failure:^(NSError *error) { + dispatch_async(dispatch_get_main_queue(), ^{ + [presentingViewController dismissViewControllerAnimated:YES + completion:^{ + if (completion) { + completion(error); + } + }]; + }); + }]; +} + +#pragma mark - Dynamic Interactions + + (ThreadDynamicInteractions *)ensureDynamicInteractionsForThread:(TSThread *)thread contactsManager:(OWSContactsManager *)contactsManager blockingManager:(OWSBlockingManager *)blockingManager diff --git a/SignalServiceKit/src/Contacts/Threads/TSGroupThread.h b/SignalServiceKit/src/Contacts/Threads/TSGroupThread.h index 15761d3a4..60d49ba8f 100644 --- a/SignalServiceKit/src/Contacts/Threads/TSGroupThread.h +++ b/SignalServiceKit/src/Contacts/Threads/TSGroupThread.h @@ -33,6 +33,9 @@ extern NSString *const TSGroupThread_NotificationKey_UniqueId; + (NSArray *)groupThreadsWithRecipientId:(NSString *)recipientId transaction:(YapDatabaseReadWriteTransaction *)transaction; +- (void)leaveGroupWithSneakyTransaction; +- (void)leaveGroupWithTransaction:(YapDatabaseReadWriteTransaction *)transaction; + - (void)updateAvatarWithAttachmentStream:(TSAttachmentStream *)attachmentStream; - (void)updateAvatarWithAttachmentStream:(TSAttachmentStream *)attachmentStream transaction:(YapDatabaseReadWriteTransaction *)transaction; diff --git a/SignalServiceKit/src/Contacts/Threads/TSGroupThread.m b/SignalServiceKit/src/Contacts/Threads/TSGroupThread.m index a06bdb092..5a3de0634 100644 --- a/SignalServiceKit/src/Contacts/Threads/TSGroupThread.m +++ b/SignalServiceKit/src/Contacts/Threads/TSGroupThread.m @@ -178,6 +178,24 @@ NSString *const TSGroupThread_NotificationKey_UniqueId = @"TSGroupThread_Notific return self.groupModel.groupName ? self.groupModel.groupName : NSLocalizedString(@"NEW_GROUP_DEFAULT_TITLE", @""); } +- (void)leaveGroupWithSneakyTransaction +{ + [self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { + [self leaveGroupWithTransaction:transaction]; + }]; +} + +- (void)leaveGroupWithTransaction:(YapDatabaseReadWriteTransaction *)transaction +{ + NSMutableArray *newGroupMemberIds = [self.groupModel.groupMemberIds mutableCopy]; + [newGroupMemberIds removeObject:[TSAccountManager localNumber]]; + + self.groupModel.groupMemberIds = newGroupMemberIds; + [self saveWithTransaction:transaction]; +} + +#pragma mark - Avatar + - (void)updateAvatarWithAttachmentStream:(TSAttachmentStream *)attachmentStream { [self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {