54 lines
2.0 KiB
Swift
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()
|
|
}
|
|
}
|
|
}
|