field_ios

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

TeraRestoreRecoveryStore.swift (2299B)


      1 import Foundation
      2 
      3 /// Each action is explicit and bounded; no worker authorizes restored delivery.
      4 @MainActor
      5 final class TeraRestoreRecoveryStore: ObservableObject {
      6   @Published private(set) var status: TeraRestoreStatus?
      7   @Published private(set) var isWorking = false
      8   @Published private(set) var message: String?
      9   @Published private(set) var reviewedInventory: String?
     10   private let client: TeraRuntimeClient
     11   private var generation = TeraSessionGeneration.initial
     12 
     13   init(client: TeraRuntimeClient) {
     14     self.client = client
     15   }
     16 
     17   func invalidate() {
     18     generation = generation.invalidated()
     19     status = nil
     20     message = nil
     21     reviewedInventory = nil
     22   }
     23 
     24   func load() async {
     25     await perform(.load)
     26   }
     27 
     28   func checkNext() async {
     29     await perform(.check)
     30   }
     31 
     32   func review() async {
     33     await perform(.review)
     34   }
     35 
     36   func resume() async {
     37     await perform(.resume)
     38   }
     39 
     40   private enum Action { case load, check, review, resume }
     41 
     42   private func perform(_ action: Action) async {
     43     guard !isWorking, generation.isActive else { return }
     44     isWorking = true
     45     let selected = generation
     46     defer { isWorking = false }
     47     do {
     48       let digest = try await execute(action)
     49       let current = try await client.restoreStatus()
     50       guard generation == selected, !Task.isCancelled else { return }
     51       status = current
     52       reviewedInventory = digest
     53       message = nil
     54     } catch {
     55       guard generation == selected, !Task.isCancelled else { return }
     56       reviewedInventory = nil
     57       message = "The recovery check could not finish. Saved work remains on this device; check again before resuming."
     58     }
     59   }
     60 
     61   private func execute(_ action: Action) async throws -> String? {
     62     switch action {
     63     case .load: return nil
     64     case .check:
     65       guard let target = status?.targets.first(where: { $0.observation == nil })
     66         ?? status?.targets.first(where: { $0.observation == .incomplete }) else { return nil }
     67       try await client.reconcileRestoredTarget(target)
     68       return nil
     69     case .review: return try await client.reviewRestoredWork()
     70     case .resume:
     71       guard let reviewedInventory else { throw TeraComposerAcknowledgment.unconfirmed }
     72       try await client.resumeRestoredWork(reviewedInventory: reviewedInventory)
     73       return nil
     74     }
     75   }
     76 }