Improve startup UX with placeholders and prefetch

This commit is contained in:
Micha
2025-11-19 23:28:12 +01:00
parent 562023519a
commit 6b8d458605
4 changed files with 150 additions and 31 deletions

View File

@@ -12,6 +12,10 @@ struct ServerDetailView: View {
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()
@@ -24,37 +28,33 @@ struct ServerDetailView: View {
.frame(height: 2)
}
if server.info == nil {
ProgressView("Fetching server info...")
.frame(maxWidth: .infinity, maxHeight: .infinity)
} else {
ZStack(alignment: .topTrailing) {
VStack(spacing: 0) {
Spacer().frame(height: 6)
TabView {
GeneralView(server: $server)
.tabItem {
Text("General")
}
ResourcesView(server: $server)
.tabItem {
Text("Resources")
}
ServicesView(server: $server)
.tabItem {
Text("Services")
}
}
}
if isFetching {
ProgressView()
.scaleEffect(0.5)
.padding()
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)
}
.padding(0)
}
.onReceive(timer) { _ in
guard showIntervalIndicator else { return }
@@ -64,6 +64,17 @@ struct ServerDetailView: View {
}
}
}
private var resolvedBinding: Binding<Server> {
if showPlaceholder {
return .constant(placeholderServer())
}
return $server
}
private func placeholderServer() -> Server {
Server(id: server.id, hostname: server.hostname, info: .placeholder, pingable: server.pingable)
}
}
#Preview {
@@ -72,3 +83,16 @@ struct ServerDetailView: View {
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())
}
}