Skip to content

Commit

Permalink
Add accept/dismiss-menu (IOS-241)
Browse files Browse the repository at this point in the history
  • Loading branch information
zeitschlag committed Jul 24, 2024
1 parent ff48cc2 commit f3c035c
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 34 deletions.
12 changes: 12 additions & 0 deletions Mastodon.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
D85DF9742C481B3500A01408 /* DataSourceFacade+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85DF9732C481B3500A01408 /* DataSourceFacade+Notifications.swift */; };
D85DF9762C4965A900A01408 /* NotificationRequestsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85DF9752C4965A900A01408 /* NotificationRequestsViewModel.swift */; };
D85DF97A2C4E49A400A01408 /* NotificationRequestCountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85DF9792C4E49A400A01408 /* NotificationRequestCountView.swift */; };
D85DF97E2C50EFA700A01408 /* AccountNotificationTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D85DF97D2C50EFA700A01408 /* AccountNotificationTimelineViewController.swift */; };
D87364F92AE28DB500C8F919 /* Kanna in Frameworks */ = {isa = PBXBuildFile; productRef = D87364F82AE28DB500C8F919 /* Kanna */; };
D87BFC8B291D5C6B00FEE264 /* MastodonLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BFC8A291D5C6B00FEE264 /* MastodonLoginView.swift */; };
D87BFC8D291EB81200FEE264 /* MastodonLoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BFC8C291EB81200FEE264 /* MastodonLoginViewModel.swift */; };
Expand Down Expand Up @@ -817,6 +818,7 @@
D85DF9732C481B3500A01408 /* DataSourceFacade+Notifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+Notifications.swift"; sourceTree = "<group>"; };
D85DF9752C4965A900A01408 /* NotificationRequestsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRequestsViewModel.swift; sourceTree = "<group>"; };
D85DF9792C4E49A400A01408 /* NotificationRequestCountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRequestCountView.swift; sourceTree = "<group>"; };
D85DF97D2C50EFA700A01408 /* AccountNotificationTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountNotificationTimelineViewController.swift; sourceTree = "<group>"; };
D87BFC8A291D5C6B00FEE264 /* MastodonLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginView.swift; sourceTree = "<group>"; };
D87BFC8C291EB81200FEE264 /* MastodonLoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginViewModel.swift; sourceTree = "<group>"; };
D87BFC8E291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginServerTableViewCell.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1852,6 +1854,7 @@
D85DF9702C481B1100A01408 /* Requests */ = {
isa = PBXGroup;
children = (
D85DF97C2C50EF8700A01408 /* Account Notifications */,
D85DF96E2C481B1100A01408 /* NotificationRequestsTableViewController.swift */,
D85DF96F2C481B1100A01408 /* NotificationRequestTableViewCell.swift */,
D85DF9752C4965A900A01408 /* NotificationRequestsViewModel.swift */,
Expand All @@ -1861,6 +1864,14 @@
path = "Notification Filtering/Requests";
sourceTree = "<group>";
};
D85DF97C2C50EF8700A01408 /* Account Notifications */ = {
isa = PBXGroup;
children = (
D85DF97D2C50EFA700A01408 /* AccountNotificationTimelineViewController.swift */,
);
path = "Account Notifications";
sourceTree = "<group>";
};
D8A6AB68291C50F3003AB663 /* Login */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -3816,6 +3827,7 @@
DB1D84382657B275000346B3 /* SegmentedControlNavigateable.swift in Sources */,
0F20220726134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift in Sources */,
DB7A9F932818F33C0016AF98 /* MastodonServerRulesViewController+Debug.swift in Sources */,
D85DF97E2C50EFA700A01408 /* AccountNotificationTimelineViewController.swift in Sources */,
2D5A3D2825CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift in Sources */,
DB6B74FC272FF55800C70B6E /* UserSection.swift in Sources */,
DB0FCB862796BDA1006C02E2 /* SearchSection.swift in Sources */,
Expand Down
6 changes: 3 additions & 3 deletions Mastodon/Coordinator/SceneCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ extension SceneCoordinator {
// Notifications
case notificationPolicy(viewModel: NotificationFilterViewModel)
case notificationRequests(viewModel: NotificationRequestsViewModel)
case notificationTimeline(viewModel: NotificationTimelineViewModel)
case accountNotificationTimeline(viewModel: NotificationTimelineViewModel, request: Mastodon.Entity.NotificationRequest)

// report
case report(viewModel: ReportViewModel)
Expand Down Expand Up @@ -567,8 +567,8 @@ private extension SceneCoordinator {
viewController = NotificationRequestsTableViewController(viewModel: viewModel)
case .notificationPolicy(let viewModel):
viewController = NotificationPolicyViewController(viewModel: viewModel)
case .notificationTimeline(let viewModel):
viewController = NotificationTimelineViewController(viewModel: viewModel, context: appContext, coordinator: self)
case .accountNotificationTimeline(let viewModel, let request):
viewController = AccountNotificationTimelineViewController(viewModel: viewModel, context: appContext, coordinator: self, notificationRequest: request)
}

setupDependency(for: viewController as? NeedsDependency)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,17 @@ extension DataSourceFacade {
static func coordinateToNotificationRequest(
request: Mastodon.Entity.NotificationRequest,
provider: ViewControllerWithDependencies & AuthContextProvider
) async {
) async -> AccountNotificationTimelineViewController? {
provider.coordinator.showLoading()

let notificationTimelineViewModel = NotificationTimelineViewModel(context: provider.context, authContext: provider.authContext, scope: .fromAccount(request.account))

provider.coordinator.hideLoading()
provider.coordinator.present(scene: .notificationTimeline(viewModel: notificationTimelineViewModel), transition: .show)

guard let viewController = provider.coordinator.present(scene: .accountNotificationTimeline(viewModel: notificationTimelineViewModel, request: request), transition: .show) as? AccountNotificationTimelineViewController else { return nil }

return viewController

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright © 2024 Mastodon gGmbH. All rights reserved.

import UIKit
import MastodonCore
import MastodonSDK

protocol AccountNotificationTimelineViewControllerDelegate: AnyObject {
func acceptRequest(_ viewController: AccountNotificationTimelineViewController, request: Mastodon.Entity.NotificationRequest, completion: @escaping (() -> Void))
func dismissRequest(_ viewController: AccountNotificationTimelineViewController, request: Mastodon.Entity.NotificationRequest, completion: @escaping (() -> Void))
}

class AccountNotificationTimelineViewController: NotificationTimelineViewController {

let request: Mastodon.Entity.NotificationRequest
weak var delegate: AccountNotificationTimelineViewControllerDelegate?

init(viewModel: NotificationTimelineViewModel, context: AppContext, coordinator: SceneCoordinator, notificationRequest: Mastodon.Entity.NotificationRequest) {
self.request = notificationRequest

super.init(viewModel: viewModel, context: context, coordinator: coordinator)

navigationItem.rightBarButtonItem = UIBarButtonItem(title: nil, image: UIImage(systemName: "ellipsis.circle"), target: nil, action: nil, menu: menu())
}

required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }

// MARK: - Actions

//TODO: Localization
func menu() -> UIMenu {
let menu = UIMenu(children: [
UIAction(title: "Accept", image: UIImage(systemName: "checkmark")) { [weak self] _ in
guard let self else { return }

coordinator.showLoading()
self.delegate?.acceptRequest(self, request: request) {
self.navigationController?.popViewController(animated: true)
}
coordinator.hideLoading()
},
UIAction(title: "Dismiss", image: UIImage(systemName: "speaker.slash")) { [weak self] _ in
guard let self else { return }

coordinator.showLoading()
self.delegate?.dismissRequest(self, request: request) {
self.navigationController?.popViewController(animated: true)
}
coordinator.hideLoading()
}
])

return menu
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class NotificationRequestTableViewCell: UITableViewCell {
acceptNotificationRequestButton.translatesAutoresizingMaskIntoConstraints = false
acceptNotificationRequestButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
acceptNotificationRequestButton.setTitleColor(.white, for: .normal)
//TODO: Localization
acceptNotificationRequestButton.setTitle("Accept", for: .normal)
acceptNotificationRequestButton.setImage(UIImage(systemName: "checkmark"), for: .normal)
acceptNotificationRequestButton.imageView?.contentMode = .scaleAspectFit
Expand All @@ -82,6 +83,7 @@ class NotificationRequestTableViewCell: UITableViewCell {
rejectNotificationRequestButton.translatesAutoresizingMaskIntoConstraints = false
rejectNotificationRequestButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
rejectNotificationRequestButton.setTitleColor(.black, for: .normal)
//TODO: Localization
rejectNotificationRequestButton.setTitle("Dismiss", for: .normal)
rejectNotificationRequestButton.setImage(UIImage(systemName: "speaker.slash"), for: .normal)
rejectNotificationRequestButton.imageView?.contentMode = .scaleAspectFit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import UIKit
import MastodonSDK
import MastodonCore
import MastodonAsset
import MastodonLocalization

enum NotificationRequestsSection: Hashable {
case main
Expand Down Expand Up @@ -58,8 +59,7 @@ class NotificationRequestsTableViewController: UIViewController, NeedsDependency
tableView.delegate = self
self.dataSource = dataSource

//TODO: Localization
title = "Filtered Notifications"
title = L10n.Scene.Notification.FilteredNotificationBanner.title
}

required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
Expand All @@ -81,8 +81,11 @@ extension NotificationRequestsTableViewController: UITableViewDelegate {

let request = viewModel.requests[indexPath.row]

Task {
await DataSourceFacade.coordinateToNotificationRequest(request: request, provider: self)
Task { [weak self] in
guard let self else { return }

let viewController = await DataSourceFacade.coordinateToNotificationRequest(request: request, provider: self)
viewController?.delegate = self
}
}

Expand Down Expand Up @@ -167,30 +170,7 @@ extension NotificationRequestsTableViewController: NotificationRequestTableViewC
Task { [weak self] in
guard let self else { return }
do {
_ = try await context.apiService.rejectNotificationRequests(authenticationBox: authContext.mastodonAuthenticationBox,
id: notificationRequest.id)

let requests = try await context.apiService.notificationRequests(authenticationBox: authContext.mastodonAuthenticationBox).value

NotificationCenter.default.post(name: .notificationFilteringChanged, object: nil)

if requests.count > 0 {

await MainActor.run { [weak self] in
guard let self else { return }
self.viewModel.requests = requests
var snapshot = NSDiffableDataSourceSnapshot<NotificationRequestsSection, NotificationRequestItem>()
snapshot.appendSections([.main])
snapshot.appendItems(self.viewModel.requests.compactMap { NotificationRequestItem.item($0) } )

self.dataSource?.apply(snapshot)
}
} else {
await MainActor.run { [weak self] in
_ = self?.navigationController?.popViewController(animated: true)
}
}

try await rejectNotificationRequest(notificationRequest)
} catch {
cell.rejectNotificationRequestActivityIndicatorView.stopAnimating()
cell.rejectNotificationRequestButton.tintColor = .black
Expand All @@ -200,4 +180,46 @@ extension NotificationRequestsTableViewController: NotificationRequestTableViewC
}
}
}

private func rejectNotificationRequest(_ notificationRequest: MastodonSDK.Mastodon.Entity.NotificationRequest) async throws {
_ = try await context.apiService.rejectNotificationRequests(authenticationBox: authContext.mastodonAuthenticationBox,
id: notificationRequest.id)

let requests = try await context.apiService.notificationRequests(authenticationBox: authContext.mastodonAuthenticationBox).value

NotificationCenter.default.post(name: .notificationFilteringChanged, object: nil)

if requests.count > 0 {
await MainActor.run { [weak self] in
guard let self else { return }
self.viewModel.requests = requests
var snapshot = NSDiffableDataSourceSnapshot<NotificationRequestsSection, NotificationRequestItem>()
snapshot.appendSections([.main])
snapshot.appendItems(self.viewModel.requests.compactMap { NotificationRequestItem.item($0) } )

self.dataSource?.apply(snapshot)
}
} else {
await MainActor.run { [weak self] in
_ = self?.navigationController?.popViewController(animated: true)
}
}

}
}

extension NotificationRequestsTableViewController: AccountNotificationTimelineViewControllerDelegate {
func acceptRequest(_ viewController: AccountNotificationTimelineViewController, request: MastodonSDK.Mastodon.Entity.NotificationRequest, completion: @escaping (() -> Void)) {
Task {
try? await rejectNotificationRequest(request)
completion()
}
}

func dismissRequest(_ viewController: AccountNotificationTimelineViewController, request: MastodonSDK.Mastodon.Entity.NotificationRequest, completion: @escaping (() -> Void)) {
Task {
try? await rejectNotificationRequest(request)
completion()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import CoreDataStack
import MastodonCore
import MastodonLocalization

final class NotificationTimelineViewController: UIViewController, NeedsDependency, MediaPreviewableViewController {
class NotificationTimelineViewController: UIViewController, NeedsDependency, MediaPreviewableViewController {

weak var context: AppContext!
weak var coordinator: SceneCoordinator!
Expand Down

0 comments on commit f3c035c

Please sign in to comment.