TeraNativeRepairView.swift (3026B)
1 import SwiftUI 2 3 struct TeraNativeRepairView: View { 4 @ObservedObject var store: TeraNativeRepairStore 5 6 var body: some View { 7 if store.message != nil || store.isRunning || !store.issues.isEmpty { 8 Section("Saved photo recovery") { 9 TeraNativeRepairProgressView(message: store.message, remaining: store.progress?.remaining) 10 if store.issues.count == TeraNativeRepairStore.previewLimit { 11 Text("Showing up to \(TeraNativeRepairStore.previewLimit) repair notices.") 12 } 13 ForEach(Array(store.issues.enumerated()), id: \.element.id) { index, issue in 14 TeraNativeRepairActions(issue: issue, index: index, isRunning: store.isRunning) { store.check(issue) } 15 } 16 if store.isRunning { 17 ProgressView("Checking saved photos") 18 } else { 19 Button("Check saved photos again") { store.retry() } 20 .accessibilityIdentifier("tera.add.recovery.check") 21 } 22 } 23 .accessibilityIdentifier("tera.add.recovery.issues") 24 } 25 } 26 } 27 28 struct TeraNativeRepairProgressView: View { 29 let message: String? 30 let remaining: Int? 31 32 var body: some View { 33 if let message { 34 Text(message).accessibilityIdentifier("tera.add.recovery.message") 35 } 36 if let remaining, remaining > 0 { 37 Text("\(remaining) more saved transfer records remain to be checked in this pass.") 38 .accessibilityIdentifier("tera.add.recovery.remaining") 39 } 40 } 41 } 42 43 struct TeraNativeRepairActions: View { 44 let issue: TeraNativeRecoveryIssue 45 let index: Int 46 let isRunning: Bool 47 let check: () -> Void 48 49 var body: some View { 50 VStack(alignment: .leading, spacing: 8) { 51 Text("Saved photo \(index + 1)").font(.headline) 52 Text(issue.reason.message) 53 .accessibilityIdentifier("tera.add.recovery.reason.\(issue.key)") 54 if issue.status == nil { 55 Text("This recovery status could not be saved. Check again when local storage is available.") 56 .foregroundStyle(.secondary) 57 } 58 Button("Check this photo’s saved evidence", action: check) 59 .disabled(isRunning) 60 .buttonStyle(.borderless) 61 .accessibilityIdentifier("tera.add.recovery.check.\(issue.key)") 62 Text("Checking keeps the evidence and does not start another upload. A missing or mismatched request must be restored before recovery can succeed.") 63 .font(.footnote) 64 } 65 .accessibilityElement(children: .contain) 66 } 67 } 68 69 extension TeraNativeRecoveryReason { 70 var message: String { 71 switch self { 72 case .missingParent: "The saved request for this photo could not be found. Its upload evidence has been kept." 73 case .invalidParent: "The saved request for this photo needs repair. Its upload evidence has been kept." 74 case .associationMismatch: "This upload does not match its saved request. Its evidence has been kept." 75 case .outcomeUnconfirmed: "This photo’s upload result is not yet confirmed. Its evidence has been kept." 76 case .resolved: "This photo has been recovered." 77 } 78 } 79 }