223 lines
5.0 KiB
Swift
223 lines
5.0 KiB
Swift
//
|
||
// 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, "Can’t determine account info from \(satisfyCompilerFolderName)")
|
||
if nameComponents.count != 2 {
|
||
return nil
|
||
}
|
||
|
||
self.type = nameComponents[0]
|
||
self.identifier = nameComponents[1]
|
||
|
||
self.dataFilePath = accountFilePathWithFolder(self.folderPath)
|
||
}
|
||
}
|
||
|
||
|