field_ios

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

TeraSubmissionInventory.swift (2080B)


      1 import Foundation
      2 
      3 /// One bounded page and one reader; continuation replaces, never accumulates.
      4 @MainActor
      5 final class TeraSubmissionInventory: ObservableObject {
      6   @Published private(set) var entries: [TeraSubmissionEntry] = []
      7   @Published private(set) var cursor: String?
      8   @Published private(set) var message: String?
      9   @Published private(set) var isLoading = false
     10   private let client: TeraRuntimeClient
     11   private var scope: TeraComposerScope?
     12   private var generation = TeraSessionGeneration.initial
     13   private var worker: Task<Void, Never>?
     14   private var pending: String??
     15 
     16   init(client: TeraRuntimeClient) {
     17     self.client = client
     18   }
     19 
     20   deinit { worker?.cancel() }
     21 
     22   func configure(scope: TeraComposerScope) {
     23     guard self.scope != scope else { return }
     24     stop()
     25     self.scope = scope
     26     entries = []
     27     cursor = nil
     28     message = nil
     29   }
     30 
     31   func stop() {
     32     generation = generation.invalidated()
     33     pending = nil
     34     worker?.cancel()
     35   }
     36 
     37   func start() {
     38     pending = .some(nil); schedule()
     39   }
     40 
     41   func more() {
     42     guard !isLoading, let cursor else { return }
     43     pending = .some(cursor)
     44     schedule()
     45   }
     46 
     47   private func schedule() {
     48     guard worker == nil, let next = pending, let scope, generation.isActive else { return }
     49     pending = nil
     50     isLoading = true
     51     let requested = generation
     52     worker = Task { [weak self] in
     53       guard let self else { return }
     54       do {
     55         let page = try await client.listSubmissions(scope: scope, limit: 100, cursor: next)
     56         guard generation == requested, !Task.isCancelled else { return finish() }
     57         guard page.scope == scope, page.entries.count <= 100 else { throw TeraComposerAcknowledgment.unconfirmed }
     58         entries = page.entries
     59         cursor = page.nextCursor
     60         message = nil
     61       } catch {
     62         if generation == requested, !Task.isCancelled {
     63           message = "Saved submissions could not be listed. Keep editing and retry the list."
     64         }
     65       }
     66       finish()
     67     }
     68   }
     69 
     70   private func finish() {
     71     worker = nil
     72     isLoading = false
     73     schedule()
     74   }
     75 }