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
@@ -37,18 +37,29 @@ final class AppModel {
|
||||
self.path = path
|
||||
}
|
||||
|
||||
/// `-demo` starts in demo mode. `-screen new` or `-thread <hostId>/<threadId>` opens that
|
||||
/// screen, which is how the screenshots in watch/screenshots are taken.
|
||||
/// `-demo` starts in demo mode. `-screen new|status|notifications`, `-host <hostId>` or
|
||||
/// `-thread <hostId>/<threadId>` opens that screen, which is how the screenshots in
|
||||
/// watch/screenshots are taken.
|
||||
convenience init(launchArguments args: [String]) {
|
||||
DeviceStore.migrateToSharedGroup()
|
||||
let demo = args.contains("-demo")
|
||||
let mode: Mode = demo ? .demo : DeviceStore.load().map(Mode.paired) ?? .unpaired
|
||||
var path: [Route] = []
|
||||
if demo, let i = args.firstIndex(of: "-screen"), i + 1 < args.count, args[i + 1] == "new" {
|
||||
path = [.newThread]
|
||||
func argument(_ flag: String) -> String? {
|
||||
guard demo, let i = args.firstIndex(of: flag), i + 1 < args.count else { return nil }
|
||||
return args[i + 1]
|
||||
}
|
||||
if demo, let i = args.firstIndex(of: "-thread"), i + 1 < args.count {
|
||||
let parts = args[i + 1].split(separator: "/", maxSplits: 1).map(String.init)
|
||||
var path: [Route] = []
|
||||
switch argument("-screen") {
|
||||
case "new": path = [.newThread]
|
||||
case "status": path = [.status]
|
||||
case "notifications": path = [.status, .notifications]
|
||||
default: break
|
||||
}
|
||||
if let hostId = argument("-host") {
|
||||
path = [.status, .host(id: hostId)]
|
||||
}
|
||||
if let thread = argument("-thread") {
|
||||
let parts = thread.split(separator: "/", maxSplits: 1).map(String.init)
|
||||
if parts.count == 2 { path = [.thread(hostId: parts[0], threadId: parts[1])] }
|
||||
}
|
||||
self.init(mode: mode, path: path)
|
||||
@@ -101,6 +112,19 @@ final class AppModel {
|
||||
path = []
|
||||
}
|
||||
|
||||
/// Revokes the device token on the gateway, then signs out. Signs out even when the gateway
|
||||
/// cannot be reached, since the token is forgotten either way.
|
||||
func unpair() async {
|
||||
if isPaired { try? await api.unpair() }
|
||||
signOut()
|
||||
}
|
||||
|
||||
/// The gateway's host name, such as `wristcode.gbrown.org`. Nil in demo mode.
|
||||
var gatewayHost: String? {
|
||||
if case let .paired(credentials) = mode { return credentials.gatewayURL.host() }
|
||||
return nil
|
||||
}
|
||||
|
||||
/// The message to show for a failed call. A 401 sends the app back to pairing and returns nil.
|
||||
func message(for error: any Error) -> String? {
|
||||
if error is CancellationError { return nil }
|
||||
|
||||
Reference in new issue
Block a user