460 lines
23 KiB
Objective-C
460 lines
23 KiB
Objective-C
//
|
|
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
|
//
|
|
|
|
#import "BlockListUIUtils.h"
|
|
#import "OWSContactsManager.h"
|
|
#import "PhoneNumber.h"
|
|
#import <PromiseKit/AnyPromise.h>
|
|
#import <SignalMessaging/SignalMessaging-Swift.h>
|
|
#import <SignalServiceKit/Contact.h>
|
|
#import <SignalServiceKit/OWSBlockingManager.h>
|
|
#import <SignalServiceKit/SignalAccount.h>
|
|
#import <SignalServiceKit/TSAccountManager.h>
|
|
#import <SignalServiceKit/TSGroupThread.h>
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
typedef void (^BlockAlertCompletionBlock)(ActionSheetAction *action);
|
|
|
|
@implementation BlockListUIUtils
|
|
|
|
#pragma mark - Block
|
|
|
|
+ (void)showBlockThreadActionSheet:(TSThread *)thread
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(nullable BlockActionCompletionBlock)completionBlock
|
|
{
|
|
if ([thread isKindOfClass:[TSContactThread class]]) {
|
|
TSContactThread *contactThread = (TSContactThread *)thread;
|
|
[self showBlockAddressActionSheet:contactThread.contactAddress
|
|
fromViewController:fromViewController
|
|
completionBlock:completionBlock];
|
|
} else if ([thread isKindOfClass:[TSGroupThread class]]) {
|
|
TSGroupThread *groupThread = (TSGroupThread *)thread;
|
|
[self showBlockGroupActionSheet:groupThread
|
|
fromViewController:fromViewController
|
|
completionBlock:completionBlock];
|
|
} else {
|
|
OWSFailDebug(@"unexpected thread type: %@", thread.class);
|
|
}
|
|
}
|
|
|
|
+ (void)showBlockAddressActionSheet:(SignalServiceAddress *)address
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(nullable BlockActionCompletionBlock)completionBlock
|
|
{
|
|
NSString *displayName = [self.contactsManager displayNameForAddress:address];
|
|
[self showBlockAddressesActionSheet:@[ address ]
|
|
displayName:displayName
|
|
fromViewController:fromViewController
|
|
completionBlock:completionBlock];
|
|
}
|
|
|
|
+ (void)showBlockSignalAccountActionSheet:(SignalAccount *)signalAccount
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(nullable BlockActionCompletionBlock)completionBlock
|
|
{
|
|
NSString *displayName = [self.contactsManager displayNameForSignalAccount:signalAccount];
|
|
[self showBlockAddressesActionSheet:@[ signalAccount.recipientAddress ]
|
|
displayName:displayName
|
|
fromViewController:fromViewController
|
|
completionBlock:completionBlock];
|
|
}
|
|
|
|
+ (void)showBlockAddressesActionSheet:(NSArray<SignalServiceAddress *> *)addresses
|
|
displayName:(NSString *)displayName
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(nullable BlockActionCompletionBlock)completionBlock
|
|
{
|
|
OWSAssertDebug(addresses.count > 0);
|
|
OWSAssertDebug(displayName.length > 0);
|
|
OWSAssertDebug(fromViewController);
|
|
|
|
for (SignalServiceAddress *address in addresses) {
|
|
OWSAssertDebug(address.isValid);
|
|
|
|
if (address.isLocalAddress) {
|
|
[self showOkAlertWithTitle:NSLocalizedString(@"BLOCK_LIST_VIEW_CANT_BLOCK_SELF_ALERT_TITLE",
|
|
@"The title of the 'You can't block yourself' alert.")
|
|
message:NSLocalizedString(@"BLOCK_LIST_VIEW_CANT_BLOCK_SELF_ALERT_MESSAGE",
|
|
@"The message of the 'You can't block yourself' alert.")
|
|
fromViewController:fromViewController
|
|
completionBlock:^(ActionSheetAction *action) {
|
|
if (completionBlock) {
|
|
completionBlock(NO);
|
|
}
|
|
}];
|
|
return;
|
|
}
|
|
}
|
|
|
|
NSString *title = [NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_BLOCK_USER_TITLE_FORMAT",
|
|
@"A format for the 'block user' action sheet title. Embeds {{the "
|
|
@"blocked user's name or phone number}}."),
|
|
[self formatDisplayNameForAlertTitle:displayName]];
|
|
|
|
ActionSheetController *actionSheet = [[ActionSheetController alloc]
|
|
initWithTitle:title
|
|
message:NSLocalizedString(@"BLOCK_USER_BEHAVIOR_EXPLANATION",
|
|
@"An explanation of the consequences of blocking another user.")];
|
|
|
|
ActionSheetAction *blockAction = [[ActionSheetAction alloc]
|
|
initWithTitle:NSLocalizedString(@"BLOCK_LIST_BLOCK_BUTTON", @"Button label for the 'block' button")
|
|
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"block")
|
|
style:ActionSheetActionStyleDestructive
|
|
handler:^(ActionSheetAction *_Nonnull action) {
|
|
[self blockAddresses:addresses
|
|
displayName:displayName
|
|
fromViewController:fromViewController
|
|
completionBlock:^(ActionSheetAction *ignore) {
|
|
if (completionBlock) {
|
|
completionBlock(YES);
|
|
}
|
|
}];
|
|
}];
|
|
[actionSheet addAction:blockAction];
|
|
|
|
ActionSheetAction *dismissAction =
|
|
[[ActionSheetAction alloc] initWithTitle:CommonStrings.cancelButton
|
|
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"dismiss")
|
|
style:ActionSheetActionStyleCancel
|
|
handler:^(ActionSheetAction *_Nonnull action) {
|
|
if (completionBlock) {
|
|
completionBlock(NO);
|
|
}
|
|
}];
|
|
[actionSheet addAction:dismissAction];
|
|
[fromViewController presentActionSheet:actionSheet];
|
|
}
|
|
|
|
+ (void)showBlockGroupActionSheet:(TSGroupThread *)groupThread
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(nullable BlockActionCompletionBlock)completionBlock
|
|
{
|
|
OWSAssertDebug(groupThread);
|
|
OWSAssertDebug(fromViewController);
|
|
|
|
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:groupThread.groupNameOrDefault]];
|
|
|
|
ActionSheetController *actionSheet =
|
|
[[ActionSheetController alloc] initWithTitle:title
|
|
message:NSLocalizedString(@"BLOCK_GROUP_BEHAVIOR_EXPLANATION",
|
|
@"An explanation of the consequences of blocking a group.")];
|
|
|
|
ActionSheetAction *blockAction = [[ActionSheetAction alloc]
|
|
initWithTitle:NSLocalizedString(@"BLOCK_LIST_BLOCK_BUTTON", @"Button label for the 'block' button")
|
|
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"block")
|
|
style:ActionSheetActionStyleDestructive
|
|
handler:^(ActionSheetAction *_Nonnull action) {
|
|
[self blockGroup:groupThread
|
|
fromViewController:fromViewController
|
|
completionBlock:^(ActionSheetAction *ignore) {
|
|
if (completionBlock) {
|
|
completionBlock(YES);
|
|
}
|
|
}];
|
|
}];
|
|
[actionSheet addAction:blockAction];
|
|
|
|
ActionSheetAction *dismissAction =
|
|
[[ActionSheetAction alloc] initWithTitle:CommonStrings.cancelButton
|
|
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"dismiss")
|
|
style:ActionSheetActionStyleCancel
|
|
handler:^(ActionSheetAction *_Nonnull action) {
|
|
if (completionBlock) {
|
|
completionBlock(NO);
|
|
}
|
|
}];
|
|
[actionSheet addAction:dismissAction];
|
|
[fromViewController presentActionSheet:actionSheet];
|
|
}
|
|
|
|
+ (void)blockAddresses:(NSArray<SignalServiceAddress *> *)addresses
|
|
displayName:(NSString *)displayName
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(BlockAlertCompletionBlock)completionBlock
|
|
{
|
|
OWSAssertDebug(addresses.count > 0);
|
|
OWSAssertDebug(displayName.length > 0);
|
|
OWSAssertDebug(fromViewController);
|
|
|
|
for (SignalServiceAddress *address in addresses) {
|
|
OWSAssertDebug(address.isValid);
|
|
[self.blockingManager addBlockedAddress:address blockMode:BlockMode_LocalShouldLeaveGroups];
|
|
}
|
|
|
|
[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 'conversation blocked' alert. "
|
|
@"Embeds the {{conversation title}}."),
|
|
[self formatDisplayNameForAlertMessage:displayName]]
|
|
fromViewController:fromViewController
|
|
completionBlock:completionBlock];
|
|
}
|
|
|
|
+ (void)blockGroup:(TSGroupThread *)groupThread
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(BlockAlertCompletionBlock)completionBlock
|
|
{
|
|
OWSAssertDebug(groupThread);
|
|
OWSAssertDebug(fromViewController);
|
|
|
|
[GroupManager
|
|
leaveGroupOrDeclineInviteAsyncWithUIWithGroupThread:groupThread
|
|
fromViewController:fromViewController
|
|
replacementAdminUuid:nil
|
|
success:^{
|
|
// block the group regardless of the ability to deliver the
|
|
// "leave group" message.
|
|
[self.blockingManager
|
|
addBlockedGroup:groupThread.groupModel
|
|
blockMode:BlockMode_LocalShouldLeaveGroups];
|
|
|
|
NSString *alertTitle = NSLocalizedString(
|
|
@"BLOCK_LIST_VIEW_BLOCKED_GROUP_ALERT_TITLE",
|
|
@"The title of the 'group blocked' alert.");
|
|
NSString *alertBodyFormat = NSLocalizedString(
|
|
@"BLOCK_LIST_VIEW_BLOCKED_ALERT_MESSAGE_FORMAT",
|
|
@"The message format of the 'conversation blocked' alert. "
|
|
@"Embeds the "
|
|
@"{{conversation title}}.");
|
|
NSString *alertBody =
|
|
[NSString stringWithFormat:alertBodyFormat,
|
|
[self formatDisplayNameForAlertMessage:
|
|
groupThread.groupNameOrDefault]];
|
|
|
|
[self showOkAlertWithTitle:alertTitle
|
|
message:alertBody
|
|
fromViewController:fromViewController
|
|
completionBlock:completionBlock];
|
|
}];
|
|
}
|
|
|
|
#pragma mark - Unblock
|
|
|
|
+ (void)showUnblockThreadActionSheet:(TSThread *)thread
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(nullable BlockActionCompletionBlock)completionBlock
|
|
{
|
|
if ([thread isKindOfClass:[TSContactThread class]]) {
|
|
TSContactThread *contactThread = (TSContactThread *)thread;
|
|
[self showUnblockAddressActionSheet:contactThread.contactAddress
|
|
fromViewController:fromViewController
|
|
completionBlock:completionBlock];
|
|
} else if ([thread isKindOfClass:[TSGroupThread class]]) {
|
|
TSGroupThread *groupThread = (TSGroupThread *)thread;
|
|
[self showUnblockGroupActionSheet:groupThread.groupModel
|
|
fromViewController:fromViewController
|
|
completionBlock:completionBlock];
|
|
} else {
|
|
OWSFailDebug(@"unexpected thread type: %@", thread.class);
|
|
}
|
|
}
|
|
|
|
+ (void)showUnblockAddressActionSheet:(SignalServiceAddress *)address
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(nullable BlockActionCompletionBlock)completionBlock
|
|
{
|
|
NSString *displayName = [self.contactsManager displayNameForAddress:address];
|
|
[self showUnblockAddressesActionSheet:@[ address ]
|
|
displayName:displayName
|
|
fromViewController:fromViewController
|
|
completionBlock:completionBlock];
|
|
}
|
|
|
|
+ (void)showUnblockSignalAccountActionSheet:(SignalAccount *)signalAccount
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(nullable BlockActionCompletionBlock)completionBlock
|
|
{
|
|
NSString *displayName = [self.contactsManager displayNameForSignalAccount:signalAccount];
|
|
[self showUnblockAddressesActionSheet:@[ signalAccount.recipientAddress ]
|
|
displayName:displayName
|
|
fromViewController:fromViewController
|
|
completionBlock:completionBlock];
|
|
}
|
|
|
|
+ (void)showUnblockAddressesActionSheet:(NSArray<SignalServiceAddress *> *)addresses
|
|
displayName:(NSString *)displayName
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(nullable BlockActionCompletionBlock)completionBlock
|
|
{
|
|
OWSAssertDebug(addresses.count > 0);
|
|
OWSAssertDebug(displayName.length > 0);
|
|
OWSAssertDebug(fromViewController);
|
|
|
|
NSString *title = [NSString
|
|
stringWithFormat:
|
|
NSLocalizedString(@"BLOCK_LIST_UNBLOCK_TITLE_FORMAT",
|
|
@"A format for the 'unblock conversation' action sheet title. Embeds the {{conversation title}}."),
|
|
[self formatDisplayNameForAlertTitle:displayName]];
|
|
|
|
ActionSheetController *actionSheet = [[ActionSheetController alloc] initWithTitle:title message:nil];
|
|
|
|
ActionSheetAction *unblockAction =
|
|
[[ActionSheetAction alloc] initWithTitle:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_BUTTON",
|
|
@"Button label for the 'unblock' button")
|
|
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"unblock")
|
|
style:ActionSheetActionStyleDestructive
|
|
handler:^(ActionSheetAction *_Nonnull action) {
|
|
[BlockListUIUtils unblockAddresses:addresses
|
|
displayName:displayName
|
|
fromViewController:fromViewController
|
|
completionBlock:^(ActionSheetAction *ignore) {
|
|
if (completionBlock) {
|
|
completionBlock(NO);
|
|
}
|
|
}];
|
|
}];
|
|
[actionSheet addAction:unblockAction];
|
|
|
|
ActionSheetAction *dismissAction =
|
|
[[ActionSheetAction alloc] initWithTitle:CommonStrings.cancelButton
|
|
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"dismiss")
|
|
style:ActionSheetActionStyleCancel
|
|
handler:^(ActionSheetAction *_Nonnull action) {
|
|
if (completionBlock) {
|
|
completionBlock(YES);
|
|
}
|
|
}];
|
|
[actionSheet addAction:dismissAction];
|
|
[fromViewController presentActionSheet:actionSheet];
|
|
}
|
|
|
|
+ (void)unblockAddresses:(NSArray<SignalServiceAddress *> *)addresses
|
|
displayName:(NSString *)displayName
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(BlockAlertCompletionBlock)completionBlock
|
|
{
|
|
OWSAssertDebug(addresses.count > 0);
|
|
OWSAssertDebug(displayName.length > 0);
|
|
OWSAssertDebug(fromViewController);
|
|
|
|
for (SignalServiceAddress *address in addresses) {
|
|
OWSAssertDebug(address.isValid);
|
|
[self.blockingManager removeBlockedAddress:address wasLocallyInitiated:YES];
|
|
}
|
|
|
|
NSString *titleFormat = NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_TITLE_FORMAT",
|
|
@"Alert title after unblocking a group or 1:1 chat. Embeds the {{conversation title}}.");
|
|
NSString *title = [NSString stringWithFormat:titleFormat, [self formatDisplayNameForAlertMessage:displayName]];
|
|
|
|
[self showOkAlertWithTitle:title message:nil fromViewController:fromViewController completionBlock:completionBlock];
|
|
}
|
|
|
|
+ (void)showUnblockGroupActionSheet:(TSGroupModel *)groupModel
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(nullable BlockActionCompletionBlock)completionBlock
|
|
{
|
|
OWSAssertDebug(fromViewController);
|
|
|
|
NSString *title =
|
|
[NSString stringWithFormat:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_GROUP_TITLE",
|
|
@"Action sheet title when confirming you want to unblock a group.")];
|
|
|
|
NSString *message = NSLocalizedString(
|
|
@"BLOCK_LIST_UNBLOCK_GROUP_BODY", @"Action sheet body when confirming you want to unblock a group");
|
|
|
|
ActionSheetController *actionSheet = [[ActionSheetController alloc] initWithTitle:title message:message];
|
|
|
|
ActionSheetAction *unblockAction =
|
|
[[ActionSheetAction alloc] initWithTitle:NSLocalizedString(@"BLOCK_LIST_UNBLOCK_BUTTON",
|
|
@"Button label for the 'unblock' button")
|
|
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"unblock")
|
|
style:ActionSheetActionStyleDestructive
|
|
handler:^(ActionSheetAction *_Nonnull action) {
|
|
[BlockListUIUtils unblockGroup:groupModel
|
|
fromViewController:fromViewController
|
|
completionBlock:^(ActionSheetAction *ignore) {
|
|
if (completionBlock) {
|
|
completionBlock(NO);
|
|
}
|
|
}];
|
|
}];
|
|
[actionSheet addAction:unblockAction];
|
|
|
|
ActionSheetAction *dismissAction =
|
|
[[ActionSheetAction alloc] initWithTitle:CommonStrings.cancelButton
|
|
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"dismiss")
|
|
style:ActionSheetActionStyleCancel
|
|
handler:^(ActionSheetAction *_Nonnull action) {
|
|
if (completionBlock) {
|
|
completionBlock(YES);
|
|
}
|
|
}];
|
|
[actionSheet addAction:dismissAction];
|
|
[fromViewController presentActionSheet:actionSheet];
|
|
}
|
|
|
|
+ (void)unblockGroup:(TSGroupModel *)groupModel
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(BlockAlertCompletionBlock)completionBlock
|
|
{
|
|
OWSAssertDebug(fromViewController);
|
|
|
|
[self.blockingManager removeBlockedGroupId:groupModel.groupId wasLocallyInitiated:YES];
|
|
|
|
NSString *titleFormat = NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_TITLE_FORMAT",
|
|
@"Alert title after unblocking a group or 1:1 chat. Embeds the {{conversation title}}.");
|
|
NSString *title =
|
|
[NSString stringWithFormat:titleFormat, [self formatDisplayNameForAlertMessage:groupModel.groupNameOrDefault]];
|
|
|
|
NSString *message
|
|
= NSLocalizedString(@"BLOCK_LIST_VIEW_UNBLOCKED_GROUP_ALERT_BODY", @"Alert body after unblocking a group.");
|
|
[self showOkAlertWithTitle:title
|
|
message:message
|
|
fromViewController:fromViewController
|
|
completionBlock:completionBlock];
|
|
}
|
|
|
|
#pragma mark - UI
|
|
|
|
+ (void)showOkAlertWithTitle:(NSString *)title
|
|
message:(nullable NSString *)message
|
|
fromViewController:(UIViewController *)fromViewController
|
|
completionBlock:(BlockAlertCompletionBlock)completionBlock
|
|
{
|
|
OWSAssertDebug(title.length > 0);
|
|
OWSAssertDebug(fromViewController);
|
|
|
|
ActionSheetController *alert = [[ActionSheetController alloc] initWithTitle:title message:message];
|
|
|
|
ActionSheetAction *okAction =
|
|
[[ActionSheetAction alloc] initWithTitle:CommonStrings.okButton
|
|
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"ok")
|
|
style:ActionSheetActionStyleDefault
|
|
handler:completionBlock];
|
|
[alert addAction:okAction];
|
|
[fromViewController presentActionSheet:alert];
|
|
}
|
|
|
|
+ (NSString *)formatDisplayNameForAlertTitle:(NSString *)displayName
|
|
{
|
|
return [self formatDisplayName:displayName withMaxLength:20];
|
|
}
|
|
|
|
+ (NSString *)formatDisplayNameForAlertMessage:(NSString *)displayName
|
|
{
|
|
return [self formatDisplayName:displayName withMaxLength:127];
|
|
}
|
|
|
|
+ (NSString *)formatDisplayName:(NSString *)displayName withMaxLength:(NSUInteger)maxLength
|
|
{
|
|
OWSAssertDebug(displayName.length > 0);
|
|
|
|
if (displayName.length > maxLength) {
|
|
return [[displayName substringToIndex:maxLength] stringByAppendingString:@"…"];
|
|
}
|
|
|
|
return displayName;
|
|
}
|
|
|
|
@end
|
|
|
|
NS_ASSUME_NONNULL_END
|