Files

121 lines
4.0 KiB
Swift

//
// Account.swift
// meterios
//
// (c) 2025 Martin "maride" Dessauer
//
import Foundation
import OSLog
enum AccountState: Int {
case Unknown // No connection attempt has been made yet; basically the startup state
case Disconnected // Not connected as per user request
case Failure // Last connection failed
case Connected // Last connection succeeded
}
/// **Account** resembles an user account in Mete and provides relevant functions to Meterios, like encoding and decoding, the set-up client for this very account and favourites management
@Observable class Account: Identifiable, Codable {
public let id: UUID = UUID()
private(set) var state: AccountState = .Unknown
private let client: MeteClient
public let address: String
public let userID: Int
private(set) var displayName: String?
private(set) var displayImageURL: URL?
private(set) var balance: Float?
private var favourites: [Int]
enum CodingKeys: String, CodingKey { case address, userID, favourites }
init(address: String, userID: Int, favourites: [Int] = []) {
self.address = address
self.userID = userID
self.favourites = favourites
self.client = MeteClient(address: address, userID: userID)
}
required convenience init(from decoder : Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let address = try container.decode(String.self, forKey: .address)
let userID = try container.decode(Int.self, forKey: .userID)
let favourites = try container.decode([Int].self, forKey: .favourites)
self.init(address: address, userID: userID, favourites: favourites)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(address, forKey: .address)
try container.encode(userID, forKey: .userID)
try container.encode(favourites, forKey: .favourites)
}
// Ping updates the state of the account by trying to connect to the backend
func Ping() async {
do {
state = try await client.CanConnect() ? .Connected : .Disconnected
} catch {
state = .Failure
}
}
// Refresh updates the account metadata; failure to do so changes the account state
func Refresh() async {
do {
let user = try await client.GetUser()
displayName = user.displayName
displayImageURL = user.displayImageURL
balance = user.balance
state = .Connected
} catch {
let _ = Logger().error("Unable to refresh user account: \(error)")
state = .Failure
}
}
// GetDrinks returns all drinks for this account and backend
func GetDrinks() async throws -> Array<MeteClientDrink> {
return try await client.GetDrinks()
}
// BookDrink books the specified drink using this account
func BookDrink(_ drinkID: Int) async throws {
try await client.BookDrink(drinkID)
await Refresh()
}
// IsFavourite returns true if the given drink ID is a favourite
func IsFavourite(_ drinkID: Int) -> Bool {
return favourites.contains(drinkID)
}
// AddFavourite adds the given drink ID to the list of favourites
func AddFavourite(_ drinkID: Int) {
if !IsFavourite(drinkID) {
favourites.append(drinkID)
}
}
// RemoveFavourite removes the given drink ID from the favourites list
func RemoveFavourite(_ drinkID: Int) {
let index = favourites.firstIndex(where: { $0 == drinkID })
if index != nil {
favourites.remove(at: index!)
}
}
// SetFavourite sets if the given drink ID is a favourite
func SetFavourite(drinkID: Int, shouldBeFavourite: Bool) {
if IsFavourite(drinkID) && !shouldBeFavourite {
RemoveFavourite(drinkID)
} else if !IsFavourite(drinkID) && shouldBeFavourite {
AddFavourite(drinkID)
}
}
}