Implement macOS share button
This commit is contained in:
parent
cea168380f
commit
31068f90a0
|
@ -11,7 +11,8 @@ import SwiftUI
|
||||||
struct SceneNavigationView: View {
|
struct SceneNavigationView: View {
|
||||||
|
|
||||||
@StateObject private var sceneModel = SceneModel()
|
@StateObject private var sceneModel = SceneModel()
|
||||||
@State private var showSheet: Bool = false
|
@State private var showSheet = false
|
||||||
|
@State private var showShareSheet = false
|
||||||
@State private var sheetToShow: ToolbarSheets = .none
|
@State private var sheetToShow: ToolbarSheets = .none
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
|
@ -148,10 +149,17 @@ struct SceneNavigationView: View {
|
||||||
.help("Open in Browser")
|
.help("Open in Browser")
|
||||||
}
|
}
|
||||||
ToolbarItem {
|
ToolbarItem {
|
||||||
|
ZStack {
|
||||||
|
if showShareSheet {
|
||||||
|
SharingServiceView(articles: sceneModel.selectedArticles, showing: $showShareSheet)
|
||||||
|
.frame(width: 20, height: 20)
|
||||||
|
}
|
||||||
Button {
|
Button {
|
||||||
|
showShareSheet = true
|
||||||
} label: {
|
} label: {
|
||||||
AppAssets.shareImage
|
AppAssets.shareImage
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.disabled(sceneModel.shareButtonState == nil)
|
.disabled(sceneModel.shareButtonState == nil)
|
||||||
.help("Share")
|
.help("Share")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
//
|
||||||
|
// SharingServiceDelegate.swift
|
||||||
|
// NetNewsWire
|
||||||
|
//
|
||||||
|
// Created by Maurice Parker on 9/7/18.
|
||||||
|
// Copyright © 2018 Ranchero Software. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
@objc final class SharingServiceDelegate: NSObject, NSSharingServiceDelegate {
|
||||||
|
|
||||||
|
weak var window: NSWindow?
|
||||||
|
|
||||||
|
init(_ window: NSWindow?) {
|
||||||
|
self.window = window
|
||||||
|
}
|
||||||
|
|
||||||
|
func sharingService(_ sharingService: NSSharingService, willShareItems items: [Any]) {
|
||||||
|
sharingService.subject = items
|
||||||
|
.compactMap { item in
|
||||||
|
let writer = item as? ArticlePasteboardWriter
|
||||||
|
return writer?.article.title
|
||||||
|
}
|
||||||
|
.joined(separator: ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func sharingService(_ sharingService: NSSharingService, sourceWindowForShareItems items: [Any], sharingContentScope: UnsafeMutablePointer<NSSharingService.SharingContentScope>) -> NSWindow? {
|
||||||
|
return window
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
//
|
||||||
|
// SharingServicePickerDelegate.swift
|
||||||
|
// NetNewsWire
|
||||||
|
//
|
||||||
|
// Created by Brent Simmons on 2/17/18.
|
||||||
|
// Copyright © 2018 Ranchero Software. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
import RSCore
|
||||||
|
|
||||||
|
@objc final class SharingServicePickerDelegate: NSObject, NSSharingServicePickerDelegate {
|
||||||
|
|
||||||
|
private let sharingServiceDelegate: SharingServiceDelegate
|
||||||
|
private let completion: (() -> Void)?
|
||||||
|
|
||||||
|
init(_ window: NSWindow?, completion: (() -> Void)?) {
|
||||||
|
self.sharingServiceDelegate = SharingServiceDelegate(window)
|
||||||
|
self.completion = completion
|
||||||
|
}
|
||||||
|
|
||||||
|
func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, sharingServicesForItems items: [Any], proposedSharingServices proposedServices: [NSSharingService]) -> [NSSharingService] {
|
||||||
|
return proposedServices + SharingServicePickerDelegate.customSharingServices(for: items)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, delegateFor sharingService: NSSharingService) -> NSSharingServiceDelegate? {
|
||||||
|
return sharingServiceDelegate
|
||||||
|
}
|
||||||
|
|
||||||
|
func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, didChoose service: NSSharingService?) {
|
||||||
|
completion?()
|
||||||
|
}
|
||||||
|
|
||||||
|
static func customSharingServices(for items: [Any]) -> [NSSharingService] {
|
||||||
|
let customServices = ExtensionPointManager.shared.activeSendToCommands.compactMap { (sendToCommand) -> NSSharingService? in
|
||||||
|
|
||||||
|
guard let object = items.first else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
guard sendToCommand.canSendObject(object, selectedText: nil) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let image = sendToCommand.image ?? NSImage()
|
||||||
|
return NSSharingService(title: sendToCommand.title, image: image, alternateImage: nil) {
|
||||||
|
sendToCommand.sendObject(object, selectedText: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return customServices
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
//
|
||||||
|
// SharingServiceView.swift
|
||||||
|
// Multiplatform macOS
|
||||||
|
//
|
||||||
|
// Created by Maurice Parker on 7/14/20.
|
||||||
|
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import AppKit
|
||||||
|
import Articles
|
||||||
|
|
||||||
|
class SharingServiceController: NSViewController {
|
||||||
|
|
||||||
|
var sharingServicePickerDelegate: SharingServicePickerDelegate? = nil
|
||||||
|
var articles = [Article]()
|
||||||
|
var completion: (() -> Void)? = nil
|
||||||
|
|
||||||
|
override func loadView() {
|
||||||
|
view = NSView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear() {
|
||||||
|
guard let anchor = view.superview?.superview else { return }
|
||||||
|
|
||||||
|
sharingServicePickerDelegate = SharingServicePickerDelegate(self.view.window, completion: completion)
|
||||||
|
|
||||||
|
let sortedArticles = articles.sortedByDate(.orderedAscending)
|
||||||
|
let items = sortedArticles.map { ArticlePasteboardWriter(article: $0) }
|
||||||
|
|
||||||
|
let sharingServicePicker = NSSharingServicePicker(items: items)
|
||||||
|
sharingServicePicker.delegate = sharingServicePickerDelegate
|
||||||
|
|
||||||
|
sharingServicePicker.show(relativeTo: anchor.bounds, of: anchor, preferredEdge: .minY)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SharingServiceView: NSViewControllerRepresentable {
|
||||||
|
|
||||||
|
var articles: [Article]
|
||||||
|
@Binding var showing: Bool
|
||||||
|
|
||||||
|
func makeNSViewController(context: Context) -> SharingServiceController {
|
||||||
|
let controller = SharingServiceController()
|
||||||
|
controller.articles = articles
|
||||||
|
controller.completion = {
|
||||||
|
showing = false
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNSViewController(_ nsViewController: SharingServiceController, context: Context) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -354,6 +354,9 @@
|
||||||
51B80EDD24BD296700C6C32D /* ArticleActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B80EDC24BD296700C6C32D /* ArticleActivityItemSource.swift */; };
|
51B80EDD24BD296700C6C32D /* ArticleActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B80EDC24BD296700C6C32D /* ArticleActivityItemSource.swift */; };
|
||||||
51B80EDF24BD298900C6C32D /* TitleActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B80EDE24BD298900C6C32D /* TitleActivityItemSource.swift */; };
|
51B80EDF24BD298900C6C32D /* TitleActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B80EDE24BD298900C6C32D /* TitleActivityItemSource.swift */; };
|
||||||
51B80EE124BD3E9600C6C32D /* FindInArticleActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B80EE024BD3E9600C6C32D /* FindInArticleActivity.swift */; };
|
51B80EE124BD3E9600C6C32D /* FindInArticleActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B80EE024BD3E9600C6C32D /* FindInArticleActivity.swift */; };
|
||||||
|
51B80F1F24BE531200C6C32D /* SharingServiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B80F1E24BE531200C6C32D /* SharingServiceView.swift */; };
|
||||||
|
51B80F4224BE588200C6C32D /* SharingServicePickerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B80F4124BE588200C6C32D /* SharingServicePickerDelegate.swift */; };
|
||||||
|
51B80F4424BE58BF00C6C32D /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B80F4324BE58BF00C6C32D /* SharingServiceDelegate.swift */; };
|
||||||
51BB7C272335A8E5008E8144 /* ArticleActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */; };
|
51BB7C272335A8E5008E8144 /* ArticleActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */; };
|
||||||
51BB7C312335ACDE008E8144 /* page.html in Resources */ = {isa = PBXBuildFile; fileRef = 51BB7C302335ACDE008E8144 /* page.html */; };
|
51BB7C312335ACDE008E8144 /* page.html in Resources */ = {isa = PBXBuildFile; fileRef = 51BB7C302335ACDE008E8144 /* page.html */; };
|
||||||
51BC4AFF247277E0000A6ED8 /* URL-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BC4ADD247277DF000A6ED8 /* URL-Extensions.swift */; };
|
51BC4AFF247277E0000A6ED8 /* URL-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BC4ADD247277DF000A6ED8 /* URL-Extensions.swift */; };
|
||||||
|
@ -2004,6 +2007,9 @@
|
||||||
51B80EDC24BD296700C6C32D /* ArticleActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleActivityItemSource.swift; sourceTree = "<group>"; };
|
51B80EDC24BD296700C6C32D /* ArticleActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleActivityItemSource.swift; sourceTree = "<group>"; };
|
||||||
51B80EDE24BD298900C6C32D /* TitleActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleActivityItemSource.swift; sourceTree = "<group>"; };
|
51B80EDE24BD298900C6C32D /* TitleActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleActivityItemSource.swift; sourceTree = "<group>"; };
|
||||||
51B80EE024BD3E9600C6C32D /* FindInArticleActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindInArticleActivity.swift; sourceTree = "<group>"; };
|
51B80EE024BD3E9600C6C32D /* FindInArticleActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindInArticleActivity.swift; sourceTree = "<group>"; };
|
||||||
|
51B80F1E24BE531200C6C32D /* SharingServiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceView.swift; sourceTree = "<group>"; };
|
||||||
|
51B80F4124BE588200C6C32D /* SharingServicePickerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServicePickerDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
51B80F4324BE58BF00C6C32D /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = "<group>"; };
|
||||||
51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleActivityItemSource.swift; sourceTree = "<group>"; };
|
51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleActivityItemSource.swift; sourceTree = "<group>"; };
|
||||||
51BB7C302335ACDE008E8144 /* page.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = page.html; sourceTree = "<group>"; };
|
51BB7C302335ACDE008E8144 /* page.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = page.html; sourceTree = "<group>"; };
|
||||||
51BC4ADD247277DF000A6ED8 /* URL-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL-Extensions.swift"; sourceTree = "<group>"; };
|
51BC4ADD247277DF000A6ED8 /* URL-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL-Extensions.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -2763,6 +2769,9 @@
|
||||||
children = (
|
children = (
|
||||||
51B54ABB24B5BEF20014348B /* ArticleView.swift */,
|
51B54ABB24B5BEF20014348B /* ArticleView.swift */,
|
||||||
51B54A6824B54A490014348B /* IconView.swift */,
|
51B54A6824B54A490014348B /* IconView.swift */,
|
||||||
|
51B80F4324BE58BF00C6C32D /* SharingServiceDelegate.swift */,
|
||||||
|
51B80F4124BE588200C6C32D /* SharingServicePickerDelegate.swift */,
|
||||||
|
51B80F1E24BE531200C6C32D /* SharingServiceView.swift */,
|
||||||
51B54B6624B6A7960014348B /* WebStatusBarView.swift */,
|
51B54B6624B6A7960014348B /* WebStatusBarView.swift */,
|
||||||
51B54AB524B5B33C0014348B /* WebViewController.swift */,
|
51B54AB524B5B33C0014348B /* WebViewController.swift */,
|
||||||
);
|
);
|
||||||
|
@ -5152,6 +5161,7 @@
|
||||||
51E498FA24A808BA00B667CB /* SingleFaviconDownloader.swift in Sources */,
|
51E498FA24A808BA00B667CB /* SingleFaviconDownloader.swift in Sources */,
|
||||||
51E4993F24A8713B00B667CB /* ArticleStatusSyncTimer.swift in Sources */,
|
51E4993F24A8713B00B667CB /* ArticleStatusSyncTimer.swift in Sources */,
|
||||||
51E4993724A8680E00B667CB /* Reachability.swift in Sources */,
|
51E4993724A8680E00B667CB /* Reachability.swift in Sources */,
|
||||||
|
51B80F4424BE58BF00C6C32D /* SharingServiceDelegate.swift in Sources */,
|
||||||
51B54AB624B5B33C0014348B /* WebViewController.swift in Sources */,
|
51B54AB624B5B33C0014348B /* WebViewController.swift in Sources */,
|
||||||
51E4994B24A8734C00B667CB /* SendToMicroBlogCommand.swift in Sources */,
|
51E4994B24A8734C00B667CB /* SendToMicroBlogCommand.swift in Sources */,
|
||||||
51E4996F24A8764C00B667CB /* ActivityType.swift in Sources */,
|
51E4996F24A8764C00B667CB /* ActivityType.swift in Sources */,
|
||||||
|
@ -5174,8 +5184,10 @@
|
||||||
51E4992224A8095600B667CB /* URL-Extensions.swift in Sources */,
|
51E4992224A8095600B667CB /* URL-Extensions.swift in Sources */,
|
||||||
51E4990424A808C300B667CB /* WebFeedIconDownloader.swift in Sources */,
|
51E4990424A808C300B667CB /* WebFeedIconDownloader.swift in Sources */,
|
||||||
51E498CB24A8085D00B667CB /* TodayFeedDelegate.swift in Sources */,
|
51E498CB24A8085D00B667CB /* TodayFeedDelegate.swift in Sources */,
|
||||||
|
51B80F1F24BE531200C6C32D /* SharingServiceView.swift in Sources */,
|
||||||
17D232A924AFF10A0005F075 /* AddWebFeedModel.swift in Sources */,
|
17D232A924AFF10A0005F075 /* AddWebFeedModel.swift in Sources */,
|
||||||
51E4993324A867E700B667CB /* AppNotifications.swift in Sources */,
|
51E4993324A867E700B667CB /* AppNotifications.swift in Sources */,
|
||||||
|
51B80F4224BE588200C6C32D /* SharingServicePickerDelegate.swift in Sources */,
|
||||||
51E4990624A808C300B667CB /* ImageDownloader.swift in Sources */,
|
51E4990624A808C300B667CB /* ImageDownloader.swift in Sources */,
|
||||||
51E4994F24A8734C00B667CB /* TwitterFeedProvider-Extensions.swift in Sources */,
|
51E4994F24A8734C00B667CB /* TwitterFeedProvider-Extensions.swift in Sources */,
|
||||||
51E498CA24A8085D00B667CB /* SmartFeedDelegate.swift in Sources */,
|
51E498CA24A8085D00B667CB /* SmartFeedDelegate.swift in Sources */,
|
||||||
|
|
Loading…
Reference in New Issue