// // ServerDetailView.swift // iKeyMon // // Created by tracer on 30.03.25. // import SwiftUI struct ServerDetailView: View { @Binding var server: Server var isFetching: Bool @AppStorage("showIntervalIndicator") private var showIntervalIndicator: Bool = true private var showPlaceholder: Bool { server.info == nil } @State private var progress: Double = 0 let timer = Timer.publish(every: 1.0 / 60.0, on: .main, in: .common).autoconnect() var body: some View { VStack(spacing: 0) { if showIntervalIndicator { ProgressView(value: progress) .progressViewStyle(LinearProgressViewStyle()) .padding(.horizontal) .frame(height: 2) } ZStack(alignment: .topTrailing) { VStack(spacing: 0) { Spacer().frame(height: 6) TabView { GeneralView(server: resolvedBinding) .tabItem { Text("General").unredacted() } ResourcesView(server: resolvedBinding) .tabItem { Text("Resources").unredacted() } ServicesView(server: resolvedBinding) .tabItem { Text("Services").unredacted() } } .redacted(reason: showPlaceholder ? .placeholder : []) .shimmering(active: showPlaceholder) } if showPlaceholder || isFetching { LoadingBadge() .padding() } } .padding(0) } .onReceive(timer) { _ in guard showIntervalIndicator else { return } withAnimation(.linear(duration: 1.0 / 60.0)) { progress += 1.0 / (60.0 * 60.0) if progress >= 1 { progress = 0 } } } } private var resolvedBinding: Binding { if showPlaceholder { return .constant(placeholderServer()) } return $server } private func placeholderServer() -> Server { Server(id: server.id, hostname: server.hostname, info: .placeholder, pingable: server.pingable) } } #Preview { ServerDetailView( server: .constant(Server(id: UUID(), hostname: "preview.example.com", info: ServerInfo.placeholder)), isFetching: false ) } private struct LoadingBadge: View { var body: some View { HStack(spacing: 6) { ProgressView() .scaleEffect(0.5) Text("Fetching latest data…") .font(.caption) } .padding(8) .background(.ultraThinMaterial, in: Capsule()) } }