field_ios

In-the-field app for Radroots on iOS
git clone https://radroots.dev/git/field_ios.git
Log | Files | Refs | README | LICENSE

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 }