216 lines
5.9 KiB
Swift
216 lines
5.9 KiB
Swift
//
|
|
// ArticlePasteboardWriter.swift
|
|
// NetNewsWire
|
|
//
|
|
// Created by Brent Simmons on 11/6/17.
|
|
// Copyright © 2017 Ranchero Software. All rights reserved.
|
|
//
|
|
|
|
import AppKit
|
|
import Articles
|
|
import RSCore
|
|
|
|
|
|
@objc final class ArticlePasteboardWriter: NSObject, NSPasteboardWriting {
|
|
|
|
let article: Article
|
|
static let articleUTI = "com.ranchero.article"
|
|
static let articleUTIType = NSPasteboard.PasteboardType(rawValue: articleUTI)
|
|
static let articleUTIInternal = "com.ranchero.NetNewsWire-Evergreen.internal.article"
|
|
static let articleUTIInternalType = NSPasteboard.PasteboardType(rawValue: articleUTIInternal)
|
|
|
|
private lazy var renderedHTML: String = {
|
|
return ArticleRenderer.articleHTML(article: article, style: ArticleStylesManager.shared.currentStyle)
|
|
}()
|
|
|
|
init(article: Article) {
|
|
self.article = article
|
|
}
|
|
|
|
// MARK: - NSPasteboardWriting
|
|
|
|
func writableTypes(for pasteboard: NSPasteboard) -> [NSPasteboard.PasteboardType] {
|
|
var types = [ArticlePasteboardWriter.articleUTIType]
|
|
|
|
if let link = article.preferredLink, let _ = URL(string: link) {
|
|
types += [.URL]
|
|
}
|
|
types += [.string, .html, ArticlePasteboardWriter.articleUTIInternalType]
|
|
|
|
return types
|
|
}
|
|
|
|
func pasteboardPropertyList(forType type: NSPasteboard.PasteboardType) -> Any? {
|
|
let plist: Any?
|
|
|
|
switch type {
|
|
case .html:
|
|
return renderedHTML
|
|
case .string:
|
|
plist = plainText()
|
|
case .URL:
|
|
return article.preferredLink ?? ""
|
|
case ArticlePasteboardWriter.articleUTIType:
|
|
plist = exportDictionary()
|
|
case ArticlePasteboardWriter.articleUTIInternalType:
|
|
plist = internalDictionary()
|
|
default:
|
|
plist = nil
|
|
}
|
|
|
|
return plist
|
|
}
|
|
}
|
|
|
|
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 {
|
|
let convertedHTML = html.rs_stringByConvertingToPlainText()
|
|
s += "\(convertedHTML)\n\n"
|
|
}
|
|
|
|
if let url = article.url {
|
|
s += "URL: \(url)\n\n"
|
|
}
|
|
if let externalURL = article.externalURL {
|
|
s += "external URL: \(externalURL)\n\n"
|
|
}
|
|
|
|
s += "Date: \(article.logicalDatePublished)\n\n"
|
|
|
|
if let feed = article.feed {
|
|
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"
|
|
static let feedID = "feedID" // may differ from feedURL if coming from a syncing system
|
|
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 userDeleted = "userDeleted"
|
|
static let authors = "authors"
|
|
static let attachments = "attachments"
|
|
|
|
// Author
|
|
static let authorName = "name"
|
|
static let authorURL = "url"
|
|
static let authorAvatarURL = "avatarURL"
|
|
static let authorEmailAddress = "emailAddress"
|
|
|
|
// Attachment
|
|
static let attachmentURL = "url"
|
|
static let attachmentMimeType = "mimeType"
|
|
static let attachmentTitle = "title"
|
|
static let attachmentSizeInBytes = "sizeInBytes"
|
|
static let attachmentDurationInSeconds = "durationInSeconds"
|
|
|
|
// Internal
|
|
static let accountID = "accountID"
|
|
}
|
|
|
|
func exportDictionary() -> [String: Any] {
|
|
var d = [String: Any]()
|
|
|
|
d[Key.articleID] = article.articleID
|
|
d[Key.uniqueID] = article.uniqueID
|
|
|
|
if let feed = article.feed {
|
|
d[Key.feedURL] = feed.url
|
|
}
|
|
|
|
d[Key.feedID] = article.feedID
|
|
d[Key.title] = article.title ?? nil
|
|
d[Key.contentHTML] = article.contentHTML ?? nil
|
|
d[Key.contentText] = article.contentText ?? nil
|
|
d[Key.url] = article.url ?? nil
|
|
d[Key.externalURL] = article.externalURL ?? nil
|
|
d[Key.summary] = article.summary ?? nil
|
|
d[Key.imageURL] = article.imageURL ?? nil
|
|
d[Key.bannerImageURL] = article.bannerImageURL ?? nil
|
|
d[Key.datePublished] = article.datePublished ?? nil
|
|
d[Key.dateModified] = article.dateModified ?? nil
|
|
d[Key.dateArrived] = article.status.dateArrived
|
|
d[Key.authors] = authorDictionaries() ?? nil
|
|
d[Key.attachments] = attachmentDictionaries() ?? 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 attachmentDictionary(_ attachment: Attachment) -> [String: Any] {
|
|
var d = [String: Any]()
|
|
|
|
d[Key.attachmentURL] = attachment.url
|
|
d[Key.attachmentMimeType] = attachment.mimeType ?? nil
|
|
d[Key.attachmentTitle] = attachment.title ?? nil
|
|
d[Key.attachmentSizeInBytes] = attachment.sizeInBytes ?? nil
|
|
d[Key.attachmentDurationInSeconds] = attachment.durationInSeconds ?? nil
|
|
|
|
return d
|
|
}
|
|
|
|
func authorDictionaries() -> [[String: Any]]? {
|
|
guard let authors = article.authors, !authors.isEmpty else {
|
|
return nil
|
|
}
|
|
return authors.map{ authorDictionary($0) }
|
|
}
|
|
|
|
func attachmentDictionaries() -> [[String: Any]]? {
|
|
guard let attachments = article.attachments, !attachments.isEmpty else {
|
|
return nil
|
|
}
|
|
return attachments.map { attachmentDictionary($0) }
|
|
}
|
|
}
|
|
|