Merge remote-tracking branch 'brentsimmons/master'
This commit is contained in:
commit
9236487945
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// SendToCommand.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Brent Simmons on 1/8/18.
|
||||
// Copyright © 2018 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
protocol SendToCommand {
|
||||
|
||||
func canSendObject(_ object: Any?) -> Bool
|
||||
func sendObject(_ object: Any?)
|
||||
}
|
||||
|
||||
extension SendToCommand {
|
||||
|
||||
func appExistsOnDisk(_ bundleIdentifier: String) -> Bool {
|
||||
|
||||
if let _ = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleIdentifier) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// SendToMarsEditCommand.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Brent Simmons on 1/8/18.
|
||||
// Copyright © 2018 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
final class SendToMarsEditCommand: SendToCommand {
|
||||
|
||||
func canSendObject(_ object: Any?) -> Bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func sendObject(_ object: Any?) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// SendToMicroBlogCommand.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Brent Simmons on 1/8/18.
|
||||
// Copyright © 2018 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
// Not undoable.
|
||||
|
||||
final class SendToMicroBlogCommand: SendToCommand {
|
||||
|
||||
private let bundleID = "blog.micro.mac"
|
||||
private var appExists = false
|
||||
|
||||
init() {
|
||||
|
||||
self.appExists = appExistsOnDisk(bundleID)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive(_:)), name: NSApplication.didBecomeActiveNotification, object: nil)
|
||||
}
|
||||
|
||||
func canSendObject(_ object: Any?) -> Bool {
|
||||
|
||||
if !appExists {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func sendObject(_ object: Any?) {
|
||||
|
||||
}
|
||||
|
||||
@objc func appDidBecomeActive(_ note: Notification) {
|
||||
|
||||
self.appExists = appExistsOnDisk(bundleID)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,6 +84,9 @@
|
|||
849C64681ED37A5D003D8FC0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 849C64671ED37A5D003D8FC0 /* Assets.xcassets */; };
|
||||
849C646B1ED37A5D003D8FC0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 849C64691ED37A5D003D8FC0 /* Main.storyboard */; };
|
||||
849C64761ED37A5D003D8FC0 /* EvergreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849C64751ED37A5D003D8FC0 /* EvergreenTests.swift */; };
|
||||
84A14FF320048CA70046AD9A /* SendToMicroBlogCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A14FF220048CA70046AD9A /* SendToMicroBlogCommand.swift */; };
|
||||
84A1500320048D660046AD9A /* SendToCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A1500220048D660046AD9A /* SendToCommand.swift */; };
|
||||
84A1500520048DDF0046AD9A /* SendToMarsEditCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A1500420048DDF0046AD9A /* SendToMarsEditCommand.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 */; };
|
||||
|
@ -512,6 +515,9 @@
|
|||
849C64711ED37A5D003D8FC0 /* EvergreenTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EvergreenTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
849C64751ED37A5D003D8FC0 /* EvergreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EvergreenTests.swift; sourceTree = "<group>"; };
|
||||
849C64771ED37A5D003D8FC0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
84A14FF220048CA70046AD9A /* SendToMicroBlogCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToMicroBlogCommand.swift; sourceTree = "<group>"; };
|
||||
84A1500220048D660046AD9A /* SendToCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToCommand.swift; sourceTree = "<group>"; };
|
||||
84A1500420048DDF0046AD9A /* SendToMarsEditCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToMarsEditCommand.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
84B06F961ED37DA000F0B54B /* RSDatabase.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSDatabase.xcodeproj; path = Frameworks/RSDatabase/RSDatabase.xcodeproj; sourceTree = "<group>"; };
|
||||
|
@ -706,6 +712,9 @@
|
|||
children = (
|
||||
84702AA31FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift */,
|
||||
84B99C9C1FAE83C600ECDEDB /* DeleteFromSidebarCommand.swift */,
|
||||
84A1500220048D660046AD9A /* SendToCommand.swift */,
|
||||
84A14FF220048CA70046AD9A /* SendToMicroBlogCommand.swift */,
|
||||
84A1500420048DDF0046AD9A /* SendToMarsEditCommand.swift */,
|
||||
);
|
||||
path = Commands;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1511,9 +1520,11 @@
|
|||
849A975B1ED9EB0D007D329B /* ArticleUtilities.swift in Sources */,
|
||||
84DAEE301F86CAFE0058304B /* OPMLImporter.swift in Sources */,
|
||||
849A975C1ED9EB0D007D329B /* DefaultFeedsImporter.swift in Sources */,
|
||||
84A14FF320048CA70046AD9A /* SendToMicroBlogCommand.swift in Sources */,
|
||||
849A97891ED9ECEF007D329B /* ArticleStyle.swift in Sources */,
|
||||
84FF69B11FC3793300DC198E /* FaviconURLFinder.swift in Sources */,
|
||||
842611A21FCB769D0086A189 /* RSHTMLMetadata+Extension.swift in Sources */,
|
||||
84A1500520048DDF0046AD9A /* SendToMarsEditCommand.swift in Sources */,
|
||||
849A978A1ED9ECEF007D329B /* ArticleStylesManager.swift in Sources */,
|
||||
849A97791ED9EC04007D329B /* TimelineStringUtilities.swift in Sources */,
|
||||
84F204CE1FAACB660076E152 /* FeedListViewController.swift in Sources */,
|
||||
|
@ -1549,6 +1560,7 @@
|
|||
84411E711FE5FBFA004B527F /* SmallIconProvider.swift in Sources */,
|
||||
844B5B591FE9FE4F00C7C76A /* SidebarKeyboardDelegate.swift in Sources */,
|
||||
849A97A31ED9F180007D329B /* FolderTreeControllerDelegate.swift in Sources */,
|
||||
84A1500320048D660046AD9A /* SendToCommand.swift in Sources */,
|
||||
845A29091FC74B8E007B49E3 /* SingleFaviconDownloader.swift in Sources */,
|
||||
849A97851ED9ECCD007D329B /* PreferencesWindowController.swift in Sources */,
|
||||
84E850861FCB60CE0072EA88 /* AuthorAvatarDownloader.swift in Sources */,
|
||||
|
|
|
@ -290,7 +290,7 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="166" height="272"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<outlineView appearanceType="aqua" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="firstColumnOnly" selectionHighlightStyle="sourceList" columnReordering="NO" columnResizing="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="24" rowSizeStyle="medium" viewBased="YES" floatsGroupRows="NO" indentationPerLevel="16" outlineTableColumn="ih9-mJ-EA7" id="cnV-kg-Dn2" customClass="SidebarOutlineView" customModule="Evergreen" customModuleProvider="target">
|
||||
<outlineView appearanceType="aqua" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="firstColumnOnly" selectionHighlightStyle="sourceList" columnReordering="NO" columnResizing="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="24" rowSizeStyle="medium" viewBased="YES" floatsGroupRows="NO" indentationPerLevel="23" outlineTableColumn="ih9-mJ-EA7" id="cnV-kg-Dn2" customClass="SidebarOutlineView" customModule="Evergreen" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="167" height="272"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
|
|
|
@ -28,14 +28,6 @@
|
|||
<key>url</key>
|
||||
<string>https://9to5mac.com/feed/</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Macalope</string>
|
||||
<key>homePageURL</key>
|
||||
<string>http://www.macalope.com/</string>
|
||||
<key>url</key>
|
||||
<string>http://www.macalope.com/feed/</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Macdrifter</string>
|
||||
|
|
|
@ -131,6 +131,8 @@ private extension ImageDownloader {
|
|||
|
||||
func postImageDidBecomeAvailableNotification(_ url: String) {
|
||||
|
||||
NotificationCenter.default.post(name: .ImageDidBecomeAvailable, object: self, userInfo: [UserInfoKey.url: url])
|
||||
DispatchQueue.main.async {
|
||||
NotificationCenter.default.post(name: .ImageDidBecomeAvailable, object: self, userInfo: [UserInfoKey.url: url])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0d30</string>
|
||||
<string>1.0d31</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>522</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
@ -37,9 +37,9 @@
|
|||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSAppleScriptEnabled</key>
|
||||
<true/>
|
||||
<key>OSAScriptingDefinition</key>
|
||||
<string>Evergreen.sdef</string>
|
||||
<key>NSAppleScriptEnabled</key>
|
||||
<true/>
|
||||
<key>OSAScriptingDefinition</key>
|
||||
<string>Evergreen.sdef</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -51,6 +51,7 @@ class TimelineViewController: NSViewController, UndoableCommandRunner {
|
|||
|
||||
private var didRegisterForNotifications = false
|
||||
private let timelineFontSizeKVOKey = "values.{AppDefaults.Key.timelineFontSize}"
|
||||
private var reloadAvailableCellsTimer: Timer?
|
||||
|
||||
private var articles = ArticleArray() {
|
||||
didSet {
|
||||
|
@ -117,6 +118,7 @@ class TimelineViewController: NSViewController, UndoableCommandRunner {
|
|||
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(feedIconDidBecomeAvailable(_:)), name: .FeedIconDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(avatarDidBecomeAvailable(_:)), name: .AvatarDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(imageDidBecomeAvailable(_:)), name: .ImageDidBecomeAvailable, object: nil)
|
||||
|
||||
NSUserDefaultsController.shared.addObserver(self, forKeyPath: timelineFontSizeKVOKey, options: NSKeyValueObservingOptions(rawValue: 0), context: nil)
|
||||
|
||||
|
@ -347,6 +349,11 @@ class TimelineViewController: NSViewController, UndoableCommandRunner {
|
|||
}
|
||||
}
|
||||
|
||||
@objc func imageDidBecomeAvailable(_ note: Notification) {
|
||||
|
||||
queueReloadAvailableCells()
|
||||
}
|
||||
|
||||
func fontSizeInDefaultsDidChange() {
|
||||
|
||||
TimelineCellData.emptyCache()
|
||||
|
@ -554,6 +561,32 @@ extension TimelineViewController: NSTableViewDelegate {
|
|||
|
||||
private extension TimelineViewController {
|
||||
|
||||
func reloadAvailableCells() {
|
||||
|
||||
if let indexesToReload = tableView.indexesOfAvailableRows() {
|
||||
reloadCells(for: indexesToReload)
|
||||
}
|
||||
}
|
||||
|
||||
func queueReloadAvailableCells() {
|
||||
|
||||
invalidateReloadTimer()
|
||||
reloadAvailableCellsTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { (timer) in
|
||||
self.reloadAvailableCells()
|
||||
self.invalidateReloadTimer()
|
||||
}
|
||||
}
|
||||
|
||||
func invalidateReloadTimer() {
|
||||
|
||||
if let timer = reloadAvailableCellsTimer {
|
||||
if timer.isValid {
|
||||
timer.invalidate()
|
||||
}
|
||||
reloadAvailableCellsTimer = nil
|
||||
}
|
||||
}
|
||||
|
||||
func updateShowAvatars() {
|
||||
|
||||
if showFeedNames {
|
||||
|
|
|
@ -30,6 +30,13 @@ public extension NSTableView {
|
|||
return indexes.isEmpty ? nil : indexes
|
||||
}
|
||||
|
||||
func indexesOfAvailableRows() -> IndexSet? {
|
||||
|
||||
var indexes = IndexSet()
|
||||
enumerateAvailableRowViews { indexes.insert($1) }
|
||||
return indexes.isEmpty ? nil : indexes
|
||||
}
|
||||
|
||||
func scrollTo(row: Int) {
|
||||
|
||||
guard let scrollView = self.enclosingScrollView else {
|
||||
|
|
|
@ -102,6 +102,7 @@
|
|||
84D81BE41EFA2D3D00652332 /* ParsedItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D81BE31EFA2D3D00652332 /* ParsedItem.swift */; };
|
||||
84D81BE61EFA2DFB00652332 /* ParsedAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D81BE51EFA2DFB00652332 /* ParsedAttachment.swift */; };
|
||||
84D81BE81EFA2E6700652332 /* ParsedHub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D81BE71EFA2E6700652332 /* ParsedHub.swift */; };
|
||||
84DA2E21200415D500A4D03B /* curt.json in Resources */ = {isa = PBXBuildFile; fileRef = 84DA2E20200415D500A4D03B /* curt.json */; };
|
||||
84DCCC661FF80E0100D2DDF1 /* EntityDecodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DCCC651FF80E0100D2DDF1 /* EntityDecodingTests.swift */; };
|
||||
84E7E69F1F85780D0046719D /* ParserData.h in Headers */ = {isa = PBXBuildFile; fileRef = 84E7E69D1F85780D0046719D /* ParserData.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
84E7E6A01F85780D0046719D /* ParserData.m in Sources */ = {isa = PBXBuildFile; fileRef = 84E7E69E1F85780D0046719D /* ParserData.m */; };
|
||||
|
@ -216,6 +217,7 @@
|
|||
84D81BE31EFA2D3D00652332 /* ParsedItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ParsedItem.swift; path = Feeds/ParsedItem.swift; sourceTree = "<group>"; };
|
||||
84D81BE51EFA2DFB00652332 /* ParsedAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ParsedAttachment.swift; path = Feeds/ParsedAttachment.swift; sourceTree = "<group>"; };
|
||||
84D81BE71EFA2E6700652332 /* ParsedHub.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ParsedHub.swift; path = Feeds/ParsedHub.swift; sourceTree = "<group>"; };
|
||||
84DA2E20200415D500A4D03B /* curt.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = curt.json; sourceTree = "<group>"; };
|
||||
84DCCC651FF80E0100D2DDF1 /* EntityDecodingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityDecodingTests.swift; sourceTree = "<group>"; };
|
||||
84E7E69D1F85780D0046719D /* ParserData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ParserData.h; sourceTree = "<group>"; };
|
||||
84E7E69E1F85780D0046719D /* ParserData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ParserData.m; sourceTree = "<group>"; };
|
||||
|
@ -380,6 +382,7 @@
|
|||
849A03C41F0081EA00122600 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84DA2E20200415D500A4D03B /* curt.json */,
|
||||
849A03C51F0081EA00122600 /* DaringFireball.html */,
|
||||
840FDCB71F0218670041F61B /* DaringFireball.atom */,
|
||||
840FDCB91F02186D0041F61B /* DaringFireball.json */,
|
||||
|
@ -600,6 +603,7 @@
|
|||
849A03EA1F01F92B00122600 /* inessential.json in Resources */,
|
||||
849A03D71F0081EA00122600 /* OneFootTsunami.atom in Resources */,
|
||||
849A03D41F0081EA00122600 /* inessential.html in Resources */,
|
||||
84DA2E21200415D500A4D03B /* curt.json in Resources */,
|
||||
849A03D31F0081EA00122600 /* furbo.html in Resources */,
|
||||
849A03E81F01F88600122600 /* ScriptingNews.json in Resources */,
|
||||
844B5B3E1FE9A13C00C7C76A /* 4fsodonline.atom in Resources */,
|
||||
|
|
|
@ -134,6 +134,13 @@ class FeedParserTypeTests: XCTestCase {
|
|||
XCTAssertTrue(type == .jsonFeed)
|
||||
}
|
||||
|
||||
func testCurtJSONFeedType() {
|
||||
|
||||
let d = parserData("curt", "json", "http://curtclifton.net/")
|
||||
let type = feedType(d)
|
||||
XCTAssertTrue(type == .jsonFeed)
|
||||
}
|
||||
|
||||
// MARK: Unknown
|
||||
|
||||
func testPartialAllThisUnknownFeedType() {
|
||||
|
|
|
@ -66,4 +66,22 @@ class JSONFeedParserTests: XCTestCase {
|
|||
|
||||
XCTAssertEqual(parsedFeed.items.count, 12)
|
||||
}
|
||||
|
||||
func testCurt() {
|
||||
|
||||
let d = parserData("curt", "json", "http://curtclifton.net/")
|
||||
let parsedFeed = try! FeedParser.parse(d)!
|
||||
|
||||
XCTAssertEqual(parsedFeed.items.count, 26)
|
||||
|
||||
var didFindTwitterQuitterArticle = false
|
||||
for article in parsedFeed.items {
|
||||
if article.title == "Twitter Quitter" {
|
||||
didFindTwitterQuitterArticle = true
|
||||
XCTAssertTrue(article.contentHTML!.hasPrefix("<p>I’ve decided to close my Twitter account. William Van Hecke <a href=\"https://tinyletter.com/fet/letters/microcosmographia-xlxi-reasons-to-stay-on-twitter\">makes a convincing case</a>"))
|
||||
}
|
||||
}
|
||||
|
||||
XCTAssertTrue(didFindTwitterQuitterArticle)
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -38,7 +38,7 @@ static BOOL bytesStartWithRSS(const char *bytes, NSUInteger numberOfBytes);
|
|||
if (![self isProbablyJSON]) {
|
||||
return NO;
|
||||
}
|
||||
return didFindString("https://jsonfeed.org/version/", self.bytes, self.length);
|
||||
return didFindString("https://jsonfeed.org/version/", self.bytes, self.length) || didFindString("https:\\/\\/jsonfeed.org\\/version\\/", self.bytes, self.length);
|
||||
}
|
||||
|
||||
- (BOOL)isProbablyRSSInJSON {
|
||||
|
|
Loading…
Reference in New Issue