// // StatusSwipeAction.swift // Tusker // // Created by Shadowfacts on 11/26/22. // Copyright © 2022 Shadowfacts. All rights reserved. // import UIKit import Pachyderm enum StatusSwipeAction: String, Codable, Hashable, CaseIterable { case reply case favorite case reblog case share case bookmark case openInSafari var displayName: String { switch self { case .reply: return "Reply" case .favorite: return "Favorite" case .reblog: return "Reblog" case .share: return "Share" case .bookmark: return "Bookmark" case .openInSafari: return "Open in Safari" } } var systemImageName: String { switch self { case .reply: return "arrowshape.turn.up.left.fill" case .favorite: return "star.fill" case .reblog: return "repeat" case .share: return "square.and.arrow.up" case .bookmark: return "bookmark.fill" case .openInSafari: return "safari" } } func createAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction? { switch self { case .reply: return createReplyAction(status: status, container: container) case .favorite: return createFavoriteAction(status: status, container: container) case .reblog: return createReblogAction(status: status, container: container) case .share: return createShareAction(status: status, container: container) case .bookmark: return createBookmarkAction(status: status, container: container) case .openInSafari: return createOpenInSafariAction(status: status, container: container) } } } protocol StatusSwipeActionContainer: UIView { var mastodonController: MastodonController! { get } var navigationDelegate: any TuskerNavigationDelegate { get } var toastableViewController: ToastableViewController? { get } // necessary b/c the reblog-handling logic only exists in the cells func performReplyAction() } private func createReplyAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction? { guard container.mastodonController.loggedIn else { return nil } let action = UIContextualAction(style: .normal, title: "Reply") { [unowned container] _, _, completion in container.performReplyAction() completion(true) } action.image = UIImage(systemName: "arrowshape.turn.up.left.fill") action.backgroundColor = container.tintColor return action } private func createFavoriteAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction? { guard container.mastodonController.loggedIn else { return nil } let title = status.favourited ? "Unfavorite" : "Favorite" let action = UIContextualAction(style: .normal, title: title) { [unowned container] _, _, completion in Task { @MainActor in await FavoriteService(status: status, mastodonController: container.mastodonController, presenter: container.navigationDelegate).toggleFavorite() completion(true) } } action.image = UIImage(systemName: "star.fill") action.backgroundColor = status.favourited ? UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1) : UIColor(displayP3Red: 1, green: 204/255, blue: 0, alpha: 1) return action } private func createReblogAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction? { guard container.mastodonController.loggedIn else { return nil } let title = status.reblogged ? "Unreblog" : "Reblog" let action = UIContextualAction(style: .normal, title: title) { [unowned container] _, _, completion in Task { @MainActor in await ReblogService(status: status, mastodonController: container.mastodonController, presenter: container.navigationDelegate).toggleReblog() completion(true) } } action.image = UIImage(systemName: "repeat") action.backgroundColor = status.reblogged ? UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1) : container.tintColor return action } private func createShareAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction { let action = UIContextualAction(style: .normal, title: "Share") { [unowned container] _, _, completion in container.navigationDelegate.showMoreOptions(forStatus: status.id, source: .view(container)) completion(true) } // bold to more closesly match other action symbols let config = UIImage.SymbolConfiguration(weight: .bold) action.image = UIImage(systemName: "square.and.arrow.up")!.applyingSymbolConfiguration(config)! action.backgroundColor = .lightGray return action } private func createBookmarkAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction? { guard container.mastodonController.loggedIn else { return nil } let bookmarked = status.bookmarked ?? false let title = bookmarked ? "Unbookmark" : "Bookmark" let action = UIContextualAction(style: .normal, title: title) { [unowned container] _, _, completion in Task { @MainActor in let request = (bookmarked ? Status.unbookmark : Status.bookmark)(status.id) do { let (status, _) = try await container.mastodonController.run(request) container.mastodonController.persistentContainer.addOrUpdate(status: status) } catch { if let toastable = container.toastableViewController { let config = ToastConfiguration(from: error, with: "Error \(bookmarked ? "Unb" : "B")ookmarking", in: toastable, retryAction: nil) toastable.showToast(configuration: config, animated: true) } } completion(true) } } action.image = UIImage(systemName: "bookmark.fill") action.backgroundColor = .systemRed return action } private func createOpenInSafariAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction { let action = UIContextualAction(style: .normal, title: "Open in Safari") { [unowned container] _, _, completion in container.navigationDelegate.selected(url: status.url!, allowUniversalLinks: false) completion(true) } action.image = UIImage(systemName: "safari") action.backgroundColor = container.tintColor return action }