// // AccountManager.swift // meterios // // (c) 2025 Martin "maride" Dessauer // import Foundation import SwiftUI import OSLog /// **AccountManager** holds multiple accounts and takes care of storing them on the device. @Observable class AccountManager { // default contains a ready-to-use instance of AccountManager, read from AppStorage static public var `default` = AccountManager() // accounts holds all saved accounts private(set) var Accounts: Array = [] { didSet { writeToRaw() } } private var selectedAccount: Int? { didSet { writeToRaw() } } // init creates a new AccountManager, reading saved account data from AppStorage init() { self.Accounts = AccountManager.readFromRaw() // Read account selection @AppStorage("selectedAccount") var selectedAccount: Int? self.selectedAccount = selectedAccount } // readFromRaw reads the current accounts from the stored data and transforms it to a handy Account Array static private func readFromRaw() -> Array { @AppStorage("accounts") var rawAccs = Data() do { return try JSONDecoder().decode([Account].self, from: rawAccs) } catch { let _ = Logger().error("Error parsing stored accounts from JSON: \(error)") rawAccs = Data() return [] } } // writeToRaw writes the current account data to the AppStorage private func writeToRaw() { @AppStorage("accounts") var rawAccs = Data() do { rawAccs = try JSONEncoder().encode(Accounts) } catch { let _ = Logger().error("Error writing current accounts to JSON: \(error)") } } // CurrentAccount returns the currently selected account if there is one func CurrentAccount() -> Account? { if selectedAccount == nil { return nil } if Accounts.count > selectedAccount! { return Accounts[selectedAccount!] } return nil } // SelectAccount sets the current main account as returned by CurrentAccount func SelectAccount(_ account: Account) { SelectAccount(IndexOfAccount(account)) } // SelectAccount sets the current main account as returned by CurrentAccount func SelectAccount(_ accountID: Int?) { self.selectedAccount = accountID // Store account selection @AppStorage("selectedAccount") var selectedAccount: Int? selectedAccount = self.selectedAccount } // AddAccount adds the given account if it doesn't exist yet func AddAccount(_ account: Account) { if IndexOfAccount(account) == nil { Accounts.append(account) } } // IndexOfAccount returns the index of the specified account identified by address and user ID func IndexOfAccount(_ account: Account) -> Int? { return Accounts.firstIndex(where: { $0.address == account.address && $0.userID == account.userID }) } // RemoveAccount removes the specified account func RemoveAccount(_ account: Account) { if IndexOfAccount(account) != nil { RemoveAccount(IndexOfAccount(account)!) } } // RemoveAccount removes the account at the specified index func RemoveAccount(_ index: Int) { Accounts.remove(at: index) } // RefreshAll refreshes all accounts held by this manager instance func RefreshAll() async { for acc in Accounts { await acc.Refresh() } } }