2017-11-06 22:20:29 +01:00
|
|
|
//
|
|
|
|
// ArticlePasteboardWriter.swift
|
2018-08-29 07:18:24 +02:00
|
|
|
// NetNewsWire
|
2017-11-06 22:20:29 +01:00
|
|
|
//
|
|
|
|
// Created by Brent Simmons on 11/6/17.
|
|
|
|
// Copyright © 2017 Ranchero Software. All rights reserved.
|
|
|
|
//
|
|
|
|
|
2018-02-03 07:51:32 +01:00
|
|
|
import AppKit
|
2018-07-24 03:29:08 +02:00
|
|
|
import Articles
|
2018-02-12 22:04:07 +01:00
|
|
|
import RSCore
|
|
|
|
|
2024-11-02 04:53:44 +01:00
|
|
|
extension Article: @retroactive PasteboardWriterOwner {
|
2020-11-03 02:33:56 +01:00
|
|
|
public var pasteboardWriter: NSPasteboardWriting {
|
|
|
|
return ArticlePasteboardWriter(article: self)
|
|
|
|
}
|
|
|
|
}
|
2017-11-06 22:20:29 +01:00
|
|
|
|
|
|
|
@objc final class ArticlePasteboardWriter: NSObject, NSPasteboardWriting {
|
|
|
|
|
2018-01-12 07:18:46 +01:00
|
|
|
let article: Article
|
2017-12-27 01:18:46 +01:00
|
|
|
static let articleUTI = "com.ranchero.article"
|
|
|
|
static let articleUTIType = NSPasteboard.PasteboardType(rawValue: articleUTI)
|
2018-08-29 07:18:24 +02:00
|
|
|
static let articleUTIInternal = "com.ranchero.NetNewsWire-Evergreen.internal.article"
|
2017-12-27 01:18:46 +01:00
|
|
|
static let articleUTIInternalType = NSPasteboard.PasteboardType(rawValue: articleUTIInternal)
|
|
|
|
|
|
|
|
private lazy var renderedHTML: String = {
|
2021-09-07 23:58:06 +02:00
|
|
|
let rendering = ArticleRenderer.articleHTML(article: article, theme: ArticleThemesManager.shared.currentTheme)
|
2019-09-21 02:28:13 +02:00
|
|
|
return rendering.html
|
2017-12-27 01:18:46 +01:00
|
|
|
}()
|
2017-11-06 22:20:29 +01:00
|
|
|
|
|
|
|
init(article: Article) {
|
|
|
|
self.article = article
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - NSPasteboardWriting
|
|
|
|
|
|
|
|
func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {
|
2018-01-22 17:02:29 +01:00
|
|
|
var types = [ArticlePasteboardWriter.articleUTIType]
|
2017-11-06 22:29:53 +01:00
|
|
|
|
2021-04-25 23:28:19 +02:00
|
|
|
if let _ = article.preferredURL {
|
2017-11-07 07:06:42 +01:00
|
|
|
types += [.URL]
|
|
|
|
}
|
2018-01-22 17:02:29 +01:00
|
|
|
types += [.string, .html, ArticlePasteboardWriter.articleUTIInternalType]
|
2017-11-06 22:29:53 +01:00
|
|
|
|
2017-12-27 01:18:46 +01:00
|
|
|
return types
|
2017-11-06 22:20:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? {
|
2017-11-06 22:29:53 +01:00
|
|
|
let plist: Any?
|
|
|
|
|
|
|
|
switch type {
|
2017-12-27 01:18:46 +01:00
|
|
|
case .html:
|
|
|
|
return renderedHTML
|
2017-11-06 22:29:53 +01:00
|
|
|
case .string:
|
2017-12-27 01:18:46 +01:00
|
|
|
plist = plainText()
|
2017-11-07 07:06:42 +01:00
|
|
|
case .URL:
|
2017-12-27 01:18:46 +01:00
|
|
|
return article.preferredLink ?? ""
|
|
|
|
case ArticlePasteboardWriter.articleUTIType:
|
|
|
|
plist = exportDictionary()
|
|
|
|
case ArticlePasteboardWriter.articleUTIInternalType:
|
|
|
|
plist = internalDictionary()
|
2017-11-06 22:29:53 +01:00
|
|
|
default:
|
|
|
|
plist = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return plist
|
2017-11-06 22:20:29 +01:00
|
|
|
}
|
|
|
|
}
|
2017-12-27 01:18:46 +01:00
|
|
|
|
|
|
|
private extension ArticlePasteboardWriter {
|
|
|
|
|
|
|
|
func plainText() -> String {
|
|
|
|
var s = ""
|
|
|
|
|
|
|
|
if let title = article.title {
|
|
|
|
s += "\(title)\n\n"
|
|
|
|
}
|
|
|
|
if let text = article.contentText {
|
|
|
|
s += "\(text)\n\n"
|
|
|
|
}
|
|
|
|
else if let summary = article.summary {
|
|
|
|
s += "\(summary)\n\n"
|
|
|
|
}
|
|
|
|
else if let html = article.contentHTML {
|
2020-01-17 03:09:18 +01:00
|
|
|
let convertedHTML = html.convertingToPlainText()
|
2017-12-27 01:18:46 +01:00
|
|
|
s += "\(convertedHTML)\n\n"
|
|
|
|
}
|
|
|
|
|
2021-09-30 05:46:11 +02:00
|
|
|
if let link = article.link {
|
2021-09-30 16:43:32 +02:00
|
|
|
s += "URL: \(link)\n\n"
|
2017-12-27 01:18:46 +01:00
|
|
|
}
|
2021-09-30 05:46:11 +02:00
|
|
|
if let externalLink = article.externalLink {
|
|
|
|
s += "external URL: \(externalLink)\n\n"
|
2017-12-27 01:18:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
s += "Date: \(article.logicalDatePublished)\n\n"
|
|
|
|
|
2024-11-02 05:58:47 +01:00
|
|
|
if let feed = article.feed {
|
2017-12-27 01:18:46 +01:00
|
|
|
s += "Feed: \(feed.nameForDisplay)\n"
|
|
|
|
if let homePageURL = feed.homePageURL {
|
|
|
|
s += "Home page: \(homePageURL)\n"
|
|
|
|
}
|
|
|
|
s += "URL: \(feed.url)"
|
|
|
|
}
|
|
|
|
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
private struct Key {
|
|
|
|
static let articleID = "articleID" // database ID, unique per account
|
|
|
|
static let uniqueID = "uniqueID" // unique ID, unique per feed (guid, or possibly calculated)
|
|
|
|
static let feedURL = "feedURL"
|
2024-11-02 19:08:58 +01:00
|
|
|
static let feedID = "feedID" // may differ from feedURL if coming from a syncing system
|
2017-12-27 01:18:46 +01:00
|
|
|
static let title = "title"
|
|
|
|
static let contentHTML = "contentHTML"
|
|
|
|
static let contentText = "contentText"
|
|
|
|
static let url = "url" // usually a permalink
|
|
|
|
static let externalURL = "externalURL" // usually not a permalink
|
|
|
|
static let summary = "summary"
|
|
|
|
static let imageURL = "imageURL"
|
|
|
|
static let bannerImageURL = "bannerImageURL"
|
|
|
|
static let datePublished = "datePublished"
|
|
|
|
static let dateModified = "dateModified"
|
|
|
|
static let dateArrived = "dateArrived"
|
|
|
|
static let read = "read"
|
|
|
|
static let starred = "starred"
|
|
|
|
static let authors = "authors"
|
|
|
|
|
|
|
|
// Author
|
|
|
|
static let authorName = "name"
|
|
|
|
static let authorURL = "url"
|
|
|
|
static let authorAvatarURL = "avatarURL"
|
|
|
|
static let authorEmailAddress = "emailAddress"
|
|
|
|
|
|
|
|
// Internal
|
|
|
|
static let accountID = "accountID"
|
|
|
|
}
|
|
|
|
|
|
|
|
func exportDictionary() -> [String: Any] {
|
|
|
|
var d = [String: Any]()
|
|
|
|
|
|
|
|
d[Key.articleID] = article.articleID
|
|
|
|
d[Key.uniqueID] = article.uniqueID
|
|
|
|
|
2024-11-02 05:58:47 +01:00
|
|
|
if let feed = article.feed {
|
2017-12-27 01:18:46 +01:00
|
|
|
d[Key.feedURL] = feed.url
|
|
|
|
}
|
|
|
|
|
2024-11-02 19:08:58 +01:00
|
|
|
d[Key.feedID] = article.feedID
|
2017-12-27 01:18:46 +01:00
|
|
|
d[Key.title] = article.title ?? nil
|
|
|
|
d[Key.contentHTML] = article.contentHTML ?? nil
|
|
|
|
d[Key.contentText] = article.contentText ?? nil
|
2021-09-30 05:33:16 +02:00
|
|
|
d[Key.url] = article.rawLink ?? nil
|
|
|
|
d[Key.externalURL] = article.rawExternalLink ?? nil
|
2017-12-27 01:18:46 +01:00
|
|
|
d[Key.summary] = article.summary ?? nil
|
2021-09-30 05:33:16 +02:00
|
|
|
d[Key.imageURL] = article.rawImageLink ?? nil
|
2017-12-27 01:18:46 +01:00
|
|
|
d[Key.datePublished] = article.datePublished ?? nil
|
|
|
|
d[Key.dateModified] = article.dateModified ?? nil
|
|
|
|
d[Key.dateArrived] = article.status.dateArrived
|
|
|
|
d[Key.authors] = authorDictionaries() ?? nil
|
|
|
|
|
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
|
|
|
func internalDictionary() -> [String: Any] {
|
|
|
|
var d = exportDictionary()
|
|
|
|
d[Key.accountID] = article.accountID
|
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
|
|
|
func authorDictionary(_ author: Author) -> [String: Any] {
|
|
|
|
var d = [String: Any]()
|
|
|
|
|
|
|
|
d[Key.authorName] = author.name ?? nil
|
|
|
|
d[Key.authorURL] = author.url ?? nil
|
|
|
|
d[Key.authorAvatarURL] = author.avatarURL ?? nil
|
|
|
|
d[Key.authorEmailAddress] = author.emailAddress ?? nil
|
|
|
|
|
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
|
|
|
func authorDictionaries() -> [[String: Any]]? {
|
|
|
|
guard let authors = article.authors, !authors.isEmpty else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return authors.map{ authorDictionary($0) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|