121 lines
4.0 KiB
Swift
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)
|
|
}
|
|
}
|
|
}
|