// // MainView.swift // meterios // // (c) 2025 Martin "maride" Dessauer // import SwiftUI import OSLog struct MainView: View { @AppStorage("meteHostAddr") private var meteHostAddr = "" @AppStorage("meteUserID") private var meteUserID = -1 @State private var availableDrinks: Array = [] @State private var outOfOrderDrinks: Array = [] @State private var user: User = User.init(meteID: 0, displayName: "", displayImageURL: "", balance: 0.00) @Environment(\.dismiss) var dismiss // Update pulls drinks off the backend and updates the user info (balance, name, profile pic) func update() { Task { do { user = try await BackendConnector().GetUser(baseAddr: meteHostAddr, userID: meteUserID) let drinks = try await BackendConnector().GetDrinks(baseAddr: meteHostAddr) availableDrinks = drinks.filter({ $0.active }) outOfOrderDrinks = drinks.filter({ !$0.active }) } catch { let _ = Logger().error("Failed to update from \(meteHostAddr): \(error)") } } } var body: some View { ScrollViewReader { scrollViewReader in NavigationView { VStack(alignment: .leading) { List () { // Account Section(header: Text("Account").id("topAnchor")) { NavigationLink(destination: UserDetailView(user: user)) { Button(action: { dismiss() }) { HStack { // Profile Image AsyncImage( url: URL(string: user.displayImageURL), content: { image in image.resizable().aspectRatio(contentMode: .fill).frame(width: 64, height: 64).clipShape(Circle()) }, placeholder: { ZStack { Circle().fill(.gray).frame(width: 64, height: 64) ProgressView() } } ).fixedSize() Spacer(minLength: 16.0) // Account, Address & Balance VStack(alignment: .leading) { HStack { Text(user.displayName).fontWeight(.bold) Divider() Text(String(format: "%.2f€", user.balance)).foregroundStyle(user.balance > 0 ? .green : .red) }.fixedSize() Text("\(meteHostAddr)").fontWeight(.thin) }.fixedSize() }.fixedSize() } } } // Drinks list if !availableDrinks.isEmpty { Section(header: Text("Available")) { ForEach(availableDrinks) { drink in NavigationLink(drink.name) { DrinkDetail(drink: drink).onDisappear(perform: { update() scrollViewReader.scrollTo("topAnchor", anchor: .top) }) } } } } if !outOfOrderDrinks.isEmpty { Section(header: Text("Out of stock")) { ForEach(outOfOrderDrinks) { drink in NavigationLink(drink.name) { DrinkDetail(drink: drink) } } } } }.navigationTitle("Meterios").refreshable(action: { update() }) } }.onAppear(perform: { update() }) } } }