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.
61 lines
2.2 KiB
Swift
61 lines
2.2 KiB
Swift
// Screens the app can open, and the `wristcode://` URLs the complication uses to open them.
|
|
|
|
import Foundation
|
|
|
|
enum Route: Hashable, Sendable {
|
|
case thread(hostId: String, threadId: String)
|
|
case newThread
|
|
/// The status overview, and from it one host or the notifications page.
|
|
case status
|
|
case host(id: String)
|
|
case notifications
|
|
|
|
static let scheme = "wristcode"
|
|
|
|
/// `wristcode://thread/<hostId>/<threadId>`, `wristcode://new`, `wristcode://status`,
|
|
/// `wristcode://host/<hostId>` or `wristcode://notifications`.
|
|
var url: URL {
|
|
var components = URLComponents()
|
|
components.scheme = Self.scheme
|
|
switch self {
|
|
case let .thread(hostId, threadId):
|
|
components.host = "thread"
|
|
components.percentEncodedPath = "/" + [hostId, threadId]
|
|
.map { $0.addingPercentEncoding(withAllowedCharacters: .alphanumerics) ?? $0 }
|
|
.joined(separator: "/")
|
|
case .newThread:
|
|
components.host = "new"
|
|
case .status:
|
|
components.host = "status"
|
|
case let .host(id):
|
|
components.host = "host"
|
|
components.percentEncodedPath = "/" + (id.addingPercentEncoding(withAllowedCharacters: .alphanumerics) ?? id)
|
|
case .notifications:
|
|
components.host = "notifications"
|
|
}
|
|
return components.url!
|
|
}
|
|
|
|
/// The route a `wristcode://` URL names, or nil when it names none.
|
|
init?(url: URL) {
|
|
guard url.scheme == Self.scheme,
|
|
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
|
|
else { return nil }
|
|
let parts = components.percentEncodedPath.split(separator: "/").map { $0.removingPercentEncoding ?? String($0) }
|
|
switch components.host {
|
|
case "thread" where parts.count == 2 && parts.allSatisfy { !$0.isEmpty }:
|
|
self = .thread(hostId: parts[0], threadId: parts[1])
|
|
case "new":
|
|
self = .newThread
|
|
case "status":
|
|
self = .status
|
|
case "host" where parts.count == 1 && !parts[0].isEmpty:
|
|
self = .host(id: parts[0])
|
|
case "notifications":
|
|
self = .notifications
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
}
|