// // PreviewViewControllerProvider.swift // Tusker // // Created by Shadowfacts on 10/10/18. // Copyright © 2018 Shadowfacts. All rights reserved. // import UIKit import SafariServices import Pachyderm protocol MenuPreviewProvider { typealias PreviewProviders = (content: UIContextMenuContentPreviewProvider, actions: () -> [UIMenuElement]) var navigationDelegate: TuskerNavigationDelegate? { get } func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? } protocol CustomPreviewPresenting { func presentFromPreview(presenter: UIViewController) } extension MenuPreviewProvider { private var mastodonController: MastodonController? { navigationDelegate?.apiController } // Default no-op implementation func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? { return nil } func actionsForProfile(accountID: String, sourceView: UIView?) -> [UIMenuElement] { guard let mastodonController = mastodonController, let account = mastodonController.persistentContainer.account(for: accountID) else { return [] } var actionsSection: [UIMenuElement] = [ createAction(identifier: "sendmessage", title: "Send Message", systemImageName: "envelope", handler: { (_) in self.navigationDelegate?.compose(mentioning: account.acct) }), ] // todo: handle pre-iOS 14 if accountID != mastodonController.account.id, #available(iOS 14.0, *) { actionsSection.append(UIDeferredMenuElement({ (elementHandler) in guard let mastodonController = self.mastodonController else { elementHandler([]) return } let request = Client.getRelationships(accounts: [account.id]) mastodonController.run(request) { (response) in if case let .success(results, _) = response, let relationship = results.first { let following = relationship.following DispatchQueue.main.async { elementHandler([ createAction(identifier: "follow", title: following ? "Unfollow" : "Follow", systemImageName: following ? "person.badge.minus" : "person.badge.minus", handler: { (_) in let request = (following ? Account.unfollow : Account.follow)(accountID) mastodonController.run(request) { (_) in } }) ]) } } } })) } let shareSection = [ openInSafariAction(url: account.url), createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in self.navigationDelegate?.showMoreOptions(forAccount: accountID, sourceView: sourceView) }) ] return [ UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: actionsSection), UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: shareSection) ] } func actionsForURL(_ url: URL, sourceView: UIView?) -> [UIAction] { return [ openInSafariAction(url: url), createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in self.navigationDelegate?.showMoreOptions(forURL: url, sourceView: sourceView) }) ] } func actionsForHashtag(_ hashtag: Hashtag, sourceView: UIView?) -> [UIMenuElement] { let account = mastodonController!.accountInfo! let saved = SavedDataManager.shared.isSaved(hashtag: hashtag, for: account) let actionsSection = [ createAction(identifier: "save", title: saved ? "Unsave Hashtag" : "Save Hashtag", systemImageName: "number", handler: { (_) in if saved { SavedDataManager.shared.remove(hashtag: hashtag, for: account) } else { SavedDataManager.shared.add(hashtag: hashtag, for: account) } }) ] let shareSection = actionsForURL(hashtag.url, sourceView: sourceView) return [ UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: actionsSection), UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: shareSection) ] } func actionsForStatus(statusID: String, sourceView: UIView?) -> [UIMenuElement] { guard let mastodonController = mastodonController, let status = mastodonController.persistentContainer.status(for: statusID) else { return [] } let bookmarked = status.bookmarked ?? false let muted = status.muted var actionsSection = [ createAction(identifier: "reply", title: "Reply", systemImageName: "arrowshape.turn.up.left", handler: { (_) in self.navigationDelegate?.reply(to: statusID) }), createAction(identifier: "bookmark", title: bookmarked ? "Unbookmark" : "Bookmark", systemImageName: bookmarked ? "bookmark.fill" : "bookmark", handler: { (_) in let request = (bookmarked ? Status.unbookmark : Status.bookmark)(statusID) self.mastodonController?.run(request) { (response) in if case let .success(status, _) = response { self.mastodonController?.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false) } } }), createAction(identifier: "mute", title: muted ? "Unmute" : "Mute", systemImageName: muted ? "speaker" : "speaker.slash", handler: { (_) in let request = (muted ? Status.unmuteConversation : Status.muteConversation)(statusID) self.mastodonController?.run(request) { (response) in if case let .success(status, _) = response { self.mastodonController?.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false) } } }) ] if mastodonController.account != nil && mastodonController.account.id == status.account.id { let pinned = status.pinned ?? false actionsSection.append(createAction(identifier: "", title: pinned ? "Unpin" : "Pin", systemImageName: pinned ? "pin.slash" : "pin", handler: { (_) in let request = (pinned ? Status.unpin : Status.pin)(statusID) self.mastodonController?.run(request, completion: { (response) in if case let .success(status, _) = response { self.mastodonController?.persistentContainer.addOrUpdate(status: status, incrementReferenceCount: false) } }) })) } let shareSection = [ openInSafariAction(url: status.url!), createAction(identifier: "share", title: "Share...", systemImageName: "square.and.arrow.up", handler: { (_) in self.navigationDelegate?.showMoreOptions(forStatus: statusID, sourceView: sourceView) }), ] return [ UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: actionsSection), UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: shareSection), ] } private func createAction(identifier: String, title: String, systemImageName: String, handler: @escaping UIActionHandler) -> UIAction { return UIAction(title: title, image: UIImage(systemName: systemImageName), identifier: UIAction.Identifier(identifier), discoverabilityTitle: nil, attributes: [], state: .off, handler: handler) } private func openInSafariAction(url: URL) -> UIAction { return createAction(identifier: "openinsafari", title: "Open in Safari", systemImageName: "safari", handler: { (_) in self.navigationDelegate?.selected(url: url) }) } } extension LargeImageViewController: CustomPreviewPresenting { func presentFromPreview(presenter: UIViewController) { presenter.present(self, animated: true) } } extension GalleryViewController: CustomPreviewPresenting { func presentFromPreview(presenter: UIViewController) { presenter.present(self, animated: true) } } extension SFSafariViewController: CustomPreviewPresenting { func presentFromPreview(presenter: UIViewController) { presenter.present(self, animated: true) } }