diff --git a/App/Backend/History/ShareableURL.swift b/App/Backend/History/ShareableURL.swift new file mode 100644 index 0000000..c73bea5 --- /dev/null +++ b/App/Backend/History/ShareableURL.swift @@ -0,0 +1,39 @@ +// +// ShareableURL.swift +// App +// +// Created by James Magahern on 4/28/21. +// + +import Foundation + +class ShareableURL : NSObject, UIActivityItemSource +{ + let url: URL + let title: String + let favicon: UIImage? + + init(url: URL, title: String, favicon: UIImage?) { + self.url = url + self.title = title + self.favicon = favicon + + super.init() + } + + func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { + return self.url + } + + func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { + return self.url + } + + func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivity.ActivityType?) -> String { + return self.title + } + + func activityViewController(_ activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: UIActivity.ActivityType?, suggestedSize size: CGSize) -> UIImage? { + return self.favicon + } +} diff --git a/App/Browser View/BrowserViewController.swift b/App/Browser View/BrowserViewController.swift index 617028b..b4e76e6 100644 --- a/App/Browser View/BrowserViewController.swift +++ b/App/Browser View/BrowserViewController.swift @@ -6,6 +6,7 @@ // import Combine +import MessageUI import UIKit import UniformTypeIdentifiers @@ -95,26 +96,8 @@ class BrowserViewController: UIViewController }), for: .touchUpInside) // Share button - toolbarController.shareButton.addAction(UIAction(handler: { [unowned self, toolbarController] _ in - if let url = self.webView.url { - let itemProvider = NSItemProvider(item: url as NSURL, typeIdentifier: UTType.url.identifier) - let config = UIActivityItemsConfiguration(itemProviders: [ itemProvider ]) - config.metadataProvider = { metadataKey in - switch metadataKey { - case .title: return self.webView.title - case .messageBody: return self.webView.title - default: return nil - } - } - - config.previewProvider = { index, intent, suggestedSize in - NSItemProvider(item: self.tab.favicon, typeIdentifier: UTType.image.identifier) - } - - let activityController = UIActivityViewController(activityItemsConfiguration: config) - activityController.popoverPresentationController?.sourceView = toolbarController.shareButton - self.present(activityController, animated: true, completion: nil) - } + toolbarController.shareButton.addAction(UIAction(handler: { [unowned self] _ in + showShareSheetForCurrentURL(fromViewController: nil) }), for: .touchUpInside) // Script button @@ -289,6 +272,11 @@ class BrowserViewController: UIViewController documentControls.dismiss(animated: true, completion: nil) }, for: .touchUpInside) + // Email + documentControls.emailView.addAction(UIAction { [unowned self] _ in + composeEmailForCurrentURL(fromViewController: documentControls) + }, for: .touchUpInside) + // Settings documentControls.settingsView.addAction(UIAction { [unowned self] _ in documentControls.dismiss(animated: false, completion: nil) @@ -321,6 +309,40 @@ class BrowserViewController: UIViewController self.view = browserView } + internal func showShareSheetForCurrentURL(fromViewController: UIViewController?) { + guard let url = self.webView.url else { return } + + let shareableURL = ShareableURL( + url: url, + title: webView.title ?? url.absoluteString, + favicon: tab.favicon + ) + + let activityController = UIActivityViewController(activityItems: [ shareableURL ], applicationActivities: nil) + activityController.popoverPresentationController?.sourceView = toolbarController.shareButton + + if let fromViewController = fromViewController { + fromViewController.dismiss(animated: false, completion: nil) + } + + self.present(activityController, animated: true, completion: nil) + } + + internal func composeEmailForCurrentURL(fromViewController: UIViewController?) { + guard let url = self.webView.url else { return } + + let composeController = MFMailComposeViewController() + composeController.setSubject(webView.title ?? url.absoluteString) + composeController.setMessageBody(url.absoluteString, isHTML: false) + composeController.mailComposeDelegate = self + + if let fromViewController = fromViewController { + fromViewController.dismiss(animated: false, completion: nil) + } + + present(composeController, animated: true, completion: nil) + } + internal func showSettingsWindow() { #if targetEnvironment(macCatalyst) let userActivity = NSUserActivity(activityType: SessionActivityType.SettingsWindow.rawValue) @@ -570,3 +592,10 @@ extension BrowserViewController: URLBarDelegate changingFocusToAutocompleteController = false } } + +extension BrowserViewController: MFMailComposeViewControllerDelegate +{ + func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { + controller.dismiss(animated: true, completion: nil) + } +} diff --git a/App/Document Controls UI/DocumentControlViewController.swift b/App/Document Controls UI/DocumentControlViewController.swift index 68a626f..5bf54ef 100644 --- a/App/Document Controls UI/DocumentControlViewController.swift +++ b/App/Document Controls UI/DocumentControlViewController.swift @@ -17,6 +17,7 @@ class DocumentControlViewController: UIViewController let readabilityView = DocumentControlItemView() let darkModeView = DocumentControlItemView() let archiveView = DocumentControlItemView() + let emailView = DocumentControlItemView() var observations: [NSKeyValueObservation] = [] @@ -37,6 +38,9 @@ class DocumentControlViewController: UIViewController archiveView.label.text = "Archive.today" archiveView.imageView.image = UIImage(systemName: "shippingbox") + emailView.label.text = "Email" + emailView.imageView.image = UIImage(systemName: "envelope") + if darkModeEnabled { darkModeView.label.text = "Disable Dark Mode" } else { @@ -47,6 +51,8 @@ class DocumentControlViewController: UIViewController documentControlsView.stackView.addArrangedSubview(navigationControlView) documentControlsView.stackView.addArrangedSubview(fontSizeAdjustView) + + documentControlsView.stackView.addArrangedSubview(emailView) documentControlsView.stackView.addArrangedSubview(findOnPageControlView) documentControlsView.stackView.addArrangedSubview(darkModeView) documentControlsView.stackView.addArrangedSubview(readabilityView) diff --git a/SBrowser.xcodeproj/project.pbxproj b/SBrowser.xcodeproj/project.pbxproj index f5fcf83..212b881 100644 --- a/SBrowser.xcodeproj/project.pbxproj +++ b/SBrowser.xcodeproj/project.pbxproj @@ -52,6 +52,7 @@ CD853BD424E77BF900D2BDCC /* HistoryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD853BD324E77BF900D2BDCC /* HistoryItem.swift */; }; CD97CF9225D5BE6F00288FEE /* NavigationControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD97CF9125D5BE6F00288FEE /* NavigationControlsView.swift */; }; CDAD9CE8263A2DF200FF7199 /* DocumentControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDAD9CE7263A2DF200FF7199 /* DocumentControlsView.swift */; }; + CDAD9CEA263A318F00FF7199 /* ShareableURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDAD9CE9263A318F00FF7199 /* ShareableURL.swift */; }; CDC4A1CF25E9D8F7007D33C6 /* Tagger.js in Resources */ = {isa = PBXBuildFile; fileRef = CDC4A1CE25E9D8F7007D33C6 /* Tagger.js */; }; CDC5DA3A25DB774D00BA8D99 /* Readability.js in Resources */ = {isa = PBXBuildFile; fileRef = CDC5DA3925DB774D00BA8D99 /* Readability.js */; }; CDC5DA3E25DB7C2C00BA8D99 /* ReaderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDC5DA3D25DB7C2C00BA8D99 /* ReaderViewController.swift */; }; @@ -145,6 +146,7 @@ CD853BD324E77BF900D2BDCC /* HistoryItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryItem.swift; sourceTree = ""; }; CD97CF9125D5BE6F00288FEE /* NavigationControlsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationControlsView.swift; sourceTree = ""; }; CDAD9CE7263A2DF200FF7199 /* DocumentControlsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentControlsView.swift; sourceTree = ""; }; + CDAD9CE9263A318F00FF7199 /* ShareableURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareableURL.swift; sourceTree = ""; }; CDC4A1CE25E9D8F7007D33C6 /* Tagger.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = Tagger.js; sourceTree = ""; }; CDC5DA3925DB774D00BA8D99 /* Readability.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = Readability.js; sourceTree = ""; }; CDC5DA3D25DB7C2C00BA8D99 /* ReaderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderViewController.swift; sourceTree = ""; }; @@ -376,6 +378,7 @@ CD853BCF24E778B800D2BDCC /* History.xcdatamodeld */, CD853BCD24E7763900D2BDCC /* BrowserHistory.swift */, CD853BD324E77BF900D2BDCC /* HistoryItem.swift */, + CDAD9CE9263A318F00FF7199 /* ShareableURL.swift */, ); path = History; sourceTree = ""; @@ -528,6 +531,7 @@ 1A03811024E71CF000826501 /* ReliefButton.swift in Sources */, 1A03811224E71EAA00826501 /* GradientView.swift in Sources */, 1ADFF4C024CA6964006DC7AE /* URLBar.swift in Sources */, + CDAD9CEA263A318F00FF7199 /* ShareableURL.swift in Sources */, CDCE2666251AA840007FE92A /* StackView.swift in Sources */, CD853BD124E778B800D2BDCC /* History.xcdatamodeld in Sources */, CDD0522125F8023700DD1771 /* Settings.swift in Sources */,