Watch: status, environment and notifications screens
A gear on the feed opens a status overview that leads with what is wrong: offline hosts, hosts on a fallback route, tokens expiring within 7 days, and push problems, with healthy hosts folded into one row. Each host opens its route ladder with Reconnect. The notifications page shows registration, the last push, Send test and Unpair. Demo mode gains a Laptop on its VPN fallback, and snapshot tests render the three screens.
This commit is contained in:
1 parent
9707daf1fe
commit
7ca0362745
17 files changed
+1063
-14
No files matched your search
@@ -1,4 +1,5 @@
|
||||
// Mock B colors and the shared card. Amber means it needs you, teal means working, grey means done.
|
||||
// Mock B colors and the shared card. Amber means it needs you, teal means working, grey means done,
|
||||
// and red means broken.
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@@ -10,6 +11,7 @@ enum Theme {
|
||||
static let control = Color(red: 0.173, green: 0.173, blue: 0.180) // #2c2c2e
|
||||
static let secondaryText = Color(red: 0.682, green: 0.682, blue: 0.698) // #aeaeb2
|
||||
static let bodyText = Color(red: 0.898, green: 0.898, blue: 0.918) // #e5e5ea
|
||||
static let bad = Color(red: 1.0, green: 0.420, blue: 0.369) // #ff6b5e
|
||||
}
|
||||
|
||||
extension Phase {
|
||||
@@ -87,6 +89,25 @@ enum Format {
|
||||
return "\(hours / 24) d"
|
||||
}
|
||||
|
||||
/// How long until a date, such as "40 s", "3 min" or "2 h".
|
||||
static func countdown(to date: Date, now: Date = .now) -> String {
|
||||
let seconds = max(0, Int(date.timeIntervalSince(now).rounded()))
|
||||
if seconds < 60 { return "\(seconds) s" }
|
||||
if seconds < 3600 { return "\(seconds / 60) min" }
|
||||
return "\(seconds / 3600) h"
|
||||
}
|
||||
|
||||
/// "expires in 29 days", "expires today" or "expired 2 days ago", for a token.
|
||||
static func expiry(_ date: Date, now: Date = .now) -> String {
|
||||
let days = Int((date.timeIntervalSince(now) / 86_400).rounded(.down))
|
||||
if date < now {
|
||||
let ago = Int(now.timeIntervalSince(date) / 86_400)
|
||||
return ago < 1 ? "expired today" : "expired \(ago) day\(ago == 1 ? "" : "s") ago"
|
||||
}
|
||||
if days < 1 { return "expires today" }
|
||||
return "expires in \(days) day\(days == 1 ? "" : "s")"
|
||||
}
|
||||
|
||||
/// A model id made readable: `claude-opus-5-5` becomes "Claude Opus 5.5".
|
||||
static func modelName(_ id: String) -> String {
|
||||
var words: [String] = []
|
||||
|
||||
Reference in new issue
Block a user