// // TuskerNavigationDelegate.swift // Tusker // // Created by Shadowfacts on 9/30/18. // Copyright © 2018 Shadowfacts. All rights reserved. // import UIKit import SafariServices import Pachyderm protocol TuskerNavigationDelegate { func selected(account accountID: String) func selected(mention: Mention) func selected(tag: Hashtag) func selected(url: URL) func selected(status statusID: String) func compose() func reply(to statusID: String) func largeImage(_ image: UIImage, description: String?, sourceView: UIView) -> LargeImageViewController func largeImage(gifData: Data, description: String?, sourceView: UIView) -> LargeImageViewController func showLargeImage(_ image: UIImage, description: String?, animatingFrom sourceView: UIView) func showLargeImage(gifData: Data, description: String?, animatingFrom sourceView: UIView) func gallery(attachments: [Attachment], sourceViews: [UIView], startIndex: Int) -> GalleryViewController func showGallery(attachments: [Attachment], sourceViews: [UIView], startIndex: Int) func showMoreOptions(forStatus statusID: String) func showMoreOptions(forURL url: URL) } extension TuskerNavigationDelegate where Self: UIViewController { func selected(account accountID: String) { // don't open if the account is the same as the current one if let profileController = self as? ProfileTableViewController, profileController.accountID == accountID { return } show(ProfileTableViewController(accountID: accountID), sender: self) } func selected(mention: Mention) { show(ProfileTableViewController(accountID: mention.id), sender: self) } func selected(tag: Hashtag) { show(TimelineTableViewController(for: .tag(hashtag: tag.name)), sender: self) } func selected(url: URL) { if (Preferences.shared.openLinksInApps) { UIApplication.shared.open(url, options: [.universalLinksOnly: true]) { (success) in if (!success) { self.present(SFSafariViewController(url: url), animated: true) } } } else { present(SFSafariViewController(url: url), animated: true) } } func selected(status statusID: String) { // don't open if the conversation is the same as the current one if let conversationController = self as? ConversationTableViewController, conversationController.mainStatusID == statusID { return } show(ConversationTableViewController(for: statusID), sender: self) } func compose() { let compose = ComposeViewController() let vc = UINavigationController(rootViewController: compose) vc.presentationController?.delegate = compose present(vc, animated: true) } func reply(to statusID: String) { let compose = ComposeViewController(inReplyTo: statusID) let vc = UINavigationController(rootViewController: compose) vc.presentationController?.delegate = compose present(vc, animated: true) } private func sourceViewInfo(_ sourceView: UIView) -> (CGRect, CGFloat) { var sourceFrame = sourceView.convert(sourceView.bounds, to: view) if let scrollView = view as? UIScrollView { let scale = scrollView.zoomScale let width = sourceFrame.width * scale let height = sourceFrame.height * scale let x = sourceFrame.minX * scale - scrollView.contentOffset.x + scrollView.frame.minX let y = sourceFrame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY sourceFrame = CGRect(x: x, y: y, width: width, height: height) } return (sourceFrame, sourceView.layer.cornerRadius) } func largeImage(_ image: UIImage, description: String?, sourceView: UIView) -> LargeImageViewController { let (sourceFrame, sourceCornerRadius) = sourceViewInfo(sourceView) let vc = LargeImageViewController(image: image, description: description, sourceFrame: sourceFrame, sourceCornerRadius: sourceCornerRadius) vc.transitioningDelegate = self return vc } func largeImage(gifData: Data, description: String?, sourceView: UIView) -> LargeImageViewController { let (sourceFrame, sourceCornerRadius) = sourceViewInfo(sourceView) let vc = LargeImageViewController(image: UIImage(data: gifData)!, description: description, sourceFrame: sourceFrame, sourceCornerRadius: sourceCornerRadius) vc.transitioningDelegate = self vc.gifData = gifData return vc } func showLargeImage(_ image: UIImage, description: String?, animatingFrom sourceView: UIView) { present(largeImage(image, description: description, sourceView: sourceView), animated: true) } func showLargeImage(gifData: Data, description: String?, animatingFrom sourceView: UIView) { present(largeImage(gifData: gifData, description: description, sourceView: sourceView), animated: true) } func gallery(attachments: [Attachment], sourceViews: [UIView], startIndex: Int) -> GalleryViewController { let sourcesInfo = sourceViews.map(sourceViewInfo) let vc = GalleryViewController(attachments: attachments, sourcesInfo: sourcesInfo, startIndex: startIndex) vc.transitioningDelegate = self return vc } func showGallery(attachments: [Attachment], sourceViews: [UIView], startIndex: Int) { present(gallery(attachments: attachments, sourceViews: sourceViews, startIndex: startIndex), animated: true) } private func moreOptions(forURL url: URL) -> UIAlertController { let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) alert.title = url.absoluteString alert.addAction(UIAlertAction(title: "Open in Safari", style: .default, handler: { (_) in let vc = SFSafariViewController(url: url) self.present(vc, animated: true) })) alert.addAction(UIAlertAction(title: "Copy", style: .default, handler: { (_) in UIPasteboard.general.url = url })) alert.addAction(UIAlertAction(title: "Share...", style: .default, handler: { (_) in let vc = UIActivityViewController(activityItems: [url], applicationActivities: nil) self.present(vc, animated: true) })) alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) return alert } private func moreOptions(forStatus statusID: String) -> UIAlertController { guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID)") } guard let url = status.url else { fatalError("Missing url for status \(statusID)") } return moreOptions(forURL: url) } func showMoreOptions(forStatus statusID: String) { present(moreOptions(forStatus: statusID), animated: true) } func showMoreOptions(forURL url: URL) { present(moreOptions(forURL: url), animated: true) } }