From c3bcf827134bc7e17f59acace8ed7ea30fb4df20 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Thu, 11 Jan 2018 22:18:46 -0800 Subject: [PATCH] Make the send-to-Micro.blog command work. Need some tweaking, but it mostly does the job. --- Commands/SendToCommand.swift | 45 +++++++++++++++++-- Commands/SendToMarsEditCommand.swift | 31 ++++++++++++- Commands/SendToMicroBlogCommand.swift | 24 +++++----- Evergreen.xcodeproj/project.pbxproj | 2 + Evergreen/AppDelegate.swift | 4 ++ .../MainWindow/MainWindowController.swift | 25 +++++++++++ .../Timeline/ArticlePasteboardWriter.swift | 2 +- 7 files changed, 115 insertions(+), 18 deletions(-) diff --git a/Commands/SendToCommand.swift b/Commands/SendToCommand.swift index 413ddb8c1..88265a6d2 100644 --- a/Commands/SendToCommand.swift +++ b/Commands/SendToCommand.swift @@ -12,15 +12,54 @@ import Cocoa protocol SendToCommand { + var title: String { get } + var image: NSImage? { get } + func canSendObject(_ object: Any?, selectedText: String?) -> Bool func sendObject(_ object: Any?, selectedText: String?) } -extension SendToCommand { - func appExistsOnDisk(_ bundleIdentifier: String) -> Bool { +final class ApplicationSpecifier { - if let _ = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleIdentifier) { + let bundleID: String + var icon: NSImage? = nil + var existsOnDisk = false + var path: String? = nil + + init(bundleID: String) { + + self.bundleID = bundleID + update() + } + + func update() { + + path = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleID) + if let path = path { + if icon == nil { + icon = NSWorkspace.shared.icon(forFile: path) + } + existsOnDisk = true + } + else { + existsOnDisk = false + icon = nil + } + } + + func launch() -> Bool { + + guard existsOnDisk, let path = path else { + return false + } + + let url = URL(fileURLWithPath: path) + if let runningApplication = try? NSWorkspace.shared.launchApplication(at: url, options: [.withErrorPresentation], configuration: [:]) { + if runningApplication.isFinishedLaunching { + return true + } + sleep(3) // Give the app time to launch. This is ugly. return true } return false diff --git a/Commands/SendToMarsEditCommand.swift b/Commands/SendToMarsEditCommand.swift index 5e221e843..6e3858375 100644 --- a/Commands/SendToMarsEditCommand.swift +++ b/Commands/SendToMarsEditCommand.swift @@ -6,16 +6,45 @@ // Copyright © 2018 Ranchero Software. All rights reserved. // -import Foundation +import Cocoa final class SendToMarsEditCommand: SendToCommand { + let title = NSLocalizedString("Send to MarsEdit", comment: "Send to command") + + var image: NSImage? { + return appSpecifierToUse()?.icon ?? nil + } + + private let marsEditApps = [ApplicationSpecifier(bundleID: "com.red-sweater.marsedit4"), ApplicationSpecifier(bundleID: "com.red-sweater.marsedit")] + func canSendObject(_ object: Any?, selectedText: String?) -> Bool { + if let _ = appSpecifierToUse() { + return true + } return false } func sendObject(_ object: Any?, selectedText: String?) { + if !canSendObject(object, selectedText: selectedText) { + return + } + } +} + +private extension SendToMarsEditCommand { + + func appSpecifierToUse() -> ApplicationSpecifier? { + + for specifier in marsEditApps { + specifier.update() + if specifier.existsOnDisk { + return specifier + } + } + + return nil } } diff --git a/Commands/SendToMicroBlogCommand.swift b/Commands/SendToMicroBlogCommand.swift index 9af95afed..7e1090328 100644 --- a/Commands/SendToMicroBlogCommand.swift +++ b/Commands/SendToMicroBlogCommand.swift @@ -13,18 +13,18 @@ import Data final class SendToMicroBlogCommand: SendToCommand { - private let bundleID = "blog.micro.mac" - private var appExists = false + let title = NSLocalizedString("Send to Micro.blog", comment: "Send to command") - init() { - - self.appExists = appExistsOnDisk(bundleID) - NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive(_:)), name: NSApplication.didBecomeActiveNotification, object: nil) + var image: NSImage? { + return microBlogApp.icon } + private let microBlogApp = ApplicationSpecifier(bundleID: "blog.micro.mac") + func canSendObject(_ object: Any?, selectedText: String?) -> Bool { - guard appExists, let article = object as? Article, let _ = article.preferredLink else { + microBlogApp.update() + guard microBlogApp.existsOnDisk, let article = (object as? ArticlePasteboardWriter)?.article, let _ = article.preferredLink else { return false } @@ -36,7 +36,10 @@ final class SendToMicroBlogCommand: SendToCommand { guard canSendObject(object, selectedText: selectedText) else { return } - guard let article = object as? Article else { + guard let article = (object as? ArticlePasteboardWriter)?.article else { + return + } + guard microBlogApp.existsOnDisk, microBlogApp.launch() else { return } @@ -67,11 +70,6 @@ final class SendToMicroBlogCommand: SendToCommand { let _ = try? NSWorkspace.shared.open(url, options: [], configuration: [:]) } - - @objc func appDidBecomeActive(_ note: Notification) { - - self.appExists = appExistsOnDisk(bundleID) - } } diff --git a/Evergreen.xcodeproj/project.pbxproj b/Evergreen.xcodeproj/project.pbxproj index a922d870f..31cc7bc63 100644 --- a/Evergreen.xcodeproj/project.pbxproj +++ b/Evergreen.xcodeproj/project.pbxproj @@ -479,6 +479,7 @@ 846E77161F6EF5D000A165E2 /* Database.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Database.xcodeproj; path = Frameworks/Database/Database.xcodeproj; sourceTree = ""; }; 846E77301F6EF5D600A165E2 /* Account.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Account.xcodeproj; path = Frameworks/Account/Account.xcodeproj; sourceTree = ""; }; 84702AA31FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkReadOrUnreadCommand.swift; sourceTree = ""; }; + 847752FE2008879500D93690 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; }; 848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FaviconDownloader.swift; sourceTree = ""; }; 849A97421ED9EAA9007D329B /* AddFolderWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddFolderWindowController.swift; sourceTree = ""; }; 849A97511ED9EAC0007D329B /* AddFeedController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AddFeedController.swift; path = AddFeed/AddFeedController.swift; sourceTree = ""; }; @@ -1094,6 +1095,7 @@ 84FB9A2C1EDCD6A4003D53B9 /* Frameworks */ = { isa = PBXGroup; children = ( + 847752FE2008879500D93690 /* CoreServices.framework */, 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */, ); name = Frameworks; diff --git a/Evergreen/AppDelegate.swift b/Evergreen/AppDelegate.swift index 068e2b3d7..3f8fe5e95 100644 --- a/Evergreen/AppDelegate.swift +++ b/Evergreen/AppDelegate.swift @@ -33,6 +33,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, return image }() + lazy var sendToCommands: [SendToCommand] = { + return [SendToMicroBlogCommand()] //, SendToMarsEditCommand()] + }() + var unreadCount = 0 { didSet { if unreadCount != oldValue { diff --git a/Evergreen/MainWindow/MainWindowController.swift b/Evergreen/MainWindow/MainWindowController.swift index 2587f5cdc..f314812d5 100644 --- a/Evergreen/MainWindow/MainWindowController.swift +++ b/Evergreen/MainWindow/MainWindowController.swift @@ -287,6 +287,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { let items = selectedArticles.map { ArticlePasteboardWriter(article: $0) } let sharingServicePicker = NSSharingServicePicker(items: items) + sharingServicePicker.delegate = self sharingServicePicker.show(relativeTo: view.bounds, of: view, preferredEdge: .minY) } @@ -299,6 +300,30 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { } } +// MARK: - NSSharingServicePickerDelegate + +extension MainWindowController: NSSharingServicePickerDelegate { + + func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, sharingServicesForItems items: [Any], proposedSharingServices proposedServices: [NSSharingService]) -> [NSSharingService] { + + let sendToServices = appDelegate.sendToCommands.flatMap { (sendToCommand) -> NSSharingService? in + + guard let object = items.first else { + return nil + } + guard sendToCommand.canSendObject(object, selectedText: nil) else { + return nil + } + + let image = sendToCommand.image ?? appDelegate.genericFeedImage ?? NSImage() + return NSSharingService(title: sendToCommand.title, image: image, alternateImage: nil) { + sendToCommand.sendObject(object, selectedText: nil) + } + } + return proposedServices + sendToServices + } +} + // MARK: - NSToolbarDelegate extension NSToolbarItem.Identifier { diff --git a/Evergreen/MainWindow/Timeline/ArticlePasteboardWriter.swift b/Evergreen/MainWindow/Timeline/ArticlePasteboardWriter.swift index 67ed7a540..f49afe0b4 100644 --- a/Evergreen/MainWindow/Timeline/ArticlePasteboardWriter.swift +++ b/Evergreen/MainWindow/Timeline/ArticlePasteboardWriter.swift @@ -11,7 +11,7 @@ import Data @objc final class ArticlePasteboardWriter: NSObject, NSPasteboardWriting { - private let article: Article + let article: Article static let articleUTI = "com.ranchero.article" static let articleUTIType = NSPasteboard.PasteboardType(rawValue: articleUTI) static let articleUTIInternal = "com.ranchero.evergreen.internal.article"