Files
wristcode/watch/WristCode/Views/Loading.swift
T
gib edc4b88990
Gateway / check (push) Successful in 9s
Gateway / image (push) Successful in 22s
Watch: snooze finished threads, name the files a file approval would edit, Settle on finished notifications, an instant launch from the saved feed, plain Loading states, and no more cancelled errors
Gateway: an approval push for a file change says which files it would edit
2026-11-02 14:45:34 -05:00

54 lines
2.0 KiB
Swift

// How every screen that loads data shows it: a static "Loading" before the first answer, the error
// and Try again when there is nothing to show, and once content is on screen, a small dim line under
// it when a refresh fails. Content is never replaced by an error.
import SwiftUI
/// The placeholder before a screen's first answer, the same as the feed's. Static, with no spinner.
struct LoadingText: View {
var body: some View {
Text("Loading")
.font(.system(size: 15, weight: .semibold))
.foregroundStyle(Theme.secondaryText)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 8)
.padding(.top, 4)
}
}
/// What a screen shows under its content about loading. `loaded` is true once content is on screen.
/// Before then: Loading, or the error with Try again when `retry` is set. After: only a failed
/// refresh's error, small and dim.
struct LoadStatus: View {
var loaded: Bool
var error: String?
var retry: (() async -> Void)?
var body: some View {
if let error {
if loaded {
Text(error)
.font(.system(size: 11))
.foregroundStyle(Theme.done)
.lineLimit(3)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 8)
} else {
VStack(alignment: .leading, spacing: 8) {
Text(error)
.font(.system(size: 12))
.foregroundStyle(Theme.secondaryText)
.padding(.horizontal, 8)
if let retry {
Button("Try again") { Task { await retry() } }
.buttonStyle(PillButtonStyle(fill: Theme.control, foreground: .white))
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
} else if !loaded {
LoadingText()
}
}
}