diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index b7da912f8..4a133156a 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 34330A5E1E787BD800DF2FB9 /* ElegantIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34330A5D1E787BD800DF2FB9 /* ElegantIcons.ttf */; }; 34330A611E788EA900DF2FB9 /* AttachmentUploadView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34330A601E788EA900DF2FB9 /* AttachmentUploadView.m */; }; 34330AA31E79686200DF2FB9 /* OWSProgressView.m in Sources */ = {isa = PBXBuildFile; fileRef = 34330AA21E79686200DF2FB9 /* OWSProgressView.m */; }; + 343D3D9B1E9283F100165CA4 /* BlockListUIUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 343D3D9A1E9283F100165CA4 /* BlockListUIUtils.m */; }; 344F2F671E57A932000D9322 /* UIViewController+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 344F2F661E57A932000D9322 /* UIViewController+OWS.m */; }; 34535D821E256BE9008A4747 /* UIView+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 34535D811E256BE9008A4747 /* UIView+OWS.m */; }; 345671011E89A5F1006EE662 /* ThreadUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 345671001E89A5F1006EE662 /* ThreadUtil.m */; }; @@ -347,6 +348,8 @@ 34330A601E788EA900DF2FB9 /* AttachmentUploadView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AttachmentUploadView.m; sourceTree = ""; }; 34330AA11E79686200DF2FB9 /* OWSProgressView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSProgressView.h; sourceTree = ""; }; 34330AA21E79686200DF2FB9 /* OWSProgressView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSProgressView.m; sourceTree = ""; }; + 343D3D991E9283F100165CA4 /* BlockListUIUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlockListUIUtils.h; sourceTree = ""; }; + 343D3D9A1E9283F100165CA4 /* BlockListUIUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlockListUIUtils.m; sourceTree = ""; }; 344F2F651E57A932000D9322 /* UIViewController+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIViewController+OWS.h"; path = "util/UIViewController+OWS.h"; sourceTree = ""; }; 344F2F661E57A932000D9322 /* UIViewController+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIViewController+OWS.m"; path = "util/UIViewController+OWS.m"; sourceTree = ""; }; 34535D801E256BE9008A4747 /* UIView+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+OWS.h"; sourceTree = ""; }; @@ -823,6 +826,8 @@ 34B3F8381E8DF1700035BE1A /* AttachmentApprovalViewController.swift */, 34B3F8391E8DF1700035BE1A /* AttachmentSharing.h */, 34B3F83A1E8DF1700035BE1A /* AttachmentSharing.m */, + 343D3D991E9283F100165CA4 /* BlockListUIUtils.h */, + 343D3D9A1E9283F100165CA4 /* BlockListUIUtils.m */, 34B3F89A1E8DF3270035BE1A /* BlockListViewController.h */, 34B3F89B1E8DF3270035BE1A /* BlockListViewController.m */, 34B3F83B1E8DF1700035BE1A /* CallViewController.swift */, @@ -1958,6 +1963,7 @@ 34B3F88F1E8DF1710035BE1A /* RegistrationViewController.m in Sources */, 34B3F8901E8DF1710035BE1A /* SettingsTableViewController.m in Sources */, 34FD93701E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m in Sources */, + 343D3D9B1E9283F100165CA4 /* BlockListUIUtils.m in Sources */, 34B3F8931E8DF1710035BE1A /* SignalsNavigationController.m in Sources */, 76EB063A18170B33006006FC /* FunctionalUtil.m in Sources */, 76EB058A18170B33006006FC /* Release.m in Sources */, diff --git a/Signal/Images.xcassets/ic_block.imageset/Contents.json b/Signal/Images.xcassets/ic_block.imageset/Contents.json new file mode 100644 index 000000000..9f5a8e00e --- /dev/null +++ b/Signal/Images.xcassets/ic_block.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_block@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "ic_block@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "ic_block@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/ic_block.imageset/ic_block@1x.png b/Signal/Images.xcassets/ic_block.imageset/ic_block@1x.png new file mode 100644 index 000000000..488f96f0d Binary files /dev/null and b/Signal/Images.xcassets/ic_block.imageset/ic_block@1x.png differ diff --git a/Signal/Images.xcassets/ic_block.imageset/ic_block@2x.png b/Signal/Images.xcassets/ic_block.imageset/ic_block@2x.png new file mode 100644 index 000000000..a87f1e334 Binary files /dev/null and b/Signal/Images.xcassets/ic_block.imageset/ic_block@2x.png differ diff --git a/Signal/Images.xcassets/ic_block.imageset/ic_block@3x.png b/Signal/Images.xcassets/ic_block.imageset/ic_block@3x.png new file mode 100644 index 000000000..a141bd0ce Binary files /dev/null and b/Signal/Images.xcassets/ic_block.imageset/ic_block@3x.png differ diff --git a/Signal/Images.xcassets/table_ic_block.imageset/Contents.json b/Signal/Images.xcassets/table_ic_block.imageset/Contents.json new file mode 100644 index 000000000..d3f232d57 --- /dev/null +++ b/Signal/Images.xcassets/table_ic_block.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "table_ic_block@1x.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "table_ic_block@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "table_ic_block@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@1x.png b/Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@1x.png new file mode 100644 index 000000000..2c874458b Binary files /dev/null and b/Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@1x.png differ diff --git a/Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@2x.png b/Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@2x.png new file mode 100644 index 000000000..66d61f3ba Binary files /dev/null and b/Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@2x.png differ diff --git a/Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@3x.png b/Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@3x.png new file mode 100644 index 000000000..8a225841c Binary files /dev/null and b/Signal/Images.xcassets/table_ic_block.imageset/table_ic_block@3x.png differ diff --git a/Signal/src/Storyboard/Main.storyboard b/Signal/src/Storyboard/Main.storyboard index 7c24dbcc5..2459c564b 100644 --- a/Signal/src/Storyboard/Main.storyboard +++ b/Signal/src/Storyboard/Main.storyboard @@ -446,18 +446,40 @@ - + + + + + + + + + + + + + + + + + - + - + @@ -506,20 +528,20 @@ - + - + @@ -528,7 +550,7 @@ - + @@ -544,7 +566,7 @@ - + @@ -555,7 +577,7 @@ - + @@ -575,7 +597,7 @@ - + @@ -592,7 +614,7 @@ - + @@ -622,22 +644,21 @@ + - - - + diff --git a/Signal/src/ViewControllers/AddToBlockListViewController.m b/Signal/src/ViewControllers/AddToBlockListViewController.m index ccc5fc369..b55727743 100644 --- a/Signal/src/ViewControllers/AddToBlockListViewController.m +++ b/Signal/src/ViewControllers/AddToBlockListViewController.m @@ -3,6 +3,7 @@ // #import "AddToBlockListViewController.h" +#import "BlockListUIUtils.h" #import "ContactTableViewCell.h" #import "CountryCodeViewController.h" #import "Environment.h" @@ -13,8 +14,8 @@ #import "UIUtil.h" #import "UIView+OWS.h" #import "ViewControllerUtils.h" -#import #import +#import NS_ASSUME_NONNULL_BEGIN @@ -273,25 +274,17 @@ NSString *const kContactsTable_CellReuseIdentifier = @"kContactsTable_CellReuseI PhoneNumber *parsedPhoneNumber = [PhoneNumber tryParsePhoneNumberFromUserSpecifiedText:possiblePhoneNumber]; OWSAssert(parsedPhoneNumber); - [_blockingManager addBlockedPhoneNumber:[parsedPhoneNumber toE164]]; - - UIAlertController *controller = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_PHONE_NUMBER_BLOCKED_ALERT_TITLE", - @"The title of the 'phone number blocked' alert in the block view.") - message:[NSString - stringWithFormat:NSLocalizedString( - @"BLOCK_LIST_VIEW_PHONE_NUMBER_BLOCKED_ALERT_MESSAGE_FORMAT", - @"The message format of the 'phone number blocked' alert in " - @"the block view. Embeds {{the blocked phone number}}."), - [parsedPhoneNumber toE164]] - preferredStyle:UIAlertControllerStyleAlert]; - - [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) - style:UIAlertActionStyleDefault - handler:nil]]; - [self presentViewController:controller animated:YES completion:nil]; - - _phoneNumberTextField.text = nil; + __weak AddToBlockListViewController *weakSelf = self; + [BlockListUIUtils showBlockPhoneNumberActionSheet:[parsedPhoneNumber toE164] + displayName:[parsedPhoneNumber toE164] + fromViewController:self + blockingManager:_blockingManager + completionBlock:^(BOOL isBlocked) { + if (isBlocked) { + // Clear phone number text field is block succeeds. + weakSelf.phoneNumberTextField.text = nil; + } + }]; } - (void)textFieldDidChange:(id)sender @@ -417,62 +410,10 @@ NSString *const kContactsTable_CellReuseIdentifier = @"kContactsTable_CellReuseI [tableView deselectRowAtIndexPath:indexPath animated:YES]; Contact *contact = self.contacts[(NSUInteger)indexPath.item]; - [self showBlockActionSheet:contact]; -} - -- (void)showBlockActionSheet:(Contact *)contact -{ - OWSAssert(contact); - - NSString *displayName = contact.fullName; - - NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_BLOCK_TITLE_FORMAT", - @"A format for the 'block phone number' action sheet title."), - displayName]; - - UIAlertController *actionSheetController = - [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - - __weak AddToBlockListViewController *weakSelf = self; - UIAlertAction *unblockAction = [UIAlertAction - actionWithTitle:NSLocalizedString(@"BLOCK_LIST_BLOCK_BUTTON", @"Button label for the 'block' button") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [weakSelf blockContact:contact displayName:displayName]; - }]; - [actionSheetController addAction:unblockAction]; - - UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"") - style:UIAlertActionStyleCancel - handler:nil]; - [actionSheetController addAction:dismissAction]; - - [self presentViewController:actionSheetController animated:YES completion:nil]; -} - -- (void)blockContact:(Contact *)contact displayName:(NSString *)displayName -{ - for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) { - if (phoneNumber.toE164.length > 0) { - [_blockingManager addBlockedPhoneNumber:phoneNumber.toE164]; - } - } - - UIAlertController *controller = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_CONTACT_BLOCKED_ALERT_TITLE", - @"The title of the 'contact blocked' alert in the block view.") - message:[NSString stringWithFormat:NSLocalizedString( - @"BLOCK_LIST_VIEW_CONTACT_BLOCKED_ALERT_MESSAGE_FORMAT", - @"The message format of the 'contact blocked' " - @"alert in the block view. It is populated with the " - @"blocked contact's name."), - displayName] - preferredStyle:UIAlertControllerStyleAlert]; - - [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) - style:UIAlertActionStyleDefault - handler:nil]]; - [self presentViewController:controller animated:YES completion:nil]; + [BlockListUIUtils showBlockContactActionSheet:contact + fromViewController:self + blockingManager:_blockingManager + completionBlock:nil]; } #pragma mark - UIScrollViewDelegate diff --git a/Signal/src/ViewControllers/BlockListUIUtils.h b/Signal/src/ViewControllers/BlockListUIUtils.h new file mode 100644 index 000000000..23fb34975 --- /dev/null +++ b/Signal/src/ViewControllers/BlockListUIUtils.h @@ -0,0 +1,33 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import + +@class Contact; +@class OWSBlockingManager; + +typedef void (^BlockActionCompletionBlock)(BOOL isBlocked); + +@interface BlockListUIUtils : NSObject + +- (instancetype)init NS_UNAVAILABLE; + ++ (void)showBlockContactActionSheet:(Contact *)contact + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockActionCompletionBlock)completionBlock; + ++ (void)showBlockPhoneNumberActionSheet:(NSString *)phoneNumber + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockActionCompletionBlock)completionBlock; + ++ (void)showUnblockPhoneNumberActionSheet:(NSString *)phoneNumber + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockActionCompletionBlock)completionBlock; + +@end diff --git a/Signal/src/ViewControllers/BlockListUIUtils.m b/Signal/src/ViewControllers/BlockListUIUtils.m new file mode 100644 index 000000000..713cb0136 --- /dev/null +++ b/Signal/src/ViewControllers/BlockListUIUtils.m @@ -0,0 +1,241 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "BlockListUIUtils.h" +#import "PhoneNumber.h" +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@implementation BlockListUIUtils + ++ (void)showBlockContactActionSheet:(Contact *)contact + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockActionCompletionBlock)completionBlock +{ + NSMutableArray *phoneNumbers = [NSMutableArray new]; + for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) { + if (phoneNumber.toE164.length > 0) { + [phoneNumbers addObject:phoneNumber.toE164]; + } + } + if (phoneNumbers.count < 1) { + [self showBlockFailedAlert:fromViewController]; + if (completionBlock) { + completionBlock(NO); + } + return; + } + [self showBlockPhoneNumbersActionSheet:phoneNumbers + displayName:contact.fullName + fromViewController:fromViewController + blockingManager:blockingManager + completionBlock:completionBlock]; +} + ++ (void)showBlockPhoneNumberActionSheet:(NSString *)phoneNumber + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockActionCompletionBlock)completionBlock +{ + [self showBlockPhoneNumbersActionSheet:@[ phoneNumber ] + displayName:displayName + fromViewController:fromViewController + blockingManager:blockingManager + completionBlock:completionBlock]; +} + ++ (void)showBlockPhoneNumbersActionSheet:(NSArray *)phoneNumbers + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockActionCompletionBlock)completionBlock +{ + OWSAssert(phoneNumbers.count > 0); + OWSAssert(displayName.length > 0); + OWSAssert(fromViewController); + OWSAssert(blockingManager); + + NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_BLOCK_TITLE_FORMAT", + @"A format for the 'block user' action sheet title."), + displayName]; + + UIAlertController *actionSheetController = + [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; + + UIAlertAction *unblockAction = [UIAlertAction + actionWithTitle:NSLocalizedString(@"BLOCK_LIST_BLOCK_BUTTON", @"Button label for the 'block' button") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *_Nonnull action) { + [self blockPhoneNumbers:phoneNumbers + displayName:displayName + fromViewController:fromViewController + blockingManager:blockingManager]; + if (completionBlock) { + completionBlock(YES); + } + }]; + [actionSheetController addAction:unblockAction]; + + UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"") + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *_Nonnull action) { + if (completionBlock) { + completionBlock(NO); + } + }]; + [actionSheetController addAction:dismissAction]; + + [fromViewController presentViewController:actionSheetController animated:YES completion:nil]; +} + ++ (void)blockPhoneNumbers:(NSArray *)phoneNumbers + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager +{ + OWSAssert(phoneNumbers.count > 0); + OWSAssert(displayName.length > 0); + OWSAssert(fromViewController); + OWSAssert(blockingManager); + + for (NSString *phoneNumber in phoneNumbers) { + OWSAssert(phoneNumber.length > 0); + [blockingManager addBlockedPhoneNumber:phoneNumber]; + } + + [self showOkAlertWithTitle:NSLocalizedString( + @"BLOCK_LIST_VIEW_BLOCKED_ALERT_TITLE", @"The title of the 'user blocked' alert.") + message:[NSString + stringWithFormat:NSLocalizedString(@"BLOCK_LIST_VIEW_BLOCKED_ALERT_MESSAGE_FORMAT", + @"The message format of the 'user blocked' " + @"alert. Embeds {{the blocked user's name or phone number}}."), + displayName] + fromViewController:fromViewController]; +} + ++ (void)showUnblockPhoneNumberActionSheet:(NSString *)phoneNumber + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager + completionBlock:(BlockActionCompletionBlock)completionBlock +{ + OWSAssert(phoneNumber.length > 0); + OWSAssert(displayName.length > 0); + OWSAssert(fromViewController); + OWSAssert(blockingManager); + + NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_TITLE_FORMAT", + @"A format for the 'unblock user' action sheet title."), + displayName]; + + UIAlertController *actionSheetController = + [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; + + UIAlertAction *unblockAction = [UIAlertAction + actionWithTitle:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_BUTTON", @"Button label for the 'unblock' button") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *_Nonnull action) { + [BlockListUIUtils unblockPhoneNumber:phoneNumber + displayName:displayName + fromViewController:fromViewController + blockingManager:blockingManager]; + if (completionBlock) { + completionBlock(NO); + } + }]; + [actionSheetController addAction:unblockAction]; + + UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"") + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *_Nonnull action) { + if (completionBlock) { + completionBlock(YES); + } + }]; + [actionSheetController addAction:dismissAction]; + + [fromViewController presentViewController:actionSheetController animated:YES completion:nil]; +} + ++ (void)unblockPhoneNumber:(NSString *)phoneNumber + displayName:(NSString *)displayName + fromViewController:(UIViewController *)fromViewController + blockingManager:(OWSBlockingManager *)blockingManager +{ + OWSAssert(phoneNumber.length > 0); + OWSAssert(displayName.length > 0); + OWSAssert(fromViewController); + OWSAssert(blockingManager); + + [blockingManager removeBlockedPhoneNumber:phoneNumber]; + + [self showOkAlertWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_TITLE", + @"The title of the 'user unblocked' alert.") + message:[NSString + stringWithFormat:NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_MESSAGE_FORMAT", + @"The message format of the 'user unblocked' " + @"alert. It is populated with the " + @"blocked phone number."), + displayName] + fromViewController:fromViewController]; +} + ++ (void)showBlockFailedAlert:(UIViewController *)fromViewController +{ + OWSAssert(fromViewController); + + [self showOkAlertWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_BLOCK_FAILED_ALERT_TITLE", + @"The title of the 'block user failed' alert.") + message:NSLocalizedString(@"BLOCK_LIST_VIEW_BLOCK_FAILED_ALERT_MESSAGE", + @"The title of the 'block user failed' alert.") + fromViewController:fromViewController]; +} + ++ (void)showUnblockFailedAlert:(UIViewController *)fromViewController +{ + OWSAssert(fromViewController); + + [self showOkAlertWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCK_FAILED_ALERT_TITLE", + @"The title of the 'unblock user failed' alert.") + message:NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCK_FAILED_ALERT_MESSAGE", + @"The title of the 'unblock user failed' alert.") + fromViewController:fromViewController]; +} + ++ (void)showOkAlertWithTitle:(NSString *)title + message:(NSString *)message + fromViewController:(UIViewController *)fromViewController +{ + OWSAssert(title.length > 0); + OWSAssert(message.length > 0); + OWSAssert(fromViewController); + + UIAlertController *controller = + [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; + + [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) + style:UIAlertActionStyleDefault + handler:nil]]; + [fromViewController presentViewController:controller animated: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/ViewControllers/BlockListViewController.m b/Signal/src/ViewControllers/BlockListViewController.m index 701a85549..8732e0936 100644 --- a/Signal/src/ViewControllers/BlockListViewController.m +++ b/Signal/src/ViewControllers/BlockListViewController.m @@ -4,6 +4,7 @@ #import "BlockListViewController.h" #import "AddToBlockListViewController.h" +#import "BlockListUIUtils.h" #import "Environment.h" #import "OWSContactsManager.h" #import "PhoneNumber.h" @@ -168,7 +169,11 @@ typedef NS_ENUM(NSInteger, BlockListViewControllerSection) { case BlockListViewControllerSection_BlockList: { NSString *phoneNumber = _blockedPhoneNumbers[(NSUInteger)indexPath.item]; NSString *displayName = [self displayNameForIndexPath:indexPath]; - [self showUnblockActionSheet:phoneNumber displayName:displayName]; + [BlockListUIUtils showUnblockPhoneNumberActionSheet:phoneNumber + displayName:displayName + fromViewController:self + blockingManager:_blockingManager + completionBlock:nil]; break; } default: @@ -176,56 +181,6 @@ typedef NS_ENUM(NSInteger, BlockListViewControllerSection) { } } -- (void)showUnblockActionSheet:(NSString *)phoneNumber displayName:(NSString *)displayName -{ - OWSAssert(phoneNumber.length > 0); - OWSAssert(displayName.length > 0); - - NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_TITLE_FORMAT", - @"A format for the 'unblock phone number' action sheet title."), - displayName]; - - UIAlertController *actionSheetController = - [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - - __weak BlockListViewController *weakSelf = self; - UIAlertAction *unblockAction = [UIAlertAction - actionWithTitle:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_BUTTON", @"Button label for the 'unblock' button") - style:UIAlertActionStyleDefault - handler:^(UIAlertAction *_Nonnull action) { - [weakSelf unblockPhoneNumber:phoneNumber displayName:displayName]; - }]; - [actionSheetController addAction:unblockAction]; - - UIAlertAction *dismissAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"") - style:UIAlertActionStyleCancel - handler:nil]; - [actionSheetController addAction:dismissAction]; - - [self presentViewController:actionSheetController animated:YES completion:nil]; -} - -- (void)unblockPhoneNumber:(NSString *)phoneNumber displayName:(NSString *)displayName -{ - [_blockingManager removeBlockedPhoneNumber:phoneNumber]; - - UIAlertController *controller = [UIAlertController - alertControllerWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_TITLE", - @"The title of the 'phone number unblocked' alert in the block view.") - message:[NSString stringWithFormat:NSLocalizedString( - @"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_MESSAGE_FORMAT", - @"The message format of the 'phone number unblocked' " - @"alert in the block view. It is populated with the " - @"blocked phone number."), - displayName] - preferredStyle:UIAlertControllerStyleAlert]; - - [controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) - style:UIAlertActionStyleDefault - handler:nil]]; - [self presentViewController:controller animated:YES completion:nil]; -} - #pragma mark - Actions - (void)blockedPhoneNumbersDidChange:(id)notification diff --git a/Signal/src/ViewControllers/DebugUITableViewController.m b/Signal/src/ViewControllers/DebugUITableViewController.m index 5203bf91f..2af90b643 100644 --- a/Signal/src/ViewControllers/DebugUITableViewController.m +++ b/Signal/src/ViewControllers/DebugUITableViewController.m @@ -34,41 +34,45 @@ NS_ASSUME_NONNULL_BEGIN OWSTableContents *contents = [OWSTableContents new]; contents.title = @"Debug: Conversation"; - - [contents addSection:[OWSTableSection sectionWithTitle:@"Messages View" - items:@[ - [OWSTableItem actionWithTitle:@"Send 10 messages (1/sec.)" - actionBlock:^{ - [DebugUITableViewController sendTextMessage:10 - thread:thread]; - }], - [OWSTableItem actionWithTitle:@"Send 100 messages (1/sec.)" - actionBlock:^{ - [DebugUITableViewController sendTextMessage:100 - thread:thread]; - }], - [OWSTableItem actionWithTitle:@"Send text/x-signal-plain" - actionBlock:^{ - [DebugUITableViewController sendOversizeTextMessage:thread]; - }], - [OWSTableItem actionWithTitle:@"Send unknown mimetype" - actionBlock:^{ - [DebugUITableViewController sendRandomAttachment:thread - uti:SignalAttachment.kUnknownTestAttachmentUTI]; - }], - [OWSTableItem actionWithTitle:@"Send pdf" - actionBlock:^{ - [DebugUITableViewController sendRandomAttachment:thread - uti:(NSString *) kUTTypePDF]; - }], - ]]]; + + [contents + addSection:[OWSTableSection + sectionWithTitle:@"Messages View" + items:@[ + [OWSTableItem itemWithTitle:@"Send 10 messages (1/sec.)" + actionBlock:^{ + [DebugUITableViewController sendTextMessage:10 thread:thread]; + }], + [OWSTableItem itemWithTitle:@"Send 100 messages (1/sec.)" + actionBlock:^{ + [DebugUITableViewController sendTextMessage:100 + thread:thread]; + }], + [OWSTableItem itemWithTitle:@"Send text/x-signal-plain" + actionBlock:^{ + [DebugUITableViewController sendOversizeTextMessage:thread]; + }], + [OWSTableItem + itemWithTitle:@"Send unknown mimetype" + actionBlock:^{ + [DebugUITableViewController + sendRandomAttachment:thread + uti:SignalAttachment.kUnknownTestAttachmentUTI]; + }], + [OWSTableItem itemWithTitle:@"Send pdf" + actionBlock:^{ + [DebugUITableViewController + sendRandomAttachment:thread + uti:(NSString *)kUTTypePDF]; + }], + ]]]; [contents addSection:[OWSTableSection sectionWithTitle:@"Print to Debug Log" - items:@[ [OWSTableItem actionWithTitle:@"Print all sessions" - actionBlock:^{ - [[TSStorageManager sharedManager] - printAllSessions]; - }] ]]]; + items:@[ [OWSTableItem itemWithTitle:@"Print all sessions" + actionBlock:^{ + [[TSStorageManager sharedManager] + printAllSessions]; + }] ]]]; DebugUITableViewController *viewController = [DebugUITableViewController new]; viewController.contents = contents; diff --git a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h index e3c1e1e79..743ea82f1 100644 --- a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h +++ b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.h @@ -1,13 +1,15 @@ -// Created by Michael Kirk on 9/21/16. -// Copyright © 2016 Open Whisper Systems. All rights reserved. +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// +#import "OWSTableViewController.h" #import NS_ASSUME_NONNULL_BEGIN @class TSThread; -@interface OWSConversationSettingsTableViewController : UITableViewController +@interface OWSConversationSettingsTableViewController : OWSTableViewController - (void)configureWithThread:(TSThread *)thread; - (void)presentedModalWasDismissed; diff --git a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m index 84db32991..750d72f0f 100644 --- a/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m +++ b/Signal/src/ViewControllers/OWSConversationSettingsTableViewController.m @@ -3,10 +3,12 @@ // #import "OWSConversationSettingsTableViewController.h" +#import "BlockListUIUtils.h" #import "Environment.h" #import "FingerprintViewController.h" #import "NewGroupViewController.h" #import "OWSAvatarBuilder.h" +#import "OWSBlockingManager.h" #import "OWSContactsManager.h" #import "PhoneNumber.h" #import "ShowGroupMembersViewController.h" @@ -27,23 +29,6 @@ NS_ASSUME_NONNULL_BEGIN -typedef NS_ENUM(NSUInteger, OWSConversationSettingsTableViewControllerSection) { - OWSConversationSettingsTableViewControllerSectionContact, - OWSConversationSettingsTableViewControllerSectionGroup -}; - -typedef NS_ENUM(NSUInteger, OWSConversationSettingsTableViewControllerContactCellIndex) { - OWSConversationSettingsTableViewControllerCellIndexShowFingerprint, - OWSConversationSettingsTableViewControllerCellIndexToggleDisappearingMessages, - OWSConversationSettingsTableViewControllerCellIndexSetDisappearingMessagesDuration -}; - -typedef NS_ENUM(NSUInteger, OWSConversationSettingsTableViewControllerGroupCellIndex) { - OWSConversationSettingsTableViewControllerCellIndexUpdateGroup, - OWSConversationSettingsTableViewControllerCellIndexLeaveGroup, - OWSConversationSettingsTableViewControllerCellIndexSeeGroupMembers -}; - static NSString *const OWSConversationSettingsTableViewControllerSegueUpdateGroup = @"OWSConversationSettingsTableViewControllerSegueUpdateGroup"; static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupMembers = @@ -51,22 +36,20 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM @interface OWSConversationSettingsTableViewController () -@property (strong, nonatomic) IBOutlet UITableViewCell *verifyPrivacyCell; -@property (strong, nonatomic) IBOutlet UITableViewCell *toggleDisappearingMessagesCell; -@property (strong, nonatomic) IBOutlet UILabel *toggleDisappearingMessagesTitleLabel; -@property (strong, nonatomic) IBOutlet UILabel *toggleDisappearingMessagesDescriptionLabel; -@property (strong, nonatomic) IBOutlet UISwitch *disappearingMessagesSwitch; -@property (strong, nonatomic) IBOutlet UITableViewCell *disappearingMessagesDurationCell; -@property (strong, nonatomic) IBOutlet UILabel *disappearingMessagesDurationLabel; -@property (strong, nonatomic) IBOutlet UISlider *disappearingMessagesDurationSlider; +@property (nonatomic) IBOutlet UITableViewCell *verifyPrivacyCell; +@property (nonatomic) IBOutlet UITableViewCell *blocklistStateCell; +@property (nonatomic) IBOutlet UITableViewCell *toggleDisappearingMessagesCell; +@property (nonatomic) IBOutlet UILabel *toggleDisappearingMessagesTitleLabel; +@property (nonatomic) IBOutlet UILabel *toggleDisappearingMessagesDescriptionLabel; +@property (nonatomic) IBOutlet UISwitch *disappearingMessagesSwitch; +@property (nonatomic) IBOutlet UITableViewCell *disappearingMessagesDurationCell; +@property (nonatomic) IBOutlet UILabel *disappearingMessagesDurationLabel; +@property (nonatomic) IBOutlet UISlider *disappearingMessagesDurationSlider; -@property (strong, nonatomic) IBOutlet UITableViewCell *updateGroupCell; -@property (strong, nonatomic) IBOutlet UITableViewCell *leaveGroupCell; -@property (strong, nonatomic) IBOutlet UITableViewCell *listGroupMembersCell; -@property (strong, nonatomic) IBOutlet UIImageView *avatar; -@property (strong, nonatomic) IBOutlet UILabel *nameLabel; -@property (strong, nonatomic) IBOutlet UILabel *signalIdLabel; -@property (strong, nonatomic) IBOutletCollection(UIImageView) NSArray *cellIcons; +@property (nonatomic) IBOutlet UIImageView *avatar; +@property (nonatomic) IBOutlet UILabel *nameLabel; +@property (nonatomic) IBOutlet UILabel *signalIdLabel; +@property (nonatomic) IBOutletCollection(UIImageView) NSArray *cellIcons; @property (nonatomic) TSThread *thread; @property (nonatomic) NSString *contactName; @@ -80,6 +63,7 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM @property (nonatomic, readonly) TSStorageManager *storageManager; @property (nonatomic, readonly) OWSContactsManager *contactsManager; @property (nonatomic, readonly) OWSMessageSender *messageSender; +@property (nonatomic, readonly) OWSBlockingManager *blockingManager; @end @@ -125,6 +109,7 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM _storageManager = [TSStorageManager sharedManager]; _contactsManager = [Environment getCurrent].contactsManager; _messageSender = [Environment getCurrent].messageSender; + _blockingManager = [OWSBlockingManager sharedManager]; } - (void)configureWithThread:(TSThread *)thread @@ -145,6 +130,15 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM #pragma mark - View Lifecycle +- (void)loadView +{ + // Initialize with empty contents. We'll populate the + // contents later. + self.contents = [OWSTableContents new]; + + [super loadView]; +} + - (void)viewDidLoad { [super viewDidLoad]; @@ -162,21 +156,10 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM // Translations self.title = NSLocalizedString(@"CONVERSATION_SETTINGS", @"title for conversation settings screen"); - self.verifyPrivacyCell.textLabel.text - = NSLocalizedString(@"VERIFY_PRIVACY", @"table cell label in conversation settings"); self.toggleDisappearingMessagesTitleLabel.text = NSLocalizedString(@"DISAPPEARING_MESSAGES", @"table cell label in conversation settings"); self.toggleDisappearingMessagesDescriptionLabel.text = NSLocalizedString(@"DISAPPEARING_MESSAGES_DESCRIPTION", @"subheading in conversation settings"); - self.updateGroupCell.textLabel.text - = NSLocalizedString(@"EDIT_GROUP_ACTION", @"table cell label in conversation settings"); - self.leaveGroupCell.textLabel.text - = NSLocalizedString(@"LEAVE_GROUP_ACTION", @"table cell label in conversation settings"); - self.listGroupMembersCell.textLabel.text - = NSLocalizedString(@"LIST_GROUP_MEMBERS_ACTION", @"table cell label in conversation settings"); - - self.toggleDisappearingMessagesCell.selectionStyle = UITableViewCellSelectionStyleNone; - self.disappearingMessagesDurationCell.selectionStyle = UITableViewCellSelectionStyleNone; self.disappearingMessagesDurations = [OWSDisappearingMessagesConfiguration validDurationsSeconds]; self.disappearingMessagesDurationSlider.maximumValue = (float)(self.disappearingMessagesDurations.count - 1); @@ -200,6 +183,131 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM for (UIImageView *cellIcon in self.cellIcons) { [cellIcon tintColorDidChange]; } + + [self updateTableContents]; +} + +- (void)updateTableContents +{ + OWSTableContents *contents = [OWSTableContents new]; + contents.title = NSLocalizedString(@"CONVERSATION_SETTINGS", @"title for conversation settings screen"); + + __weak OWSConversationSettingsTableViewController *weakSelf = self; + + NSMutableArray *firstSectionItems = [NSMutableArray new]; + if (!self.isGroupThread && self.thread.hasSafetyNumbers) { + [firstSectionItems addObject:[OWSTableItem itemWithCustomCellBlock:^{ + weakSelf.verifyPrivacyCell.textLabel.text + = NSLocalizedString(@"VERIFY_PRIVACY", @"table cell label in conversation settings"); + return weakSelf.verifyPrivacyCell; + } + actionBlock:^{ + [weakSelf + performSegueWithIdentifier: + @"OWSConversationSettingsTableViewControllerSegueSafetyNumbers" + sender:weakSelf]; + }]]; + } + + if (!self.isGroupThread) { + BOOL isBlocked = [[_blockingManager blockedPhoneNumbers] containsObject:self.signalId]; + + [firstSectionItems addObject:[OWSTableItem itemWithCustomCellBlock:^{ + UITableViewCell *cell = [UITableViewCell new]; + cell.textLabel.text = NSLocalizedString( + @"CONVERSATION_SETTINGS_BLOCK_THIS_USER", @"table cell label in conversation settings"); + cell.textLabel.textColor = [UIColor blackColor]; + cell.textLabel.font = [UIFont ows_regularFontWithSize:17.f]; + cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail; + UIImage *icon = [UIImage imageNamed:@"ic_block"]; + OWSAssert(icon); + cell.imageView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; + cell.imageView.contentMode = UIViewContentModeScaleToFill; + cell.imageView.tintColor = [UIColor blackColor]; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + + UISwitch *blockUserSwitch = [UISwitch new]; + blockUserSwitch.on = isBlocked; + [blockUserSwitch addTarget:self + action:@selector(blockUserSwitchDidChange:) + forControlEvents:UIControlEventValueChanged]; + cell.accessoryView = blockUserSwitch; + return cell; + } + actionBlock:nil]]; + } + + [firstSectionItems addObject:[OWSTableItem itemWithCustomCellBlock:^{ + weakSelf.toggleDisappearingMessagesCell.selectionStyle = UITableViewCellSelectionStyleNone; + return weakSelf.toggleDisappearingMessagesCell; + } + customRowHeight:108.f + actionBlock:nil]]; + + if (self.disappearingMessagesSwitch.isOn) { + [firstSectionItems addObject:[OWSTableItem itemWithCustomCellBlock:^{ + weakSelf.disappearingMessagesDurationCell.selectionStyle = UITableViewCellSelectionStyleNone; + return weakSelf.disappearingMessagesDurationCell; + } + customRowHeight:76.f + actionBlock:nil]]; + } + + [contents addSection:[OWSTableSection sectionWithTitle:nil items:firstSectionItems]]; + + if (self.isGroupThread) { + NSArray *groupItems = @[ + [OWSTableItem itemWithCustomCellBlock:^{ + UITableViewCell *cell = [UITableViewCell new]; + cell.textLabel.text + = NSLocalizedString(@"EDIT_GROUP_ACTION", @"table cell label in conversation settings"); + cell.textLabel.textColor = [UIColor blackColor]; + cell.textLabel.font = [UIFont ows_regularFontWithSize:17.f]; + cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail; + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + return cell; + } + actionBlock:^{ + [weakSelf performSegueWithIdentifier:@"OWSConversationSettingsTableViewControllerSegueUpdateGroup" + sender:weakSelf]; + }], + [OWSTableItem itemWithCustomCellBlock:^{ + UITableViewCell *cell = [UITableViewCell new]; + cell.textLabel.text + = NSLocalizedString(@"LEAVE_GROUP_ACTION", @"table cell label in conversation settings"); + cell.textLabel.textColor = [UIColor blackColor]; + cell.textLabel.font = [UIFont ows_regularFontWithSize:17.f]; + cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail; + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + return cell; + } + actionBlock:^{ + [weakSelf didTapLeaveGroup]; + }], + [OWSTableItem itemWithCustomCellBlock:^{ + UITableViewCell *cell = [UITableViewCell new]; + cell.textLabel.text + = NSLocalizedString(@"LIST_GROUP_MEMBERS_ACTION", @"table cell label in conversation settings"); + cell.textLabel.textColor = [UIColor blackColor]; + cell.textLabel.font = [UIFont ows_regularFontWithSize:17.f]; + cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail; + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + return cell; + } + actionBlock:^{ + [weakSelf + performSegueWithIdentifier:@"OWSConversationSettingsTableViewControllerSegueShowGroupMembers" + sender:weakSelf]; + }], + ]; + + [contents addSection:[OWSTableSection sectionWithTitle:NSLocalizedString(@"GROUP_MANAGEMENT_SECTION", + @"Conversation settings table section title") + items:groupItems]]; + } + + self.contents = contents; + [self.tableView reloadData]; } - (void)viewWillAppear:(BOOL)animated @@ -244,105 +352,6 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM self.avatar.layer.cornerRadius = self.avatar.frame.size.height / 2.0f; } -#pragma mark - UITableViewDelegate - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - NSInteger baseCount = [super tableView:tableView numberOfRowsInSection:section]; - - if (section == OWSConversationSettingsTableViewControllerSectionGroup) { - if (self.isGroupThread) { - return baseCount; - } else { - return 0; - } - } - - if (section == OWSConversationSettingsTableViewControllerSectionContact) { - if (!self.thread.hasSafetyNumbers) { - baseCount -= 1; - } - - if (!self.disappearingMessagesSwitch.isOn) { - // hide duration slider when disappearing messages is off. - baseCount -= 1; - } - } - return baseCount; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath -{ - if (indexPath.section == OWSConversationSettingsTableViewControllerSectionContact - && !self.thread.hasSafetyNumbers) { - - // Since fingerprint cell is hidden for some threads we offset our index path - NSIndexPath *offsetIndexPath = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section]; - return [super tableView:tableView cellForRowAtIndexPath:offsetIndexPath]; - } - - return [super tableView:tableView cellForRowAtIndexPath:indexPath]; -} - -- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath -{ - UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath]; - - // group vs. contact thread have some cells slider at different index. - if (cell == self.disappearingMessagesDurationCell) { - NSIndexPath *originalIndexPath = [NSIndexPath - indexPathForRow:OWSConversationSettingsTableViewControllerCellIndexSetDisappearingMessagesDuration - inSection:OWSConversationSettingsTableViewControllerSectionContact]; - - return [super tableView:tableView heightForRowAtIndexPath:originalIndexPath]; - } - if (cell == self.toggleDisappearingMessagesCell) { - NSIndexPath *originalIndexPath = - [NSIndexPath indexPathForRow:OWSConversationSettingsTableViewControllerCellIndexToggleDisappearingMessages - inSection:OWSConversationSettingsTableViewControllerSectionContact]; - - return [super tableView:tableView heightForRowAtIndexPath:originalIndexPath]; - } else { - return [super tableView:tableView heightForRowAtIndexPath:indexPath]; - } -} - -// Called before the user changes the selection. Return a new indexPath, or nil, to change the proposed selection. -- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; - - // Don't highlight rows that have no selection style. - if (cell.selectionStyle == UITableViewCellSelectionStyleNone) { - return nil; - } - return indexPath; -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - DDLogDebug(@"%@ tapped indexPath:%@", self.tag, indexPath); - - if (indexPath.section == OWSConversationSettingsTableViewControllerSectionGroup - && indexPath.row == OWSConversationSettingsTableViewControllerCellIndexLeaveGroup) { - - [self didTapLeaveGroup]; - } -} - -- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section -{ - if (section == OWSConversationSettingsTableViewControllerSectionGroup) { - if (self.isGroupThread) { - return NSLocalizedString(@"GROUP_MANAGEMENT_SECTION", @"Conversation settings table section title"); - } else { - return nil; - } - } else { - return [super tableView:tableView titleForHeaderInSection:section]; - } -} - #pragma mark - Actions - (void)didTapLeaveGroup @@ -407,6 +416,48 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM } UISwitch *disappearingMessagesSwitch = (UISwitch *)sender; [self toggleDisappearingMessages:disappearingMessagesSwitch.isOn]; + + [self updateTableContents]; +} + +- (void)blockUserSwitchDidChange:(id)sender +{ + OWSAssert(!self.isGroupThread); + + if (![sender isKindOfClass:[UISwitch class]]) { + DDLogError(@"%@ Unexpected sender for block user switch: %@", self.tag, sender); + } + UISwitch *blockUserSwitch = (UISwitch *)sender; + + BOOL isCurrentlyBlocked = [[_blockingManager blockedPhoneNumbers] containsObject:self.signalId]; + + if (blockUserSwitch.isOn) { + OWSAssert(!isCurrentlyBlocked); + if (isCurrentlyBlocked) { + return; + } + [BlockListUIUtils showBlockPhoneNumberActionSheet:self.thread.contactIdentifier + displayName:self.thread.name + fromViewController:self + blockingManager:_blockingManager + completionBlock:^(BOOL isBlocked) { + // Update switch state if user cancels action. + blockUserSwitch.on = isBlocked; + }]; + } else { + OWSAssert(isCurrentlyBlocked); + if (!isCurrentlyBlocked) { + return; + } + [BlockListUIUtils showUnblockPhoneNumberActionSheet:self.thread.contactIdentifier + displayName:self.thread.name + fromViewController:self + blockingManager:_blockingManager + completionBlock:^(BOOL isBlocked) { + // Update switch state if user cancels action. + blockUserSwitch.on = isBlocked; + }]; + } } - (void)toggleDisappearingMessages:(BOOL)flag @@ -417,28 +468,6 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM // but it allows us to resuse the method to set the switch programmatically in view setup. self.disappearingMessagesSwitch.on = flag; [self durationSliderDidChange:self.disappearingMessagesDurationSlider]; - - // Animate show/hide of duration settings. - if (flag) { - [self.tableView insertRowsAtIndexPaths:@[ self.indexPathForDurationSlider ] - withRowAnimation:UITableViewRowAnimationFade]; - } else { - [self.tableView deleteRowsAtIndexPaths:@[ self.indexPathForDurationSlider ] - withRowAnimation:UITableViewRowAnimationFade]; - } -} - -- (NSIndexPath *)indexPathForDurationSlider -{ - if (!self.thread.hasSafetyNumbers) { - return [NSIndexPath - indexPathForRow:OWSConversationSettingsTableViewControllerCellIndexSetDisappearingMessagesDuration - 1 - inSection:OWSConversationSettingsTableViewControllerSectionContact]; - } else { - return [NSIndexPath - indexPathForRow:OWSConversationSettingsTableViewControllerCellIndexSetDisappearingMessagesDuration - inSection:OWSConversationSettingsTableViewControllerSectionContact]; - } } - (IBAction)durationSliderDidChange:(UISlider *)slider diff --git a/Signal/src/ViewControllers/OWSTableViewController.h b/Signal/src/ViewControllers/OWSTableViewController.h index 860c22015..2fb2b47a0 100644 --- a/Signal/src/ViewControllers/OWSTableViewController.h +++ b/Signal/src/ViewControllers/OWSTableViewController.h @@ -32,15 +32,30 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - typedef NS_ENUM(NSInteger, OWSTableItemType) { + OWSTableItemTypeDefault, OWSTableItemTypeAction, }; typedef void (^OWSTableActionBlock)(); +typedef UITableViewCell * (^OWSTableCustomCellBlock)(); @interface OWSTableItem : NSObject -+ (OWSTableItem *)actionWithTitle:(NSString *)title - actionBlock:(OWSTableActionBlock)actionBlock; ++ (OWSTableItem *)itemWithTitle:(NSString *)title actionBlock:(OWSTableActionBlock)actionBlock; + ++ (OWSTableItem *)itemWithCustomCell:(UITableViewCell *)customCell + customRowHeight:(CGFloat)customRowHeight + actionBlock:(OWSTableActionBlock)actionBlock; + ++ (OWSTableItem *)itemWithCustomCellBlock:(OWSTableCustomCellBlock)customCellBlock + customRowHeight:(CGFloat)customRowHeight + actionBlock:(OWSTableActionBlock)actionBlock; + ++ (OWSTableItem *)itemWithCustomCellBlock:(OWSTableCustomCellBlock)customCellBlock + actionBlock:(OWSTableActionBlock)actionBlock; + +- (UITableViewCell *)customCell; +- (NSNumber *)customRowHeight; @end diff --git a/Signal/src/ViewControllers/OWSTableViewController.m b/Signal/src/ViewControllers/OWSTableViewController.m index d556a8844..7c66ca72f 100644 --- a/Signal/src/ViewControllers/OWSTableViewController.m +++ b/Signal/src/ViewControllers/OWSTableViewController.m @@ -78,13 +78,17 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, nullable) NSString *title; @property (nonatomic, nullable) OWSTableActionBlock actionBlock; +@property (nonatomic) OWSTableCustomCellBlock customCellBlock; +@property (nonatomic) UITableViewCell *customCell; +@property (nonatomic) NSNumber *customRowHeight; + @end #pragma mark - @implementation OWSTableItem -+ (OWSTableItem *)actionWithTitle:(NSString *)title actionBlock:(OWSTableActionBlock)actionBlock ++ (OWSTableItem *)itemWithTitle:(NSString *)title actionBlock:(OWSTableActionBlock)actionBlock { OWSAssert(title.length > 0); @@ -95,6 +99,60 @@ NS_ASSUME_NONNULL_BEGIN return item; } ++ (OWSTableItem *)itemWithCustomCell:(UITableViewCell *)customCell + customRowHeight:(CGFloat)customRowHeight + actionBlock:(OWSTableActionBlock)actionBlock +{ + OWSAssert(customCell); + OWSAssert(customRowHeight > 0); + + OWSTableItem *item = [OWSTableItem new]; + item.itemType = (actionBlock != nil ? OWSTableItemTypeAction : OWSTableItemTypeDefault); + item.actionBlock = actionBlock; + item.customCell = customCell; + item.customRowHeight = @(customRowHeight); + return item; +} + ++ (OWSTableItem *)itemWithCustomCellBlock:(OWSTableCustomCellBlock)customCellBlock + customRowHeight:(CGFloat)customRowHeight + actionBlock:(OWSTableActionBlock)actionBlock +{ + OWSAssert(customCellBlock); + OWSAssert(customRowHeight > 0); + + OWSTableItem *item = [OWSTableItem new]; + item.itemType = (actionBlock != nil ? OWSTableItemTypeAction : OWSTableItemTypeDefault); + item.actionBlock = actionBlock; + item.customCellBlock = customCellBlock; + item.customRowHeight = @(customRowHeight); + return item; +} + ++ (OWSTableItem *)itemWithCustomCellBlock:(OWSTableCustomCellBlock)customCellBlock + actionBlock:(OWSTableActionBlock)actionBlock +{ + OWSAssert(customCellBlock); + + OWSTableItem *item = [OWSTableItem new]; + item.itemType = (actionBlock != nil ? OWSTableItemTypeAction : OWSTableItemTypeDefault); + item.actionBlock = actionBlock; + item.customCellBlock = customCellBlock; + return item; +} + +- (UITableViewCell *)customCell +{ + if (_customCell) { + return _customCell; + } + if (_customCellBlock) { + return _customCellBlock(); + } + OWSAssert(0); + return nil; +} + @end #pragma mark - @@ -173,6 +231,12 @@ NSString * const kOWSTableCellIdentifier = @"kOWSTableCellIdentifier"; - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { OWSTableItem *item = [self itemForIndexPath:indexPath]; + + UITableViewCell *customCell = [item customCell]; + if (customCell) { + return customCell; + } + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kOWSTableCellIdentifier]; OWSAssert(cell); @@ -181,13 +245,32 @@ NSString * const kOWSTableCellIdentifier = @"kOWSTableCellIdentifier"; return cell; } +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + OWSTableItem *item = [self itemForIndexPath:indexPath]; + if (item.customRowHeight) { + return [item.customRowHeight floatValue]; + } + return [super tableView:tableView heightForRowAtIndexPath:indexPath]; +} + +// Called before the user changes the selection. Return a new indexPath, or nil, to change the proposed selection. +- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + OWSTableItem *item = [self itemForIndexPath:indexPath]; + if (!item.actionBlock) { + return nil; + } + + return indexPath; +} + - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; OWSTableItem *item = [self itemForIndexPath:indexPath]; - if (item.itemType == OWSTableItemTypeAction) { - OWSAssert(item.actionBlock); + if (item.actionBlock) { item.actionBlock(); } } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index b496d7a7f..c1ca8d5cb 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -100,23 +100,29 @@ /* Button label for the 'block' button */ "BLOCK_LIST_BLOCK_BUTTON" = "Block"; -/* A format for the 'block phone number' action sheet title. */ +/* A format for the 'block user' action sheet title. */ "BLOCK_LIST_BLOCK_TITLE_FORMAT" = "Block %@?"; /* Button label for the 'unblock' button */ "BLOCK_LIST_UNBLOCK_BUTTON" = "Unblock"; -/* A format for the 'unblock phone number' action sheet title. */ +/* A format for the 'unblock user' action sheet title. */ "BLOCK_LIST_UNBLOCK_TITLE_FORMAT" = "Unblock %@?"; /* A label for the block button in the block list view */ "BLOCK_LIST_VIEW_BLOCK_BUTTON" = "Block"; -/* The message format of the 'contact blocked' alert in the block view. It is populated with the blocked contact's name. */ -"BLOCK_LIST_VIEW_CONTACT_BLOCKED_ALERT_MESSAGE_FORMAT" = "%@ has been blocked"; +/* The title of the 'block user failed' alert. */ +"BLOCK_LIST_VIEW_BLOCK_FAILED_ALERT_MESSAGE" = "Block user failed."; -/* The title of the 'contact blocked' alert in the block view. */ -"BLOCK_LIST_VIEW_CONTACT_BLOCKED_ALERT_TITLE" = "Contact Blocked"; +/* The title of the 'block user failed' alert. */ +"BLOCK_LIST_VIEW_BLOCK_FAILED_ALERT_TITLE" = "Error"; + +/* The message format of the 'user blocked' alert. It is populated with the blocked contact's name. */ +"BLOCK_LIST_VIEW_BLOCKED_ALERT_MESSAGE_FORMAT" = "%@ has been blocked"; + +/* The title of the 'user blocked' alert. */ +"BLOCK_LIST_VIEW_BLOCKED_ALERT_TITLE" = "User Blocked"; /* A title for the contacts section of the blocklist view. */ "BLOCK_LIST_VIEW_CONTACTS_SECTION_TITLE" = "Contacts"; @@ -127,11 +133,17 @@ /* The title of the 'phone number blocked' alert in the block view. */ "BLOCK_LIST_VIEW_PHONE_NUMBER_BLOCKED_ALERT_TITLE" = "Phone Number Blocked"; -/* The message format of the 'phone number unblocked' alert in the block view. It is populated with the blocked phone number. */ +/* The title of the 'unblock user failed' alert. */ +"BLOCK_LIST_VIEW_UNBLOCK_FAILED_ALERT_MESSAGE" = "Unblock user failed."; + +/* The title of the 'unblock user failed' alert. */ +"BLOCK_LIST_VIEW_UNBLOCK_FAILED_ALERT_TITLE" = "Error"; + +/* The message format of the 'user unblocked' alert. It is populated with the blocked phone number. */ "BLOCK_LIST_VIEW_UNBLOCKED_ALERT_MESSAGE_FORMAT" = "%@ has been unblocked."; -/* The title of the 'phone number unblocked' alert in the block view. */ -"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_TITLE" = "Phone Number Unblocked"; +/* The title of the 'user unblocked' alert. */ +"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_TITLE" = "User Unblocked"; /* Accessibilty label for placing call button */ "CALL_LABEL" = "Call"; @@ -193,6 +205,9 @@ /* title for conversation settings screen */ "CONVERSATION_SETTINGS" = "Conversation Settings"; +/* table cell label in conversation settings */ +"CONVERSATION_SETTINGS_BLOCK_THIS_USER" = "Block this user"; + /* The message of the 'text message too large' alert. */ "CONVERSATION_VIEW_TEXT_MESSAGE_TOO_LARGE_ALERT_MESSAGE" = "This message is too long to send.";