watch: polish refresh status and compact live activity
This commit is contained in:
@@ -112,6 +112,7 @@ private enum WatchRefreshState {
|
|||||||
case stale(lastUpdated: String)
|
case stale(lastUpdated: String)
|
||||||
case checking
|
case checking
|
||||||
case requested(lastUpdated: String)
|
case requested(lastUpdated: String)
|
||||||
|
case checkedNoNewData(lastUpdated: String)
|
||||||
case needsPhone
|
case needsPhone
|
||||||
case noData
|
case noData
|
||||||
|
|
||||||
@@ -120,7 +121,8 @@ private enum WatchRefreshState {
|
|||||||
case .idle(let lastUpdated): return "Updated \(lastUpdated)"
|
case .idle(let lastUpdated): return "Updated \(lastUpdated)"
|
||||||
case .stale(let lastUpdated): return "Cache from \(lastUpdated)"
|
case .stale(let lastUpdated): return "Cache from \(lastUpdated)"
|
||||||
case .checking: return "Checking…"
|
case .checking: return "Checking…"
|
||||||
case .requested(let lastUpdated): return "Requested · \(lastUpdated)"
|
case .requested(let lastUpdated): return "Updated · \(lastUpdated)"
|
||||||
|
case .checkedNoNewData(let lastUpdated): return "No newer data · \(lastUpdated)"
|
||||||
case .needsPhone: return "Refresh on iPhone"
|
case .needsPhone: return "Refresh on iPhone"
|
||||||
case .noData: return "No cached prices yet"
|
case .noData: return "No cached prices yet"
|
||||||
}
|
}
|
||||||
@@ -132,6 +134,7 @@ private enum WatchRefreshState {
|
|||||||
case .stale: return .yellow
|
case .stale: return .yellow
|
||||||
case .checking: return .yellow
|
case .checking: return .yellow
|
||||||
case .requested: return .green
|
case .requested: return .green
|
||||||
|
case .checkedNoNewData: return .secondary
|
||||||
case .needsPhone: return .orange
|
case .needsPhone: return .orange
|
||||||
case .noData: return .secondary
|
case .noData: return .secondary
|
||||||
}
|
}
|
||||||
@@ -143,6 +146,7 @@ private enum WatchRefreshState {
|
|||||||
case .stale: return "exclamationmark.circle"
|
case .stale: return "exclamationmark.circle"
|
||||||
case .checking: return "arrow.triangle.2.circlepath"
|
case .checking: return "arrow.triangle.2.circlepath"
|
||||||
case .requested: return "checkmark.circle"
|
case .requested: return "checkmark.circle"
|
||||||
|
case .checkedNoNewData: return "clock.badge.exclamationmark"
|
||||||
case .needsPhone: return "iphone"
|
case .needsPhone: return "iphone"
|
||||||
case .noData: return "tray"
|
case .noData: return "tray"
|
||||||
}
|
}
|
||||||
@@ -551,6 +555,7 @@ struct ContentView: View {
|
|||||||
|
|
||||||
private func runCheckNow() {
|
private func runCheckNow() {
|
||||||
refreshState = .checking
|
refreshState = .checking
|
||||||
|
let previousLastRefresh = WatchFuelCache.loadLastRefresh()
|
||||||
let requestDate = Date()
|
let requestDate = Date()
|
||||||
WatchFuelCache.requestRefresh(requestDate)
|
WatchFuelCache.requestRefresh(requestDate)
|
||||||
WatchSyncManager.shared.requestRefresh(at: requestDate)
|
WatchSyncManager.shared.requestRefresh(at: requestDate)
|
||||||
@@ -560,8 +565,16 @@ struct ContentView: View {
|
|||||||
if WatchFuelCache.wasRefreshHandled(since: requestDate) {
|
if WatchFuelCache.wasRefreshHandled(since: requestDate) {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
reloadFromCache()
|
reloadFromCache()
|
||||||
let label = WatchFuelCache.loadLastRefresh().map(relativeRefreshText(for:)) ?? "just now"
|
let currentLastRefresh = WatchFuelCache.loadLastRefresh()
|
||||||
refreshState = .requested(lastUpdated: label)
|
if let currentLastRefresh,
|
||||||
|
currentLastRefresh >= requestDate,
|
||||||
|
currentLastRefresh > (previousLastRefresh ?? .distantPast) {
|
||||||
|
refreshState = .requested(lastUpdated: relativeRefreshText(for: currentLastRefresh))
|
||||||
|
} else if let currentLastRefresh {
|
||||||
|
refreshState = .checkedNoNewData(lastUpdated: relativeRefreshText(for: currentLastRefresh))
|
||||||
|
} else {
|
||||||
|
refreshState = .noData
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,9 +147,9 @@ private struct FuelBoardLiveActivityView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Minimal strip for small space (CarPlay small / Watch smart stack):
|
/// Minimal strip for small space (CarPlay small / Watch smart stack):
|
||||||
/// fuel type + bold price on one line, station · distance below.
|
/// fuel type + distance + bold price on one line, station name below.
|
||||||
/// Deliberately no app name and no "Tap for directions" — CarPlay is
|
/// Deliberately no app name and no full address — the watch-sized slot is
|
||||||
/// display-only, and the user's asks here are just fuel + price + distance.
|
/// too tight, so the second line is reserved for the station name only.
|
||||||
private var compactBody: some View {
|
private var compactBody: some View {
|
||||||
VStack(alignment: .leading, spacing: 3) {
|
VStack(alignment: .leading, spacing: 3) {
|
||||||
HStack(spacing: 5) {
|
HStack(spacing: 5) {
|
||||||
@@ -157,16 +157,21 @@ private struct FuelBoardLiveActivityView: View {
|
|||||||
.font(.caption.bold())
|
.font(.caption.bold())
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
Spacer(minLength: 4)
|
Spacer(minLength: 4)
|
||||||
|
Text(context.state.distanceText)
|
||||||
|
.font(.caption2)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.lineLimit(1)
|
||||||
|
Spacer(minLength: 4)
|
||||||
FuelStore.priceTextAttributed(context.state.pricePence,
|
FuelStore.priceTextAttributed(context.state.pricePence,
|
||||||
style: context.state.priceDisplayStyle,
|
style: context.state.priceDisplayStyle,
|
||||||
size: 15, weight: .bold)
|
size: 15, weight: .bold)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
}
|
}
|
||||||
Text("\(context.state.stationName) · \(context.state.distanceText)")
|
Text(context.state.stationName)
|
||||||
.font(.system(size: 9))
|
.font(.system(size: 10, weight: .medium))
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.minimumScaleFactor(0.7)
|
.minimumScaleFactor(0.75)
|
||||||
.truncationMode(.tail)
|
.truncationMode(.tail)
|
||||||
}
|
}
|
||||||
.padding(8)
|
.padding(8)
|
||||||
|
|||||||
Reference in New Issue
Block a user