TM-SGNL-iOS/SignalUI/Utils/BlockListUIUtils.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

369 lines
14 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
public import SignalServiceKit
public class BlockListUIUtils {
public typealias Completion = (Bool) -> Void
private init() {}
// MARK: Block
public static func showBlockThreadActionSheet(
_ thread: TSThread,
from viewController: UIViewController,
completion: Completion?
) {
if let contactThread = thread as? TSContactThread {
showBlockAddressActionSheet(contactThread.contactAddress, from: viewController, completion: completion)
return
}
if let groupThread = thread as? TSGroupThread {
showBlockGroupActionSheet(groupThread, from: viewController, completion: completion)
return
}
owsFailDebug("Unexpected thread type: \(thread.self)")
}
public static func showBlockAddressActionSheet(
_ address: SignalServiceAddress,
from viewController: UIViewController,
completion: Completion?
) {
let displayName = SSKEnvironment.shared.databaseStorageRef.read { tx in SSKEnvironment.shared.contactManagerRef.displayName(for: address, tx: tx).resolvedValue() }
showBlockAddressesActionSheet(address, displayName: displayName, from: viewController, completion: completion)
}
private static func showBlockAddressesActionSheet(
_ address: SignalServiceAddress,
displayName: String,
from viewController: UIViewController,
completion: Completion?
) {
owsAssertDebug(address.isValid)
owsAssertDebug(!displayName.isEmpty)
if address.isLocalAddress {
showOkActionSheet(
title: OWSLocalizedString(
"BLOCK_LIST_VIEW_CANT_BLOCK_SELF_ALERT_TITLE",
comment: "The title of the 'You can't block yourself' alert."
),
message: OWSLocalizedString(
"BLOCK_LIST_VIEW_CANT_BLOCK_SELF_ALERT_MESSAGE",
comment: "The message of the 'You can't block yourself' alert."
),
from: viewController,
completion: { _ in
completion?(false)
}
)
return
}
let actionSheetTitle = String(
format: OWSLocalizedString(
"BLOCK_LIST_BLOCK_USER_TITLE_FORMAT",
comment: "A format for the 'block user' action sheet title. Embeds {{the blocked user's name or phone number}}."
),
displayName.formattedForActionSheetTitle()
)
let actionSheet = ActionSheetController(
title: actionSheetTitle,
message: OWSLocalizedString(
"BLOCK_USER_BEHAVIOR_EXPLANATION",
comment: "An explanation of the consequences of blocking another user."
)
)
actionSheet.addAction(ActionSheetAction(
title: OWSLocalizedString("BLOCK_LIST_BLOCK_BUTTON", comment: "Button label for the 'block' button"),
accessibilityIdentifier: "BlockListUIUtils.block",
style: .destructive,
handler: { _ in
blockAddress(address, displayName: displayName, from: viewController) { _ in
completion?(true)
}
}
))
actionSheet.addAction(ActionSheetAction(
title: CommonStrings.cancelButton,
accessibilityIdentifier: "BlockListUIUtils.dismiss",
style: .cancel,
handler: { _ in
completion?(false)
}
))
viewController.presentActionSheet(actionSheet)
}
private static func showBlockGroupActionSheet(
_ groupThread: TSGroupThread,
from viewController: UIViewController,
completion: Completion?
) {
let actionSheetTitle = String(
format: OWSLocalizedString(
"BLOCK_LIST_BLOCK_GROUP_TITLE_FORMAT",
comment: "A format for the 'block group' action sheet title. Embeds the {{group name}}."
),
groupThread.groupNameOrDefault.formattedForActionSheetTitle()
)
let actionSheet = ActionSheetController(
title: actionSheetTitle,
message: OWSLocalizedString(
"BLOCK_GROUP_BEHAVIOR_EXPLANATION",
comment: "An explanation of the consequences of blocking a group."
)
)
actionSheet.addAction(ActionSheetAction(
title: OWSLocalizedString("BLOCK_LIST_BLOCK_BUTTON", comment: "Button label for the 'block' button"),
accessibilityIdentifier: "BlockListUIUtils.block",
style: .destructive,
handler: { _ in
blockGroup(groupThread, from: viewController) { _ in
completion?(true)
}
}
))
actionSheet.addAction(ActionSheetAction(
title: CommonStrings.cancelButton,
accessibilityIdentifier: "BlockListUIUtils.dismiss",
style: .cancel,
handler: { _ in
completion?(false)
}
))
viewController.presentActionSheet(actionSheet)
}
private static func blockAddress(
_ address: SignalServiceAddress,
displayName: String,
from viewController: UIViewController,
completion: ((ActionSheetAction) -> Void)?
) {
owsAssertDebug(!displayName.isEmpty)
owsAssertDebug(address.isValid)
SSKEnvironment.shared.databaseStorageRef.write { tx in
SSKEnvironment.shared.blockingManagerRef.addBlockedAddress(address, blockMode: .localShouldLeaveGroups, transaction: tx)
}
showOkActionSheet(
title: OWSLocalizedString(
"BLOCK_LIST_VIEW_BLOCKED_ALERT_TITLE",
comment: "The title of the 'user blocked' alert."
),
message: String(
format: OWSLocalizedString(
"BLOCK_LIST_VIEW_BLOCKED_ALERT_MESSAGE_FORMAT",
comment: "The message format of the 'conversation blocked' alert. Embeds the {{conversation title}}."
),
displayName.formattedForActionSheetMessage()
),
from: viewController,
completion: completion
)
}
private static func blockGroup(
_ groupThread: TSGroupThread,
from viewController: UIViewController,
completion: ((ActionSheetAction) -> Void)?
) {
guard groupThread.isLocalUserMemberOfAnyKind else {
blockGroupStep2(groupThread, from: viewController, completion: completion)
return
}
GroupManager.leaveGroupOrDeclineInviteAsyncWithUI(
groupThread: groupThread,
fromViewController: viewController,
success: {
blockGroupStep2(groupThread, from: viewController, completion: completion)
}
)
}
private static func blockGroupStep2(
_ groupThread: TSGroupThread,
from viewController: UIViewController,
completion: ((ActionSheetAction) -> Void)?
) {
// block the group regardless of the ability to deliver the
// "leave group" message.
SSKEnvironment.shared.databaseStorageRef.write(block: { tx in
SSKEnvironment.shared.blockingManagerRef.addBlockedGroup(
groupModel: groupThread.groupModel,
blockMode: .localShouldLeaveGroups,
transaction: tx
)
})
let actionSheetTitle = OWSLocalizedString(
"BLOCK_LIST_VIEW_BLOCKED_GROUP_ALERT_TITLE",
comment: "The title of the 'group blocked' alert."
)
let actionSheetMessageFormat = OWSLocalizedString(
"BLOCK_LIST_VIEW_BLOCKED_ALERT_MESSAGE_FORMAT",
comment: "The message format of the 'conversation blocked' alert. Embeds the {{conversation title}}."
)
let actionSheetMessage = String(format: actionSheetMessageFormat, groupThread.groupNameOrDefault.formattedForActionSheetMessage())
showOkActionSheet(title: actionSheetTitle, message: actionSheetMessage, from: viewController, completion: completion)
}
// MARK: Unblock
public static func showUnblockThreadActionSheet(
_ thread: TSThread,
from viewController: UIViewController,
completion: Completion?
) {
if let contactThread = thread as? TSContactThread {
showUnblockAddressActionSheet(contactThread.contactAddress, from: viewController, completion: completion)
return
}
if let groupThread = thread as? TSGroupThread {
showUnblockGroupActionSheet(groupThread.groupModel, from: viewController, completion: completion)
return
}
owsFailDebug("unexpected thread type: \(thread.self)")
}
public static func showUnblockAddressActionSheet(
_ address: SignalServiceAddress,
from viewController: UIViewController,
completion: Completion?
) {
let displayName = SSKEnvironment.shared.databaseStorageRef.read { tx in SSKEnvironment.shared.contactManagerRef.displayName(for: address, tx: tx).resolvedValue() }
owsAssertDebug(!displayName.isEmpty)
let actionSheetTitle = String(
format: OWSLocalizedString(
"BLOCK_LIST_UNBLOCK_TITLE_FORMAT",
comment: "A format for the 'unblock conversation' action sheet title. Embeds the {{conversation title}}."
),
displayName.formattedForActionSheetTitle()
)
let actionSheet = ActionSheetController(title: actionSheetTitle)
actionSheet.addAction(ActionSheetAction(
title: OWSLocalizedString("BLOCK_LIST_UNBLOCK_BUTTON", comment: "Button label for the 'unblock' button"),
accessibilityIdentifier: "BlockListUIUtils.unblock",
style: .destructive,
handler: { _ in
unblockAddress(address, displayName: displayName, from: viewController) { _ in
completion?(false)
}
}
))
actionSheet.addAction(ActionSheetAction(
title: CommonStrings.cancelButton,
accessibilityIdentifier: "BlockListUIUtils.dismiss",
style: .cancel,
handler: { _ in
completion?(true)
}
))
viewController.presentActionSheet(actionSheet)
}
public static func showUnblockGroupActionSheet(
_ groupModel: TSGroupModel,
from viewController: UIViewController,
completion: Completion?
) {
let actionSheetTitle = OWSLocalizedString(
"BLOCK_LIST_UNBLOCK_GROUP_TITLE",
comment: "Action sheet title when confirming you want to unblock a group."
)
let actionSheetMessage = OWSLocalizedString(
"BLOCK_LIST_UNBLOCK_GROUP_BODY",
comment: "Action sheet body when confirming you want to unblock a group"
)
let actionSheet = ActionSheetController(title: actionSheetTitle, message: actionSheetMessage)
actionSheet.addAction(ActionSheetAction(
title: OWSLocalizedString("BLOCK_LIST_UNBLOCK_BUTTON", comment: "Button label for the 'unblock' button"),
accessibilityIdentifier: "BlockListUIUtils.unblock",
style: .destructive,
handler: { _ in
unblockGroup(groupModel, from: viewController) { _ in
completion?(false)
}
}
))
actionSheet.addAction(ActionSheetAction(
title: CommonStrings.cancelButton,
accessibilityIdentifier: "BlockListUIUtils.dismiss",
style: .cancel,
handler: { _ in
completion?(true)
}
))
viewController.presentActionSheet(actionSheet)
}
private static func unblockAddress(
_ address: SignalServiceAddress,
displayName: String,
from viewController: UIViewController,
completion: ((ActionSheetAction) -> Void)?
) {
owsAssertDebug(address.isValid)
owsAssertDebug(!displayName.isEmpty)
SSKEnvironment.shared.databaseStorageRef.write { tx in
SSKEnvironment.shared.blockingManagerRef.removeBlockedAddress(address, wasLocallyInitiated: true, transaction: tx)
}
let actionSheetTitleFormat = OWSLocalizedString(
"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_TITLE_FORMAT",
comment: "Alert title after unblocking a group or 1:1 chat. Embeds the {{conversation title}}."
)
let actionSheetTitle = String(format: actionSheetTitleFormat, displayName.formattedForActionSheetTitle())
showOkActionSheet(title: actionSheetTitle, message: nil, from: viewController, completion: completion)
}
private static func unblockGroup(
_ groupModel: TSGroupModel,
from viewController: UIViewController,
completion: ((ActionSheetAction) -> Void)?
) {
SSKEnvironment.shared.databaseStorageRef.write { tx in
SSKEnvironment.shared.blockingManagerRef.removeBlockedGroup(groupId: groupModel.groupId, wasLocallyInitiated: true, transaction: tx)
}
let actionSheetTitleFormat = OWSLocalizedString(
"BLOCK_LIST_VIEW_UNBLOCKED_ALERT_TITLE_FORMAT",
comment: "Alert title after unblocking a group or 1:1 chat. Embeds the {{conversation title}}."
)
let actionSheetTitle = String(format: actionSheetTitleFormat, groupModel.groupNameOrDefault.formattedForActionSheetMessage())
let actionSheetMessage = OWSLocalizedString(
"BLOCK_LIST_VIEW_UNBLOCKED_GROUP_ALERT_BODY",
comment: "Alert body after unblocking a group."
)
showOkActionSheet(title: actionSheetTitle, message: actionSheetMessage, from: viewController, completion: completion)
}
// MARK: UI Utils
private static func showOkActionSheet(
title: String,
message: String?,
from viewController: UIViewController,
completion: ((ActionSheetAction) -> Void)?
) {
let actionSheet = ActionSheetController(title: title, message: message)
actionSheet.addAction(ActionSheetAction(
title: CommonStrings.okButton,
accessibilityIdentifier: "BlockListUIUtils.ok",
style: .default,
handler: completion
))
viewController.presentActionSheet(actionSheet)
}
}