mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Make multiplatform compile shared app resources
This commit is contained in:
286
Multiplatform/macOS/AppDelegate.swift
Normal file
286
Multiplatform/macOS/AppDelegate.swift
Normal file
@@ -0,0 +1,286 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// Multiplatform macOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/28/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import os.log
|
||||
import UserNotifications
|
||||
import Articles
|
||||
import RSTree
|
||||
import RSWeb
|
||||
import Account
|
||||
import RSCore
|
||||
|
||||
// If we're not going to import Sparkle, provide dummy protocols to make it easy
|
||||
// for AppDelegate to comply
|
||||
#if MAC_APP_STORE || TEST
|
||||
protocol SPUStandardUserDriverDelegate {}
|
||||
protocol SPUUpdaterDelegate {}
|
||||
#else
|
||||
import Sparkle
|
||||
#endif
|
||||
|
||||
var appDelegate: AppDelegate!
|
||||
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate, UnreadCountProvider, SPUStandardUserDriverDelegate, SPUUpdaterDelegate
|
||||
{
|
||||
|
||||
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Application")
|
||||
|
||||
var userNotificationManager: UserNotificationManager!
|
||||
var faviconDownloader: FaviconDownloader!
|
||||
var imageDownloader: ImageDownloader!
|
||||
var authorAvatarDownloader: AuthorAvatarDownloader!
|
||||
var webFeedIconDownloader: WebFeedIconDownloader!
|
||||
|
||||
var refreshTimer: AccountRefreshTimer?
|
||||
var syncTimer: ArticleStatusSyncTimer?
|
||||
|
||||
var shuttingDown = false {
|
||||
didSet {
|
||||
if shuttingDown {
|
||||
refreshTimer?.shuttingDown = shuttingDown
|
||||
refreshTimer?.invalidate()
|
||||
syncTimer?.shuttingDown = shuttingDown
|
||||
syncTimer?.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unreadCount = 0 {
|
||||
didSet {
|
||||
if unreadCount != oldValue {
|
||||
CoalescingQueue.standard.add(self, #selector(updateDockBadge))
|
||||
postUnreadCountDidChangeNotification()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var appName: String!
|
||||
|
||||
private let appNewsURLString = "https://nnw.ranchero.com/feed.json"
|
||||
private let appMovementMonitor = RSAppMovementMonitor()
|
||||
#if !MAC_APP_STORE && !TEST
|
||||
private var softwareUpdater: SPUUpdater!
|
||||
#endif
|
||||
|
||||
override init() {
|
||||
NSWindow.allowsAutomaticWindowTabbing = false
|
||||
super.init()
|
||||
|
||||
AccountManager.shared = AccountManager(accountsFolder: Platform.dataSubfolder(forApplication: nil, folderName: "Accounts")!)
|
||||
FeedProviderManager.shared.delegate = ExtensionPointManager.shared
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||
NSWorkspace.shared.notificationCenter.addObserver(self, selector: #selector(didWakeNotification(_:)), name: NSWorkspace.didWakeNotification, object: nil)
|
||||
|
||||
appDelegate = self
|
||||
}
|
||||
|
||||
// MARK: - NSApplicationDelegate
|
||||
|
||||
func applicationWillFinishLaunching(_ notification: Notification) {
|
||||
// TODO: add Apple Events back in
|
||||
// installAppleEventHandlers()
|
||||
|
||||
CacheCleaner.purgeIfNecessary()
|
||||
|
||||
// Try to establish a cache in the Caches folder, but if it fails for some reason fall back to a temporary dir
|
||||
let cacheFolder: String
|
||||
if let userCacheFolder = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: false).path {
|
||||
cacheFolder = userCacheFolder
|
||||
}
|
||||
else {
|
||||
let bundleIdentifier = (Bundle.main.infoDictionary!["CFBundleIdentifier"]! as! String)
|
||||
cacheFolder = (NSTemporaryDirectory() as NSString).appendingPathComponent(bundleIdentifier)
|
||||
}
|
||||
|
||||
let faviconsFolder = (cacheFolder as NSString).appendingPathComponent("Favicons")
|
||||
let faviconsFolderURL = URL(fileURLWithPath: faviconsFolder)
|
||||
try! FileManager.default.createDirectory(at: faviconsFolderURL, withIntermediateDirectories: true, attributes: nil)
|
||||
faviconDownloader = FaviconDownloader(folder: faviconsFolder)
|
||||
|
||||
let imagesFolder = (cacheFolder as NSString).appendingPathComponent("Images")
|
||||
let imagesFolderURL = URL(fileURLWithPath: imagesFolder)
|
||||
try! FileManager.default.createDirectory(at: imagesFolderURL, withIntermediateDirectories: true, attributes: nil)
|
||||
imageDownloader = ImageDownloader(folder: imagesFolder)
|
||||
|
||||
authorAvatarDownloader = AuthorAvatarDownloader(imageDownloader: imageDownloader)
|
||||
webFeedIconDownloader = WebFeedIconDownloader(imageDownloader: imageDownloader, folder: cacheFolder)
|
||||
|
||||
appName = (Bundle.main.infoDictionary!["CFBundleExecutable"]! as! String)
|
||||
}
|
||||
|
||||
func applicationDidFinishLaunching(_ note: Notification) {
|
||||
|
||||
#if MAC_APP_STORE || TEST
|
||||
checkForUpdatesMenuItem.isHidden = true
|
||||
#else
|
||||
// Initialize Sparkle...
|
||||
let hostBundle = Bundle.main
|
||||
let updateDriver = SPUStandardUserDriver(hostBundle: hostBundle, delegate: self)
|
||||
self.softwareUpdater = SPUUpdater(hostBundle: hostBundle, applicationBundle: hostBundle, userDriver: updateDriver, delegate: self)
|
||||
|
||||
do {
|
||||
try self.softwareUpdater.start()
|
||||
}
|
||||
catch {
|
||||
NSLog("Failed to start software updater with error: \(error)")
|
||||
}
|
||||
#endif
|
||||
|
||||
AppDefaults.registerDefaults()
|
||||
let isFirstRun = AppDefaults.isFirstRun
|
||||
if isFirstRun {
|
||||
os_log(.debug, log: log, "Is first run.")
|
||||
}
|
||||
let localAccount = AccountManager.shared.defaultAccount
|
||||
|
||||
if isFirstRun && !AccountManager.shared.anyAccountHasAtLeastOneFeed() {
|
||||
DefaultFeedsImporter.importDefaultFeeds(account: localAccount)
|
||||
}
|
||||
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(webFeedSettingDidChange(_:)), name: .WebFeedSettingDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.unreadCount = AccountManager.shared.unreadCount
|
||||
}
|
||||
|
||||
refreshTimer = AccountRefreshTimer()
|
||||
syncTimer = ArticleStatusSyncTimer()
|
||||
|
||||
UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .sound, .alert]) { (granted, error) in }
|
||||
NSApplication.shared.registerForRemoteNotifications()
|
||||
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
userNotificationManager = UserNotificationManager()
|
||||
|
||||
// TODO: Add a debug menu
|
||||
// if AppDefaults.showDebugMenu {
|
||||
// refreshTimer!.update()
|
||||
// syncTimer!.update()
|
||||
//
|
||||
// // The Web Inspector uses SPI and can never appear in a MAC_APP_STORE build.
|
||||
// #if MAC_APP_STORE
|
||||
// let debugMenu = debugMenuItem.submenu!
|
||||
// let toggleWebInspectorItemIndex = debugMenu.indexOfItem(withTarget: self, andAction: #selector(toggleWebInspectorEnabled(_:)))
|
||||
// if toggleWebInspectorItemIndex != -1 {
|
||||
// debugMenu.removeItem(at: toggleWebInspectorItemIndex)
|
||||
// }
|
||||
// #endif
|
||||
// } else {
|
||||
// debugMenuItem.menu?.removeItem(debugMenuItem)
|
||||
// DispatchQueue.main.async {
|
||||
// self.refreshTimer!.timedRefresh(nil)
|
||||
// self.syncTimer!.timedRefresh(nil)
|
||||
// }
|
||||
// }
|
||||
|
||||
// TODO: Add back in crash reporter
|
||||
// #if !MAC_APP_STORE
|
||||
// DispatchQueue.main.async {
|
||||
// CrashReporter.check(appName: "NetNewsWire")
|
||||
// }
|
||||
// #endif
|
||||
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(_ notification: Notification) {
|
||||
fireOldTimers()
|
||||
}
|
||||
|
||||
func applicationDidResignActive(_ notification: Notification) {
|
||||
ArticleStringFormatter.emptyCaches()
|
||||
}
|
||||
|
||||
func application(_ application: NSApplication, didReceiveRemoteNotification userInfo: [String : Any]) {
|
||||
AccountManager.shared.receiveRemoteNotification(userInfo: userInfo)
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ notification: Notification) {
|
||||
shuttingDown = true
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
@objc func unreadCountDidChange(_ note: Notification) {
|
||||
if note.object is AccountManager {
|
||||
unreadCount = AccountManager.shared.unreadCount
|
||||
}
|
||||
}
|
||||
|
||||
@objc func webFeedSettingDidChange(_ note: Notification) {
|
||||
guard let feed = note.object as? WebFeed, let key = note.userInfo?[WebFeed.WebFeedSettingUserInfoKey] as? String else {
|
||||
return
|
||||
}
|
||||
if key == WebFeed.WebFeedSettingKey.homePageURL || key == WebFeed.WebFeedSettingKey.faviconURL {
|
||||
let _ = faviconDownloader.favicon(for: feed)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func userDefaultsDidChange(_ note: Notification) {
|
||||
refreshTimer?.update()
|
||||
updateDockBadge()
|
||||
}
|
||||
|
||||
@objc func didWakeNotification(_ note: Notification) {
|
||||
fireOldTimers()
|
||||
}
|
||||
|
||||
// MARK: UNUserNotificationCenterDelegate
|
||||
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
||||
completionHandler([.banner, .badge, .sound])
|
||||
}
|
||||
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
||||
// TODO: Add back in Notification handling
|
||||
// mainWindowController?.handle(response)
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
// MARK: - Dock Badge
|
||||
@objc func updateDockBadge() {
|
||||
let label = unreadCount > 0 && !AppDefaults.hideDockUnreadCount ? "\(unreadCount)" : ""
|
||||
NSApplication.shared.dockTile.badgeLabel = label
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension AppDelegate {
|
||||
|
||||
func fireOldTimers() {
|
||||
// It’s possible there’s a refresh timer set to go off in the past.
|
||||
// In that case, refresh now and update the timer.
|
||||
refreshTimer?.fireOldTimer()
|
||||
syncTimer?.fireOldTimer()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
the ScriptingAppDelegate protocol exposes a narrow set of accessors with
|
||||
internal visibility which are very similar to some private vars.
|
||||
|
||||
These would be unnecessary if the similar accessors were marked internal rather than private,
|
||||
but for now, we'll keep the stratification of visibility
|
||||
*/
|
||||
//extension AppDelegate : ScriptingAppDelegate {
|
||||
//
|
||||
// internal var scriptingMainWindowController: ScriptingMainWindowController? {
|
||||
// return mainWindowController
|
||||
// }
|
||||
//
|
||||
// internal var scriptingCurrentArticle: Article? {
|
||||
// return self.scriptingMainWindowController?.scriptingCurrentArticle
|
||||
// }
|
||||
//
|
||||
// internal var scriptingSelectedArticles: [Article] {
|
||||
// return self.scriptingMainWindowController?.scriptingSelectedArticles ?? []
|
||||
// }
|
||||
//}
|
||||
@@ -24,5 +24,16 @@
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2020 Ranchero Software. All rights reserved.</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>UserAgent</key>
|
||||
<string>NetNewsWire (RSS Reader; https://ranchero.com/netnewswire/)</string>
|
||||
<key>OrganizationIdentifier</key>
|
||||
<string>$(ORGANIZATION_IDENTIFIER)</string>
|
||||
<key>DeveloperEntitlements</key>
|
||||
<string>$(DEVELOPER_ENTITLEMENTS)</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
Reference in New Issue
Block a user