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:
gib committed 2026-11-01 22:32:19 -05:00
1 parent 2a127327bf
commit a5222e14e8
13 files changed
+384 -52

No files matched your search

+58 -4
View File
@@ -30,18 +30,26 @@ final class AppModel {
private(set) var settling: Set<String> = []
/// Why settling an item failed, by item id. Its row shows the message.
private(set) var settleErrors: [String: String] = [:]
/// Every thread in Recent, for the full Recent screen, which loads it when it appears.
private(set) var allRecent: [FeedItem]?
private(set) var allRecentError: String?
/// True while Settle all is out. Completed Recent threads are hidden meanwhile.
private(set) var settlingAll = false
/// Why Settle all failed, or how many threads it could not settle. The Recent header shows it.
private(set) var settleAllError: String?
private var apnsToken: String?
private var refreshing = false
/// The counts the complication was last told about.
private var sharedSummary: Summary?
init(mode: Mode, feed: Feed? = nil, path: [Route] = []) {
init(mode: Mode, feed: Feed? = nil, allRecent: [FeedItem]? = nil, path: [Route] = []) {
self.mode = mode
self.feed = feed
self.allRecent = allRecent
self.path = path
}
/// `-demo` starts in demo mode. `-screen new|status|notifications`, `-host <hostId>` or
/// `-demo` starts in demo mode. `-screen new|recent|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]) {
@@ -55,6 +63,7 @@ final class AppModel {
var path: [Route] = []
switch argument("-screen") {
case "new": path = [.newThread]
case "recent": path = [.recent]
case "status": path = [.status]
case "notifications": path = [.status, .notifications]
default: break
@@ -182,11 +191,56 @@ final class AppModel {
for (id, error) in failures {
settleErrors[id] = message(for: error)
}
feed?.recent.removeAll { ids.contains($0.id) && failures[$0.id] == nil }
let settled = ids.filter { failures[$0] == nil }
removeFromRecent(settled.count) { settled.contains($0.id) }
settling.subtract(ids)
await refreshFeed()
}
/// Recent threads to show: those not being settled.
func visibleRecent(_ items: [FeedItem]) -> [FeedItem] {
items.filter { !settling.contains($0.id) && !(settlingAll && $0.phase == .completed) }
}
/// `GET /v1/recent`, for the full Recent screen.
func loadAllRecent() async {
guard mode != .unpaired else { return }
do {
allRecent = try await api.recent().items
allRecentError = nil
} catch {
allRecentError = message(for: error)
}
}
/// Settles every completed thread in Recent with one call, including those past the feed's first
/// 10. They leave at once. Then the feed, and the full list when it was loaded, refresh.
func settleAllRecent() async {
settlingAll = true
settleAllError = nil
do {
let result = try await api.settleRecent()
removeFromRecent(result.settled) { $0.phase == .completed }
if result.failed > 0 {
settleAllError = result.failed == 1 ? "1 thread didn't settle." : "\(result.failed) threads didn't settle."
}
} catch {
settleAllError = message(for: error)
}
settlingAll = false
await refreshFeed()
if allRecent != nil { await loadAllRecent() }
}
/// Drops settled threads from the feed and the full list until the next refresh confirms it.
private func removeFromRecent(_ count: Int, where settled: (FeedItem) -> Bool) {
allRecent?.removeAll(where: settled)
guard var feed else { return }
feed.recent.removeAll(where: settled)
feed.recentTotal = max(feed.recent.count, feed.recentTotal - count)
self.feed = feed
}
func open(_ route: Route) {
path = [route]
}
@@ -262,6 +316,6 @@ final class AppModel {
extension AppModel {
/// A demo model with the fixture feed already loaded, for previews.
static func preview(path: [Route] = []) -> AppModel {
AppModel(mode: .demo, feed: DemoFixtures.previewFeed(), path: path)
AppModel(mode: .demo, feed: DemoFixtures.previewFeed(), allRecent: DemoFixtures.previewRecent(), path: path)
}
}