diff --git a/Localizations/Localizable.strings b/Localizations/Localizable.strings index 034fdb7..22e22a0 100644 --- a/Localizations/Localizable.strings +++ b/Localizations/Localizable.strings @@ -8,6 +8,7 @@ "account.accept-follow-request-button.accessibility-label" = "Accept follow request"; "account.avatar.accessibility-label-%@" = "Avatar: %@"; "account.block" = "Block"; +"account.block-and-report" = "Block & report"; "account.block.confirm-%@" = "Block %@?"; "account.domain-block-%@" = "Block domain %@"; "account.domain-block.confirm-%@" = "Block domain %@?"; diff --git a/View Controllers/ProfileViewController.swift b/View Controllers/ProfileViewController.swift index e34043b..0236d91 100644 --- a/View Controllers/ProfileViewController.swift +++ b/View Controllers/ProfileViewController.swift @@ -105,6 +105,22 @@ private extension ProfileViewController { }) } + if relationship.blocking { + actions.append(UIAction( + title: NSLocalizedString("account.unblock", comment: ""), + image: UIImage(systemName: "slash.circle"), + attributes: .destructive) { _ in + accountViewModel.confirmUnblock() + }) + } else { + actions.append(UIAction( + title: NSLocalizedString("account.block", comment: ""), + image: UIImage(systemName: "slash.circle"), + attributes: .destructive) { _ in + accountViewModel.confirmBlock() + }) + } + actions.append(UIAction( title: NSLocalizedString("report", comment: ""), image: UIImage(systemName: "flag"), @@ -116,30 +132,6 @@ private extension ProfileViewController { self.report(reportViewModel: reportViewModel) }) - if relationship.blocking { - actions.append(UIAction( - title: NSLocalizedString("account.unblock", comment: ""), - image: UIImage(systemName: "slash.circle"), - attributes: .destructive) { [weak self] _ in - self?.confirm(message: String.localizedStringWithFormat( - NSLocalizedString("account.unblock.confirm-%@", comment: ""), - accountViewModel.accountName)) { - accountViewModel.unblock() - } - }) - } else { - actions.append(UIAction( - title: NSLocalizedString("account.block", comment: ""), - image: UIImage(systemName: "slash.circle"), - attributes: .destructive) { [weak self] _ in - self?.confirm(message: String.localizedStringWithFormat( - NSLocalizedString("account.block.confirm-%@", comment: ""), - accountViewModel.accountName)) { - accountViewModel.block() - } - }) - } - if !accountViewModel.isLocal, let domain = accountViewModel.domain { if relationship.domainBlocking { actions.append(UIAction( @@ -147,12 +139,8 @@ private extension ProfileViewController { NSLocalizedString("account.domain-unblock-%@", comment: ""), domain), image: UIImage(systemName: "slash.circle"), - attributes: .destructive) { [weak self] _ in - self?.confirm(message: String.localizedStringWithFormat( - NSLocalizedString("account.domain-unblock.confirm-%@", comment: ""), - domain)) { - accountViewModel.domainUnblock() - } + attributes: .destructive) { _ in + accountViewModel.confirmDomainUnblock(domain: domain) }) } else { actions.append(UIAction( @@ -160,30 +148,12 @@ private extension ProfileViewController { NSLocalizedString("account.domain-block-%@", comment: ""), domain), image: UIImage(systemName: "slash.circle"), - attributes: .destructive) { [weak self] _ in - self?.confirm(message: String.localizedStringWithFormat( - NSLocalizedString("account.domain-block.confirm-%@", comment: ""), - domain)) { - accountViewModel.domainBlock() - } - }) + attributes: .destructive) { _ in + accountViewModel.confirmDomainBlock(domain: domain) + }) } } return UIMenu(children: actions) } - - func confirm(message: String, action: @escaping () -> Void) { - let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert) - - let cancelAction = UIAlertAction(title: NSLocalizedString("cancel", comment: ""), style: .cancel, handler: nil) - let okAction = UIAlertAction(title: NSLocalizedString("ok", comment: ""), style: .destructive) { _ in - action() - } - - alertController.addAction(cancelAction) - alertController.addAction(okAction) - - present(alertController, animated: true) - } } diff --git a/View Controllers/TableViewController.swift b/View Controllers/TableViewController.swift index daa2961..0f77828 100644 --- a/View Controllers/TableViewController.swift +++ b/View Controllers/TableViewController.swift @@ -158,6 +158,20 @@ class TableViewController: UITableViewController { } extension TableViewController { + func confirm(message: String, style: UIAlertAction.Style = .default, action: @escaping () -> Void) { + let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert) + + let cancelAction = UIAlertAction(title: NSLocalizedString("cancel", comment: ""), style: .cancel, handler: nil) + let okAction = UIAlertAction(title: NSLocalizedString("ok", comment: ""), style: style) { _ in + action() + } + + alertController.addAction(cancelAction) + alertController.addAction(okAction) + + present(alertController, animated: true) + } + func report(reportViewModel: ReportViewModel) { let reportViewController = ReportViewController(viewModel: reportViewModel) let navigationController = UINavigationController(rootViewController: reportViewController) @@ -412,6 +426,7 @@ private extension TableViewController { } } + // swiftlint:disable:next cyclomatic_complexity func handle(event: CollectionItemEvent) { switch event { case .ignorableOutput: @@ -428,6 +443,14 @@ private extension TableViewController { compose(inReplyToViewModel: inReplyToViewModel, redraft: redraft) case let .confirmDelete(statusViewModel, redraft): confirmDelete(statusViewModel: statusViewModel, redraft: redraft) + case let .confirmBlock(accountViewModel): + confirmBlock(accountViewModel: accountViewModel) + case let .confirmUnblock(accountViewModel): + confirmUnblock(accountViewModel: accountViewModel) + case let .confirmDomainBlock(accountViewModel): + confirmDomainBlock(accountViewModel: accountViewModel) + case let .confirmDomainUnblock(accountViewModel): + confirmDomainUnblock(accountViewModel: accountViewModel) case let .report(reportViewModel): report(reportViewModel: reportViewModel) case let .accountListEdit(accountViewModel, edit): @@ -548,6 +571,59 @@ private extension TableViewController { present(alertController, animated: true) } + func confirmBlock(accountViewModel: AccountViewModel) { + let alertController = UIAlertController( + title: nil, + message: String.localizedStringWithFormat( + NSLocalizedString("account.block.confirm-%@", comment: ""), + accountViewModel.accountName), preferredStyle: .alert) + let blockAction = UIAlertAction(title: NSLocalizedString("account.block", comment: ""), + style: .destructive) { _ in + accountViewModel.block() + } + let blockAndReportAction = UIAlertAction(title: NSLocalizedString("account.block-and-report", comment: ""), + style: .destructive) { [weak self] _ in + accountViewModel.block() + self?.report(reportViewModel: accountViewModel.reportViewModel()) + } + let cancelAction = UIAlertAction(title: NSLocalizedString("cancel", comment: ""), style: .cancel) { _ in } + + alertController.addAction(blockAction) + alertController.addAction(blockAndReportAction) + alertController.addAction(cancelAction) + + present(alertController, animated: true) + } + + func confirmUnblock(accountViewModel: AccountViewModel) { + confirm(message: String.localizedStringWithFormat( + NSLocalizedString("account.unblock.confirm-%@", comment: ""), + accountViewModel.accountName)) { + accountViewModel.unblock() + } + } + + func confirmDomainBlock(accountViewModel: AccountViewModel) { + guard let domain = accountViewModel.domain else { return } + + confirm(message: String.localizedStringWithFormat( + NSLocalizedString("account.domain-block.confirm-%@", comment: ""), + domain), + style: .destructive) { + accountViewModel.domainBlock() + } + } + + func confirmDomainUnblock(accountViewModel: AccountViewModel) { + guard let domain = accountViewModel.domain else { return } + + confirm(message: String.localizedStringWithFormat( + NSLocalizedString("account.domain-unblock.confirm-%@", comment: ""), + domain)) { + accountViewModel.domainUnblock() + } + } + func accountListEdit(accountViewModel: AccountViewModel, edit: CollectionItemEvent.AccountListEdit) { viewModel.applyAccountListEdit(viewModel: accountViewModel, edit: edit) } diff --git a/ViewModels/Sources/ViewModels/Entities/CollectionItemEvent.swift b/ViewModels/Sources/ViewModels/Entities/CollectionItemEvent.swift index 1a1615f..cef43b6 100644 --- a/ViewModels/Sources/ViewModels/Entities/CollectionItemEvent.swift +++ b/ViewModels/Sources/ViewModels/Entities/CollectionItemEvent.swift @@ -11,6 +11,10 @@ public enum CollectionItemEvent { case attachment(AttachmentViewModel, StatusViewModel) case compose(inReplyTo: StatusViewModel?, redraft: Status?) case confirmDelete(StatusViewModel, redraft: Bool) + case confirmBlock(AccountViewModel) + case confirmUnblock(AccountViewModel) + case confirmDomainBlock(AccountViewModel) + case confirmDomainUnblock(AccountViewModel) case report(ReportViewModel) case share(URL) case accountListEdit(AccountViewModel, AccountListEdit) diff --git a/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift b/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift index ecbaedb..ba95545 100644 --- a/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift +++ b/ViewModels/Sources/ViewModels/View Models/AccountViewModel.swift @@ -111,10 +111,18 @@ public extension AccountViewModel { ignorableOutputEvent(accountService.showReblogs()) } + func confirmBlock() { + eventsSubject.send(Just(.confirmBlock(self)).setFailureType(to: Error.self).eraseToAnyPublisher()) + } + func block() { ignorableOutputEvent(accountService.block()) } + func confirmUnblock() { + eventsSubject.send(Just(.confirmUnblock(self)).setFailureType(to: Error.self).eraseToAnyPublisher()) + } + func unblock() { ignorableOutputEvent(accountService.unblock()) } @@ -147,10 +155,18 @@ public extension AccountViewModel { accountListEdit(accountService.rejectFollowRequest(), event: .rejectFollowRequest) } + func confirmDomainBlock(domain: String) { + eventsSubject.send(Just(.confirmDomainBlock(self)).setFailureType(to: Error.self).eraseToAnyPublisher()) + } + func domainBlock() { ignorableOutputEvent(accountService.domainBlock()) } + func confirmDomainUnblock(domain: String) { + eventsSubject.send(Just(.confirmDomainUnblock(self)).setFailureType(to: Error.self).eraseToAnyPublisher()) + } + func domainUnblock() { ignorableOutputEvent(accountService.domainUnblock()) } diff --git a/Views/UIKit/Content Views/StatusView.swift b/Views/UIKit/Content Views/StatusView.swift index d854543..217073f 100644 --- a/Views/UIKit/Content Views/StatusView.swift +++ b/Views/UIKit/Content Views/StatusView.swift @@ -625,10 +625,11 @@ private extension StatusView { accessibilityCustomActions = accessibilityCustomActions(viewModel: viewModel) } - // swiftlint:enable function_body_length func menu(viewModel: StatusViewModel) -> UIMenu { - var menuItems = [ + var sections = [UIMenu]() + + var firstSectionItems = [ UIAction( title: viewModel.bookmarked ? NSLocalizedString("status.unbookmark", comment: "") @@ -639,7 +640,7 @@ private extension StatusView { ] if let pinned = viewModel.pinned { - menuItems.append(UIAction( + firstSectionItems.append(UIAction( title: pinned ? NSLocalizedString("status.unpin", comment: "") : NSLocalizedString("status.pin", comment: ""), @@ -648,8 +649,12 @@ private extension StatusView { }) } + sections.append(UIMenu(options: .displayInline, children: firstSectionItems)) + + var secondSectionItems = [UIAction]() + if viewModel.isMine { - menuItems += [ + secondSectionItems += [ UIAction( title: viewModel.muted ? NSLocalizedString("status.unmute", comment: "") @@ -671,16 +676,80 @@ private extension StatusView { } ] } else { - menuItems.append(UIAction( + if let relationship = viewModel.accountViewModel.relationship { + if relationship.muting { + secondSectionItems.append(UIAction( + title: NSLocalizedString("account.unmute", comment: ""), + image: UIImage(systemName: "speaker")) { _ in + viewModel.accountViewModel.unmute() + }) + } else { + secondSectionItems.append(UIAction( + title: NSLocalizedString("account.mute", comment: ""), + image: UIImage(systemName: "speaker.slash")) { _ in + viewModel.accountViewModel.mute() + }) + } + + if relationship.blocking { + secondSectionItems.append(UIAction( + title: NSLocalizedString("account.unblock", comment: ""), + image: UIImage(systemName: "slash.circle"), + attributes: .destructive) { _ in +// viewModel.accountViewModel.unblock() + viewModel.accountViewModel.confirmUnblock() + }) + } else { + secondSectionItems.append(UIAction( + title: NSLocalizedString("account.block", comment: ""), + image: UIImage(systemName: "slash.circle"), + attributes: .destructive) { _ in + viewModel.accountViewModel.confirmBlock() + }) + } + } + + secondSectionItems.append(UIAction( title: NSLocalizedString("report", comment: ""), image: UIImage(systemName: "flag"), attributes: .destructive) { _ in viewModel.reportStatus() }) + + sections.append(UIMenu(options: .displayInline, children: secondSectionItems)) + + if !viewModel.accountViewModel.isLocal, + let domain = viewModel.accountViewModel.domain, + let relationship = viewModel.accountViewModel.relationship { + let domainBlockAction: UIAction + + if relationship.domainBlocking { + domainBlockAction = UIAction( + title: String.localizedStringWithFormat( + NSLocalizedString("account.domain-unblock-%@", comment: ""), + domain), + image: UIImage(systemName: "slash.circle"), + attributes: .destructive) { _ in + viewModel.accountViewModel.confirmDomainUnblock(domain: domain) + } + } else { + domainBlockAction = UIAction( + title: String.localizedStringWithFormat( + NSLocalizedString("account.domain-block-%@", comment: ""), + domain), + image: UIImage(systemName: "slash.circle"), + attributes: .destructive) { _ in + viewModel.accountViewModel.confirmDomainBlock(domain: domain) + } + } + + sections.append(UIMenu(options: .displayInline, children: [domainBlockAction])) + } } - return UIMenu(children: menuItems) + return UIMenu(children: sections) } + // swiftlint:enable function_body_length func setButtonImages(scale: UIImage.SymbolScale) { let visibility = statusConfiguration.viewModel.visibility