Fix lint issues.

This commit is contained in:
Brent Simmons 2025-01-22 22:17:02 -08:00
parent 8f1379360c
commit 40ada2ba5a
91 changed files with 1278 additions and 1350 deletions

View File

@ -72,9 +72,9 @@ final class AppDefaults {
return true return true
}() }()
var windowState: [AnyHashable : Any]? { var windowState: [AnyHashable: Any]? {
get { get {
return UserDefaults.standard.object(forKey: Key.windowState) as? [AnyHashable : Any] return UserDefaults.standard.object(forKey: Key.windowState) as? [AnyHashable: Any]
} }
set { set {
UserDefaults.standard.set(newValue, forKey: Key.windowState) UserDefaults.standard.set(newValue, forKey: Key.windowState)
@ -105,8 +105,7 @@ final class AppDefaults {
if let appGroupID = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as? String, if let appGroupID = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as? String,
let appGroupDefaults = UserDefaults(suiteName: appGroupID) { let appGroupDefaults = UserDefaults(suiteName: appGroupID) {
return appGroupDefaults return appGroupDefaults
} } else {
else {
return UserDefaults.standard return UserDefaults.standard
} }
} }
@ -312,7 +311,7 @@ final class AppDefaults {
let showDebugMenu = false let showDebugMenu = false
#endif #endif
let defaults: [String : Any] = [ let defaults: [String: Any] = [
Key.sidebarFontSize: FontSize.medium.rawValue, Key.sidebarFontSize: FontSize.medium.rawValue,
Key.timelineFontSize: FontSize.medium.rawValue, Key.timelineFontSize: FontSize.medium.rawValue,
Key.detailFontSize: FontSize.medium.rawValue, Key.detailFontSize: FontSize.medium.rawValue,
@ -416,7 +415,7 @@ private extension AppDefaults {
UserDefaults.standard.set(date, forKey: key) UserDefaults.standard.set(date, forKey: key)
} }
static func sortDirection(for key:String) -> ComparisonResult { static func sortDirection(for key: String) -> ComparisonResult {
let rawInt = int(for: key) let rawInt = int(for: key)
if rawInt == ComparisonResult.orderedAscending.rawValue { if rawInt == ComparisonResult.orderedAscending.rawValue {
return .orderedAscending return .orderedAscending
@ -427,8 +426,7 @@ private extension AppDefaults {
static func setSortDirection(for key: String, _ value: ComparisonResult) { static func setSortDirection(for key: String, _ value: ComparisonResult) {
if value == .orderedAscending { if value == .orderedAscending {
setInt(for: key, ComparisonResult.orderedAscending.rawValue) setInt(for: key, ComparisonResult.orderedAscending.rawValue)
} } else {
else {
setInt(for: key, ComparisonResult.orderedDescending.rawValue) setInt(for: key, ComparisonResult.orderedDescending.rawValue)
} }
} }

View File

@ -22,8 +22,7 @@ import Sparkle
var appDelegate: AppDelegate! var appDelegate: AppDelegate!
@NSApplicationMain @NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, UNUserNotificationCenterDelegate, UnreadCountProvider, SPUStandardUserDriverDelegate, SPUUpdaterDelegate class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, UNUserNotificationCenterDelegate, UnreadCountProvider, SPUStandardUserDriverDelegate, SPUUpdaterDelegate {
{
private struct WindowRestorationIdentifiers { private struct WindowRestorationIdentifiers {
static let mainWindow = "mainWindow" static let mainWindow = "mainWindow"
@ -139,8 +138,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
let cacheFolder: String let cacheFolder: String
if let userCacheFolder = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: false).path { if let userCacheFolder = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: false).path {
cacheFolder = userCacheFolder cacheFolder = userCacheFolder
} } else {
else {
let bundleIdentifier = (Bundle.main.infoDictionary!["CFBundleIdentifier"]! as! String) let bundleIdentifier = (Bundle.main.infoDictionary!["CFBundleIdentifier"]! as! String)
cacheFolder = (NSTemporaryDirectory() as NSString).appendingPathComponent(bundleIdentifier) cacheFolder = (NSTemporaryDirectory() as NSString).appendingPathComponent(bundleIdentifier)
} }
@ -166,8 +164,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
do { do {
try self.softwareUpdater.start() try self.softwareUpdater.start()
} } catch {
catch {
NSLog("Failed to start software updater with error: \(error)") NSLog("Failed to start software updater with error: \(error)")
} }
@ -214,7 +211,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
refreshTimer = AccountRefreshTimer() refreshTimer = AccountRefreshTimer()
syncTimer = ArticleStatusSyncTimer() syncTimer = ArticleStatusSyncTimer()
UNUserNotificationCenter.current().requestAuthorization(options:[.badge]) { (granted, error) in } UNUserNotificationCenter.current().requestAuthorization(options: [.badge]) { (_, _) in }
UNUserNotificationCenter.current().getNotificationSettings { (settings) in UNUserNotificationCenter.current().getNotificationSettings { (settings) in
if settings.authorizationStatus == .authorized { if settings.authorizationStatus == .authorized {
@ -282,7 +279,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
saveState() saveState()
} }
func application(_ application: NSApplication, didReceiveRemoteNotification userInfo: [String : Any]) { func application(_ application: NSApplication, didReceiveRemoteNotification userInfo: [String: Any]) {
AccountManager.shared.receiveRemoteNotification(userInfo: userInfo) AccountManager.shared.receiveRemoteNotification(userInfo: userInfo)
} }
@ -298,7 +295,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
ArticleThemeDownloader.shared.cleanUp() ArticleThemeDownloader.shared.cleanUp()
AccountManager.shared.sendArticleStatusAll() { AccountManager.shared.sendArticleStatusAll {
self.isShutDownSyncDone = true self.isShutDownSyncDone = true
} }
@ -318,7 +315,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
return return
} }
if key == Feed.FeedSettingKey.homePageURL || key == Feed.FeedSettingKey.faviconURL { if key == Feed.FeedSettingKey.homePageURL || key == Feed.FeedSettingKey.faviconURL {
let _ = faviconDownloader.favicon(for: feed) _ = faviconDownloader.favicon(for: feed)
} }
} }
@ -530,8 +527,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
if inspectorWindowController!.isOpen { if inspectorWindowController!.isOpen {
inspectorWindowController!.window!.performClose(self) inspectorWindowController!.window!.performClose(self)
} } else {
else {
inspectorWindowController!.objects = objectsForInspector() inspectorWindowController!.objects = objectsForInspector()
inspectorWindowController!.showWindow(self) inspectorWindowController!.showWindow(self)
} }
@ -581,7 +577,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
Browser.open(URL.releaseNotes.absoluteString, inBackground: false) Browser.open(URL.releaseNotes.absoluteString, inBackground: false)
} }
@IBAction func openHowToSupport(_ sender: Any?) { @IBAction func openHowToSupport(_ sender: Any?) {
Browser.open("https://github.com/brentsimmons/NetNewsWire/blob/main/Technotes/HowToSupportNetNewsWire.markdown", inBackground: false) Browser.open("https://github.com/brentsimmons/NetNewsWire/blob/main/Technotes/HowToSupportNetNewsWire.markdown", inBackground: false)
@ -751,7 +746,7 @@ internal extension AppDelegate {
let localizedMessageText = NSLocalizedString("Install theme “%@” by %@?", comment: "Theme message text") let localizedMessageText = NSLocalizedString("Install theme “%@” by %@?", comment: "Theme message text")
alert.messageText = NSString.localizedStringWithFormat(localizedMessageText as NSString, theme.name, theme.creatorName) as String alert.messageText = NSString.localizedStringWithFormat(localizedMessageText as NSString, theme.name, theme.creatorName) as String
var attrs = [NSAttributedString.Key : Any]() var attrs = [NSAttributedString.Key: Any]()
attrs[.font] = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize) attrs[.font] = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize)
attrs[.foregroundColor] = NSColor.textColor attrs[.foregroundColor] = NSColor.textColor
@ -811,7 +806,7 @@ internal extension AppDelegate {
} }
} }
} catch { } catch {
NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error" : error, "path": filename]) NotificationCenter.default.post(name: .didFailToImportThemeWithError, object: nil, userInfo: ["error": error, "path": filename])
} }
} }
@ -900,7 +895,7 @@ internal extension AppDelegate {
These would be unnecessary if the similar accessors were marked internal rather than private, These would be unnecessary if the similar accessors were marked internal rather than private,
but for now, we'll keep the stratification of visibility but for now, we'll keep the stratification of visibility
*/ */
extension AppDelegate : ScriptingAppDelegate { extension AppDelegate: ScriptingAppDelegate {
internal var scriptingMainWindowController: ScriptingMainWindowController? { internal var scriptingMainWindowController: ScriptingMainWindowController? {
return mainWindowController return mainWindowController
@ -918,7 +913,7 @@ extension AppDelegate : ScriptingAppDelegate {
extension AppDelegate: NSWindowRestoration { extension AppDelegate: NSWindowRestoration {
@objc static func restoreWindow(withIdentifier identifier: NSUserInterfaceItemIdentifier, state: NSCoder, completionHandler: @escaping (NSWindow?, Error?) -> Void) { @objc static func restoreWindow(withIdentifier identifier: NSUserInterfaceItemIdentifier, state: NSCoder, completionHandler: @escaping (NSWindow?, Error?) -> Void) {
var mainWindow: NSWindow? = nil var mainWindow: NSWindow?
if identifier.rawValue == WindowRestorationIdentifiers.mainWindow { if identifier.rawValue == WindowRestorationIdentifiers.mainWindow {
mainWindow = appDelegate.createAndShowMainWindow().window mainWindow = appDelegate.createAndShowMainWindow().window
} }
@ -932,7 +927,7 @@ extension AppDelegate: NSWindowRestoration {
private extension AppDelegate { private extension AppDelegate {
func handleMarkAsRead(userInfo: [AnyHashable: Any]) { func handleMarkAsRead(userInfo: [AnyHashable: Any]) {
guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [AnyHashable : Any], guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [AnyHashable: Any],
let accountID = articlePathUserInfo[ArticlePathKey.accountID] as? String, let accountID = articlePathUserInfo[ArticlePathKey.accountID] as? String,
let articleID = articlePathUserInfo[ArticlePathKey.articleID] as? String else { let articleID = articlePathUserInfo[ArticlePathKey.articleID] as? String else {
return return
@ -952,7 +947,7 @@ private extension AppDelegate {
} }
func handleMarkAsStarred(userInfo: [AnyHashable: Any]) { func handleMarkAsStarred(userInfo: [AnyHashable: Any]) {
guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [AnyHashable : Any], guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [AnyHashable: Any],
let accountID = articlePathUserInfo[ArticlePathKey.accountID] as? String, let accountID = articlePathUserInfo[ArticlePathKey.accountID] as? String,
let articleID = articlePathUserInfo[ArticlePathKey.articleID] as? String else { let articleID = articlePathUserInfo[ArticlePathKey.articleID] as? String else {
return return

View File

@ -23,7 +23,6 @@ struct Browser {
return nil return nil
} }
/// Opens a URL in the default browser. /// Opens a URL in the default browser.
/// ///
/// - Parameters: /// - Parameters:
@ -34,7 +33,6 @@ struct Browser {
open(urlString, inBackground: invert ? !AppDefaults.shared.openInBrowserInBackground : AppDefaults.shared.openInBrowserInBackground) open(urlString, inBackground: invert ? !AppDefaults.shared.openInBrowserInBackground : AppDefaults.shared.openInBrowserInBackground)
} }
/// Opens a URL in the default browser. /// Opens a URL in the default browser.
/// ///
/// - Parameters: /// - Parameters:
@ -52,7 +50,7 @@ struct Browser {
configuration.activates = false configuration.activates = false
} }
NSWorkspace.shared.open(preparedURL, configuration: configuration) { (runningApplication, error) in NSWorkspace.shared.open(preparedURL, configuration: configuration) { (_, error) in
guard error != nil else { return } guard error != nil else { return }
if let defaultBrowser = defaultBrowser { if let defaultBrowser = defaultBrowser {
defaultBrowser.openURL(url, inBackground: inBackground) defaultBrowser.openURL(url, inBackground: inBackground)

View File

@ -59,4 +59,3 @@ final class CrashReportWindowController: NSWindowController {
close() close()
} }
} }

View File

@ -48,7 +48,7 @@ struct CrashReporter {
let boundary = "0xKhTmLbOuNdArY" let boundary = "0xKhTmLbOuNdArY"
let contentType = "multipart/form-data; boundary=\(boundary)" let contentType = "multipart/form-data; boundary=\(boundary)"
request.setValue(contentType, forHTTPHeaderField:HTTPRequestHeader.contentType) request.setValue(contentType, forHTTPHeaderField: HTTPRequestHeader.contentType)
let formString = "--\(boundary)\r\nContent-Disposition: form-data; name=\"crashlog\"\r\n\r\n\(crashLogText)\r\n--\(boundary)--\r\n" let formString = "--\(boundary)\r\nContent-Disposition: form-data; name=\"crashlog\"\r\n\r\n\(crashLogText)\r\n--\(boundary)--\r\n"
let formData = formString.data(using: .utf8, allowLossyConversion: true) let formData = formString.data(using: .utf8, allowLossyConversion: true)

View File

@ -83,7 +83,7 @@ final class FeedInspectorViewController: NSViewController, Inspector {
self.feed?.isNotifyAboutNewArticles = (self.isNotifyAboutNewArticlesCheckBox?.state ?? .off) == .on ? true : false self.feed?.isNotifyAboutNewArticles = (self.isNotifyAboutNewArticlesCheckBox?.state ?? .off) == .on ? true : false
} }
} else { } else {
UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .sound, .alert]) { (granted, error) in UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .sound, .alert]) { (granted, _) in
self.updateNotificationSettings() self.updateNotificationSettings()
if granted { if granted {
DispatchQueue.main.async { DispatchQueue.main.async {
@ -130,7 +130,6 @@ private extension FeedInspectorViewController {
feed = singleFeed feed = singleFeed
} }
@objc func updateUI() { @objc func updateUI() {
updateImage() updateImage()
updateName() updateName()

View File

@ -19,7 +19,6 @@ protocol Inspector: AnyObject {
typealias InspectorViewController = Inspector & NSViewController typealias InspectorViewController = Inspector & NSViewController
final class InspectorWindowController: NSWindowController { final class InspectorWindowController: NSWindowController {
class var shouldOpenAtStartup: Bool { class var shouldOpenAtStartup: Bool {
@ -28,7 +27,7 @@ final class InspectorWindowController: NSWindowController {
var objects: [Any]? { var objects: [Any]? {
didSet { didSet {
let _ = window _ = window
currentInspector = inspector(for: objects) currentInspector = inspector(for: objects)
} }
} }
@ -67,21 +66,19 @@ final class InspectorWindowController: NSWindowController {
if let savedOrigin = originFromDefaults() { if let savedOrigin = originFromDefaults() {
window?.setFlippedOriginAdjustingForScreen(savedOrigin) window?.setFlippedOriginAdjustingForScreen(savedOrigin)
} } else {
else {
window?.flippedOrigin = NSPoint(x: 256, y: 256) window?.flippedOrigin = NSPoint(x: 256, y: 256)
} }
} }
func inspector(for objects: [Any]?) -> InspectorViewController { func inspector(for objects: [Any]?) -> InspectorViewController {
var fallbackInspector: InspectorViewController? = nil var fallbackInspector: InspectorViewController?
for inspector in inspectors { for inspector in inspectors {
if inspector.isFallbackInspector { if inspector.isFallbackInspector {
fallbackInspector = inspector fallbackInspector = inspector
} } else if let objects = objects, inspector.canInspect(objects) {
else if let objects = objects, inspector.canInspect(objects) {
return inspector return inspector
} }
} }

View File

@ -39,8 +39,7 @@ private extension NothingInspectorViewController {
if let objects = objects, objects.count > 1 { if let objects = objects, objects.count > 1 {
nothingTextField?.isHidden = true nothingTextField?.isHidden = true
multipleTextField?.isHidden = false multipleTextField?.isHidden = false
} } else {
else {
nothingTextField?.isHidden = false nothingTextField?.isHidden = false
multipleTextField?.isHidden = true multipleTextField?.isHidden = true
} }

View File

@ -156,7 +156,7 @@ private extension AddFeedController {
// MARK: Progress // MARK: Progress
func beginShowingProgress() { func beginShowingProgress() {
runIndeterminateProgressWithMessage(NSLocalizedString("Finding feed…", comment:"Feed finder")) runIndeterminateProgressWithMessage(NSLocalizedString("Finding feed…", comment: "Feed finder"))
} }
func endShowingProgress() { func endShowingProgress() {
@ -165,4 +165,3 @@ private extension AddFeedController {
} }
} }

View File

@ -16,8 +16,8 @@ protocol AddFeedWindowControllerDelegate: AnyObject {
func addFeedWindowControllerUserDidCancel(_: AddFeedWindowController) func addFeedWindowControllerUserDidCancel(_: AddFeedWindowController)
} }
//protocol AddFeedWindowController { // protocol AddFeedWindowController {
// //
// var window: NSWindow? { get } // var window: NSWindow? { get }
// func runSheetOnWindow(_ hostWindow: NSWindow) // func runSheetOnWindow(_ hostWindow: NSWindow)
//} // }

View File

@ -12,7 +12,7 @@ import RSTree
import Articles import Articles
import Account import Account
final class AddFeedWindowController : NSWindowController { final class AddFeedWindowController: NSWindowController {
@IBOutlet var urlTextField: NSTextField! @IBOutlet var urlTextField: NSTextField!
@IBOutlet var nameTextField: NSTextField! @IBOutlet var nameTextField: NSTextField!
@ -48,7 +48,7 @@ final class AddFeedWindowController : NSWindowController {
} }
func runSheetOnWindow(_ hostWindow: NSWindow) { func runSheetOnWindow(_ hostWindow: NSWindow) {
hostWindow.beginSheet(window!) { (returnCode: NSApplication.ModalResponse) -> Void in hostWindow.beginSheet(window!) { (_: NSApplication.ModalResponse) in
} }
} }
@ -89,7 +89,7 @@ final class AddFeedWindowController : NSWindowController {
if normalizedURLString.isEmpty { if normalizedURLString.isEmpty {
cancelSheet() cancelSheet()
return; return
} }
guard let url = URL(string: normalizedURLString) else { guard let url = URL(string: normalizedURLString) else {
cancelSheet() cancelSheet()

View File

@ -10,7 +10,7 @@ import AppKit
import Articles import Articles
import Account import Account
class AddFolderWindowController : NSWindowController { class AddFolderWindowController: NSWindowController {
@IBOutlet var folderNameTextField: NSTextField! @IBOutlet var folderNameTextField: NSTextField!
@IBOutlet var accountPopupButton: NSPopUpButton! @IBOutlet var accountPopupButton: NSPopUpButton!
@ -25,7 +25,7 @@ class AddFolderWindowController : NSWindowController {
func runSheetOnWindow(_ w: NSWindow) { func runSheetOnWindow(_ w: NSWindow) {
hostWindow = w hostWindow = w
hostWindow!.beginSheet(window!) { (returnCode: NSApplication.ModalResponse) -> Void in hostWindow!.beginSheet(window!) { (returnCode: NSApplication.ModalResponse) in
if returnCode == NSApplication.ModalResponse.OK { if returnCode == NSApplication.ModalResponse.OK {
self.addFolderIfNeeded() self.addFolderIfNeeded()

View File

@ -38,7 +38,7 @@ final class DetailContainerView: NSView {
override func draw(_ dirtyRect: NSRect) { override func draw(_ dirtyRect: NSRect) {
NSColor.controlBackgroundColor.set() NSColor.controlBackgroundColor.set()
let r = NSIntersectionRect(dirtyRect, bounds) let r = dirtyRect.intersection(bounds)
r.fill() r.fill()
} }
} }

View File

@ -25,8 +25,7 @@ final class DetailStatusBarView: NSView {
if let link = linkForDisplay { if let link = linkForDisplay {
urlLabel.stringValue = link urlLabel.stringValue = link
self.isHidden = false self.isHidden = false
} } else {
else {
urlLabel.stringValue = "" urlLabel.stringValue = ""
self.isHidden = true self.isHidden = true
} }
@ -68,11 +67,8 @@ private extension DetailStatusBarView {
func updateLinkForDisplay() { func updateLinkForDisplay() {
if let mouseoverLink = mouseoverLink, !mouseoverLink.isEmpty { if let mouseoverLink = mouseoverLink, !mouseoverLink.isEmpty {
linkForDisplay = mouseoverLink.strippingHTTPOrHTTPSScheme linkForDisplay = mouseoverLink.strippingHTTPOrHTTPSScheme
} } else {
else {
linkForDisplay = nil linkForDisplay = nil
} }
} }
} }

View File

@ -110,7 +110,7 @@ final class DetailViewController: NSViewController, WKUIDelegate {
// MARK: State Restoration // MARK: State Restoration
func saveState(to state: inout [AnyHashable : Any]) { func saveState(to state: inout [AnyHashable: Any]) {
currentWebViewController.saveState(to: &state) currentWebViewController.saveState(to: &state)
} }
@ -158,7 +158,7 @@ private extension DetailViewController {
} }
} }
@objc func userDefaultsDidChange(_ : Notification) { @objc func userDefaultsDidChange(_: Notification) {
if AppDefaults.shared.isArticleContentJavascriptEnabled != isArticleContentJavascriptEnabled { if AppDefaults.shared.isArticleContentJavascriptEnabled != isArticleContentJavascriptEnabled {
isArticleContentJavascriptEnabled = AppDefaults.shared.isArticleContentJavascriptEnabled isArticleContentJavascriptEnabled = AppDefaults.shared.isArticleContentJavascriptEnabled
createNewWebViewsAndRestoreState() createNewWebViewsAndRestoreState()

View File

@ -58,7 +58,7 @@ final class DetailWebView: WKWebView {
override func setFrameSize(_ newSize: NSSize) { override func setFrameSize(_ newSize: NSSize) {
super.setFrameSize(newSize) super.setFrameSize(newSize)
if (!inLiveResize) { if !inLiveResize {
bigSurOffsetFix() bigSurOffsetFix()
} }
} }
@ -128,4 +128,3 @@ private extension DetailWebView {
return false return false
} }
} }

View File

@ -64,7 +64,7 @@ final class DetailWebViewController: NSViewController {
private var isShowingExtractedArticle: Bool { private var isShowingExtractedArticle: Bool {
switch state { switch state {
case .extracted(_, _, _): case .extracted:
return true return true
default: default:
return false return false
@ -164,7 +164,7 @@ final class DetailWebViewController: NSViewController {
// MARK: State Restoration // MARK: State Restoration
func saveState(to state: inout [AnyHashable : Any]) { func saveState(to state: inout [AnyHashable: Any]) {
state[UserInfoKey.isShowingExtractedArticle] = isShowingExtractedArticle state[UserInfoKey.isShowingExtractedArticle] = isShowingExtractedArticle
state[UserInfoKey.articleWindowScrollY] = windowScrollY state[UserInfoKey.articleWindowScrollY] = windowScrollY
} }
@ -281,7 +281,7 @@ private extension DetailWebViewController {
} }
func reloadHTMLMaintainingScrollPosition() { func reloadHTMLMaintainingScrollPosition() {
fetchScrollInfo() { scrollInfo in fetchScrollInfo { scrollInfo in
self.windowScrollY = scrollInfo?.offsetY self.windowScrollY = scrollInfo?.offsetY
self.reloadHTML() self.reloadHTML()
} }
@ -320,7 +320,7 @@ private extension DetailWebViewController {
func fetchScrollInfo(_ completion: @escaping (ScrollInfo?) -> Void) { func fetchScrollInfo(_ completion: @escaping (ScrollInfo?) -> Void) {
let javascriptString = "var x = {contentHeight: document.body.scrollHeight, offsetY: window.pageYOffset}; x" let javascriptString = "var x = {contentHeight: document.body.scrollHeight, offsetY: window.pageYOffset}; x"
webView.evaluateJavaScript(javascriptString) { (info, error) in webView.evaluateJavaScript(javascriptString) { (info, _) in
guard let info = info as? [String: Any] else { guard let info = info as? [String: Any] else {
completion(nil) completion(nil)
return return

View File

@ -10,7 +10,7 @@ import AppKit
final class IconView: NSView { final class IconView: NSView {
var iconImage: IconImage? = nil { var iconImage: IconImage? {
didSet { didSet {
if iconImage !== oldValue { if iconImage !== oldValue {
imageView.image = iconImage?.image imageView.image = iconImage?.image
@ -76,7 +76,7 @@ final class IconView: NSView {
} }
override func layout() { override func layout() {
resizeSubviews(withOldSize: NSZeroSize) resizeSubviews(withOldSize: NSSize.zero)
} }
override func resizeSubviews(withOldSize oldSize: NSSize) { override func resizeSubviews(withOldSize oldSize: NSSize) {
@ -89,7 +89,7 @@ final class IconView: NSView {
let color = NSApplication.shared.effectiveAppearance.isDarkMode ? IconView.darkBackgroundColor : IconView.lightBackgroundColor let color = NSApplication.shared.effectiveAppearance.isDarkMode ? IconView.darkBackgroundColor : IconView.lightBackgroundColor
color.set() color.set()
let r = NSIntersectionRect(dirtyRect, bounds) let r = dirtyRect.intersection(bounds)
r.fill() r.fill()
} }
} }
@ -104,7 +104,7 @@ private extension IconView {
func rectForImageView() -> NSRect { func rectForImageView() -> NSRect {
guard !(iconImage?.isSymbol ?? false) else { guard !(iconImage?.isSymbol ?? false) else {
return NSMakeRect(0.0, 0.0, bounds.size.width, bounds.size.height) return NSRect(x: 0.0, y: 0.0, width: bounds.size.width, height: bounds.size.height)
} }
guard let image = iconImage?.image else { guard let image = iconImage?.image else {
@ -116,22 +116,21 @@ private extension IconView {
if imageSize.height == imageSize.width { if imageSize.height == imageSize.width {
if imageSize.height >= viewSize.height * 0.75 { if imageSize.height >= viewSize.height * 0.75 {
// Close enough to viewSize to scale up the image. // Close enough to viewSize to scale up the image.
return NSMakeRect(0.0, 0.0, viewSize.width, viewSize.height) return NSRect(x: 0.0, y: 0.0, width: viewSize.width, height: viewSize.height)
} }
let offset = floor((viewSize.height - imageSize.height) / 2.0) let offset = floor((viewSize.height - imageSize.height) / 2.0)
return NSMakeRect(offset, offset, imageSize.width, imageSize.height) return NSRect(x: offset, y: offset, width: imageSize.width, height: imageSize.height)
} } else if imageSize.height > imageSize.width {
else if imageSize.height > imageSize.width {
let factor = viewSize.height / imageSize.height let factor = viewSize.height / imageSize.height
let width = imageSize.width * factor let width = imageSize.width * factor
let originX = floor((viewSize.width - width) / 2.0) let originX = floor((viewSize.width - width) / 2.0)
return NSMakeRect(originX, 0.0, width, viewSize.height) return NSRect(x: originX, y: 0.0, width: width, height: viewSize.height)
} }
// Wider than tall: imageSize.width > imageSize.height // Wider than tall: imageSize.width > imageSize.height
let factor = viewSize.width / imageSize.width let factor = viewSize.width / imageSize.width
let height = imageSize.height * factor let height = imageSize.height * factor
let originY = floor((viewSize.height - height) / 2.0) let originY = floor((viewSize.height - height) / 2.0)
return NSMakeRect(0.0, originY, viewSize.width, height) return NSRect(x: 0.0, y: originY, width: viewSize.width, height: height)
} }
} }

View File

@ -33,4 +33,3 @@ final class MainWindowKeyboardHandler: KeyboardDelegate {
return true return true
} }
} }

View File

@ -16,14 +16,14 @@ enum TimelineSourceMode {
case regular, search case regular, search
} }
class MainWindowController : NSWindowController, NSUserInterfaceValidations { class MainWindowController: NSWindowController, NSUserInterfaceValidations {
@IBOutlet weak var articleThemePopUpButton: NSPopUpButton? @IBOutlet weak var articleThemePopUpButton: NSPopUpButton?
private var activityManager = ActivityManager() private var activityManager = ActivityManager()
private var isShowingExtractedArticle = false private var isShowingExtractedArticle = false
private var articleExtractor: ArticleExtractor? = nil private var articleExtractor: ArticleExtractor?
private var sharingServicePickerDelegate: NSSharingServicePickerDelegate? private var sharingServicePickerDelegate: NSSharingServicePickerDelegate?
private let windowAutosaveName = NSWindow.FrameAutosaveName("MainWindow") private let windowAutosaveName = NSWindow.FrameAutosaveName("MainWindow")
@ -45,17 +45,17 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
private var sidebarViewController: SidebarViewController? private var sidebarViewController: SidebarViewController?
private var timelineContainerViewController: TimelineContainerViewController? private var timelineContainerViewController: TimelineContainerViewController?
private var detailViewController: DetailViewController? private var detailViewController: DetailViewController?
private var currentSearchField: NSSearchField? = nil private var currentSearchField: NSSearchField?
private let articleThemeMenuToolbarItem = NSMenuToolbarItem(itemIdentifier: .articleThemeMenu) private let articleThemeMenuToolbarItem = NSMenuToolbarItem(itemIdentifier: .articleThemeMenu)
private var searchString: String? = nil private var searchString: String?
private var lastSentSearchString: String? = nil private var lastSentSearchString: String?
private var timelineSourceMode: TimelineSourceMode = .regular { private var timelineSourceMode: TimelineSourceMode = .regular {
didSet { didSet {
timelineContainerViewController?.showTimeline(for: timelineSourceMode) timelineContainerViewController?.showTimeline(for: timelineSourceMode)
detailViewController?.showDetail(for: timelineSourceMode) detailViewController?.showDetail(for: timelineSourceMode)
} }
} }
private var searchSmartFeed: SmartFeed? = nil private var searchSmartFeed: SmartFeed?
private var restoreArticleWindowScrollY: CGFloat? private var restoreArticleWindowScrollY: CGFloat?
// MARK: - NSWindowController // MARK: - NSWindowController
@ -115,14 +115,14 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
func handle(_ response: UNNotificationResponse) { func handle(_ response: UNNotificationResponse) {
let userInfo = response.notification.request.content.userInfo let userInfo = response.notification.request.content.userInfo
guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [AnyHashable : Any] else { return } guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [AnyHashable: Any] else { return }
sidebarViewController?.deepLinkRevealAndSelect(for: articlePathUserInfo) sidebarViewController?.deepLinkRevealAndSelect(for: articlePathUserInfo)
currentTimelineViewController?.goToDeepLink(for: articlePathUserInfo) currentTimelineViewController?.goToDeepLink(for: articlePathUserInfo)
} }
func handle(_ activity: NSUserActivity) { func handle(_ activity: NSUserActivity) {
guard let userInfo = activity.userInfo else { return } guard let userInfo = activity.userInfo else { return }
guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [AnyHashable : Any] else { return } guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [AnyHashable: Any] else { return }
sidebarViewController?.deepLinkRevealAndSelect(for: articlePathUserInfo) sidebarViewController?.deepLinkRevealAndSelect(for: articlePathUserInfo)
currentTimelineViewController?.goToDeepLink(for: articlePathUserInfo) currentTimelineViewController?.goToDeepLink(for: articlePathUserInfo)
} }
@ -283,7 +283,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
return return
} }
detailViewController.canScrollUp { (canScroll) in detailViewController.canScrollUp { (canScroll) in
if (canScroll) { if canScroll {
NSCursor.setHiddenUntilMouseMoves(true) NSCursor.setHiddenUntilMouseMoves(true)
detailViewController.scrollPageUp(sender) detailViewController.scrollPageUp(sender)
} }
@ -340,8 +340,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
// TODO: handle search mode // TODO: handle search mode
if timelineViewController.canGoToNextUnread(wrappingToTop: false) { if timelineViewController.canGoToNextUnread(wrappingToTop: false) {
goToNextUnreadInTimeline(wrappingToTop: false) goToNextUnreadInTimeline(wrappingToTop: false)
} } else if sidebarViewController.canGoToNextUnread(wrappingToTop: true) {
else if sidebarViewController.canGoToNextUnread(wrappingToTop: true) {
sidebarViewController.goToNextUnread(wrappingToTop: true) sidebarViewController.goToNextUnread(wrappingToTop: true)
// If we ended up on the same timelineViewController, we may need to wrap // If we ended up on the same timelineViewController, we may need to wrap
@ -414,7 +413,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
} }
@IBAction func markAllAsReadAndGoToNextUnread(_ sender: Any?) { @IBAction func markAllAsReadAndGoToNextUnread(_ sender: Any?) {
currentTimelineViewController?.markAllAsRead() { currentTimelineViewController?.markAllAsRead {
self.nextUnread(sender) self.nextUnread(sender)
} }
} }
@ -528,7 +527,7 @@ extension MainWindowController: NSWindowDelegate {
} }
func window(_ window: NSWindow, didDecodeRestorableState coder: NSCoder) { func window(_ window: NSWindow, didDecodeRestorableState coder: NSCoder) {
guard let state = try? coder.decodeTopLevelObject(forKey: UserInfoKey.windowState) as? [AnyHashable : Any] else { return } guard let state = try? coder.decodeTopLevelObject(forKey: UserInfoKey.windowState) as? [AnyHashable: Any] else { return }
restoreState(from: state) restoreState(from: state)
} }
@ -710,7 +709,7 @@ extension MainWindowController: ArticleExtractorDelegate {
but for now, we'll keep the stratification of visibility but for now, we'll keep the stratification of visibility
*/ */
extension MainWindowController : ScriptingMainWindowController { extension MainWindowController: ScriptingMainWindowController {
internal var scriptingCurrentArticle: Article? { internal var scriptingCurrentArticle: Article? {
return self.oneSelectedArticle return self.oneSelectedArticle
@ -947,8 +946,8 @@ private extension MainWindowController {
// MARK: - State Restoration // MARK: - State Restoration
func savableState() -> [AnyHashable : Any] { func savableState() -> [AnyHashable: Any] {
var state = [AnyHashable : Any]() var state = [AnyHashable: Any]()
state[UserInfoKey.windowFullScreenState] = window?.styleMask.contains(.fullScreen) ?? false state[UserInfoKey.windowFullScreenState] = window?.styleMask.contains(.fullScreen) ?? false
saveSplitViewState(to: &state) saveSplitViewState(to: &state)
sidebarViewController?.saveState(to: &state) sidebarViewController?.saveState(to: &state)
@ -957,7 +956,7 @@ private extension MainWindowController {
return state return state
} }
func restoreState(from state: [AnyHashable : Any]) { func restoreState(from state: [AnyHashable: Any]) {
if let fullScreen = state[UserInfoKey.windowFullScreenState] as? Bool, fullScreen { if let fullScreen = state[UserInfoKey.windowFullScreenState] as? Bool, fullScreen {
window?.toggleFullScreen(self) window?.toggleFullScreen(self)
} }
@ -1235,16 +1234,16 @@ private extension MainWindowController {
} }
} }
func saveSplitViewState(to state: inout [AnyHashable : Any]) { func saveSplitViewState(to state: inout [AnyHashable: Any]) {
guard let splitView = splitViewController?.splitView else { guard let splitView = splitViewController?.splitView else {
return return
} }
let widths = splitView.arrangedSubviews.map{ Int(floor($0.frame.width)) } let widths = splitView.arrangedSubviews.map { Int(floor($0.frame.width)) }
state[MainWindowController.mainWindowWidthsStateKey] = widths state[MainWindowController.mainWindowWidthsStateKey] = widths
} }
func restoreSplitViewState(from state: [AnyHashable : Any]) { func restoreSplitViewState(from state: [AnyHashable: Any]) {
guard let splitView = splitViewController?.splitView, guard let splitView = splitViewController?.splitView,
let widths = state[MainWindowController.mainWindowWidthsStateKey] as? [Int], let widths = state[MainWindowController.mainWindowWidthsStateKey] as? [Int],
widths.count == 3, widths.count == 3,
@ -1322,4 +1321,3 @@ private extension MainWindowController {
} }
} }

View File

@ -141,4 +141,3 @@ extension NNW3Feed: OPMLRepresentable {
return s return s
} }
} }

View File

@ -94,8 +94,7 @@ class ExportOPMLWindowController: NSWindowController {
let opmlString = OPMLExporter.OPMLString(with: account, title: filename) let opmlString = OPMLExporter.OPMLString(with: account, title: filename)
do { do {
try opmlString.write(to: url, atomically: true, encoding: String.Encoding.utf8) try opmlString.write(to: url, atomically: true, encoding: String.Encoding.utf8)
} } catch let error as NSError {
catch let error as NSError {
NSApplication.shared.presentError(error) NSApplication.shared.presentError(error)
} }
} }

View File

@ -105,4 +105,3 @@ class ImportOPMLWindowController: NSWindowController {
} }
} }

View File

@ -11,7 +11,7 @@ import RSCore
import Account import Account
import RSTree import RSTree
class SidebarCell : NSTableCellView { class SidebarCell: NSTableCellView {
var iconImage: IconImage? { var iconImage: IconImage? {
didSet { didSet {
@ -73,7 +73,7 @@ class SidebarCell : NSTableCellView {
}() }()
private let faviconImageView = IconView() private let faviconImageView = IconView()
private let unreadCountView = UnreadCountView(frame: NSZeroRect) private let unreadCountView = UnreadCountView(frame: NSRect.zero)
override var backgroundStyle: NSView.BackgroundStyle { override var backgroundStyle: NSView.BackgroundStyle {
didSet { didSet {
@ -99,7 +99,7 @@ class SidebarCell : NSTableCellView {
if let cellAppearance = cellAppearance { if let cellAppearance = cellAppearance {
titleView.font = cellAppearance.textFieldFont titleView.font = cellAppearance.textFieldFont
} }
resizeSubviews(withOldSize: NSZeroSize) resizeSubviews(withOldSize: NSSize.zero)
} }
override func resizeSubviews(withOldSize oldSize: NSSize) { override func resizeSubviews(withOldSize oldSize: NSSize) {
@ -165,4 +165,3 @@ private extension SidebarCell {
} }
} }

View File

@ -32,4 +32,3 @@ struct SidebarCellAppearance: Equatable {
self.textFieldFont = NSFont.systemFont(ofSize: textFieldFontSize) self.textFieldFont = NSFont.systemFont(ofSize: textFieldFontSize)
} }
} }

View File

@ -32,7 +32,7 @@ struct SidebarCellLayout {
var rTextField = NSRect(x: 0.0, y: 0.0, width: textFieldSize.width, height: textFieldSize.height) var rTextField = NSRect(x: 0.0, y: 0.0, width: textFieldSize.width, height: textFieldSize.height)
if shouldShowImage { if shouldShowImage {
rTextField.origin.x = NSMaxX(rFavicon) + appearance.imageMarginRight rTextField.origin.x = rFavicon.maxX + appearance.imageMarginRight
} }
rTextField = rTextField.centeredVertically(in: bounds) rTextField = rTextField.centeredVertically(in: bounds)
@ -42,17 +42,17 @@ struct SidebarCellLayout {
var rUnread = NSRect.zero var rUnread = NSRect.zero
if !unreadCountIsHidden { if !unreadCountIsHidden {
rUnread.size = unreadCountSize rUnread.size = unreadCountSize
rUnread.origin.x = NSMaxX(bounds) - unreadCountSize.width rUnread.origin.x = bounds.maxX - unreadCountSize.width
rUnread = rUnread.centeredVertically(in: bounds) rUnread = rUnread.centeredVertically(in: bounds)
let textFieldMaxX = NSMinX(rUnread) - appearance.unreadCountMarginLeft let textFieldMaxX = rUnread.minX - appearance.unreadCountMarginLeft
if NSMaxX(rTextField) > textFieldMaxX { if rTextField.maxX > textFieldMaxX {
rTextField.size.width = textFieldMaxX - NSMinX(rTextField) rTextField.size.width = textFieldMaxX - rTextField.minX
} }
} }
self.unreadCountRect = rUnread self.unreadCountRect = rUnread
if NSMaxX(rTextField) > NSMaxX(bounds) { if rTextField.maxX > bounds.maxX {
rTextField.size.width = NSMaxX(bounds) - NSMinX(rTextField) rTextField.size.width = bounds.maxX - rTextField.minX
} }
self.titleRect = rTextField self.titleRect = rTextField
} }

View File

@ -39,4 +39,3 @@ import RSCore
return true return true
} }
} }

View File

@ -60,7 +60,7 @@ struct PasteboardFeed: Hashable {
let feedID = dictionary[Key.feedID] let feedID = dictionary[Key.feedID]
let editedName = dictionary[Key.editedName] let editedName = dictionary[Key.editedName]
var accountType: AccountType? = nil var accountType: AccountType?
if let accountTypeString = dictionary[Key.accountType], let accountTypeInt = Int(accountTypeString) { if let accountTypeString = dictionary[Key.accountType], let accountTypeInt = Int(accountTypeString) {
accountType = AccountType(rawValue: accountTypeInt) accountType = AccountType(rawValue: accountTypeInt)
} }
@ -72,8 +72,7 @@ struct PasteboardFeed: Hashable {
var pasteboardType: NSPasteboard.PasteboardType? var pasteboardType: NSPasteboard.PasteboardType?
if pasteboardItem.types.contains(FeedPasteboardWriter.feedUTIInternalType) { if pasteboardItem.types.contains(FeedPasteboardWriter.feedUTIInternalType) {
pasteboardType = FeedPasteboardWriter.feedUTIInternalType pasteboardType = FeedPasteboardWriter.feedUTIInternalType
} } else if pasteboardItem.types.contains(FeedPasteboardWriter.feedUTIType) {
else if pasteboardItem.types.contains(FeedPasteboardWriter.feedUTIType) {
pasteboardType = FeedPasteboardWriter.feedUTIType pasteboardType = FeedPasteboardWriter.feedUTIType
} }
if let foundType = pasteboardType { if let foundType = pasteboardType {
@ -87,8 +86,7 @@ struct PasteboardFeed: Hashable {
// Check for URL or a string that may be a URL. // Check for URL or a string that may be a URL.
if pasteboardItem.types.contains(.URL) { if pasteboardItem.types.contains(.URL) {
pasteboardType = .URL pasteboardType = .URL
} } else if pasteboardItem.types.contains(.string) {
else if pasteboardItem.types.contains(.string) {
pasteboardType = .string pasteboardType = .string
} }
if let foundType = pasteboardType { if let foundType = pasteboardType {
@ -161,7 +159,6 @@ extension Feed: @retroactive PasteboardWriterOwner {
static let feedUTIInternal = "com.ranchero.NetNewsWire-Evergreen.internal.feed" static let feedUTIInternal = "com.ranchero.NetNewsWire-Evergreen.internal.feed"
static let feedUTIInternalType = NSPasteboard.PasteboardType(rawValue: feedUTIInternal) static let feedUTIInternalType = NSPasteboard.PasteboardType(rawValue: feedUTIInternal)
init(feed: Feed) { init(feed: Feed) {
self.feed = feed self.feed = feed
} }

View File

@ -21,7 +21,6 @@ struct PasteboardFolder: Hashable {
static let accountID = "accountID" static let accountID = "accountID"
} }
let name: String let name: String
let folderID: String? let folderID: String?
let accountID: String? let accountID: String?

View File

@ -16,7 +16,7 @@ import Account
let treeController: TreeController let treeController: TreeController
static let dragOperationNone = NSDragOperation(rawValue: 0) static let dragOperationNone = NSDragOperation(rawValue: 0)
private var draggedNodes: Set<Node>? = nil private var draggedNodes: Set<Node>?
init(treeController: TreeController) { init(treeController: TreeController) {
self.treeController = treeController self.treeController = treeController
@ -159,8 +159,7 @@ private extension SidebarOutlineDataSource {
for feed in draggedFeeds { for feed in draggedFeeds {
if feed.isLocalFeed { if feed.isLocalFeed {
hasLocalFeed = true hasLocalFeed = true
} } else {
else {
hasNonLocalFeed = true hasNonLocalFeed = true
} }
if hasLocalFeed && hasNonLocalFeed { if hasLocalFeed && hasNonLocalFeed {

View File

@ -10,7 +10,7 @@ import AppKit
import RSCore import RSCore
import RSTree import RSTree
class SidebarOutlineView : NSOutlineView { class SidebarOutlineView: NSOutlineView {
@IBOutlet var keyboardDelegate: KeyboardDelegate! @IBOutlet var keyboardDelegate: KeyboardDelegate!

View File

@ -119,7 +119,7 @@ extension SidebarViewController {
NotificationCenter.default.post(Notification(name: .DidUpdateFeedPreferencesFromContextMenu)) NotificationCenter.default.post(Notification(name: .DidUpdateFeedPreferencesFromContextMenu))
} }
} else { } else {
UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .sound, .alert]) { (granted, error) in UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .sound, .alert]) { (granted, _) in
if granted { if granted {
DispatchQueue.main.async { DispatchQueue.main.async {
if feed.isNotifyAboutNewArticles == nil { feed.isNotifyAboutNewArticles = false } if feed.isNotifyAboutNewArticles == nil { feed.isNotifyAboutNewArticles = false }
@ -362,4 +362,3 @@ private extension SidebarViewController {
return articles return articles
} }
} }

View File

@ -94,13 +94,13 @@ protocol SidebarDelegate: AnyObject {
// MARK: State Restoration // MARK: State Restoration
func saveState(to state: inout [AnyHashable : Any]) { func saveState(to state: inout [AnyHashable: Any]) {
state[UserInfoKey.readFeedsFilterState] = isReadFiltered state[UserInfoKey.readFeedsFilterState] = isReadFiltered
state[UserInfoKey.containerExpandedWindowState] = expandedTable.map { $0.userInfo } state[UserInfoKey.containerExpandedWindowState] = expandedTable.map { $0.userInfo }
state[UserInfoKey.selectedFeedsState] = selectedFeeds.compactMap { $0.sidebarItemID?.userInfo } state[UserInfoKey.selectedFeedsState] = selectedFeeds.compactMap { $0.sidebarItemID?.userInfo }
} }
func restoreState(from state: [AnyHashable : Any]) { func restoreState(from state: [AnyHashable: Any]) {
if let containerExpandedWindowState = state[UserInfoKey.containerExpandedWindowState] as? [[AnyHashable: AnyHashable]] { if let containerExpandedWindowState = state[UserInfoKey.containerExpandedWindowState] as? [[AnyHashable: AnyHashable]] {
let containerIdentifiers = containerExpandedWindowState.compactMap( { ContainerIdentifier(userInfo: $0) }) let containerIdentifiers = containerExpandedWindowState.compactMap( { ContainerIdentifier(userInfo: $0) })
@ -354,7 +354,6 @@ protocol SidebarDelegate: AnyObject {
menu.takeItems(from: contextualMenu) menu.takeItems(from: contextualMenu)
} }
// MARK: - NSOutlineViewDelegate // MARK: - NSOutlineViewDelegate
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
@ -421,7 +420,7 @@ protocol SidebarDelegate: AnyObject {
} }
} }
//MARK: - Node Manipulation // MARK: - Node Manipulation
func deleteNodes(_ nodes: [Node]) { func deleteNodes(_ nodes: [Node]) {
let nodesToDelete = treeController.normalizedSelectedNodes(nodes) let nodesToDelete = treeController.normalizedSelectedNodes(nodes)
@ -464,7 +463,7 @@ protocol SidebarDelegate: AnyObject {
revealAndSelectRepresentedObject(feed as AnyObject) revealAndSelectRepresentedObject(feed as AnyObject)
} }
func deepLinkRevealAndSelect(for userInfo: [AnyHashable : Any]) { func deepLinkRevealAndSelect(for userInfo: [AnyHashable: Any]) {
guard let accountNode = findAccountNode(userInfo), guard let accountNode = findAccountNode(userInfo),
let feedNode = findFeedNode(userInfo, beginningAt: accountNode), let feedNode = findFeedNode(userInfo, beginningAt: accountNode),
let feed = feedNode.representedObject as? SidebarItem else { let feed = feedNode.representedObject as? SidebarItem else {
@ -497,7 +496,7 @@ extension SidebarViewController: NSUserInterfaceValidations {
} }
} }
//MARK: - Private // MARK: - Private
private extension SidebarViewController { private extension SidebarViewController {
@ -563,7 +562,6 @@ private extension SidebarViewController {
treeControllerDelegate.addFilterException(folderFeedID) treeControllerDelegate.addFilterException(folderFeedID)
} }
func queueRebuildTreeAndRestoreSelection() { func queueRebuildTreeAndRestoreSelection() {
rebuildTreeAndRestoreSelectionQueue.add(self, #selector(rebuildTreeAndRestoreSelection)) rebuildTreeAndRestoreSelectionQueue.add(self, #selector(rebuildTreeAndRestoreSelection))
} }
@ -720,7 +718,7 @@ private extension SidebarViewController {
return nil return nil
} }
func findAccountNode(_ userInfo: [AnyHashable : Any]?) -> Node? { func findAccountNode(_ userInfo: [AnyHashable: Any]?) -> Node? {
guard let accountID = userInfo?[ArticlePathKey.accountID] as? String else { guard let accountID = userInfo?[ArticlePathKey.accountID] as? String else {
return nil return nil
} }
@ -740,7 +738,7 @@ private extension SidebarViewController {
return nil return nil
} }
func findFeedNode(_ userInfo: [AnyHashable : Any]?, beginningAt startingNode: Node) -> Node? { func findFeedNode(_ userInfo: [AnyHashable: Any]?, beginningAt startingNode: Node) -> Node? {
guard let feedID = userInfo?[ArticlePathKey.feedID] as? String else { guard let feedID = userInfo?[ArticlePathKey.feedID] as? String else {
return nil return nil
} }
@ -819,7 +817,7 @@ private extension SidebarViewController {
} }
func applyToAvailableCells(_ completion: (SidebarCell, Node) -> Void) { func applyToAvailableCells(_ completion: (SidebarCell, Node) -> Void) {
outlineView.enumerateAvailableRowViews { (rowView: NSTableRowView, row: Int) -> Void in outlineView.enumerateAvailableRowViews { (rowView: NSTableRowView, row: Int) in
guard let cell = cellForRowView(rowView), let node = nodeForRow(row) else { guard let cell = cellForRowView(rowView), let node = nodeForRow(row) else {
return return
} }

View File

@ -8,7 +8,7 @@
import AppKit import AppKit
class UnreadCountView : NSView { class UnreadCountView: NSView {
struct Appearance { struct Appearance {
static let padding = NSEdgeInsets(top: 1.0, left: 7.0, bottom: 1.0, right: 7.0) static let padding = NSEdgeInsets(top: 1.0, left: 7.0, bottom: 1.0, right: 7.0)
@ -31,11 +31,11 @@ class UnreadCountView : NSView {
} }
private var intrinsicContentSizeIsValid = false private var intrinsicContentSizeIsValid = false
private var _intrinsicContentSize = NSZeroSize private var _intrinsicContentSize = NSSize.zero
override var intrinsicContentSize: NSSize { override var intrinsicContentSize: NSSize {
if !intrinsicContentSizeIsValid { if !intrinsicContentSizeIsValid {
var size = NSZeroSize var size = NSSize.zero
if unreadCount > 0 { if unreadCount > 0 {
size = textSize() size = textSize()
size.width += (Appearance.padding.left + Appearance.padding.right) size.width += (Appearance.padding.left + Appearance.padding.right)
@ -59,7 +59,7 @@ class UnreadCountView : NSView {
private func textSize() -> NSSize { private func textSize() -> NSSize {
if unreadCount < 1 { if unreadCount < 1 {
return NSZeroSize return NSSize.zero
} }
if let cachedSize = UnreadCountView.textSizeCache[unreadCount] { if let cachedSize = UnreadCountView.textSizeCache[unreadCount] {
@ -76,9 +76,9 @@ class UnreadCountView : NSView {
private func textRect() -> NSRect { private func textRect() -> NSRect {
let size = textSize() let size = textSize()
var r = NSZeroRect var r = NSRect.zero
r.size = size r.size = size
r.origin.x = (NSMaxX(bounds) - Appearance.padding.right) - r.size.width r.origin.x = (bounds.maxX - Appearance.padding.right) - r.size.width
r.origin.y = Appearance.padding.top r.origin.y = Appearance.padding.top
return r return r
} }
@ -93,4 +93,3 @@ class UnreadCountView : NSView {
} }
} }
} }

View File

@ -78,11 +78,9 @@ private extension ArticlePasteboardWriter {
} }
if let text = article.contentText { if let text = article.contentText {
s += "\(text)\n\n" s += "\(text)\n\n"
} } else if let summary = article.summary {
else if let summary = article.summary {
s += "\(summary)\n\n" s += "\(summary)\n\n"
} } else if let html = article.contentHTML {
else if let html = article.contentHTML {
let convertedHTML = html.convertingToPlainText() let convertedHTML = html.convertingToPlainText()
s += "\(convertedHTML)\n\n" s += "\(convertedHTML)\n\n"
} }
@ -184,7 +182,6 @@ private extension ArticlePasteboardWriter {
guard let authors = article.authors, !authors.isEmpty else { guard let authors = article.authors, !authors.isEmpty else {
return nil return nil
} }
return authors.map{ authorDictionary($0) } return authors.map { authorDictionary($0) }
} }
} }

View File

@ -30,7 +30,7 @@ final class MultilineTextFieldSizer {
private let numberOfLines: Int private let numberOfLines: Int
private let font: NSFont private let font: NSFont
private let textField:NSTextField private let textField: NSTextField
private let singleLineHeightEstimate: Int private let singleLineHeightEstimate: Int
private let doubleLineHeightEstimate: Int private let doubleLineHeightEstimate: Int
private var cache = [String: WidthHeightCache]() // Each string has a cache. private var cache = [String: WidthHeightCache]() // Each string has a cache.
@ -227,8 +227,7 @@ private extension MultilineTextFieldSizer {
if oneWidth < width && (oneWidth > smallNeighbor.width || smallNeighbor.width == 0) { if oneWidth < width && (oneWidth > smallNeighbor.width || smallNeighbor.width == 0) {
smallNeighbor = (oneWidth, oneHeight) smallNeighbor = (oneWidth, oneHeight)
} } else if oneWidth > width && (oneWidth < largeNeighbor.width || largeNeighbor.width == 0) {
else if oneWidth > width && (oneWidth < largeNeighbor.width || largeNeighbor.width == 0) {
largeNeighbor = (oneWidth, oneHeight) largeNeighbor = (oneWidth, oneHeight)
} }

View File

@ -62,7 +62,7 @@ struct TimelineCellData {
self.starred = article.status.starred self.starred = article.status.starred
} }
init() { //Empty init() { // Empty
self.title = "" self.title = ""
self.text = "" self.text = ""
self.dateString = "" self.dateString = ""

View File

@ -42,8 +42,7 @@ struct TimelineCellLayout {
if height > 0.1 { if height > 0.1 {
self.height = height self.height = height
} } else {
else {
self.height = [feedNameRect, dateRect, titleRect, summaryRect, textRect, unreadIndicatorRect, iconImageRect].maxY() + paddingBottom self.height = [feedNameRect, dateRect, titleRect, summaryRect, textRect, unreadIndicatorRect, iconImageRect].maxY() + paddingBottom
} }
} }
@ -62,8 +61,7 @@ struct TimelineCellLayout {
var lastTextRect = titleRect var lastTextRect = titleRect
if numberOfLinesForTitle == 0 { if numberOfLinesForTitle == 0 {
lastTextRect = textRect lastTextRect = textRect
} } else if numberOfLinesForTitle < appearance.titleNumberOfLines {
else if numberOfLinesForTitle < appearance.titleNumberOfLines {
if summaryRect.height > 0.1 { if summaryRect.height > 0.1 {
lastTextRect = summaryRect lastTextRect = summaryRect
} }
@ -130,7 +128,7 @@ private extension TimelineCellLayout {
} }
var r = textBoxRect var r = textBoxRect
r.origin.y = NSMaxY(titleRect) r.origin.y = titleRect.maxY
let summaryNumberOfLines = appearance.titleNumberOfLines - titleNumberOfLines let summaryNumberOfLines = appearance.titleNumberOfLines - titleNumberOfLines
let sizeInfo = MultilineTextFieldSizer.size(for: cellData.text, font: appearance.textOnlyFont, numberOfLines: summaryNumberOfLines, width: Int(textBoxRect.width)) let sizeInfo = MultilineTextFieldSizer.size(for: cellData.text, font: appearance.textOnlyFont, numberOfLines: summaryNumberOfLines, width: Int(textBoxRect.width))
@ -161,9 +159,9 @@ private extension TimelineCellLayout {
static func rectForDate(_ textBoxRect: NSRect, _ rectAbove: NSRect, _ appearance: TimelineCellAppearance, _ cellData: TimelineCellData) -> NSRect { static func rectForDate(_ textBoxRect: NSRect, _ rectAbove: NSRect, _ appearance: TimelineCellAppearance, _ cellData: TimelineCellData) -> NSRect {
let textFieldSize = SingleLineTextFieldSizer.size(for: cellData.dateString, font: appearance.dateFont) let textFieldSize = SingleLineTextFieldSizer.size(for: cellData.dateString, font: appearance.dateFont)
var r = NSZeroRect var r = NSRect.zero
r.size = textFieldSize r.size = textFieldSize
r.origin.y = NSMaxY(rectAbove) + appearance.titleBottomMargin r.origin.y = rectAbove.maxY + appearance.titleBottomMargin
r.size.width = textFieldSize.width r.size.width = textFieldSize.width
r.origin.x = textBoxRect.maxX - textFieldSize.width r.origin.x = textBoxRect.maxX - textFieldSize.width
@ -173,11 +171,11 @@ private extension TimelineCellLayout {
static func rectForFeedName(_ textBoxRect: NSRect, _ dateRect: NSRect, _ appearance: TimelineCellAppearance, _ cellData: TimelineCellData) -> NSRect { static func rectForFeedName(_ textBoxRect: NSRect, _ dateRect: NSRect, _ appearance: TimelineCellAppearance, _ cellData: TimelineCellData) -> NSRect {
if cellData.showFeedName == .none { if cellData.showFeedName == .none {
return NSZeroRect return NSRect.zero
} }
let textFieldSize = SingleLineTextFieldSizer.size(for: cellData.feedName, font: appearance.feedNameFont) let textFieldSize = SingleLineTextFieldSizer.size(for: cellData.feedName, font: appearance.feedNameFont)
var r = NSZeroRect var r = NSRect.zero
r.size = textFieldSize r.size = textFieldSize
r.origin.y = dateRect.minY r.origin.y = dateRect.minY
r.origin.x = textBoxRect.origin.x r.origin.x = textBoxRect.origin.x
@ -188,7 +186,7 @@ private extension TimelineCellLayout {
static func rectForUnreadIndicator(_ appearance: TimelineCellAppearance, _ titleRect: NSRect) -> NSRect { static func rectForUnreadIndicator(_ appearance: TimelineCellAppearance, _ titleRect: NSRect) -> NSRect {
var r = NSZeroRect var r = NSRect.zero
r.size = NSSize(width: appearance.unreadCircleDimension, height: appearance.unreadCircleDimension) r.size = NSSize(width: appearance.unreadCircleDimension, height: appearance.unreadCircleDimension)
r.origin.x = appearance.cellPadding.left r.origin.x = appearance.cellPadding.left
r.origin.y = titleRect.minY + 6 r.origin.y = titleRect.minY + 6
@ -237,4 +235,3 @@ private extension Array where Element == NSRect {
return y return y
} }
} }

View File

@ -14,7 +14,7 @@ class TimelineTableCellView: NSTableCellView {
private let titleView = TimelineTableCellView.multiLineTextField() private let titleView = TimelineTableCellView.multiLineTextField()
private let summaryView = TimelineTableCellView.multiLineTextField() private let summaryView = TimelineTableCellView.multiLineTextField()
private let textView = TimelineTableCellView.multiLineTextField() private let textView = TimelineTableCellView.multiLineTextField()
private let unreadIndicatorView = UnreadIndicatorView(frame: NSZeroRect) private let unreadIndicatorView = UnreadIndicatorView(frame: NSRect.zero)
private let dateView = TimelineTableCellView.singleLineTextField() private let dateView = TimelineTableCellView.singleLineTextField()
private let feedNameView = TimelineTableCellView.singleLineTextField() private let feedNameView = TimelineTableCellView.singleLineTextField()
@ -91,7 +91,7 @@ class TimelineTableCellView: NSTableCellView {
override func layout() { override func layout() {
resizeSubviews(withOldSize: NSZeroSize) resizeSubviews(withOldSize: NSSize.zero)
} }
override func resizeSubviews(withOldSize oldSize: NSSize) { override func resizeSubviews(withOldSize oldSize: NSSize) {
@ -149,8 +149,7 @@ private extension TimelineTableCellView {
if Int(floor(rect.height)) == 0 || Int(floor(rect.width)) == 0 { if Int(floor(rect.height)) == 0 || Int(floor(rect.width)) == 0 {
hideView(textField) hideView(textField)
} } else {
else {
showView(textField) showView(textField)
textField.setFrame(ifNotEqualTo: rect) textField.setFrame(ifNotEqualTo: rect)
} }

View File

@ -34,4 +34,3 @@ final class TimelineContainerView: NSView {
} }
} }
} }

View File

@ -107,7 +107,7 @@ final class TimelineContainerViewController: NSViewController {
return false return false
} }
for object in representedObjects { for object in representedObjects {
guard let _ = currentObjects.firstIndex(where: { $0 === object } ) else { guard let _ = currentObjects.firstIndex(where: { $0 === object }) else {
return false return false
} }
} }
@ -125,11 +125,11 @@ final class TimelineContainerViewController: NSViewController {
// MARK: State Restoration // MARK: State Restoration
func saveState(to state: inout [AnyHashable : Any]) { func saveState(to state: inout [AnyHashable: Any]) {
regularTimelineViewController.saveState(to: &state) regularTimelineViewController.saveState(to: &state)
} }
func restoreState(from state: [AnyHashable : Any]) { func restoreState(from state: [AnyHashable: Any]) {
regularTimelineViewController.restoreState(from: state) regularTimelineViewController.restoreState(from: state)
updateReadFilterButton() updateReadFilterButton()
} }
@ -170,8 +170,7 @@ private extension TimelineContainerViewController {
func mode(for timelineViewController: TimelineViewController) -> TimelineSourceMode { func mode(for timelineViewController: TimelineViewController) -> TimelineSourceMode {
if timelineViewController === regularTimelineViewController { if timelineViewController === regularTimelineViewController {
return .regular return .regular
} } else if timelineViewController === searchTimelineViewController {
else if timelineViewController === searchTimelineViewController {
return .search return .search
} }
assertionFailure("Expected timelineViewController to match either regular or search timelineViewController, but it doesnt.") assertionFailure("Expected timelineViewController to match either regular or search timelineViewController, but it doesnt.")

View File

@ -8,7 +8,7 @@
import AppKit import AppKit
class TimelineTableRowView : NSTableRowView { class TimelineTableRowView: NSTableRowView {
private var separator: NSView? private var separator: NSView?

View File

@ -106,7 +106,6 @@ extension TimelineViewController {
} }
} }
private extension TimelineViewController { private extension TimelineViewController {
func markArticles(_ articles: [Article], read: Bool) { func markArticles(_ articles: [Article], read: Bool) {
@ -282,7 +281,6 @@ private extension TimelineViewController {
return menuItem(NSLocalizedString("Copy External URL", comment: "Command"), #selector(copyURLFromContextualMenu(_:)), urlString) return menuItem(NSLocalizedString("Copy External URL", comment: "Command"), #selector(copyURLFromContextualMenu(_:)), urlString)
} }
func menuItem(_ title: String, _ action: Selector, _ representedObject: Any) -> NSMenuItem { func menuItem(_ title: String, _ action: Selector, _ representedObject: Any) -> NSMenuItem {
let item = NSMenuItem(title: title, action: action, keyEquivalent: "") let item = NSMenuItem(title: title, action: action, keyEquivalent: "")

View File

@ -268,7 +268,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
// MARK: State Restoration // MARK: State Restoration
func saveState(to state: inout [AnyHashable : Any]) { func saveState(to state: inout [AnyHashable: Any]) {
state[UserInfoKey.readArticlesFilterStateKeys] = readFilterEnabledTable.keys.compactMap { $0.userInfo } state[UserInfoKey.readArticlesFilterStateKeys] = readFilterEnabledTable.keys.compactMap { $0.userInfo }
state[UserInfoKey.readArticlesFilterStateValues] = readFilterEnabledTable.values.compactMap( { $0 }) state[UserInfoKey.readArticlesFilterStateValues] = readFilterEnabledTable.values.compactMap( { $0 })
@ -277,7 +277,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
} }
} }
func restoreState(from state: [AnyHashable : Any]) { func restoreState(from state: [AnyHashable: Any]) {
guard let readArticlesFilterStateKeys = state[UserInfoKey.readArticlesFilterStateKeys] as? [[AnyHashable: AnyHashable]], guard let readArticlesFilterStateKeys = state[UserInfoKey.readArticlesFilterStateKeys] as? [[AnyHashable: AnyHashable]],
let readArticlesFilterStateValues = state[UserInfoKey.readArticlesFilterStateValues] as? [Bool] else { let readArticlesFilterStateValues = state[UserInfoKey.readArticlesFilterStateValues] as? [Bool] else {
return return
@ -289,7 +289,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
} }
} }
if let articlePathUserInfo = state[UserInfoKey.articlePath] as? [AnyHashable : Any], if let articlePathUserInfo = state[UserInfoKey.articlePath] as? [AnyHashable: Any],
let accountID = articlePathUserInfo[ArticlePathKey.accountID] as? String, let accountID = articlePathUserInfo[ArticlePathKey.accountID] as? String,
let account = AccountManager.shared.existingAccount(with: accountID), let account = AccountManager.shared.existingAccount(with: accountID),
let articleID = articlePathUserInfo[ArticlePathKey.articleID] as? String { let articleID = articlePathUserInfo[ArticlePathKey.articleID] as? String {
@ -331,8 +331,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
if markAsRead { if markAsRead {
markSelectedArticlesAsRead(sender) markSelectedArticlesAsRead(sender)
} } else {
else {
markSelectedArticlesAsUnread(sender) markSelectedArticlesAsUnread(sender)
} }
} }
@ -485,12 +484,11 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
func markOlderArticlesRead(_ selectedArticles: [Article]) { func markOlderArticlesRead(_ selectedArticles: [Article]) {
// Mark articles older than the selectedArticles(s) as read. // Mark articles older than the selectedArticles(s) as read.
var cutoffDate: Date? = nil var cutoffDate: Date?
for article in selectedArticles { for article in selectedArticles {
if cutoffDate == nil { if cutoffDate == nil {
cutoffDate = article.logicalDatePublished cutoffDate = article.logicalDatePublished
} } else if cutoffDate! > article.logicalDatePublished {
else if cutoffDate! > article.logicalDatePublished {
cutoffDate = article.logicalDatePublished cutoffDate = article.logicalDatePublished
} }
} }
@ -531,7 +529,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
// MARK: - Navigation // MARK: - Navigation
func goToDeepLink(for userInfo: [AnyHashable : Any]) { func goToDeepLink(for userInfo: [AnyHashable: Any]) {
guard let articleID = userInfo[ArticlePathKey.articleID] as? String else { return } guard let articleID = userInfo[ArticlePathKey.articleID] as? String else { return }
if isReadFiltered ?? false { if isReadFiltered ?? false {
@ -833,8 +831,7 @@ extension TimelineViewController: NSTableViewDelegate {
cell.cellAppearance = showIcons ? cellAppearanceWithIcon : cellAppearance cell.cellAppearance = showIcons ? cellAppearanceWithIcon : cellAppearance
if let article = articles.articleAtRow(row) { if let article = articles.articleAtRow(row) {
configureTimelineCell(cell, article: article) configureTimelineCell(cell, article: article)
} } else {
else {
makeTimelineCellEmpty(cell) makeTimelineCellEmpty(cell)
} }
} }
@ -917,16 +914,16 @@ extension TimelineViewController: NSTableViewDelegate {
switch edge { switch edge {
case .leading: case .leading:
let action = NSTableViewRowAction(style: .regular, title: article.status.read ? "Unread" : "Read") { (action, row) in let action = NSTableViewRowAction(style: .regular, title: article.status.read ? "Unread" : "Read") { (_, _) in
self.toggleArticleRead(article); self.toggleArticleRead(article)
tableView.rowActionsVisible = false tableView.rowActionsVisible = false
} }
action.image = article.status.read ? AppAssets.swipeMarkUnreadImage : AppAssets.swipeMarkReadImage action.image = article.status.read ? AppAssets.swipeMarkUnreadImage : AppAssets.swipeMarkReadImage
return [action] return [action]
case .trailing: case .trailing:
let action = NSTableViewRowAction(style: .regular, title: article.status.starred ? "Unstar" : "Star") { (action, row) in let action = NSTableViewRowAction(style: .regular, title: article.status.starred ? "Unstar" : "Star") { (_, _) in
self.toggleArticleStarred(article); self.toggleArticleStarred(article)
tableView.rowActionsVisible = false tableView.rowActionsVisible = false
} }
action.backgroundColor = AppAssets.starColor action.backgroundColor = AppAssets.starColor
@ -1141,7 +1138,7 @@ private extension TimelineViewController {
func fetchUnsortedArticlesSync(for representedObjects: [Any]) -> Set<Article> { func fetchUnsortedArticlesSync(for representedObjects: [Any]) -> Set<Article> {
cancelPendingAsyncFetches() cancelPendingAsyncFetches()
let fetchers = representedObjects.compactMap{ $0 as? ArticleFetcher } let fetchers = representedObjects.compactMap { $0 as? ArticleFetcher }
if fetchers.isEmpty { if fetchers.isEmpty {
return Set<Article>() return Set<Article>()
} }
@ -1236,8 +1233,7 @@ private extension TimelineViewController {
return true return true
} }
} }
} } else if let folder = representedObject as? Folder {
else if let folder = representedObject as? Folder {
for oneFeed in feeds { for oneFeed in feeds {
if folder.hasFeed(with: oneFeed.feedID) || folder.hasFeed(withURL: oneFeed.url) { if folder.hasFeed(with: oneFeed.feedID) || folder.hasFeed(withURL: oneFeed.url) {
return true return true

View File

@ -53,7 +53,7 @@ class AccountsAddCloudKitWindowController: NSWindowController {
return return
} }
let _ = AccountManager.shared.createAccount(type: .cloudKit) _ = AccountManager.shared.createAccount(type: .cloudKit)
hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.OK) hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.OK)
} }

View File

@ -88,7 +88,6 @@ final class AccountsDetailViewController: NSViewController, NSTextFieldDelegate
accountsReaderAPIWindowController.account = account accountsReaderAPIWindowController.account = account
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!) accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
accountsWindowController = accountsReaderAPIWindowController accountsWindowController = accountsReaderAPIWindowController
break
case .newsBlur: case .newsBlur:
let accountsNewsBlurWindowController = AccountsNewsBlurWindowController() let accountsNewsBlurWindowController = AccountsNewsBlurWindowController()
accountsNewsBlurWindowController.account = account accountsNewsBlurWindowController.account = account

View File

@ -103,7 +103,7 @@ class AccountsFeedbinWindowController: NSWindowController {
try self.account?.removeCredentials(type: .basic) try self.account?.removeCredentials(type: .basic)
try self.account?.storeCredentials(validatedCredentials) try self.account?.storeCredentials(validatedCredentials)
self.account?.refreshAll() { result in self.account?.refreshAll { result in
switch result { switch result {
case .success: case .success:
break break

View File

@ -101,7 +101,7 @@ class AccountsNewsBlurWindowController: NSWindowController {
try self.account?.storeCredentials(credentials) try self.account?.storeCredentials(credentials)
try self.account?.storeCredentials(validatedCredentials) try self.account?.storeCredentials(validatedCredentials)
self.account?.refreshAll() { result in self.account?.refreshAll { result in
switch result { switch result {
case .success: case .success:
break break

View File

@ -40,7 +40,6 @@ final class AccountsPreferencesViewController: NSViewController {
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .UserDidAddAccount, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .UserDidAddAccount, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .UserDidDeleteAccount, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .UserDidDeleteAccount, object: nil)
// Fix tableView frame  for some reason IB wants it 1pt wider than the clip view. This leads to unwanted horizontal scrolling. // Fix tableView frame  for some reason IB wants it 1pt wider than the clip view. This leads to unwanted horizontal scrolling.
var rTable = tableView.frame var rTable = tableView.frame
rTable.size.width = tableView.superview!.frame.size.width rTable.size.width = tableView.superview!.frame.size.width

View File

@ -158,7 +158,7 @@ class AccountsReaderAPIWindowController: NSWindowController {
try self.account?.storeCredentials(credentials) try self.account?.storeCredentials(credentials)
try self.account?.storeCredentials(validatedCredentials) try self.account?.storeCredentials(validatedCredentials)
self.account?.refreshAll() { result in self.account?.refreshAll { result in
switch result { switch result {
case .success: case .success:
break break

View File

@ -69,9 +69,6 @@ enum AddAccountSections: Int, CaseIterable {
} }
} }
} }
struct AddAccountsView: View { struct AddAccountsView: View {
@ -270,10 +267,8 @@ struct AddAccountsView: View {
} }
struct AddAccountsView_Previews: PreviewProvider { struct AddAccountsView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
AddAccountsView(delegate: nil) AddAccountsView(delegate: nil)
} }
} }

View File

@ -53,8 +53,7 @@ private extension AdvancedPreferencesViewController {
func updateUI() { func updateUI() {
if wantsTestBuilds { if wantsTestBuilds {
testBuildsButton.state = .on testBuildsButton.state = .on
} } else {
else {
releaseBuildsButton.state = .on releaseBuildsButton.state = .on
} }
} }

View File

@ -29,7 +29,7 @@ final class PreferencesControlsBackgroundView: NSView {
let fillColor = self.effectiveAppearance.isDarkMode ? darkModeFillColor : lightModeFillColor let fillColor = self.effectiveAppearance.isDarkMode ? darkModeFillColor : lightModeFillColor
fillColor.setFill() fillColor.setFill()
let r = NSIntersectionRect(dirtyRect, bounds) let r = dirtyRect.intersection(bounds)
r.fill() r.fill()
let borderColor = self.effectiveAppearance.isDarkMode ? darkModeBorderColor : lightModeBorderColor let borderColor = self.effectiveAppearance.isDarkMode ? darkModeBorderColor : lightModeBorderColor

View File

@ -17,7 +17,7 @@ final class PreferencesTableViewBackgroundView: NSView {
let color = self.effectiveAppearance.isDarkMode ? darkBorderColor : lightBorderColor let color = self.effectiveAppearance.isDarkMode ? darkBorderColor : lightBorderColor
color.setFill() color.setFill()
let r = NSIntersectionRect(dirtyRect, bounds) let r = dirtyRect.intersection(bounds)
r.fill() r.fill()
} }
} }

View File

@ -27,7 +27,7 @@ private struct ToolbarItemIdentifier {
static let Advanced = "Advanced" static let Advanced = "Advanced"
} }
class PreferencesWindowController : NSWindowController, NSToolbarDelegate { class PreferencesWindowController: NSWindowController, NSToolbarDelegate {
private let windowWidth = CGFloat(512.0) // Width is constant for all views; only the height changes private let windowWidth = CGFloat(512.0) // Width is constant for all views; only the height changes
private var viewControllers = [String: NSViewController]() private var viewControllers = [String: NSViewController]()
@ -140,8 +140,7 @@ private extension PreferencesWindowController {
if let currentView = currentView { if let currentView = currentView {
window!.contentView?.replaceSubview(currentView, with: newViewController.view) window!.contentView?.replaceSubview(currentView, with: newViewController.view)
} } else {
else {
window!.contentView?.addSubview(newViewController.view) window!.contentView?.addSubview(newViewController.view)
} }
@ -168,17 +167,17 @@ private extension PreferencesWindowController {
let windowFrame = window!.frame let windowFrame = window!.frame
let contentViewFrame = window!.contentView!.frame let contentViewFrame = window!.contentView!.frame
let deltaHeight = NSHeight(contentViewFrame) - NSHeight(viewFrame) let deltaHeight = contentViewFrame.height - viewFrame.height
let heightForWindow = NSHeight(windowFrame) - deltaHeight let heightForWindow = windowFrame.height - deltaHeight
let windowOriginY = NSMinY(windowFrame) + deltaHeight let windowOriginY = windowFrame.minY + deltaHeight
var updatedWindowFrame = windowFrame var updatedWindowFrame = windowFrame
updatedWindowFrame.size.height = heightForWindow updatedWindowFrame.size.height = heightForWindow
updatedWindowFrame.origin.y = windowOriginY updatedWindowFrame.origin.y = windowOriginY
updatedWindowFrame.size.width = windowWidth //NSWidth(viewFrame) updatedWindowFrame.size.width = windowWidth // NSWidth(viewFrame)
var updatedViewFrame = viewFrame var updatedViewFrame = viewFrame
updatedViewFrame.origin = NSZeroPoint updatedViewFrame.origin = NSPoint.zero
updatedViewFrame.size.width = windowWidth updatedViewFrame.size.width = windowWidth
if viewFrame != updatedViewFrame { if viewFrame != updatedViewFrame {
view.frame = updatedViewFrame view.frame = updatedViewFrame

View File

@ -29,7 +29,7 @@ class SafariExtensionHandler: SFSafariExtensionHandler {
} }
// Maps from UUID to a validation wrapper // Maps from UUID to a validation wrapper
static var gPingPongMap = Dictionary<String, ValidationWrapper>() static var gPingPongMap = [String: ValidationWrapper]()
static var validationQueue = DispatchQueue(label: "Toolbar Validation") static var validationQueue = DispatchQueue(label: "Toolbar Validation")
// Bottleneck for calling through to a validation handler we have saved, and removing it from the list. // Bottleneck for calling through to a validation handler we have saved, and removing it from the list.
@ -40,8 +40,8 @@ class SafariExtensionHandler: SFSafariExtensionHandler {
} }
} }
override func messageReceived(withName messageName: String, from page: SFSafariPage, userInfo: [String : Any]?) { override func messageReceived(withName messageName: String, from page: SFSafariPage, userInfo: [String: Any]?) {
if (messageName == "subscribeToFeed") { if messageName == "subscribeToFeed" {
if var feedURLString = userInfo?["url"] as? String { if var feedURLString = userInfo?["url"] as? String {
var openInDefaultBrowser = false var openInDefaultBrowser = false
@ -61,12 +61,11 @@ class SafariExtensionHandler: SFSafariExtensionHandler {
NSWorkspace.shared.open(feedURL) NSWorkspace.shared.open(feedURL)
} }
} }
} } else if messageName == "pong" {
else if (messageName == "pong") {
if let validationIDString = userInfo?["validationID"] as? String { if let validationIDString = userInfo?["validationID"] as? String {
// Should we validate the button? // Should we validate the button?
let shouldValidate = userInfo?["shouldValidate"] as? Bool ?? false let shouldValidate = userInfo?["shouldValidate"] as? Bool ?? false
SafariExtensionHandler.callValidationHandler(forHandlerID: validationIDString, withShouldValidate:shouldValidate) SafariExtensionHandler.callValidationHandler(forHandlerID: validationIDString, withShouldValidate: shouldValidate)
} }
} }
} }
@ -97,7 +96,7 @@ class SafariExtensionHandler: SFSafariExtensionHandler {
if thisValidationID != uniqueValidationID { if thisValidationID != uniqueValidationID {
// Default to valid ... we'll know soon enough whether the latest state // Default to valid ... we'll know soon enough whether the latest state
// is actually still valid or not... // is actually still valid or not...
SafariExtensionHandler.callValidationHandler(forHandlerID: thisValidationID, withShouldValidate: true); SafariExtensionHandler.callValidationHandler(forHandlerID: thisValidationID, withShouldValidate: true)
} }
} }
@ -108,13 +107,13 @@ class SafariExtensionHandler: SFSafariExtensionHandler {
// a timeout period has elapsed // a timeout period has elapsed
window.getActiveTab { (activeTab) in window.getActiveTab { (activeTab) in
guard let activeTab = activeTab else { guard let activeTab = activeTab else {
SafariExtensionHandler.callValidationHandler(forHandlerID: uniqueValidationID, withShouldValidate:false); SafariExtensionHandler.callValidationHandler(forHandlerID: uniqueValidationID, withShouldValidate: false)
return return
} }
activeTab.getActivePage { (activePage) in activeTab.getActivePage { (activePage) in
guard let activePage = activePage else { guard let activePage = activePage else {
SafariExtensionHandler.callValidationHandler(forHandlerID: uniqueValidationID, withShouldValidate:false); SafariExtensionHandler.callValidationHandler(forHandlerID: uniqueValidationID, withShouldValidate: false)
return return
} }
@ -127,7 +126,7 @@ class SafariExtensionHandler: SFSafariExtensionHandler {
let pongTimeoutInNanoseconds = Int(Double(NSEC_PER_SEC) * 0.5) let pongTimeoutInNanoseconds = Int(Double(NSEC_PER_SEC) * 0.5)
let timeoutDeadline = DispatchTime.now() + DispatchTimeInterval.nanoseconds(pongTimeoutInNanoseconds) let timeoutDeadline = DispatchTime.now() + DispatchTimeInterval.nanoseconds(pongTimeoutInNanoseconds)
DispatchQueue.main.asyncAfter(deadline: timeoutDeadline, execute: { [timedOutValidationID = uniqueValidationID] in DispatchQueue.main.asyncAfter(deadline: timeoutDeadline, execute: { [timedOutValidationID = uniqueValidationID] in
SafariExtensionHandler.callValidationHandler(forHandlerID: timedOutValidationID, withShouldValidate:false) SafariExtensionHandler.callValidationHandler(forHandlerID: timedOutValidationID, withShouldValidate: false)
}) })
} }
} }

View File

@ -13,7 +13,7 @@ class SafariExtensionViewController: SFSafariExtensionViewController {
// This would be the place to handle a popover that could, for example, list the possibly multiple feeds offered by a site. // This would be the place to handle a popover that could, for example, list the possibly multiple feeds offered by a site.
static let shared: SafariExtensionViewController = { static let shared: SafariExtensionViewController = {
let shared = SafariExtensionViewController() let shared = SafariExtensionViewController()
shared.preferredContentSize = NSSize(width:320, height:240) shared.preferredContentSize = NSSize(width: 320, height: 240)
return shared return shared
}() }()

View File

@ -14,15 +14,15 @@ import RSCore
@objc(ScriptableAccount) @objc(ScriptableAccount)
class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectContainer { class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectContainer {
let account:Account let account: Account
init (_ account:Account) { init (_ account: Account) {
self.account = account self.account = account
} }
@objc(objectSpecifier) @objc(objectSpecifier)
override var objectSpecifier: NSScriptObjectSpecifier? { override var objectSpecifier: NSScriptObjectSpecifier? {
let myContainer = NSApplication.shared let myContainer = NSApplication.shared
let scriptObjectSpecifier = myContainer.makeFormUniqueIDScriptObjectSpecifier(forObject:self) let scriptObjectSpecifier = myContainer.makeFormUniqueIDScriptObjectSpecifier(forObject: self)
return (scriptObjectSpecifier) return (scriptObjectSpecifier)
} }
@ -57,7 +57,7 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
// I am not sure if account should prefer to be specified by name or by ID // I am not sure if account should prefer to be specified by name or by ID
// but in either case it seems like the accountID would be used as the keydata, so I chose ID // but in either case it seems like the accountID would be used as the keydata, so I chose ID
@objc(uniqueId) @objc(uniqueId)
var scriptingUniqueId:Any { var scriptingUniqueId: Any {
return account.accountID return account.accountID
} }
@ -67,65 +67,65 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
return self.classDescription as! NSScriptClassDescription return self.classDescription as! NSScriptClassDescription
} }
func deleteElement(_ element:ScriptingObject) { func deleteElement(_ element: ScriptingObject) {
if let scriptableFolder = element as? ScriptableFolder { if let scriptableFolder = element as? ScriptableFolder {
BatchUpdate.shared.perform { BatchUpdate.shared.perform {
account.removeFolder(scriptableFolder.folder) { result in account.removeFolder(scriptableFolder.folder) { _ in
} }
} }
} else if let scriptableFeed = element as? ScriptableFeed { } else if let scriptableFeed = element as? ScriptableFeed {
BatchUpdate.shared.perform { BatchUpdate.shared.perform {
var container: Container? = nil var container: Container?
if let scriptableFolder = scriptableFeed.container as? ScriptableFolder { if let scriptableFolder = scriptableFeed.container as? ScriptableFolder {
container = scriptableFolder.folder container = scriptableFolder.folder
} else { } else {
container = account container = account
} }
account.removeFeed(scriptableFeed.feed, from: container!) { result in account.removeFeed(scriptableFeed.feed, from: container!) { _ in
} }
} }
} }
} }
@objc(isLocationRequiredToCreateForKey:) @objc(isLocationRequiredToCreateForKey:)
func isLocationRequiredToCreate(forKey key:String) -> Bool { func isLocationRequiredToCreate(forKey key: String) -> Bool {
return false; return false
} }
// MARK: --- Scriptable elements --- // MARK: --- Scriptable elements ---
@objc(feeds) @objc(feeds)
var feeds:NSArray { var feeds: NSArray {
return account.topLevelFeeds.map { ScriptableFeed($0, container:self) } as NSArray return account.topLevelFeeds.map { ScriptableFeed($0, container: self) } as NSArray
} }
@objc(valueInFeedsWithUniqueID:) @objc(valueInFeedsWithUniqueID:)
func valueInFeeds(withUniqueID id:String) -> ScriptableFeed? { func valueInFeeds(withUniqueID id: String) -> ScriptableFeed? {
guard let feed = account.existingFeed(withFeedID: id) else { return nil } guard let feed = account.existingFeed(withFeedID: id) else { return nil }
return ScriptableFeed(feed, container:self) return ScriptableFeed(feed, container: self)
} }
@objc(valueInFeedsWithName:) @objc(valueInFeedsWithName:)
func valueInFeeds(withName name:String) -> ScriptableFeed? { func valueInFeeds(withName name: String) -> ScriptableFeed? {
let feeds = Array(account.flattenedFeeds()) let feeds = Array(account.flattenedFeeds())
guard let feed = feeds.first(where:{$0.name == name}) else { return nil } guard let feed = feeds.first(where: {$0.name == name}) else { return nil }
return ScriptableFeed(feed, container:self) return ScriptableFeed(feed, container: self)
} }
@objc(folders) @objc(folders)
var folders:NSArray { var folders: NSArray {
let foldersSet = account.folders ?? Set<Folder>() let foldersSet = account.folders ?? Set<Folder>()
let folders = Array(foldersSet) let folders = Array(foldersSet)
return folders.map { ScriptableFolder($0, container:self) } as NSArray return folders.map { ScriptableFolder($0, container: self) } as NSArray
} }
@objc(valueInFoldersWithUniqueID:) @objc(valueInFoldersWithUniqueID:)
func valueInFolders(withUniqueID id:NSNumber) -> ScriptableFolder? { func valueInFolders(withUniqueID id: NSNumber) -> ScriptableFolder? {
let folderId = id.intValue let folderId = id.intValue
let foldersSet = account.folders ?? Set<Folder>() let foldersSet = account.folders ?? Set<Folder>()
let folders = Array(foldersSet) let folders = Array(foldersSet)
guard let folder = folders.first(where:{$0.folderID == folderId}) else { return nil } guard let folder = folders.first(where: {$0.folderID == folderId}) else { return nil }
return ScriptableFolder(folder, container:self) return ScriptableFolder(folder, container: self)
} }
// MARK: --- Scriptable properties --- // MARK: --- Scriptable properties ---
@ -148,13 +148,13 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
} }
@objc(opmlRepresentation) @objc(opmlRepresentation)
var opmlRepresentation:String { var opmlRepresentation: String {
return self.account.OPMLString(indentLevel:0) return self.account.OPMLString(indentLevel: 0)
} }
@objc(accountType) @objc(accountType)
var accountType:OSType { var accountType: OSType {
var osType:String = "" var osType: String = ""
switch self.account.type { switch self.account.type {
case .onMyMac: case .onMyMac:
osType = "Locl" osType = "Locl"

View File

@ -27,10 +27,10 @@ protocol AppDelegateAppleEvents {
protocol ScriptingAppDelegate { protocol ScriptingAppDelegate {
var scriptingCurrentArticle: Article? {get} var scriptingCurrentArticle: Article? {get}
var scriptingSelectedArticles: [Article] {get} var scriptingSelectedArticles: [Article] {get}
var scriptingMainWindowController:ScriptingMainWindowController? {get} var scriptingMainWindowController: ScriptingMainWindowController? {get}
} }
extension AppDelegate : AppDelegateAppleEvents { extension AppDelegate: AppDelegateAppleEvents {
// MARK: GetURL Apple Event // MARK: GetURL Apple Event
@ -54,7 +54,7 @@ extension AppDelegate : AppDelegateAppleEvents {
if let themeURL = URL(string: themeURLString) { if let themeURL = URL(string: themeURLString) {
let request = URLRequest(url: themeURL) let request = URLRequest(url: themeURL)
let task = URLSession.shared.downloadTask(with: request) { location, response, error in let task = URLSession.shared.downloadTask(with: request) { location, _, error in
guard let location = location else { guard let location = location else {
return return
} }
@ -71,7 +71,6 @@ extension AppDelegate : AppDelegateAppleEvents {
} }
// Special case URL with specific scheme handler x-netnewswire-feed: intended to ensure we open // Special case URL with specific scheme handler x-netnewswire-feed: intended to ensure we open
// it regardless of which news reader may be set as the default // it regardless of which news reader may be set as the default
let nnwScheme = "x-netnewswire-feed:" let nnwScheme = "x-netnewswire-feed:"
@ -91,13 +90,13 @@ extension AppDelegate : AppDelegateAppleEvents {
} }
} }
class NetNewsWireCreateElementCommand : NSCreateCommand { class NetNewsWireCreateElementCommand: NSCreateCommand {
override func performDefaultImplementation() -> Any? { override func performDefaultImplementation() -> Any? {
let classDescription = self.createClassDescription let classDescription = self.createClassDescription
if (classDescription.className == "feed") { if classDescription.className == "feed" {
return ScriptableFeed.handleCreateElement(command:self) return ScriptableFeed.handleCreateElement(command: self)
} else if (classDescription.className == "folder") { } else if classDescription.className == "folder" {
return ScriptableFolder.handleCreateElement(command:self) return ScriptableFolder.handleCreateElement(command: self)
} }
return nil return nil
} }
@ -111,7 +110,7 @@ class NetNewsWireCreateElementCommand : NSCreateCommand {
is ambiguity about whether specifiers are lists or single objects, the code switches is ambiguity about whether specifiers are lists or single objects, the code switches
based on which it is. based on which it is.
*/ */
class NetNewsWireDeleteCommand : NSDeleteCommand { class NetNewsWireDeleteCommand: NSDeleteCommand {
/* /*
delete(objectToDelete:, from container:) delete(objectToDelete:, from container:)
@ -119,10 +118,10 @@ class NetNewsWireDeleteCommand : NSDeleteCommand {
Here the code unravels the case of objectToDelete being a list or a single object, Here the code unravels the case of objectToDelete being a list or a single object,
ultimately calling container.deleteElement(element) for each element to delete ultimately calling container.deleteElement(element) for each element to delete
*/ */
func delete(objectToDelete:Any, from container:ScriptingObjectContainer) { func delete(objectToDelete: Any, from container: ScriptingObjectContainer) {
if let objectList = objectToDelete as? [Any] { if let objectList = objectToDelete as? [Any] {
for nthObject in objectList { for nthObject in objectList {
self.delete(objectToDelete:nthObject, from:container) self.delete(objectToDelete: nthObject, from: container)
} }
} else if let element = objectToDelete as? ScriptingObject { } else if let element = objectToDelete as? ScriptingObject {
container.deleteElement(element) container.deleteElement(element)
@ -138,14 +137,14 @@ class NetNewsWireDeleteCommand : NSDeleteCommand {
After resolving, we call delete(objectToDelete:, from container:) with the container and After resolving, we call delete(objectToDelete:, from container:) with the container and
the resolved objects the resolved objects
*/ */
func delete(specifier:NSScriptObjectSpecifier, from container:Any) { func delete(specifier: NSScriptObjectSpecifier, from container: Any) {
if let containerList = container as? [Any] { if let containerList = container as? [Any] {
for nthObject in containerList { for nthObject in containerList {
self.delete(specifier:specifier, from:nthObject) self.delete(specifier: specifier, from: nthObject)
} }
} else if let container = container as? ScriptingObjectContainer { } else if let container = container as? ScriptingObjectContainer {
if let resolvedObjects = specifier.objectsByEvaluating(withContainers:container) { if let resolvedObjects = specifier.objectsByEvaluating(withContainers: container) {
self.delete(objectToDelete:resolvedObjects, from:container) self.delete(objectToDelete: resolvedObjects, from: container)
} }
} }
} }
@ -159,14 +158,14 @@ class NetNewsWireDeleteCommand : NSDeleteCommand {
override func performDefaultImplementation() -> Any? { override func performDefaultImplementation() -> Any? {
if let receiversSpecifier = self.receiversSpecifier { if let receiversSpecifier = self.receiversSpecifier {
if let receiverObjects = receiversSpecifier.objectsByEvaluatingSpecifier { if let receiverObjects = receiversSpecifier.objectsByEvaluatingSpecifier {
self.delete(specifier:self.keySpecifier, from:receiverObjects) self.delete(specifier: self.keySpecifier, from: receiverObjects)
} }
} }
return nil return nil
} }
} }
class NetNewsWireExistsCommand : NSExistsCommand { class NetNewsWireExistsCommand: NSExistsCommand {
// cocoa default behavior doesn't work here, because of cases where we define an object's property // cocoa default behavior doesn't work here, because of cases where we define an object's property
// to be another object type. e.g., 'permalink of the current article' parses as // to be another object type. e.g., 'permalink of the current article' parses as
@ -179,8 +178,7 @@ class NetNewsWireExistsCommand : NSExistsCommand {
// handle that case as well // handle that case as well
override func performDefaultImplementation() -> Any? { override func performDefaultImplementation() -> Any? {
guard let result = super.performDefaultImplementation() else { return NSNumber(booleanLiteral:false) } guard let result = super.performDefaultImplementation() else { return NSNumber(booleanLiteral: false) }
return result return result
} }
} }

View File

@ -13,17 +13,17 @@ import Articles
@objc(ScriptableArticle) @objc(ScriptableArticle)
class ScriptableArticle: NSObject, UniqueIdScriptingObject, ScriptingObjectContainer { class ScriptableArticle: NSObject, UniqueIdScriptingObject, ScriptingObjectContainer {
let article:Article let article: Article
let container:ScriptingObjectContainer let container: ScriptingObjectContainer
init (_ article:Article, container:ScriptingObjectContainer) { init (_ article: Article, container: ScriptingObjectContainer) {
self.article = article self.article = article
self.container = container self.container = container
} }
@objc(objectSpecifier) @objc(objectSpecifier)
override var objectSpecifier: NSScriptObjectSpecifier? { override var objectSpecifier: NSScriptObjectSpecifier? {
let scriptObjectSpecifier = self.container.makeFormUniqueIDScriptObjectSpecifier(forObject:self) let scriptObjectSpecifier = self.container.makeFormUniqueIDScriptObjectSpecifier(forObject: self)
return (scriptObjectSpecifier) return (scriptObjectSpecifier)
} }
@ -39,7 +39,7 @@ class ScriptableArticle: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
// article.uniqueID here is the feed unique id // article.uniqueID here is the feed unique id
@objc(uniqueId) @objc(uniqueId)
var scriptingUniqueId:Any { var scriptingUniqueId: Any {
return article.uniqueID return article.uniqueID
} }
@ -49,66 +49,66 @@ class ScriptableArticle: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
return self.classDescription as! NSScriptClassDescription return self.classDescription as! NSScriptClassDescription
} }
func deleteElement(_ element:ScriptingObject) { func deleteElement(_ element: ScriptingObject) {
print ("delete event not handled") print("delete event not handled")
} }
// MARK: --- Scriptable properties --- // MARK: --- Scriptable properties ---
@objc(url) @objc(url)
var url:String? { var url: String? {
return article.preferredLink return article.preferredLink
} }
@objc(permalink) @objc(permalink)
var permalink:String? { var permalink: String? {
return article.link return article.link
} }
@objc(externalUrl) @objc(externalUrl)
var externalUrl:String? { var externalUrl: String? {
return article.externalLink return article.externalLink
} }
@objc(title) @objc(title)
var title:String { var title: String {
return article.title ?? "" return article.title ?? ""
} }
@objc(contents) @objc(contents)
var contents:String { var contents: String {
return article.contentText ?? "" return article.contentText ?? ""
} }
@objc(html) @objc(html)
var html:String { var html: String {
return article.contentHTML ?? "" return article.contentHTML ?? ""
} }
@objc(summary) @objc(summary)
var summary:String { var summary: String {
return article.summary ?? "" return article.summary ?? ""
} }
@objc(datePublished) @objc(datePublished)
var datePublished:Date? { var datePublished: Date? {
return article.datePublished return article.datePublished
} }
@objc(dateModified) @objc(dateModified)
var dateModified:Date? { var dateModified: Date? {
return article.dateModified return article.dateModified
} }
@objc(dateArrived) @objc(dateArrived)
var dateArrived:Date { var dateArrived: Date {
return article.status.dateArrived return article.status.dateArrived
} }
@objc(read) @objc(read)
var read:Bool { var read: Bool {
get { get {
return article.status.boolStatus(forKey:.read) return article.status.boolStatus(forKey: .read)
} }
set { set {
markArticles([self.article], statusKey: .read, flag: newValue) markArticles([self.article], statusKey: .read, flag: newValue)
@ -116,9 +116,9 @@ class ScriptableArticle: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
} }
@objc(starred) @objc(starred)
var starred:Bool { var starred: Bool {
get { get {
return article.status.boolStatus(forKey:.starred) return article.status.boolStatus(forKey: .starred)
} }
set { set {
markArticles([self.article], statusKey: .starred, flag: newValue) markArticles([self.article], statusKey: .starred, flag: newValue)
@ -126,19 +126,19 @@ class ScriptableArticle: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
} }
@objc(deleted) @objc(deleted)
var deleted:Bool { var deleted: Bool {
return false return false
} }
@objc(imageURL) @objc(imageURL)
var imageURL:String { var imageURL: String {
return article.imageLink ?? "" return article.imageLink ?? ""
} }
@objc(authors) @objc(authors)
var authors:NSArray { var authors: NSArray {
let articleAuthors = article.authors ?? [] let articleAuthors = article.authors ?? []
return articleAuthors.map { ScriptableAuthor($0, container:self) } as NSArray return articleAuthors.map { ScriptableAuthor($0, container: self) } as NSArray
} }
@objc(feed) @objc(feed)

View File

@ -13,17 +13,17 @@ import Articles
@objc(ScriptableAuthor) @objc(ScriptableAuthor)
class ScriptableAuthor: NSObject, UniqueIdScriptingObject { class ScriptableAuthor: NSObject, UniqueIdScriptingObject {
let author:Author let author: Author
let container:ScriptingObjectContainer let container: ScriptingObjectContainer
init (_ author:Author, container:ScriptingObjectContainer) { init (_ author: Author, container: ScriptingObjectContainer) {
self.author = author self.author = author
self.container = container self.container = container
} }
@objc(objectSpecifier) @objc(objectSpecifier)
override var objectSpecifier: NSScriptObjectSpecifier? { override var objectSpecifier: NSScriptObjectSpecifier? {
let scriptObjectSpecifier = self.container.makeFormUniqueIDScriptObjectSpecifier(forObject:self) let scriptObjectSpecifier = self.container.makeFormUniqueIDScriptObjectSpecifier(forObject: self)
return (scriptObjectSpecifier) return (scriptObjectSpecifier)
} }
@ -41,29 +41,29 @@ class ScriptableAuthor: NSObject, UniqueIdScriptingObject {
// MARK: --- UniqueIdScriptingObject protocol --- // MARK: --- UniqueIdScriptingObject protocol ---
@objc(uniqueId) @objc(uniqueId)
var scriptingUniqueId:Any { var scriptingUniqueId: Any {
return author.authorID return author.authorID
} }
// MARK: --- Scriptable properties --- // MARK: --- Scriptable properties ---
@objc(url) @objc(url)
var url:String { var url: String {
return self.author.url ?? "" return self.author.url ?? ""
} }
@objc(name) @objc(name)
var name:String { var name: String {
return self.author.name ?? "" return self.author.name ?? ""
} }
@objc(avatarURL) @objc(avatarURL)
var avatarURL:String { var avatarURL: String {
return self.author.avatarURL ?? "" return self.author.avatarURL ?? ""
} }
@objc(emailAddress) @objc(emailAddress)
var emailAddress:String { var emailAddress: String {
return self.author.emailAddress ?? "" return self.author.emailAddress ?? ""
} }
} }

View File

@ -14,17 +14,17 @@ import RSCore
@objc(ScriptableFolder) @objc(ScriptableFolder)
class ScriptableFolder: NSObject, UniqueIdScriptingObject, ScriptingObjectContainer { class ScriptableFolder: NSObject, UniqueIdScriptingObject, ScriptingObjectContainer {
let folder:Folder let folder: Folder
let container:ScriptingObjectContainer let container: ScriptingObjectContainer
init (_ folder:Folder, container:ScriptingObjectContainer) { init (_ folder: Folder, container: ScriptingObjectContainer) {
self.folder = folder self.folder = folder
self.container = container self.container = container
} }
@objc(objectSpecifier) @objc(objectSpecifier)
override var objectSpecifier: NSScriptObjectSpecifier? { override var objectSpecifier: NSScriptObjectSpecifier? {
let scriptObjectSpecifier = self.container.makeFormUniqueIDScriptObjectSpecifier(forObject:self) let scriptObjectSpecifier = self.container.makeFormUniqueIDScriptObjectSpecifier(forObject: self)
return (scriptObjectSpecifier) return (scriptObjectSpecifier)
} }
@ -40,7 +40,7 @@ class ScriptableFolder: NSObject, UniqueIdScriptingObject, ScriptingObjectContai
// but in either case it seems like the accountID would be used as the keydata, so I chose ID // but in either case it seems like the accountID would be used as the keydata, so I chose ID
@objc(uniqueId) @objc(uniqueId)
var scriptingUniqueId:Any { var scriptingUniqueId: Any {
return folder.folderID return folder.folderID
} }
@ -50,10 +50,10 @@ class ScriptableFolder: NSObject, UniqueIdScriptingObject, ScriptingObjectContai
return self.classDescription as! NSScriptClassDescription return self.classDescription as! NSScriptClassDescription
} }
func deleteElement(_ element:ScriptingObject) { func deleteElement(_ element: ScriptingObject) {
if let scriptableFeed = element as? ScriptableFeed { if let scriptableFeed = element as? ScriptableFeed {
BatchUpdate.shared.perform { BatchUpdate.shared.perform {
folder.account?.removeFeed(scriptableFeed.feed, from: folder) { result in } folder.account?.removeFeed(scriptableFeed.feed, from: folder) { _ in }
} }
} }
} }
@ -65,15 +65,15 @@ class ScriptableFolder: NSObject, UniqueIdScriptingObject, ScriptingObjectContai
or or
tell account X to make new folder at end with properties {name:"new folder name"} tell account X to make new folder at end with properties {name:"new folder name"}
*/ */
class func handleCreateElement(command:NSCreateCommand) -> Any? { class func handleCreateElement(command: NSCreateCommand) -> Any? {
guard command.isCreateCommand(forClass:"fold") else { return nil } guard command.isCreateCommand(forClass: "fold") else { return nil }
let name = command.property(forKey:"name") as? String ?? "" let name = command.property(forKey: "name") as? String ?? ""
// some combination of the tell target and the location specifier ("in" or "at") // some combination of the tell target and the location specifier ("in" or "at")
// identifies where the new folder should be created // identifies where the new folder should be created
let (account, folder) = command.accountAndFolderForNewChild() let (account, folder) = command.accountAndFolderForNewChild()
guard folder == nil else { guard folder == nil else {
print("support for folders within folders is NYI"); print("support for folders within folders is NYI")
return nil return nil
} }
@ -83,10 +83,10 @@ class ScriptableFolder: NSObject, UniqueIdScriptingObject, ScriptingObjectContai
switch result { switch result {
case .success(let folder): case .success(let folder):
let scriptableAccount = ScriptableAccount(account) let scriptableAccount = ScriptableAccount(account)
let scriptableFolder = ScriptableFolder(folder, container:scriptableAccount) let scriptableFolder = ScriptableFolder(folder, container: scriptableAccount)
command.resumeExecution(withResult:scriptableFolder.objectSpecifier) command.resumeExecution(withResult: scriptableFolder.objectSpecifier)
case .failure: case .failure:
command.resumeExecution(withResult:nil) command.resumeExecution(withResult: nil)
} }
} }
@ -96,21 +96,21 @@ class ScriptableFolder: NSObject, UniqueIdScriptingObject, ScriptingObjectContai
// MARK: --- Scriptable elements --- // MARK: --- Scriptable elements ---
@objc(feeds) @objc(feeds)
var feeds:NSArray { var feeds: NSArray {
let feeds = Array(folder.topLevelFeeds) let feeds = Array(folder.topLevelFeeds)
return feeds.map { ScriptableFeed($0, container:self) } as NSArray return feeds.map { ScriptableFeed($0, container: self) } as NSArray
} }
// MARK: --- Scriptable properties --- // MARK: --- Scriptable properties ---
@objc(name) @objc(name)
var name:String { var name: String {
return self.folder.name ?? "" return self.folder.name ?? ""
} }
@objc(opmlRepresentation) @objc(opmlRepresentation)
var opmlRepresentation:String { var opmlRepresentation: String {
return self.folder.OPMLString(indentLevel:0) return self.folder.OPMLString(indentLevel: 0)
} }
} }

View File

@ -13,4 +13,3 @@ protocol ScriptingMainWindowController {
var scriptingCurrentArticle: Article? { get } var scriptingCurrentArticle: Article? { get }
var scriptingSelectedArticles: [Article] { get } var scriptingSelectedArticles: [Article] { get }
} }

View File

@ -10,7 +10,7 @@ import AppKit
import Account import Account
import Articles import Articles
extension NSApplication : ScriptingObjectContainer { extension NSApplication: ScriptingObjectContainer {
// MARK: --- ScriptingObjectContainer protocol --- // MARK: --- ScriptingObjectContainer protocol ---
@ -18,8 +18,8 @@ extension NSApplication : ScriptingObjectContainer {
return NSApplication.shared.classDescription as! NSScriptClassDescription return NSApplication.shared.classDescription as! NSScriptClassDescription
} }
func deleteElement(_ element:ScriptingObject) { func deleteElement(_ element: ScriptingObject) {
print ("delete event not handled") print("delete event not handled")
} }
var scriptingKey: String { var scriptingKey: String {
@ -31,8 +31,8 @@ extension NSApplication : ScriptingObjectContainer {
var scriptableArticle: ScriptableArticle? var scriptableArticle: ScriptableArticle?
if let currentArticle = appDelegate.scriptingCurrentArticle { if let currentArticle = appDelegate.scriptingCurrentArticle {
if let feed = currentArticle.feed { if let feed = currentArticle.feed {
let scriptableFeed = ScriptableFeed(feed, container:self) let scriptableFeed = ScriptableFeed(feed, container: self)
scriptableArticle = ScriptableArticle(currentArticle, container:scriptableFeed) scriptableArticle = ScriptableArticle(currentArticle, container: scriptableFeed)
} }
} }
return scriptableArticle return scriptableArticle
@ -41,7 +41,7 @@ extension NSApplication : ScriptingObjectContainer {
@objc(selectedArticles) @objc(selectedArticles)
func selectedArticles() -> NSArray { func selectedArticles() -> NSArray {
let articles = appDelegate.scriptingSelectedArticles let articles = appDelegate.scriptingSelectedArticles
let scriptableArticles:[ScriptableArticle] = articles.compactMap { article in let scriptableArticles: [ScriptableArticle] = articles.compactMap { article in
if let feed = article.feed, let account = feed.account { if let feed = article.feed, let account = feed.account {
let scriptableFeed = ScriptableFeed(feed, container: ScriptableAccount(account)) let scriptableFeed = ScriptableFeed(feed, container: ScriptableAccount(account))
return ScriptableArticle(article, container: scriptableFeed) return ScriptableArticle(article, container: scriptableFeed)
@ -61,9 +61,9 @@ extension NSApplication : ScriptingObjectContainer {
} }
@objc(valueInAccountsWithUniqueID:) @objc(valueInAccountsWithUniqueID:)
func valueInAccounts(withUniqueID id:String) -> ScriptableAccount? { func valueInAccounts(withUniqueID id: String) -> ScriptableAccount? {
let accounts = AccountManager.shared.accounts let accounts = AccountManager.shared.accounts
guard let account = accounts.first(where:{$0.accountID == id}) else { return nil } guard let account = accounts.first(where: {$0.accountID == id}) else { return nil }
return ScriptableAccount(account) return ScriptableAccount(account)
} }
@ -75,7 +75,7 @@ extension NSApplication : ScriptingObjectContainer {
func allFeeds() -> [Feed] { func allFeeds() -> [Feed] {
let accounts = AccountManager.shared.activeAccounts let accounts = AccountManager.shared.activeAccounts
let emptyFeeds:[Feed] = [] let emptyFeeds: [Feed] = []
return accounts.reduce(emptyFeeds) { (result, nthAccount) -> [Feed] in return accounts.reduce(emptyFeeds) { (result, nthAccount) -> [Feed] in
let accountFeeds = Array(nthAccount.topLevelFeeds) let accountFeeds = Array(nthAccount.topLevelFeeds)
return result + accountFeeds return result + accountFeeds
@ -85,15 +85,13 @@ extension NSApplication : ScriptingObjectContainer {
@objc(feeds) @objc(feeds)
func feeds() -> NSArray { func feeds() -> NSArray {
let feeds = self.allFeeds() let feeds = self.allFeeds()
return feeds.map { ScriptableFeed($0, container:self) } as NSArray return feeds.map { ScriptableFeed($0, container: self) } as NSArray
} }
@objc(valueInFeedsWithUniqueID:) @objc(valueInFeedsWithUniqueID:)
func valueInFeeds(withUniqueID id:String) -> ScriptableFeed? { func valueInFeeds(withUniqueID id: String) -> ScriptableFeed? {
let feeds = self.allFeeds() let feeds = self.allFeeds()
guard let feed = feeds.first(where:{$0.feedID == id}) else { return nil } guard let feed = feeds.first(where: {$0.feedID == id}) else { return nil }
return ScriptableFeed(feed, container:self) return ScriptableFeed(feed, container: self)
} }
} }

View File

@ -10,7 +10,7 @@ import Foundation
import Account import Account
extension NSScriptCommand { extension NSScriptCommand {
func property(forKey key:String) -> Any? { func property(forKey key: String) -> Any? {
if let evaluatedArguments = self.evaluatedArguments { if let evaluatedArguments = self.evaluatedArguments {
if let props = evaluatedArguments["KeyDictionary"] as? [String: Any] { if let props = evaluatedArguments["KeyDictionary"] as? [String: Any] {
return props[key] return props[key]
@ -19,34 +19,34 @@ extension NSScriptCommand {
return nil return nil
} }
func isCreateCommand(forClass whatClass:String) -> Bool { func isCreateCommand(forClass whatClass: String) -> Bool {
guard let arguments = self.arguments else {return false} guard let arguments = self.arguments else {return false}
guard let newObjectClass = arguments["ObjectClass"] as? Int else {return false} guard let newObjectClass = arguments["ObjectClass"] as? Int else {return false}
guard (newObjectClass.fourCharCode == whatClass.fourCharCode) else {return false} guard newObjectClass.fourCharCode == whatClass.fourCharCode else {return false}
return true return true
} }
func accountAndFolderForNewChild() -> (Account, Folder?) { func accountAndFolderForNewChild() -> (Account, Folder?) {
let appleEvent = self.appleEvent let appleEvent = self.appleEvent
var account = AccountManager.shared.defaultAccount var account = AccountManager.shared.defaultAccount
var folder:Folder? = nil var folder: Folder?
if let appleEvent = appleEvent { if let appleEvent = appleEvent {
var descriptorToConsider:NSAppleEventDescriptor? var descriptorToConsider: NSAppleEventDescriptor?
if let insertionLocationDescriptor = appleEvent.paramDescriptor(forKeyword:keyAEInsertHere) { if let insertionLocationDescriptor = appleEvent.paramDescriptor(forKeyword: keyAEInsertHere) {
print("insertionLocation : \(insertionLocationDescriptor)") print("insertionLocation : \(insertionLocationDescriptor)")
// insertion location can be a typeObjectSpecifier, e.g. 'in account "Acct"' // insertion location can be a typeObjectSpecifier, e.g. 'in account "Acct"'
// or a typeInsertionLocation, e.g. 'at end of folder " // or a typeInsertionLocation, e.g. 'at end of folder "
if (insertionLocationDescriptor.descriptorType == "insl".fourCharCode) { if insertionLocationDescriptor.descriptorType == "insl".fourCharCode {
descriptorToConsider = insertionLocationDescriptor.forKeyword("kobj".fourCharCode) descriptorToConsider = insertionLocationDescriptor.forKeyword("kobj".fourCharCode)
} else if ( insertionLocationDescriptor.descriptorType == "obj ".fourCharCode) { } else if insertionLocationDescriptor.descriptorType == "obj ".fourCharCode {
descriptorToConsider = insertionLocationDescriptor descriptorToConsider = insertionLocationDescriptor
} }
} else if let subjectDescriptor = appleEvent.attributeDescriptor(forKeyword:"subj".fourCharCode) { } else if let subjectDescriptor = appleEvent.attributeDescriptor(forKeyword: "subj".fourCharCode) {
descriptorToConsider = subjectDescriptor descriptorToConsider = subjectDescriptor
} }
if let descriptorToConsider = descriptorToConsider { if let descriptorToConsider = descriptorToConsider {
guard let newContainerSpecifier = NSScriptObjectSpecifier(descriptor:descriptorToConsider) else {return (account, folder)} guard let newContainerSpecifier = NSScriptObjectSpecifier(descriptor: descriptorToConsider) else {return (account, folder)}
let newContainer = newContainerSpecifier.objectsByEvaluatingSpecifier let newContainer = newContainerSpecifier.objectsByEvaluatingSpecifier
if let scriptableAccount = newContainer as? ScriptableAccount { if let scriptableAccount = newContainer as? ScriptableAccount {
account = scriptableAccount.account account = scriptableAccount.account

View File

@ -14,9 +14,9 @@ protocol ScriptingObject {
} }
protocol NamedScriptingObject: ScriptingObject { protocol NamedScriptingObject: ScriptingObject {
var name:String { get } var name: String { get }
} }
protocol UniqueIdScriptingObject: ScriptingObject { protocol UniqueIdScriptingObject: ScriptingObject {
var scriptingUniqueId:Any { get } var scriptingUniqueId: Any { get }
} }

View File

@ -10,30 +10,29 @@ import AppKit
import Account import Account
protocol ScriptingObjectContainer: ScriptingObject { protocol ScriptingObjectContainer: ScriptingObject {
var scriptingClassDescription:NSScriptClassDescription { get } var scriptingClassDescription: NSScriptClassDescription { get }
func deleteElement(_ element:ScriptingObject) func deleteElement(_ element: ScriptingObject)
} }
extension ScriptingObjectContainer { extension ScriptingObjectContainer {
func makeFormNameScriptObjectSpecifier(forObject object:NamedScriptingObject) -> NSScriptObjectSpecifier? { func makeFormNameScriptObjectSpecifier(forObject object: NamedScriptingObject) -> NSScriptObjectSpecifier? {
let containerClassDescription = self.scriptingClassDescription let containerClassDescription = self.scriptingClassDescription
let containerScriptObjectSpecifier = self.objectSpecifier let containerScriptObjectSpecifier = self.objectSpecifier
let scriptingKey = object.scriptingKey let scriptingKey = object.scriptingKey
let name = object.name let name = object.name
let specifier = NSNameSpecifier(containerClassDescription:containerClassDescription, let specifier = NSNameSpecifier(containerClassDescription: containerClassDescription,
containerSpecifier:containerScriptObjectSpecifier, key:scriptingKey, name:name) containerSpecifier: containerScriptObjectSpecifier, key: scriptingKey, name: name)
return specifier return specifier
} }
func makeFormUniqueIDScriptObjectSpecifier(forObject object:UniqueIdScriptingObject) -> NSScriptObjectSpecifier? { func makeFormUniqueIDScriptObjectSpecifier(forObject object: UniqueIdScriptingObject) -> NSScriptObjectSpecifier? {
let containerClassDescription = self.scriptingClassDescription let containerClassDescription = self.scriptingClassDescription
let containerScriptObjectSpecifier = self.objectSpecifier let containerScriptObjectSpecifier = self.objectSpecifier
let scriptingKey = object.scriptingKey let scriptingKey = object.scriptingKey
let uniqueId = object.scriptingUniqueId let uniqueId = object.scriptingUniqueId
let specifier = NSUniqueIDSpecifier(containerClassDescription:containerClassDescription, let specifier = NSUniqueIDSpecifier(containerClassDescription: containerClassDescription,
containerSpecifier:containerScriptObjectSpecifier, key:scriptingKey, uniqueID: uniqueId) containerSpecifier: containerScriptObjectSpecifier, key: scriptingKey, uniqueID: uniqueId)
return specifier return specifier
} }
} }

View File

@ -17,14 +17,14 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
let feed: Feed let feed: Feed
let container: ScriptingObjectContainer let container: ScriptingObjectContainer
init (_ feed:Feed, container:ScriptingObjectContainer) { init (_ feed: Feed, container: ScriptingObjectContainer) {
self.feed = feed self.feed = feed
self.container = container self.container = container
} }
@objc(objectSpecifier) @objc(objectSpecifier)
override var objectSpecifier: NSScriptObjectSpecifier? { override var objectSpecifier: NSScriptObjectSpecifier? {
let scriptObjectSpecifier = self.container.makeFormUniqueIDScriptObjectSpecifier(forObject:self) let scriptObjectSpecifier = self.container.makeFormUniqueIDScriptObjectSpecifier(forObject: self)
return (scriptObjectSpecifier) return (scriptObjectSpecifier)
} }
@ -44,7 +44,7 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
// I am not sure if account should prefer to be specified by name or by ID // I am not sure if account should prefer to be specified by name or by ID
// but in either case it seems like the accountID would be used as the keydata, so I chose ID // but in either case it seems like the accountID would be used as the keydata, so I chose ID
@objc(uniqueId) @objc(uniqueId)
var scriptingUniqueId:Any { var scriptingUniqueId: Any {
return feed.feedID return feed.feedID
} }
@ -54,42 +54,42 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
return self.classDescription as! NSScriptClassDescription return self.classDescription as! NSScriptClassDescription
} }
func deleteElement(_ element:ScriptingObject) { func deleteElement(_ element: ScriptingObject) {
} }
// MARK: --- handle NSCreateCommand --- // MARK: --- handle NSCreateCommand ---
class func urlForNewFeed(arguments:[String:Any]) -> String? { class func urlForNewFeed(arguments: [String: Any]) -> String? {
var url:String? var url: String?
if let withDataParam = arguments["ObjectData"] { if let withDataParam = arguments["ObjectData"] {
if let objectDataDescriptor = withDataParam as? NSAppleEventDescriptor { if let objectDataDescriptor = withDataParam as? NSAppleEventDescriptor {
url = objectDataDescriptor.stringValue url = objectDataDescriptor.stringValue
} }
} else if let withPropsParam = arguments["ObjectProperties"] as? [String:Any] { } else if let withPropsParam = arguments["ObjectProperties"] as? [String: Any] {
url = withPropsParam["url"] as? String url = withPropsParam["url"] as? String
} }
return url return url
} }
class func scriptableFeed(_ feed:Feed, account:Account, folder:Folder?) -> ScriptableFeed { class func scriptableFeed(_ feed: Feed, account: Account, folder: Folder?) -> ScriptableFeed {
let scriptableAccount = ScriptableAccount(account) let scriptableAccount = ScriptableAccount(account)
if let folder = folder { if let folder = folder {
let scriptableFolder = ScriptableFolder(folder, container:scriptableAccount) let scriptableFolder = ScriptableFolder(folder, container: scriptableAccount)
return ScriptableFeed(feed, container:scriptableFolder) return ScriptableFeed(feed, container: scriptableFolder)
} else { } else {
return ScriptableFeed(feed, container:scriptableAccount) return ScriptableFeed(feed, container: scriptableAccount)
} }
} }
class func handleCreateElement(command:NSCreateCommand) -> Any? { class func handleCreateElement(command: NSCreateCommand) -> Any? {
guard command.isCreateCommand(forClass:"Feed") else { return nil } guard command.isCreateCommand(forClass: "Feed") else { return nil }
guard let arguments = command.arguments else {return nil} guard let arguments = command.arguments else {return nil}
let titleFromArgs = command.property(forKey:"name") as? String let titleFromArgs = command.property(forKey: "name") as? String
let (account, folder) = command.accountAndFolderForNewChild() let (account, folder) = command.accountAndFolderForNewChild()
guard let url = self.urlForNewFeed(arguments:arguments) else {return nil} guard let url = self.urlForNewFeed(arguments: arguments) else {return nil}
if let existingFeed = account.existingFeed(withURL:url) { if let existingFeed = account.existingFeed(withURL: url) {
return scriptableFeed(existingFeed, account:account, folder:folder).objectSpecifier return scriptableFeed(existingFeed, account: account, folder: folder).objectSpecifier
} }
let container: Container = folder != nil ? folder! : account let container: Container = folder != nil ? folder! : account
@ -106,10 +106,10 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
switch result { switch result {
case .success(let feed): case .success(let feed):
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed]) NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
let scriptableFeed = self.scriptableFeed(feed, account:account, folder:folder) let scriptableFeed = self.scriptableFeed(feed, account: account, folder: folder)
command.resumeExecution(withResult:scriptableFeed.objectSpecifier) command.resumeExecution(withResult: scriptableFeed.objectSpecifier)
case .failure: case .failure:
command.resumeExecution(withResult:nil) command.resumeExecution(withResult: nil)
} }
} }
@ -120,64 +120,64 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
// MARK: --- Scriptable properties --- // MARK: --- Scriptable properties ---
@objc(url) @objc(url)
var url:String { var url: String {
return self.feed.url return self.feed.url
} }
@objc(name) @objc(name)
var name:String { var name: String {
return self.feed.name ?? "" return self.feed.name ?? ""
} }
@objc(homePageURL) @objc(homePageURL)
var homePageURL:String { var homePageURL: String {
return self.feed.homePageURL ?? "" return self.feed.homePageURL ?? ""
} }
@objc(iconURL) @objc(iconURL)
var iconURL:String { var iconURL: String {
return self.feed.iconURL ?? "" return self.feed.iconURL ?? ""
} }
@objc(faviconURL) @objc(faviconURL)
var faviconURL:String { var faviconURL: String {
return self.feed.faviconURL ?? "" return self.feed.faviconURL ?? ""
} }
@objc(opmlRepresentation) @objc(opmlRepresentation)
var opmlRepresentation:String { var opmlRepresentation: String {
return self.feed.OPMLString(indentLevel:0) return self.feed.OPMLString(indentLevel: 0)
} }
// MARK: --- scriptable elements --- // MARK: --- scriptable elements ---
@objc(authors) @objc(authors)
var authors:NSArray { var authors: NSArray {
let feedAuthors = feed.authors ?? [] let feedAuthors = feed.authors ?? []
return feedAuthors.map { ScriptableAuthor($0, container:self) } as NSArray return feedAuthors.map { ScriptableAuthor($0, container: self) } as NSArray
} }
@objc(valueInAuthorsWithUniqueID:) @objc(valueInAuthorsWithUniqueID:)
func valueInAuthors(withUniqueID id:String) -> ScriptableAuthor? { func valueInAuthors(withUniqueID id: String) -> ScriptableAuthor? {
guard let author = feed.authors?.first(where:{$0.authorID == id}) else { return nil } guard let author = feed.authors?.first(where: {$0.authorID == id}) else { return nil }
return ScriptableAuthor(author, container:self) return ScriptableAuthor(author, container: self)
} }
@objc(articles) @objc(articles)
var articles:NSArray { var articles: NSArray {
let feedArticles = (try? feed.fetchArticles()) ?? Set<Article>() let feedArticles = (try? feed.fetchArticles()) ?? Set<Article>()
// the articles are a set, use the sorting algorithm from the viewer // the articles are a set, use the sorting algorithm from the viewer
let sortedArticles = feedArticles.sorted(by:{ let sortedArticles = feedArticles.sorted(by: {
return $0.logicalDatePublished > $1.logicalDatePublished return $0.logicalDatePublished > $1.logicalDatePublished
}) })
return sortedArticles.map { ScriptableArticle($0, container:self) } as NSArray return sortedArticles.map { ScriptableArticle($0, container: self) } as NSArray
} }
@objc(valueInArticlesWithUniqueID:) @objc(valueInArticlesWithUniqueID:)
func valueInArticles(withUniqueID id:String) -> ScriptableArticle? { func valueInArticles(withUniqueID id: String) -> ScriptableArticle? {
let articles = (try? feed.fetchArticles()) ?? Set<Article>() let articles = (try? feed.fetchArticles()) ?? Set<Article>()
guard let article = articles.first(where:{$0.uniqueID == id}) else { return nil } guard let article = articles.first(where: {$0.uniqueID == id}) else { return nil }
return ScriptableArticle(article, container:self) return ScriptableArticle(article, container: self)
} }
} }

View File

@ -29,7 +29,7 @@ class ShareViewController: NSViewController {
extensionContainers = ExtensionContainersFile.read() extensionContainers = ExtensionContainersFile.read()
buildFolderPopupMenu() buildFolderPopupMenu()
var provider: NSItemProvider? = nil var provider: NSItemProvider?
// Try to get any HTML that is maybe passed in // Try to get any HTML that is maybe passed in
for item in self.extensionContext!.inputItems as! [NSExtensionItem] { for item in self.extensionContext!.inputItems as! [NSExtensionItem] {
@ -117,7 +117,7 @@ private extension ShareViewController {
} }
let defaultContainer = ShareDefaultContainer.defaultContainer(containers: extensionContainers) let defaultContainer = ShareDefaultContainer.defaultContainer(containers: extensionContainers)
var defaultMenuItem: NSMenuItem? = nil var defaultMenuItem: NSMenuItem?
for account in extensionContainers.accounts { for account in extensionContainers.accounts {