Make AccountManager init itself, and use AccountManager.shared in AppDelegate.
This commit is contained in:
parent
aa807249b9
commit
c784569040
|
@ -101,21 +101,16 @@ import Sparkle
|
|||
#endif
|
||||
|
||||
private var themeImportPath: String?
|
||||
private let accountManager: AccountManager
|
||||
|
||||
override init() {
|
||||
|
||||
NSWindow.allowsAutomaticWindowTabbing = false
|
||||
|
||||
self.accountManager = AccountManager(accountsFolder: Platform.dataSubfolder(forApplication: nil, folderName: "Accounts")!)
|
||||
AccountManager.shared = self.accountManager
|
||||
|
||||
super.init()
|
||||
|
||||
#if !MAC_APP_STORE
|
||||
let crashReporterConfig = PLCrashReporterConfig.defaultConfiguration()
|
||||
crashReporter = PLCrashReporter(configuration: crashReporterConfig)
|
||||
crashReporter.enable()
|
||||
self.crashReporter = PLCrashReporter(configuration: crashReporterConfig)
|
||||
self.crashReporter.enable()
|
||||
#endif
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||
|
@ -172,9 +167,9 @@ import Sparkle
|
|||
|
||||
FaviconGenerator.faviconTemplateImage = AppAssets.faviconTemplateImage
|
||||
|
||||
let localAccount = accountManager.defaultAccount
|
||||
let localAccount = AccountManager.shared.defaultAccount
|
||||
|
||||
if isFirstRun && !accountManager.anyAccountHasAtLeastOneFeed() {
|
||||
if isFirstRun && !AccountManager.shared.anyAccountHasAtLeastOneFeed() {
|
||||
// Import feeds. Either old NNW 3 feeds or the default feeds.
|
||||
if !NNW3ImportController.importSubscriptionsIfFileExists(account: localAccount) {
|
||||
DefaultFeedsImporter.importDefaultFeeds(account: localAccount)
|
||||
|
@ -197,7 +192,7 @@ import Sparkle
|
|||
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil)
|
||||
|
||||
Task {
|
||||
self.unreadCount = self.accountManager.unreadCount
|
||||
self.unreadCount = AccountManager.shared.unreadCount
|
||||
}
|
||||
|
||||
if InspectorWindowController.shouldOpenAtStartup {
|
||||
|
@ -290,7 +285,7 @@ import Sparkle
|
|||
ArticleStringFormatter.emptyCaches()
|
||||
MultilineTextFieldSizer.emptyCache()
|
||||
IconImageCache.shared.emptyCache()
|
||||
accountManager.emptyCaches()
|
||||
AccountManager.shared.emptyCaches()
|
||||
|
||||
saveState()
|
||||
|
||||
|
@ -302,7 +297,7 @@ import Sparkle
|
|||
func application(_ application: NSApplication, didReceiveRemoteNotification userInfo: [String : Any]) {
|
||||
|
||||
Task {
|
||||
await self.accountManager.receiveRemoteNotification(userInfo: userInfo)
|
||||
await AccountManager.shared.receiveRemoteNotification(userInfo: userInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -319,7 +314,7 @@ import Sparkle
|
|||
ArticleThemeDownloader.cleanUp()
|
||||
|
||||
Task {
|
||||
await accountManager.sendArticleStatusAll()
|
||||
await AccountManager.shared.sendArticleStatusAll()
|
||||
self.isShutDownSyncDone = true
|
||||
}
|
||||
|
||||
|
@ -330,7 +325,7 @@ import Sparkle
|
|||
// MARK: Notifications
|
||||
@objc func unreadCountDidChange(_ note: Notification) {
|
||||
if note.object is AccountManager {
|
||||
unreadCount = accountManager.unreadCount
|
||||
unreadCount = AccountManager.shared.unreadCount
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,15 +425,15 @@ import Sparkle
|
|||
let isDisplayingSheet = mainWindowController?.isDisplayingSheet ?? false
|
||||
|
||||
if item.action == #selector(refreshAll(_:)) {
|
||||
return !accountManager.refreshInProgress && !accountManager.activeAccounts.isEmpty
|
||||
return !AccountManager.shared.refreshInProgress && !AccountManager.shared.activeAccounts.isEmpty
|
||||
}
|
||||
|
||||
if item.action == #selector(importOPMLFromFile(_:)) {
|
||||
return accountManager.activeAccounts.contains(where: { !$0.behaviors.contains(where: { $0 == .disallowOPMLImports }) })
|
||||
return AccountManager.shared.activeAccounts.contains(where: { !$0.behaviors.contains(where: { $0 == .disallowOPMLImports }) })
|
||||
}
|
||||
|
||||
if item.action == #selector(addAppNews(_:)) {
|
||||
return !isDisplayingSheet && !accountManager.anyAccountHasNetNewsWireNewsSubscription() && !accountManager.activeAccounts.isEmpty
|
||||
return !isDisplayingSheet && !AccountManager.shared.anyAccountHasNetNewsWireNewsSubscription() && !AccountManager.shared.activeAccounts.isEmpty
|
||||
}
|
||||
|
||||
if item.action == #selector(sortByNewestArticleOnTop(_:)) || item.action == #selector(sortByOldestArticleOnTop(_:)) {
|
||||
|
@ -446,7 +441,7 @@ import Sparkle
|
|||
}
|
||||
|
||||
if item.action == #selector(showAddFeedWindow(_:)) || item.action == #selector(showAddFolderWindow(_:)) {
|
||||
return !isDisplayingSheet && !accountManager.activeAccounts.isEmpty
|
||||
return !isDisplayingSheet && !AccountManager.shared.activeAccounts.isEmpty
|
||||
}
|
||||
|
||||
#if !MAC_APP_STORE
|
||||
|
@ -526,7 +521,7 @@ import Sparkle
|
|||
@IBAction func refreshAll(_ sender: Any?) {
|
||||
|
||||
Task {
|
||||
await accountManager.refreshAll(errorHandler: ErrorHandler.present)
|
||||
await AccountManager.shared.refreshAll(errorHandler: ErrorHandler.present)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -601,7 +596,7 @@ import Sparkle
|
|||
}
|
||||
|
||||
@IBAction func addAppNews(_ sender: Any?) {
|
||||
if accountManager.anyAccountHasNetNewsWireNewsSubscription() {
|
||||
if AccountManager.shared.anyAccountHasNetNewsWireNewsSubscription() {
|
||||
return
|
||||
}
|
||||
addFeed(AccountManager.netNewsWireNewsURL, name: "NetNewsWire News")
|
||||
|
@ -698,12 +693,12 @@ import Sparkle
|
|||
extension AppDelegate {
|
||||
|
||||
@IBAction func debugSearch(_ sender: Any?) {
|
||||
accountManager.defaultAccount.debugRunSearch()
|
||||
AccountManager.shared.defaultAccount.debugRunSearch()
|
||||
}
|
||||
|
||||
@IBAction func debugDropConditionalGetInfo(_ sender: Any?) {
|
||||
#if DEBUG
|
||||
for account in accountManager.activeAccounts {
|
||||
for account in AccountManager.shared.activeAccounts {
|
||||
account.debugDropConditionalGetInfo()
|
||||
}
|
||||
#endif
|
||||
|
@ -969,7 +964,7 @@ private extension AppDelegate {
|
|||
|
||||
func handleMarkAsRead(articlePathInfo: ArticlePathInfo) {
|
||||
|
||||
guard let accountID = articlePathInfo.accountID, let account = accountManager.existingAccount(with: accountID) else {
|
||||
guard let accountID = articlePathInfo.accountID, let account = AccountManager.shared.existingAccount(with: accountID) else {
|
||||
os_log(.debug, "No account found from notification.")
|
||||
return
|
||||
}
|
||||
|
@ -989,7 +984,7 @@ private extension AppDelegate {
|
|||
|
||||
func handleMarkAsStarred(articlePathInfo: ArticlePathInfo) {
|
||||
|
||||
guard let accountID = articlePathInfo.accountID, let account = accountManager.existingAccount(with: accountID) else {
|
||||
guard let accountID = articlePathInfo.accountID, let account = AccountManager.shared.existingAccount(with: accountID) else {
|
||||
os_log(.debug, "No account found from notification.")
|
||||
return
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import Web
|
|||
import Articles
|
||||
import ArticlesDatabase
|
||||
import Database
|
||||
import Core
|
||||
|
||||
@MainActor public final class AccountManager: UnreadCountProvider {
|
||||
|
||||
|
@ -20,7 +21,11 @@ import Database
|
|||
|
||||
public let defaultAccount: Account
|
||||
|
||||
private let accountsFolder: String
|
||||
private let accountsFolderURL: URL
|
||||
private var accountsFolder: String {
|
||||
accountsFolderURL.path
|
||||
}
|
||||
|
||||
private var accountsDictionary = [String: Account]()
|
||||
|
||||
private let defaultAccountFolderName = "OnMyMac"
|
||||
|
@ -91,19 +96,12 @@ import Database
|
|||
return CombinedRefreshProgress(downloadProgressArray: downloadProgressArray)
|
||||
}
|
||||
|
||||
public init(accountsFolder: String) {
|
||||
public init() {
|
||||
|
||||
self.accountsFolder = accountsFolder
|
||||
self.accountsFolderURL = AppConfig.dataSubfolder(named: "Accounts")
|
||||
|
||||
// The local "On My Mac" account must always exist, even if it's empty.
|
||||
let localAccountFolder = (accountsFolder as NSString).appendingPathComponent("OnMyMac")
|
||||
do {
|
||||
try FileManager.default.createDirectory(atPath: localAccountFolder, withIntermediateDirectories: true, attributes: nil)
|
||||
}
|
||||
catch {
|
||||
assertionFailure("Could not create folder for OnMyMac account.")
|
||||
abort()
|
||||
}
|
||||
let localAccountFolder = AppConfig.ensureSubfolder(named: "OnMyMac", folderURL: self.accountsFolderURL).path
|
||||
|
||||
defaultAccount = Account(dataFolder: localAccountFolder, type: .onMyMac, accountID: defaultAccountIdentifier)
|
||||
accountsDictionary[defaultAccount.accountID] = defaultAccount
|
||||
|
|
|
@ -22,7 +22,7 @@ public final class AppConfig {
|
|||
let bundleIdentifier = (Bundle.main.infoDictionary!["CFBundleIdentifier"]! as! String)
|
||||
let tempFolder = (NSTemporaryDirectory() as NSString).appendingPathComponent(bundleIdentifier)
|
||||
folderURL = URL(fileURLWithPath: tempFolder, isDirectory: true)
|
||||
try! FileManager.default.createDirectory(at: folderURL, withIntermediateDirectories: true, attributes: nil)
|
||||
createFolderIfNecessary(folderURL)
|
||||
}
|
||||
|
||||
return folderURL
|
||||
|
@ -30,36 +30,38 @@ public final class AppConfig {
|
|||
|
||||
/// Returns URL to subfolder in cache folder (creating the folder if it doesn’t exist)
|
||||
public static func cacheSubfolder(named name: String) -> URL {
|
||||
subfolder(name, in: cacheFolder)
|
||||
ensureSubfolder(named: name, folderURL: cacheFolder)
|
||||
}
|
||||
|
||||
public static let dataFolder: URL = {
|
||||
|
||||
#if os(macOS)
|
||||
var dataFolder = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
|
||||
var dataFolder = try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
|
||||
dataFolder = dataFolder.appendingPathComponent(appName)
|
||||
|
||||
try! FileManager.default.createDirectory(at: dataFolder, withIntermediateDirectories: true, attributes: nil)
|
||||
return dataFolder
|
||||
|
||||
#elseif os(iOS)
|
||||
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
var dataFolder = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
#endif
|
||||
|
||||
createFolderIfNecessary(dataFolder)
|
||||
return dataFolder
|
||||
}()
|
||||
|
||||
/// Returns URL to subfolder in data folder (creating the folder if it doesn’t exist)
|
||||
public static func dataSubfolder(named name: String) -> URL {
|
||||
subfolder(name, in: dataFolder)
|
||||
ensureSubfolder(named: name, folderURL: dataFolder)
|
||||
}
|
||||
|
||||
public static func ensureSubfolder(named name: String, folderURL: URL) -> URL {
|
||||
|
||||
let folder = folderURL.appendingPathComponent(name, isDirectory: true)
|
||||
createFolderIfNecessary(folder)
|
||||
return folder
|
||||
}
|
||||
}
|
||||
|
||||
private extension AppConfig {
|
||||
|
||||
static func subfolder(_ name: String, in folderURL: URL) -> URL {
|
||||
|
||||
let folder = folderURL.appendingPathComponent(name, isDirectory: true)
|
||||
try! FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true, attributes: nil)
|
||||
return folder
|
||||
static func createFolderIfNecessary(_ folderURL: URL) {
|
||||
try! FileManager.default.createDirectory(at: folderURL, withIntermediateDirectories: true, attributes: nil)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,16 +53,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
var isSyncArticleStatusRunning = false
|
||||
var isWaitingForSyncTasks = false
|
||||
|
||||
let accountManager: AccountManager
|
||||
|
||||
override init() {
|
||||
|
||||
let documentFolder = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
let documentAccountsFolder = documentFolder.appendingPathComponent("Accounts").absoluteString
|
||||
let documentAccountsFolderPath = String(documentAccountsFolder.suffix(from: documentAccountsFolder.index(documentAccountsFolder.startIndex, offsetBy: 7)))
|
||||
self.accountManager = AccountManager(accountsFolder: documentAccountsFolderPath)
|
||||
AccountManager.shared = accountManager
|
||||
|
||||
super.init()
|
||||
|
||||
appDelegate = self
|
||||
|
@ -79,8 +71,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
os_log("Is first run.", log: log, type: .info)
|
||||
}
|
||||
|
||||
if isFirstRun && !accountManager.anyAccountHasAtLeastOneFeed() {
|
||||
let localAccount = accountManager.defaultAccount
|
||||
if isFirstRun && !AccountManager.shared.anyAccountHasAtLeastOneFeed() {
|
||||
let localAccount = AccountManager.shared.defaultAccount
|
||||
DefaultFeedsImporter.importDefaultFeeds(account: localAccount)
|
||||
}
|
||||
|
||||
|
@ -92,7 +84,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
initializeHomeScreenQuickActions()
|
||||
|
||||
Task { @MainActor in
|
||||
self.unreadCount = accountManager.unreadCount
|
||||
self.unreadCount = AccountManager.shared.unreadCount
|
||||
}
|
||||
|
||||
UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .sound, .alert]) { (granted, error) in
|
||||
|
@ -121,7 +113,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) async -> UIBackgroundFetchResult {
|
||||
|
||||
resumeDatabaseProcessingIfNecessary()
|
||||
await accountManager.receiveRemoteNotification(userInfo: userInfo)
|
||||
await AccountManager.shared.receiveRemoteNotification(userInfo: userInfo)
|
||||
suspendApplication()
|
||||
return .newData
|
||||
}
|
||||
|
@ -136,7 +128,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
MultilineUILabelSizer.emptyCache()
|
||||
SingleLineUILabelSizer.emptyCache()
|
||||
IconImageCache.shared.emptyCache()
|
||||
accountManager.emptyCaches()
|
||||
AccountManager.shared.emptyCaches()
|
||||
|
||||
Task.detached {
|
||||
await DownloadWithCacheManager.shared.cleanupCache()
|
||||
|
@ -147,7 +139,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
|
||||
@objc func unreadCountDidChange(_ note: Notification) {
|
||||
if note.object is AccountManager {
|
||||
unreadCount = accountManager.unreadCount
|
||||
unreadCount = AccountManager.shared.unreadCount
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,13 +157,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
}
|
||||
|
||||
Task { @MainActor in
|
||||
await self.accountManager.refreshAll(errorHandler: errorHandler)
|
||||
await AccountManager.shared.refreshAll(errorHandler: errorHandler)
|
||||
}
|
||||
}
|
||||
|
||||
func resumeDatabaseProcessingIfNecessary() {
|
||||
if accountManager.isSuspended {
|
||||
accountManager.resumeAll()
|
||||
if AccountManager.shared.isSuspended {
|
||||
AccountManager.shared.resumeAll()
|
||||
os_log("Application processing resumed.", log: self.log, type: .info)
|
||||
}
|
||||
}
|
||||
|
@ -191,12 +183,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
Task { @MainActor in
|
||||
if let lastRefresh = AppDefaults.shared.lastRefresh {
|
||||
if Date() > lastRefresh.addingTimeInterval(15 * 60) {
|
||||
await accountManager.refreshAll(errorHandler: ErrorHandler.log)
|
||||
await AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log)
|
||||
} else {
|
||||
await accountManager.syncArticleStatusAll()
|
||||
await AccountManager.shared.syncArticleStatusAll()
|
||||
}
|
||||
} else {
|
||||
await accountManager.refreshAll(errorHandler: ErrorHandler.log)
|
||||
await AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -281,7 +273,7 @@ private extension AppDelegate {
|
|||
return
|
||||
}
|
||||
|
||||
if accountManager.refreshInProgress || isSyncArticleStatusRunning {
|
||||
if AccountManager.shared.refreshInProgress || isSyncArticleStatusRunning {
|
||||
os_log("Waiting for sync to finish...", log: self.log, type: .info)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
|
||||
self?.waitToComplete(completion: completion)
|
||||
|
@ -318,7 +310,7 @@ private extension AppDelegate {
|
|||
}
|
||||
|
||||
Task { @MainActor in
|
||||
await self.accountManager.syncArticleStatusAll()
|
||||
await AccountManager.shared.syncArticleStatusAll()
|
||||
completeProcessing()
|
||||
}
|
||||
}
|
||||
|
@ -326,8 +318,8 @@ private extension AppDelegate {
|
|||
func suspendApplication() {
|
||||
guard UIApplication.shared.applicationState == .background else { return }
|
||||
|
||||
accountManager.suspendNetworkAll()
|
||||
accountManager.suspendDatabaseAll()
|
||||
AccountManager.shared.suspendNetworkAll()
|
||||
AccountManager.shared.suspendDatabaseAll()
|
||||
ArticleThemeDownloader.cleanUp()
|
||||
|
||||
CoalescingQueue.standard.performCallsImmediately()
|
||||
|
@ -382,11 +374,11 @@ private extension AppDelegate {
|
|||
os_log("Woken to perform account refresh.", log: self.log, type: .info)
|
||||
|
||||
Task { @MainActor in
|
||||
if self.accountManager.isSuspended {
|
||||
self.accountManager.resumeAll()
|
||||
if AccountManager.shared.isSuspended {
|
||||
AccountManager.shared.resumeAll()
|
||||
}
|
||||
await self.accountManager.refreshAll(errorHandler: ErrorHandler.log)
|
||||
if !self.accountManager.isSuspended {
|
||||
await AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log)
|
||||
if !AccountManager.shared.isSuspended {
|
||||
try? WidgetDataEncoder.shared.encodeWidgetData()
|
||||
self.suspendApplication()
|
||||
os_log("Account refresh operation completed.", log: self.log, type: .info)
|
||||
|
@ -418,7 +410,7 @@ private extension AppDelegate {
|
|||
|
||||
resumeDatabaseProcessingIfNecessary()
|
||||
|
||||
guard let accountID = articlePathInfo.accountID, let account = accountManager.existingAccount(with: accountID) else {
|
||||
guard let accountID = articlePathInfo.accountID, let account = AccountManager.shared.existingAccount(with: accountID) else {
|
||||
os_log(.debug, "No account found from notification.")
|
||||
return
|
||||
}
|
||||
|
@ -438,7 +430,7 @@ private extension AppDelegate {
|
|||
self.prepareAccountsForBackground()
|
||||
|
||||
try? await account.syncArticleStatus()
|
||||
if !self.accountManager.isSuspended {
|
||||
if !AccountManager.shared.isSuspended {
|
||||
try? WidgetDataEncoder.shared.encodeWidgetData()
|
||||
self.prepareAccountsForBackground()
|
||||
self.suspendApplication()
|
||||
|
@ -454,7 +446,7 @@ private extension AppDelegate {
|
|||
|
||||
resumeDatabaseProcessingIfNecessary()
|
||||
|
||||
guard let accountID = articlePathInfo.accountID, let account = accountManager.existingAccount(with: accountID) else {
|
||||
guard let accountID = articlePathInfo.accountID, let account = AccountManager.shared.existingAccount(with: accountID) else {
|
||||
os_log(.debug, "No account found from notification.")
|
||||
return
|
||||
}
|
||||
|
@ -473,7 +465,7 @@ private extension AppDelegate {
|
|||
try? await account.markArticles(articles, statusKey: .starred, flag: true)
|
||||
|
||||
try? await account.syncArticleStatus()
|
||||
if !self.accountManager.isSuspended {
|
||||
if !AccountManager.shared.isSuspended {
|
||||
try? WidgetDataEncoder.shared.encodeWidgetData()
|
||||
self.prepareAccountsForBackground()
|
||||
self.suspendApplication()
|
||||
|
|
Loading…
Reference in New Issue