TeraTodayReconciler.swift (2096B)
1 import Foundation 2 3 struct TeraTodayReconcileRequest: Sendable { 4 let context: TeraLocalNetwork 5 let asOfUnixSeconds: UInt64 6 let cardIDs: [String] 7 var expectedGeneration: UInt64? 8 let calendar: TeraViewerCalendarContext 9 } 10 11 enum TeraTodayReconciler { 12 static func unique<Value: Identifiable>(_ values: [Value]) -> [Value] { 13 var identifiers = Set<Value.ID>() 14 return values.filter { identifiers.insert($0.id).inserted } 15 } 16 17 /// Sequential batches share one generation. No changed snapshot is partially 18 /// installed and no newly discovered identity is inserted into a scrolled feed. 19 static func read( 20 client: TeraRuntimeClient, context: TeraLocalNetwork, 21 calendar: TeraViewerCalendarContext, cards: [TeraTodayCard] 22 ) async throws -> TeraTodayPage { 23 let identifiers = cards.map(\.id) 24 var current: [String: TeraTodayCard] = [:] 25 var generation: UInt64? 26 for offset in stride(from: 0, to: max(identifiers.count, 1), by: 100) { 27 try Task.checkCancellation() 28 let end = min(offset + 100, identifiers.count) 29 let page = try await client.reconcileToday(request: TeraTodayReconcileRequest( 30 context: context, asOfUnixSeconds: calendar.asOfUnixSeconds, 31 cardIDs: Array(identifiers[offset ..< end]), expectedGeneration: generation, calendar: calendar 32 )) 33 guard let received = page.projectionGeneration, 34 generation == nil || generation == received, page.calendar == calendar, 35 page.asOfUnixSeconds == calendar.asOfUnixSeconds 36 else { throw TeraTodayReconciliationError.changed } 37 generation = received 38 for card in page.items { 39 guard identifiers[offset ..< end].contains(card.id), 40 current.updateValue(card, forKey: card.id) == nil 41 else { throw TeraTodayReconciliationError.changed } 42 } 43 } 44 return TeraTodayPage( 45 asOfUnixSeconds: calendar.asOfUnixSeconds, items: identifiers.compactMap { current[$0] }, 46 nextCursor: nil, projectionGeneration: generation, calendar: calendar 47 ) 48 } 49 } 50 51 enum TeraTodayReconciliationError: Error { 52 case changed 53 }