From 273305cda9533b09828c986b9d8c30138034c74f Mon Sep 17 00:00:00 2001 From: sunxiaojian Date: Wed, 28 Apr 2021 19:56:30 +0800 Subject: [PATCH] chore: show menu in statusCell --- .../Section/NotificationSection.swift | 1 + .../Diffiable/Section/ReportSection.swift | 1 + .../Diffiable/Section/StatusSection.swift | 73 +++++++------- .../UserProvider/UserProviderFacade.swift | 99 ++++++++++++------- .../Scene/Profile/ProfileViewController.swift | 17 +++- .../Search/SearchViewController+Follow.swift | 4 +- 6 files changed, 119 insertions(+), 76 deletions(-) diff --git a/Mastodon/Diffiable/Section/NotificationSection.swift b/Mastodon/Diffiable/Section/NotificationSection.swift index 9c59350b4..20db29c93 100644 --- a/Mastodon/Diffiable/Section/NotificationSection.swift +++ b/Mastodon/Diffiable/Section/NotificationSection.swift @@ -50,6 +50,7 @@ extension NotificationSection { let frame = CGRect(x: 0, y: 0, width: tableView.readableContentGuide.layoutFrame.width - NotificationStatusTableViewCell.statusPadding.left - NotificationStatusTableViewCell.statusPadding.right, height: tableView.readableContentGuide.layoutFrame.height) StatusSection.configure( cell: cell, + indexPath: indexPath, dependency: dependency, readableLayoutFrame: frame, timestampUpdatePublisher: timestampUpdatePublisher, diff --git a/Mastodon/Diffiable/Section/ReportSection.swift b/Mastodon/Diffiable/Section/ReportSection.swift index 6faaae6c2..07321be96 100644 --- a/Mastodon/Diffiable/Section/ReportSection.swift +++ b/Mastodon/Diffiable/Section/ReportSection.swift @@ -41,6 +41,7 @@ extension ReportSection { let status = managedObjectContext.object(with: objectID) as! Status StatusSection.configure( cell: cell, + indexPath: indexPath, dependency: dependency, readableLayoutFrame: tableView.readableContentGuide.layoutFrame, timestampUpdatePublisher: timestampUpdatePublisher, diff --git a/Mastodon/Diffiable/Section/StatusSection.swift b/Mastodon/Diffiable/Section/StatusSection.swift index b897de47f..36eecdbe5 100644 --- a/Mastodon/Diffiable/Section/StatusSection.swift +++ b/Mastodon/Diffiable/Section/StatusSection.swift @@ -49,6 +49,7 @@ extension StatusSection { let timelineIndex = managedObjectContext.object(with: objectID) as! HomeTimelineIndex StatusSection.configure( cell: cell, + indexPath: indexPath, dependency: dependency, readableLayoutFrame: tableView.readableContentGuide.layoutFrame, timestampUpdatePublisher: timestampUpdatePublisher, @@ -71,6 +72,7 @@ extension StatusSection { let status = managedObjectContext.object(with: objectID) as! Status StatusSection.configure( cell: cell, + indexPath: indexPath, dependency: dependency, readableLayoutFrame: tableView.readableContentGuide.layoutFrame, timestampUpdatePublisher: timestampUpdatePublisher, @@ -136,6 +138,7 @@ extension StatusSection { static func configure( cell: StatusCell, + indexPath: IndexPath, dependency: NeedsDependency, readableLayoutFrame: CGRect?, timestampUpdatePublisher: AnyPublisher, @@ -223,7 +226,6 @@ extension StatusSection { meta.blurhashImagePublisher() .receive(on: DispatchQueue.main) .sink { [weak cell] image in - guard let cell = cell else { return } blurhashOverlayImageView.image = image image?.pngData().flatMap { blurhashImageCache.setObject($0 as NSData, forKey: blurhashImageDataKey) @@ -401,16 +403,16 @@ extension StatusSection { .store(in: &cell.disposeBag) } - // toolbar - StatusSection.configureActionToolBar( - cell: cell, - dependency: dependency, - status: status, - requestUserID: requestUserID - ) - - // separator line if let statusTableViewCell = cell as? StatusTableViewCell { + // toolbar + StatusSection.configureActionToolBar( + cell: statusTableViewCell, + indexPath: indexPath, + dependency: dependency, + status: status, + requestUserID: requestUserID + ) + // separator line statusTableViewCell.separatorLine.isHidden = statusItemAttribute.isSeparatorLineHidden } @@ -434,8 +436,10 @@ extension StatusSection { guard let dependency = dependency else { return } guard case .update(let object) = change.changeType, let status = object as? Status else { return } + guard let cell = cell as? StatusTableViewCell else { return } StatusSection.configureActionToolBar( cell: cell, + indexPath: indexPath, dependency: dependency, status: status, requestUserID: requestUserID @@ -593,7 +597,8 @@ extension StatusSection { } static func configureActionToolBar( - cell: StatusCell, + cell: StatusTableViewCell, + indexPath: IndexPath, dependency: NeedsDependency, status: Status, requestUserID: String @@ -623,7 +628,7 @@ extension StatusSection { cell.statusView.actionToolbarContainer.favoriteButton.setTitle(favoriteCountTitle, for: .normal) cell.statusView.actionToolbarContainer.isFavoriteButtonHighlight = isLike - self.setupStatusMoreButtonMenu(cell: cell, dependency: dependency, status: status) + self.setupStatusMoreButtonMenu(cell: cell, indexPath: indexPath, dependency: dependency, status: status) } static func configurePoll( @@ -752,37 +757,35 @@ extension StatusSection { } private static func setupStatusMoreButtonMenu( - cell: StatusCell, + cell: StatusTableViewCell, + indexPath: IndexPath, dependency: NeedsDependency, status: Status) { - cell.statusView.actionToolbarContainer.moreButton.menu = nil + guard let userProvider = dependency as? UserProvider else { fatalError() } guard let authenticationBox = dependency.context.authenticationService.activeMastodonAuthenticationBox.value else { return } let author = (status.reblog ?? status).author - guard authenticationBox.userID != author.id else { - return - } - var children: [UIMenuElement] = [] - let name = author.displayNameWithFallback - let reportAction = UIAction(title: L10n.Common.Controls.Actions.reportUser(name), image: UIImage(systemName: "exclamationmark.bubble"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { - [weak dependency] _ in - guard let dependency = dependency else { return } - let viewModel = ReportViewModel( - context: dependency.context, - domain: authenticationBox.domain, - user: status.author, - status: status) - dependency.coordinator.present( - scene: .report(viewModel: viewModel), - from: nil, - transition: .modal(animated: true, completion: nil) - ) - } - children.append(reportAction) - cell.statusView.actionToolbarContainer.moreButton.menu = UIMenu(title: "", options: [], children: children) + let canReport = authenticationBox.userID != author.id + + let isMuting = (author.mutingBy ?? Set()).map(\.id).contains(authenticationBox.userID) + let isBlocking = (author.blockingBy ?? Set()).map(\.id).contains(authenticationBox.userID) + cell.statusView.actionToolbarContainer.moreButton.showsMenuAsPrimaryAction = true + cell.statusView.actionToolbarContainer.moreButton.menu = UserProviderFacade.createProfileActionMenu( + for: author, + isMuting: isMuting, + isBlocking: isBlocking, + canReport: canReport, + provider: userProvider, + cell: cell, + indexPath: indexPath, + sourceView: cell.statusView.actionToolbarContainer.moreButton, + barButtonItem: nil, + shareUser: nil, + shareStatus: status + ) } } diff --git a/Mastodon/Protocol/UserProvider/UserProviderFacade.swift b/Mastodon/Protocol/UserProvider/UserProviderFacade.swift index 729e2a932..0d839bd17 100644 --- a/Mastodon/Protocol/UserProvider/UserProviderFacade.swift +++ b/Mastodon/Protocol/UserProvider/UserProviderFacade.swift @@ -57,19 +57,28 @@ extension UserProviderFacade { extension UserProviderFacade { static func toggleUserBlockRelationship( - provider: UserProvider + provider: UserProvider, + cell: UITableViewCell?, + indexPath: IndexPath? ) -> AnyPublisher, Error> { // prepare authentication guard let activeMastodonAuthenticationBox = provider.context.authenticationService.activeMastodonAuthenticationBox.value else { assertionFailure() return Fail(error: APIService.APIError.implicit(.authenticationMissing)).eraseToAnyPublisher() } - - return _toggleUserBlockRelationship( - context: provider.context, - activeMastodonAuthenticationBox: activeMastodonAuthenticationBox, - mastodonUser: provider.mastodonUser().eraseToAnyPublisher() - ) + if let cell = cell, let indexPath = indexPath { + return _toggleUserBlockRelationship( + context: provider.context, + activeMastodonAuthenticationBox: activeMastodonAuthenticationBox, + mastodonUser: provider.mastodonUser(for: cell, indexPath: indexPath).eraseToAnyPublisher() + ) + } else { + return _toggleUserBlockRelationship( + context: provider.context, + activeMastodonAuthenticationBox: activeMastodonAuthenticationBox, + mastodonUser: provider.mastodonUser().eraseToAnyPublisher() + ) + } } private static func _toggleUserBlockRelationship( @@ -97,19 +106,28 @@ extension UserProviderFacade { extension UserProviderFacade { static func toggleUserMuteRelationship( - provider: UserProvider + provider: UserProvider, + cell: UITableViewCell?, + indexPath: IndexPath? ) -> AnyPublisher, Error> { // prepare authentication guard let activeMastodonAuthenticationBox = provider.context.authenticationService.activeMastodonAuthenticationBox.value else { assertionFailure() return Fail(error: APIService.APIError.implicit(.authenticationMissing)).eraseToAnyPublisher() } - - return _toggleUserMuteRelationship( - context: provider.context, - activeMastodonAuthenticationBox: activeMastodonAuthenticationBox, - mastodonUser: provider.mastodonUser().eraseToAnyPublisher() - ) + if let cell = cell, let indexPath = indexPath { + return _toggleUserMuteRelationship( + context: provider.context, + activeMastodonAuthenticationBox: activeMastodonAuthenticationBox, + mastodonUser: provider.mastodonUser(for: cell, indexPath: indexPath).eraseToAnyPublisher() + ) + } else { + return _toggleUserMuteRelationship( + context: provider.context, + activeMastodonAuthenticationBox: activeMastodonAuthenticationBox, + mastodonUser: provider.mastodonUser().eraseToAnyPublisher() + ) + } } private static func _toggleUserMuteRelationship( @@ -140,10 +158,14 @@ extension UserProviderFacade { for mastodonUser: MastodonUser, isMuting: Bool, isBlocking: Bool, - needsShareAction: Bool, + canReport: Bool, provider: UserProvider, + cell: UITableViewCell?, + indexPath: IndexPath?, sourceView: UIView?, - barButtonItem: UIBarButtonItem? + barButtonItem: UIBarButtonItem?, + shareUser: MastodonUser?, + shareStatus: Status? ) -> UIMenu { var children: [UIMenuElement] = [] let name = mastodonUser.displayNameWithFallback @@ -159,7 +181,9 @@ extension UserProviderFacade { guard let provider = provider else { return } UserProviderFacade.toggleUserMuteRelationship( - provider: provider + provider: provider, + cell: cell, + indexPath: indexPath ) .sink { _ in // do nothing @@ -186,7 +210,9 @@ extension UserProviderFacade { guard let provider = provider else { return } UserProviderFacade.toggleUserBlockRelationship( - provider: provider + provider: provider, + cell: cell, + indexPath: indexPath ) .sink { _ in // do nothing @@ -201,29 +227,30 @@ extension UserProviderFacade { let blockMenu = UIMenu(title: L10n.Common.Controls.Firendship.blockUser(name), image: UIImage(systemName: "hand.raised"), options: [], children: [blockAction]) children.append(blockMenu) } - - let reportAction = UIAction(title: L10n.Common.Controls.Actions.reportUser(name), image: UIImage(systemName: "flag"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak provider] _ in - guard let provider = provider else { return } - guard let authenticationBox = provider.context.authenticationService.activeMastodonAuthenticationBox.value else { - return + if canReport { + let reportAction = UIAction(title: L10n.Common.Controls.Actions.reportUser(name), image: UIImage(systemName: "flag"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak provider] _ in + guard let provider = provider else { return } + guard let authenticationBox = provider.context.authenticationService.activeMastodonAuthenticationBox.value else { + return + } + let viewModel = ReportViewModel( + context: provider.context, + domain: authenticationBox.domain, + user: mastodonUser, + status: nil) + provider.coordinator.present( + scene: .report(viewModel: viewModel), + from: provider, + transition: .modal(animated: true, completion: nil) + ) } - let viewModel = ReportViewModel( - context: provider.context, - domain: authenticationBox.domain, - user: mastodonUser, - status: nil) - provider.coordinator.present( - scene: .report(viewModel: viewModel), - from: provider, - transition: .modal(animated: true, completion: nil) - ) + children.append(reportAction) } - children.append(reportAction) - if needsShareAction { + if let shareUser = shareUser { let shareAction = UIAction(title: L10n.Common.Controls.Actions.shareUser(name), image: UIImage(systemName: "square.and.arrow.up"), identifier: nil, discoverabilityTitle: nil, attributes: [], state: .off) { [weak provider] _ in guard let provider = provider else { return } - let activityViewController = createActivityViewControllerForMastodonUser(mastodonUser: mastodonUser, dependency: provider) + let activityViewController = createActivityViewControllerForMastodonUser(mastodonUser: shareUser, dependency: provider) provider.coordinator.present( scene: .activityViewController( activityViewController: activityViewController, diff --git a/Mastodon/Scene/Profile/ProfileViewController.swift b/Mastodon/Scene/Profile/ProfileViewController.swift index 8fc915a0a..7a8742f18 100644 --- a/Mastodon/Scene/Profile/ProfileViewController.swift +++ b/Mastodon/Scene/Profile/ProfileViewController.swift @@ -377,7 +377,18 @@ extension ProfileViewController { let isMuting = relationshipActionOptionSet.contains(.muting) let isBlocking = relationshipActionOptionSet.contains(.blocking) let needsShareAction = self.viewModel.isMeBarButtonItemsHidden.value - self.moreMenuBarButtonItem.menu = UserProviderFacade.createProfileActionMenu(for: mastodonUser, isMuting: isMuting, isBlocking: isBlocking, needsShareAction: needsShareAction, provider: self, sourceView: nil, barButtonItem: self.moreMenuBarButtonItem) + self.moreMenuBarButtonItem.menu = UserProviderFacade.createProfileActionMenu( + for: mastodonUser, + isMuting: isMuting, + isBlocking: isBlocking, + canReport: true, + provider: self, + cell: nil, + indexPath: nil, + sourceView: nil, + barButtonItem: self.moreMenuBarButtonItem, + shareUser: needsShareAction ? mastodonUser : nil, + shareStatus: nil) } .store(in: &disposeBag) viewModel.isRelationshipActionButtonHidden @@ -692,7 +703,7 @@ extension ProfileViewController: ProfileHeaderViewDelegate { ) let unmuteAction = UIAlertAction(title: L10n.Common.Controls.Firendship.unmute, style: .default) { [weak self] _ in guard let self = self else { return } - UserProviderFacade.toggleUserMuteRelationship(provider: self) + UserProviderFacade.toggleUserMuteRelationship(provider: self, cell: nil, indexPath: nil) .sink { _ in // do nothing } receiveValue: { _ in @@ -714,7 +725,7 @@ extension ProfileViewController: ProfileHeaderViewDelegate { ) let unblockAction = UIAlertAction(title: L10n.Common.Controls.Firendship.unblock, style: .default) { [weak self] _ in guard let self = self else { return } - UserProviderFacade.toggleUserBlockRelationship(provider: self) + UserProviderFacade.toggleUserBlockRelationship(provider: self, cell: nil, indexPath: nil) .sink { _ in // do nothing } receiveValue: { _ in diff --git a/Mastodon/Scene/Search/SearchViewController+Follow.swift b/Mastodon/Scene/Search/SearchViewController+Follow.swift index 8986dd680..6682d846b 100644 --- a/Mastodon/Scene/Search/SearchViewController+Follow.swift +++ b/Mastodon/Scene/Search/SearchViewController+Follow.swift @@ -54,7 +54,7 @@ extension SearchViewController: SearchRecommendAccountsCollectionViewCellDelegat ) let unmuteAction = UIAlertAction(title: L10n.Common.Controls.Firendship.unmute, style: .default) { [weak self] _ in guard let self = self else { return } - UserProviderFacade.toggleUserMuteRelationship(provider: self) + UserProviderFacade.toggleUserMuteRelationship(provider: self, cell: nil, indexPath: nil) .sink { _ in // do nothing } receiveValue: { _ in @@ -76,7 +76,7 @@ extension SearchViewController: SearchRecommendAccountsCollectionViewCellDelegat ) let unblockAction = UIAlertAction(title: L10n.Common.Controls.Firendship.unblock, style: .default) { [weak self] _ in guard let self = self else { return } - UserProviderFacade.toggleUserBlockRelationship(provider: self) + UserProviderFacade.toggleUserBlockRelationship(provider: self, cell: nil, indexPath: nil) .sink { _ in // do nothing } receiveValue: { _ in