Merge remote-tracking branch 'brentsimmons/master'

This commit is contained in:
Olof Hellman 2018-01-08 23:43:03 -08:00
commit 9236487945
15 changed files with 181 additions and 16 deletions

View File

@ -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
}
}

View File

@ -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?) {
}
}

View File

@ -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)
}
}

View File

@ -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 */,

View File

@ -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"/>

View File

@ -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>

View File

@ -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])
}
}
}

View File

@ -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>

View File

@ -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 {

View File

@ -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 {

View File

@ -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 */,

View File

@ -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() {

View File

@ -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>Ive 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

View File

@ -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 {