Watch: keep Recent past 10 behind an N more row with a full list, and settle all in one call
This commit is contained in:
1 parent
2a127327bf
commit
a5222e14e8
13 files changed
+384
-52
No files matched your search
@@ -51,6 +51,10 @@ protocol GatewayAPI: Sendable {
|
||||
func stop(hostId: String, threadId: String) async throws
|
||||
/// Settles a thread, as in T3. A settled thread that finished or failed leaves the feed.
|
||||
func settle(hostId: String, threadId: String) async throws
|
||||
/// Every unsettled finished thread, including those past the feed's first 10.
|
||||
func recent() async throws -> RecentResponse
|
||||
/// Settles every completed thread in Recent. Failed threads stay.
|
||||
func settleRecent() async throws -> SettleRecentResponse
|
||||
func projects() async throws -> ProjectsResponse
|
||||
func newThread(_ request: NewThreadRequest) async throws -> NewThreadResponse
|
||||
func registerPush(_ registration: PushRegistration) async throws
|
||||
@@ -101,6 +105,14 @@ struct GatewayClient: GatewayAPI {
|
||||
try await callIgnoringBody("POST", ["v1", "threads", hostId, threadId, "settle"], body: nil as SendMessageRequest?)
|
||||
}
|
||||
|
||||
func recent() async throws -> RecentResponse {
|
||||
try await call("GET", ["v1", "recent"])
|
||||
}
|
||||
|
||||
func settleRecent() async throws -> SettleRecentResponse {
|
||||
try await call("POST", ["v1", "recent", "settle"])
|
||||
}
|
||||
|
||||
func projects() async throws -> ProjectsResponse {
|
||||
try await call("GET", ["v1", "projects"])
|
||||
}
|
||||
|
||||
@@ -167,8 +167,51 @@ struct FeedItem: Codable, Hashable, Sendable, Identifiable {
|
||||
struct Feed: Codable, Hashable, Sendable {
|
||||
var needsYou: [FeedItem]
|
||||
var working: [FeedItem]
|
||||
/// Finished or failed threads not settled yet, newest first and at most 10.
|
||||
var recent: [FeedItem]
|
||||
/// How many threads Recent holds in all. `GET /v1/recent` lists the ones past `recent`.
|
||||
var recentTotal: Int
|
||||
var hosts: [HostSummary]
|
||||
|
||||
/// `recentTotal` defaults to the threads in `recent`, as when nothing is left out.
|
||||
init(needsYou: [FeedItem], working: [FeedItem], recent: [FeedItem], recentTotal: Int? = nil, hosts: [HostSummary]) {
|
||||
self.needsYou = needsYou
|
||||
self.working = working
|
||||
self.recent = recent
|
||||
self.recentTotal = recentTotal ?? recent.count
|
||||
self.hosts = hosts
|
||||
}
|
||||
|
||||
/// Recent threads the feed leaves out, which the "N more" row counts.
|
||||
var moreRecent: Int { max(0, recentTotal - recent.count) }
|
||||
}
|
||||
|
||||
extension Feed {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case needsYou, working, recent, recentTotal, hosts
|
||||
}
|
||||
|
||||
/// `recentTotal` is newer than the rest, so a gateway that leaves it out has nothing past `recent`.
|
||||
init(from decoder: any Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.init(
|
||||
needsYou: try container.decode([FeedItem].self, forKey: .needsYou),
|
||||
working: try container.decode([FeedItem].self, forKey: .working),
|
||||
recent: try container.decode([FeedItem].self, forKey: .recent),
|
||||
recentTotal: try container.decodeIfPresent(Int.self, forKey: .recentTotal),
|
||||
hosts: try container.decode([HostSummary].self, forKey: .hosts))
|
||||
}
|
||||
}
|
||||
|
||||
/// `GET /v1/recent`: every unsettled finished thread, newest first. Older ones may lack `lastMessage`.
|
||||
struct RecentResponse: Codable, Hashable, Sendable {
|
||||
var items: [FeedItem]
|
||||
}
|
||||
|
||||
/// `POST /v1/recent/settle`. `failed` counts threads T3 refused or whose host was offline.
|
||||
struct SettleRecentResponse: Codable, Hashable, Sendable {
|
||||
var settled: Int
|
||||
var failed: Int
|
||||
}
|
||||
|
||||
/// `GET /v1/summary`: the counts behind the complication.
|
||||
|
||||
@@ -5,6 +5,8 @@ import Foundation
|
||||
enum Route: Hashable, Sendable {
|
||||
case thread(hostId: String, threadId: String)
|
||||
case newThread
|
||||
/// Every thread in Recent, past the feed's first 10.
|
||||
case recent
|
||||
/// The status overview, and from it one host or the notifications page.
|
||||
case status
|
||||
case host(id: String)
|
||||
@@ -12,7 +14,7 @@ enum Route: Hashable, Sendable {
|
||||
|
||||
static let scheme = "wristcode"
|
||||
|
||||
/// `wristcode://thread/<hostId>/<threadId>`, `wristcode://new`, `wristcode://status`,
|
||||
/// `wristcode://thread/<hostId>/<threadId>`, `wristcode://new`, `wristcode://recent`, `wristcode://status`,
|
||||
/// `wristcode://host/<hostId>` or `wristcode://notifications`.
|
||||
var url: URL {
|
||||
var components = URLComponents()
|
||||
@@ -25,6 +27,8 @@ enum Route: Hashable, Sendable {
|
||||
.joined(separator: "/")
|
||||
case .newThread:
|
||||
components.host = "new"
|
||||
case .recent:
|
||||
components.host = "recent"
|
||||
case .status:
|
||||
components.host = "status"
|
||||
case let .host(id):
|
||||
@@ -47,6 +51,8 @@ enum Route: Hashable, Sendable {
|
||||
self = .thread(hostId: parts[0], threadId: parts[1])
|
||||
case "new":
|
||||
self = .newThread
|
||||
case "recent":
|
||||
self = .recent
|
||||
case "status":
|
||||
self = .status
|
||||
case "host" where parts.count == 1 && !parts[0].isEmpty:
|
||||
|
||||
Reference in new issue
Block a user