NetNewsWire/Evergreen/Data/AccountManager.swift

223 lines
5.0 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// AccountManager.swift
// Evergreen
//
// Created by Brent Simmons on 7/18/15.
// Copyright © 2015 Ranchero Software, LLC. All rights reserved.
//
import Foundation
import RSCore
import DataModel
import LocalAccount
let AccountsDidChangeNotification = "AccountsDidChangeNotification"
private let localAccountFolderName = "OnMyMac"
private let localAccountIdentifier = "OnMyMac"
final class AccountManager: UnreadCountProvider {
static let sharedInstance = AccountManager()
private let accountsFolder = RSDataSubfolder(nil, "Accounts")!
private var accountsDictionary = [String: Account]()
let localAccount: Account
var unreadCount = 0 {
didSet {
postUnreadCountDidChangeNotification()
}
}
var accounts: [Account] {
get {
return Array(accountsDictionary.values)
}
}
var sortedAccounts: [Account] {
get {
return accountsSortedByName()
}
}
var refreshInProgress: Bool {
get {
for oneAccount in accountsDictionary.values {
if oneAccount.refreshInProgress {
return true
}
}
return false
}
}
init() {
// 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 localAccountSettingsFile = accountFilePathWithFolder(localAccountFolder)
localAccount = LocalAccount(settingsFile: localAccountSettingsFile, dataFolder: localAccountFolder, identifier: localAccountIdentifier)
accountsDictionary[localAccount.identifier] = localAccount
readNonLocalAccountsFromDisk()
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
}
// MARK: API
func existingAccountWithIdentifier(_ identifier: String) -> Account? {
return accountsDictionary[identifier]
}
func refreshAll() {
accounts.forEach { (oneAccount) in
oneAccount.refreshAll()
}
}
func anyAccountHasAtLeastOneFeed() -> Bool {
for oneAccount in accounts {
if oneAccount.hasAtLeastOneFeed {
return true
}
}
return false
}
func anyAccountHasFeedWithURL(_ urlString: String) -> Bool {
for oneAccount in accounts {
if let _ = oneAccount.existingFeedWithURL(urlString) {
return true
}
}
return false
}
// MARK: UnreadCountProvider
func updateUnreadCount() {
let updatedUnreadCount = calculateUnreadCount(accounts)
if updatedUnreadCount != unreadCount {
unreadCount = updatedUnreadCount
}
}
// MARK: Notifications
dynamic func unreadCountDidChange(_ notification: Notification) {
guard let _ = notification.object as? Account else {
return
}
updateUnreadCount()
}
// MARK: Private
private func createAccount(_ accountSpecifier: AccountSpecifier) -> Account? {
return nil
}
private func createAccount(_ filename: String) -> Account? {
let folderPath = (accountsFolder as NSString).appendingPathComponent(filename)
if let accountSpecifier = AccountSpecifier(folderPath: folderPath) {
return createAccount(accountSpecifier)
}
return nil
}
private func readNonLocalAccountsFromDisk() {
var filenames: [String]?
do {
filenames = try FileManager.default.contentsOfDirectory(atPath: accountsFolder)
}
catch {
print("Error reading Accounts folder: \(error)")
return
}
filenames?.forEach { (oneFilename) in
guard oneFilename != localAccountFolderName else {
return
}
if let oneAccount = createAccount(oneFilename) {
accountsDictionary[oneAccount.identifier] = oneAccount
}
}
}
private func accountsSortedByName() -> [Account] {
// LocalAccount is first.
return accounts.sorted { (account1, account2) -> Bool in
if account1 === localAccount {
return true
}
if account2 === localAccount {
return false
}
//TODO: Use localizedCaseInsensitiveCompare:
return account1.nameForDisplay < account2.nameForDisplay
}
}
}
private let accountDataFileName = "AccountData.plist"
private func accountFilePathWithFolder(_ folderPath: String) -> String {
return NSString(string: folderPath).appendingPathComponent(accountDataFileName)
}
private struct AccountSpecifier {
let type: String
let identifier: String
let folderPath: String
let folderName: String
let dataFilePath: String
init?(folderPath: String) {
self.folderPath = folderPath
self.folderName = NSString(string: folderPath).lastPathComponent
let nameComponents = self.folderName.components(separatedBy: "-")
let satisfyCompilerFolderName = self.folderName
assert(nameComponents.count == 2, "Cant determine account info from \(satisfyCompilerFolderName)")
if nameComponents.count != 2 {
return nil
}
self.type = nameComponents[0]
self.identifier = nameComponents[1]
self.dataFilePath = accountFilePathWithFolder(self.folderPath)
}
}