Merge branch 'master' of https://github.com/brentsimmons/Evergreen
This commit is contained in:
commit
5fdb50b255
|
@ -103,6 +103,7 @@
|
|||
84A1500520048DDF0046AD9A /* SendToMarsEditCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A1500420048DDF0046AD9A /* SendToMarsEditCommand.swift */; };
|
||||
84A37CB5201ECD610087C5AF /* RenameWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A37CB4201ECD610087C5AF /* RenameWindowController.swift */; };
|
||||
84A37CBB201ECE590087C5AF /* RenameSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 84A37CB9201ECE590087C5AF /* RenameSheet.xib */; };
|
||||
84AAF2BF202CF684004A0BC4 /* TimelineContextualMenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AAF2BE202CF684004A0BC4 /* TimelineContextualMenuDelegate.swift */; };
|
||||
84B06FAE1ED37DBD00F0B54B /* RSCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06FA91ED37DAD00F0B54B /* RSCore.framework */; };
|
||||
84B06FAF1ED37DBD00F0B54B /* RSCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06FA91ED37DAD00F0B54B /* RSCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
84B06FB21ED37DBD00F0B54B /* RSDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84B06F9D1ED37DA000F0B54B /* RSDatabase.framework */; };
|
||||
|
@ -166,6 +167,8 @@
|
|||
D5A267C120131B8300A8D3C0 /* testFeedOPML.applescript in Sources */ = {isa = PBXBuildFile; fileRef = D5A267B220131B8300A8D3C0 /* testFeedOPML.applescript */; };
|
||||
D5A267C220131BA000A8D3C0 /* testFeedOPML.applescript in CopyFiles */ = {isa = PBXBuildFile; fileRef = D5A267B220131B8300A8D3C0 /* testFeedOPML.applescript */; };
|
||||
D5D1751220020B980047B29D /* Evergreen.sdef in Resources */ = {isa = PBXBuildFile; fileRef = D5D175012002039D0047B29D /* Evergreen.sdef */; };
|
||||
D5E4CC54202C1361009B4FFC /* AppDelegate+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5E4CC53202C1361009B4FFC /* AppDelegate+Scriptability.swift */; };
|
||||
D5E4CC64202C1AC1009B4FFC /* MainWindowController+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5E4CC63202C1AC1009B4FFC /* MainWindowController+Scriptability.swift */; };
|
||||
D5F4EDB5200744A700B9E363 /* ScriptingObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB4200744A700B9E363 /* ScriptingObject.swift */; };
|
||||
D5F4EDB720074D6500B9E363 /* Feed+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */; };
|
||||
D5F4EDB920074D7C00B9E363 /* Folder+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */; };
|
||||
|
@ -600,6 +603,7 @@
|
|||
84A37CBA201ECE590087C5AF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Evergreen/Base.lproj/RenameSheet.xib; sourceTree = SOURCE_ROOT; };
|
||||
84A6B6931FB8D43C006754AC /* DinosaursWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DinosaursWindow.xib; sourceTree = "<group>"; };
|
||||
84A6B6951FB8DBD2006754AC /* DinosaursWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DinosaursWindowController.swift; sourceTree = "<group>"; };
|
||||
84AAF2BE202CF684004A0BC4 /* TimelineContextualMenuDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineContextualMenuDelegate.swift; sourceTree = "<group>"; };
|
||||
84B06F961ED37DA000F0B54B /* RSDatabase.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSDatabase.xcodeproj; path = Frameworks/RSDatabase/RSDatabase.xcodeproj; sourceTree = "<group>"; };
|
||||
84B06FA21ED37DAC00F0B54B /* RSCore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSCore.xcodeproj; path = Frameworks/RSCore/RSCore.xcodeproj; sourceTree = "<group>"; };
|
||||
84B06FB61ED37E8B00F0B54B /* RSWeb.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSWeb.xcodeproj; path = Frameworks/RSWeb/RSWeb.xcodeproj; sourceTree = "<group>"; };
|
||||
|
@ -654,6 +658,8 @@
|
|||
D5A2679B201312F900A8D3C0 /* testNameOfAuthors.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = testNameOfAuthors.applescript; sourceTree = "<group>"; };
|
||||
D5A267B220131B8300A8D3C0 /* testFeedOPML.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = testFeedOPML.applescript; sourceTree = "<group>"; };
|
||||
D5D175012002039D0047B29D /* Evergreen.sdef */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = Evergreen.sdef; path = ../Resources/Evergreen.sdef; sourceTree = "<group>"; };
|
||||
D5E4CC53202C1361009B4FFC /* AppDelegate+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Scriptability.swift"; sourceTree = "<group>"; };
|
||||
D5E4CC63202C1AC1009B4FFC /* MainWindowController+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainWindowController+Scriptability.swift"; sourceTree = "<group>"; };
|
||||
D5F4EDB4200744A700B9E363 /* ScriptingObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptingObject.swift; sourceTree = "<group>"; };
|
||||
D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Feed+Scriptability.swift"; sourceTree = "<group>"; };
|
||||
D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Folder+Scriptability.swift"; sourceTree = "<group>"; };
|
||||
|
@ -940,6 +946,7 @@
|
|||
844B5B6C1FEA282400C7C76A /* Keyboard */,
|
||||
84E95D231FB1087500552D99 /* ArticlePasteboardWriter.swift */,
|
||||
8414AD241FCF5A1E00955102 /* TimelineHeaderView.swift */,
|
||||
84AAF2BE202CF684004A0BC4 /* TimelineContextualMenuDelegate.swift */,
|
||||
849A976F1ED9EC04007D329B /* Cell */,
|
||||
);
|
||||
path = Timeline;
|
||||
|
@ -1311,10 +1318,12 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
D5907D962004B7EB005947E5 /* Account+Scriptability.swift */,
|
||||
D5E4CC53202C1361009B4FFC /* AppDelegate+Scriptability.swift */,
|
||||
D553737C20186C1F006D8857 /* Article+Scriptability.swift */,
|
||||
D5A2678B20130ECF00A8D3C0 /* Author+Scriptability.swift */,
|
||||
D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */,
|
||||
D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */,
|
||||
D5E4CC63202C1AC1009B4FFC /* MainWindowController+Scriptability.swift */,
|
||||
D5907D7E2004AC00005947E5 /* NSApplication+Scriptability.swift */,
|
||||
D5907DB12004BB37005947E5 /* ScriptingObjectContainer.swift */,
|
||||
D5F4EDB4200744A700B9E363 /* ScriptingObject.swift */,
|
||||
|
@ -1877,6 +1886,7 @@
|
|||
848F6AE51FC29CFB002D422E /* FaviconDownloader.swift in Sources */,
|
||||
849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */,
|
||||
849A97531ED9EAC0007D329B /* AddFeedController.swift in Sources */,
|
||||
84AAF2BF202CF684004A0BC4 /* TimelineContextualMenuDelegate.swift in Sources */,
|
||||
849A97831ED9EC63007D329B /* SidebarStatusBarView.swift in Sources */,
|
||||
84F2D5381FC22FCC00998D64 /* TodayFeedDelegate.swift in Sources */,
|
||||
841ABA5E20145E9200980E11 /* FolderInspectorViewController.swift in Sources */,
|
||||
|
@ -1891,6 +1901,7 @@
|
|||
849A97771ED9EC04007D329B /* TimelineCellData.swift in Sources */,
|
||||
84B99C6B1FAE370B00ECDEDB /* FeedListFeed.swift in Sources */,
|
||||
841ABA6020145EC100980E11 /* BuiltinSmartFeedInspectorViewController.swift in Sources */,
|
||||
D5E4CC54202C1361009B4FFC /* AppDelegate+Scriptability.swift in Sources */,
|
||||
D5F4EDB5200744A700B9E363 /* ScriptingObject.swift in Sources */,
|
||||
D5F4EDB920074D7C00B9E363 /* Folder+Scriptability.swift in Sources */,
|
||||
842611A01FCB72600086A189 /* FeaturedImageDownloader.swift in Sources */,
|
||||
|
@ -1902,6 +1913,7 @@
|
|||
849A97541ED9EAC0007D329B /* AddFeedWindowController.swift in Sources */,
|
||||
849A976D1ED9EBC8007D329B /* TimelineTableView.swift in Sources */,
|
||||
84D52E951FE588BB00D14F5B /* DetailStatusBarView.swift in Sources */,
|
||||
D5E4CC64202C1AC1009B4FFC /* MainWindowController+Scriptability.swift in Sources */,
|
||||
84B99C671FAE35E600ECDEDB /* FeedListTreeControllerDelegate.swift in Sources */,
|
||||
84D5BA20201E8FB6009092BD /* SidebarGearMenuDelegate.swift in Sources */,
|
||||
84B99C691FAE36B800ECDEDB /* FeedListFolder.swift in Sources */,
|
||||
|
|
|
@ -526,3 +526,26 @@ private extension AppDelegate {
|
|||
sortByOldestArticleOnTopMenuItem.state = sortByNewestOnTop ? .off : .on
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
the ScriptingAppDelegate protocol exposes a narrow set of accessors with
|
||||
internal visibility which are very similar to some private vars.
|
||||
|
||||
These would be unnecessary if the similar accessors were marked internal rather than private,
|
||||
but for now, we'll keep the stratification of visibility
|
||||
*/
|
||||
extension AppDelegate : ScriptingAppDelegate {
|
||||
|
||||
internal var scriptingMainWindowController: ScriptingMainWindowController? {
|
||||
return mainWindowController
|
||||
}
|
||||
|
||||
internal var scriptingCurrentArticle: Article? {
|
||||
return self.scriptingMainWindowController?.scriptingCurrentArticle
|
||||
}
|
||||
|
||||
internal var scriptingSelectedArticles: [Article] {
|
||||
return self.scriptingMainWindowController?.scriptingSelectedArticles ?? []
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -592,6 +592,7 @@
|
|||
<outlet property="dataSource" destination="36G-bQ-b96" id="OpB-zC-ItJ"/>
|
||||
<outlet property="delegate" destination="36G-bQ-b96" id="s1m-42-GQ4"/>
|
||||
<outlet property="keyboardDelegate" destination="ZOV-xh-WJE" id="HiG-Bz-vD0"/>
|
||||
<outlet property="menu" destination="gb5-z4-YPr" id="pey-0u-ogu"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
|
@ -623,15 +624,33 @@
|
|||
</userDefinedRuntimeAttributes>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="contextualMenuDelegate" destination="iD1-KK-gFc" id="b0j-aW-e4B"/>
|
||||
<outlet property="tableView" destination="DRs-j8-R9a" id="2AG-SP-7n2"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="Ebq-4s-EwK" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
<menu id="gb5-z4-YPr">
|
||||
<items>
|
||||
<menuItem title="Item 1" id="Ikx-w7-cua">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Item 2" id="QX3-hL-Dqh">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Item 3" id="IsQ-j7-Njb">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="iD1-KK-gFc" id="q4j-wE-tnf"/>
|
||||
</connections>
|
||||
</menu>
|
||||
<customObject id="ZOV-xh-WJE" customClass="TimelineKeyboardDelegate" customModule="Evergreen" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="timelineViewController" destination="36G-bQ-b96" id="rED-2Z-kh6"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="iD1-KK-gFc" customClass="TimelineContextualMenuDelegate" customModule="Evergreen" customModuleProvider="target"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="62" y="394"/>
|
||||
</scene>
|
||||
|
|
|
@ -400,6 +400,28 @@ extension MainWindowController: NSToolbarDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Scripting Access
|
||||
|
||||
/*
|
||||
the ScriptingMainWindowController protocol exposes a narrow set of accessors with
|
||||
internal visibility which are very similar to some private vars.
|
||||
|
||||
These would be unnecessary if the similar accessors were marked internal rather than private,
|
||||
but for now, we'll keep the stratification of visibility
|
||||
*/
|
||||
|
||||
extension MainWindowController : ScriptingMainWindowController {
|
||||
|
||||
internal var scriptingCurrentArticle: Article? {
|
||||
return self.oneSelectedArticle
|
||||
}
|
||||
|
||||
internal var scriptingSelectedArticles: [Article] {
|
||||
return self.selectedArticles ?? []
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private extension MainWindowController {
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// TimelineContextualMenuDelegate.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Brent Simmons on 2/8/18.
|
||||
// Copyright © 2018 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
|
||||
@objc final class TimelineContextualMenuDelegate: NSObject, NSMenuDelegate {
|
||||
|
||||
@IBOutlet weak var timelineViewController: TimelineViewController?
|
||||
|
||||
public func menuNeedsUpdate(_ menu: NSMenu) {
|
||||
|
||||
// guard let timelineViewController = timelineViewController else {
|
||||
// return
|
||||
// }
|
||||
|
||||
// menu.removeAllItems()
|
||||
|
||||
// guard let contextualMenu = sidebarViewController.contextualMenuForClickedRows() else {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// let items = contextualMenu.items
|
||||
// contextualMenu.removeAllItems()
|
||||
// for menuItem in items {
|
||||
// menu.addItem(menuItem)
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -15,7 +15,8 @@ import Account
|
|||
class TimelineViewController: NSViewController, UndoableCommandRunner {
|
||||
|
||||
@IBOutlet var tableView: TimelineTableView!
|
||||
|
||||
@IBOutlet var contextualMenuDelegate: TimelineContextualMenuDelegate?
|
||||
|
||||
var selectedArticles: [Article] {
|
||||
get {
|
||||
return Array(articles.articlesForIndexes(tableView.selectedRowIndexes))
|
||||
|
|
|
@ -20,7 +20,13 @@
|
|||
<!-- the name of the Cocoa class where we have provided the
|
||||
various accessor methods for our application class. -->
|
||||
<cocoa class="NSApplication"/>
|
||||
|
||||
<property name="current article" code="CurA" type="article" access="r" description="The article, if any, being currently displayed">
|
||||
<cocoa key="currentArticle"/>
|
||||
</property>
|
||||
<property name="selected articles" code="SelA" access="r" description="All articles in the main window that are currently selected, if any.">
|
||||
<type type="article" list="yes"/>
|
||||
<cocoa key="selectedArticles"/>
|
||||
</property>
|
||||
<element type="account">
|
||||
<cocoa key="accounts"/>
|
||||
</element>
|
||||
|
@ -42,7 +48,7 @@
|
|||
<property name="name" code="pnam" type="text" access="r" description="The name of the account">
|
||||
<cocoa key="name"/>
|
||||
</property>
|
||||
<property name="id" code="id " type="text" access="r" description="The unique id of the account">
|
||||
<property name="id" code="ID " type="text" access="r" description="The unique id of the account">
|
||||
<cocoa key="uniqueId"/>
|
||||
</property>
|
||||
<property name="type" code="ATyp" type="account type" access="r" description="The type of the account">
|
||||
|
@ -71,7 +77,7 @@
|
|||
<property name="name" code="pnam" type="text" access="r" description="The name of the feed">
|
||||
<cocoa key="name"/>
|
||||
</property>
|
||||
<property name="id" code="id " type="text" access="r" description="The unique id of the account">
|
||||
<property name="id" code="ID " type="text" access="r" description="The unique id of the account">
|
||||
<cocoa key="uniqueId"/>
|
||||
</property>
|
||||
<property name="url" code="URL " type="text" access="r" description="The type of the account">
|
||||
|
@ -101,7 +107,7 @@
|
|||
<property name="name" code="pnam" type="text" access="r" description="The name of the author">
|
||||
<cocoa key="name"/>
|
||||
</property>
|
||||
<property name="id" code="id " type="text" access="r" description="The unique id of the author">
|
||||
<property name="id" code="ID " type="text" access="r" description="The unique id of the author">
|
||||
<cocoa key="uniqueId"/>
|
||||
</property>
|
||||
<property name="url" code="URL " type="text" access="r" description="url for the author">
|
||||
|
@ -120,7 +126,7 @@
|
|||
<property name="name" code="pnam" type="text" access="r" description="The name of the account">
|
||||
<cocoa key="name"/>
|
||||
</property>
|
||||
<property name="id" code="id " type="integer" access="r" description="The unique id of the account">
|
||||
<property name="id" code="ID " type="integer" access="r" description="The unique id of the account">
|
||||
<cocoa key="uniqueId"/>
|
||||
</property>
|
||||
<property name="opml representation" code="OPML" type="text" access="r" description="OPML representation for the folder">
|
||||
|
@ -130,7 +136,7 @@
|
|||
|
||||
<class name="article" code="Arcl" plural="articles" description="An article in a feed">
|
||||
<cocoa class="ScriptableArticle"/>
|
||||
<property name="id" code="id " type="text" access="r" description="The unique id of the article as set by the feed">
|
||||
<property name="id" code="ID " type="text" access="r" description="The unique id of the article as set by the feed">
|
||||
<cocoa key="uniqueId"/>
|
||||
</property>
|
||||
<property name="title" code="titl" type="text" access="r" description="The article title">
|
||||
|
|
|
@ -54,12 +54,29 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
|
|||
return feeds.map { ScriptableFeed($0, container:self) } as NSArray
|
||||
}
|
||||
|
||||
@objc(valueInFeedsWithUniqueID:)
|
||||
func valueInFeeds(withUniqueID id:String) -> ScriptableFeed? {
|
||||
let feeds = account.children.compactMap { $0 as? Feed }
|
||||
guard let feed = feeds.first(where:{$0.feedID == id}) else { return nil }
|
||||
return ScriptableFeed(feed, container:self)
|
||||
}
|
||||
|
||||
|
||||
@objc(folders)
|
||||
var folders:NSArray {
|
||||
let folders = account.children.compactMap { $0 as? Folder }
|
||||
return folders.map { ScriptableFolder($0, container:self) } as NSArray
|
||||
}
|
||||
|
||||
@objc(valueInFoldersWithUniqueID:)
|
||||
func valueInFolders(withUniqueID id:NSNumber) -> ScriptableFolder? {
|
||||
let folderId = id.intValue
|
||||
let folders = account.children.compactMap { $0 as? Folder }
|
||||
guard let folder = folders.first(where:{$0.folderID == folderId}) else { return nil }
|
||||
return ScriptableFolder(folder, container:self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: --- Scriptable properties ---
|
||||
|
||||
@objc(contents)
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// AppDelegate+Scriptability.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Olof Hellman on 2/7/18.
|
||||
// Copyright © 2018 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
/*
|
||||
Note: strictly, the AppDelegate doesn't appear as part of the scripting model,
|
||||
so this file is rather unlike the other Object+Scriptability.swift files.
|
||||
However, the AppDelegate object is the de facto scripting accessor for some
|
||||
application elements and properties. For, example, the main window is accessed
|
||||
via the AppDelegate's MainWindowController, and the main window itself has
|
||||
selected feeds, selected articles and a current article. This file supplies the glue to access
|
||||
these scriptable objects, while being completely separate from the core AppDelegate code,
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import Data
|
||||
|
||||
protocol ScriptingAppDelegate {
|
||||
var scriptingCurrentArticle: Article? {get}
|
||||
var scriptingSelectedArticles: [Article] {get}
|
||||
var scriptingMainWindowController:ScriptingMainWindowController? {get}
|
||||
}
|
||||
|
||||
|
|
@ -85,11 +85,19 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
|
|||
return self.feed.OPMLString(indentLevel:0)
|
||||
}
|
||||
|
||||
// MARK: --- scriptable elements ---
|
||||
|
||||
@objc(authors)
|
||||
var authors:NSArray {
|
||||
let feedAuthors = feed.authors ?? []
|
||||
return feedAuthors.map { ScriptableAuthor($0, container:self) } as NSArray
|
||||
}
|
||||
|
||||
@objc(valueInAuthorsWithUniqueID:)
|
||||
func valueInAuthors(withUniqueID id:String) -> ScriptableAuthor? {
|
||||
guard let author = feed.authors?.first(where:{$0.authorID == id}) else { return nil }
|
||||
return ScriptableAuthor(author, container:self)
|
||||
}
|
||||
|
||||
@objc(articles)
|
||||
var articles:NSArray {
|
||||
|
@ -100,5 +108,12 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
|
|||
})
|
||||
return sortedArticles.map { ScriptableArticle($0, container:self) } as NSArray
|
||||
}
|
||||
|
||||
@objc(valueInArticlesWithUniqueID:)
|
||||
func valueInArticles(withUniqueID id:String) -> ScriptableArticle? {
|
||||
let articles = feed.fetchArticles()
|
||||
guard let article = articles.first(where:{$0.uniqueID == id}) else { return nil }
|
||||
return ScriptableArticle(article, container:self)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// MainWindowController+Scriptability.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Olof Hellman on 2/7/18.
|
||||
// Copyright © 2018 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Data
|
||||
|
||||
protocol ScriptingMainWindowController {
|
||||
var scriptingCurrentArticle: Article? { get }
|
||||
var scriptingSelectedArticles: [Article] { get }
|
||||
}
|
||||
|
|
@ -20,27 +20,74 @@ extension NSApplication : ScriptingObjectContainer {
|
|||
return "application"
|
||||
}
|
||||
|
||||
@objc(currentArticle)
|
||||
func currentArticle() -> ScriptableArticle? {
|
||||
var scriptableArticle: ScriptableArticle?
|
||||
if let currentArticle = appDelegate.scriptingCurrentArticle {
|
||||
if let feed = currentArticle.feed {
|
||||
let scriptableFeed = ScriptableFeed(feed, container:self)
|
||||
scriptableArticle = ScriptableArticle(currentArticle, container:scriptableFeed)
|
||||
}
|
||||
}
|
||||
return scriptableArticle
|
||||
}
|
||||
|
||||
@objc(selectedArticles)
|
||||
func selectedArticles() -> NSArray {
|
||||
let articles = appDelegate.scriptingSelectedArticles
|
||||
let scriptableArticles:[ScriptableArticle] = articles.compactMap { article in
|
||||
if let feed = article.feed {
|
||||
let scriptableFeed = ScriptableFeed(feed, container:self)
|
||||
return ScriptableArticle(article, container:scriptableFeed)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return scriptableArticles as NSArray
|
||||
}
|
||||
|
||||
// MARK: --- scriptable elements ---
|
||||
|
||||
@objc(accounts)
|
||||
func accounts() -> NSArray {
|
||||
let accounts = AccountManager.shared.accounts
|
||||
return accounts.map { ScriptableAccount($0) } as NSArray
|
||||
}
|
||||
|
||||
@objc(valueInAccountsWithUniqueID:)
|
||||
func valueInAccounts(withUniqueID id:String) -> ScriptableAccount? {
|
||||
let accounts = AccountManager.shared.accounts
|
||||
guard let account = accounts.first(where:{$0.accountID == id}) else { return nil }
|
||||
return ScriptableAccount(account)
|
||||
}
|
||||
|
||||
/*
|
||||
accessing feeds from the application object skips the 'account' containment hierarchy
|
||||
this allows a script like 'articles of feed "The Shape of Everything"' as a shorthand
|
||||
for 'articles of feed "The Shape of Everything" of account "On My Mac"'
|
||||
*/
|
||||
@objc(feeds)
|
||||
func feeds() -> NSArray {
|
||||
*/
|
||||
|
||||
func allFeeds() -> [Feed] {
|
||||
let accounts = AccountManager.shared.accounts
|
||||
let emptyFeeds:[Feed] = []
|
||||
let feeds = accounts.reduce(emptyFeeds) { (result, nthAccount) -> [Feed] in
|
||||
return accounts.reduce(emptyFeeds) { (result, nthAccount) -> [Feed] in
|
||||
let accountFeeds = nthAccount.children.compactMap { $0 as? Feed }
|
||||
return result + accountFeeds
|
||||
}
|
||||
}
|
||||
|
||||
@objc(feeds)
|
||||
func feeds() -> NSArray {
|
||||
let feeds = self.allFeeds()
|
||||
return feeds.map { ScriptableFeed($0, container:self) } as NSArray
|
||||
}
|
||||
|
||||
@objc(valueInFeedsWithUniqueID:)
|
||||
func valueInFeeds(withUniqueID id:String) -> ScriptableFeed? {
|
||||
let feeds = self.allFeeds()
|
||||
guard let feed = feeds.first(where:{$0.feedID == id}) else { return nil }
|
||||
return ScriptableFeed(feed, container:self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue