TeraSubmissionOperationModels.swift (4638B)
1 import Foundation 2 3 struct TeraSubmissionMedia: Sendable, Equatable { 4 let opaqueReference: String 5 let progress: TeraDraftMediaStatus 6 var authorizations: [TeraUploadAttemptIdentity] = [] 7 } 8 9 struct TeraUploadAttemptIdentity: Sendable, Equatable { 10 let operationID: String 11 let revision: UInt64? 12 let expirationUnixSeconds: UInt64 13 } 14 15 struct TeraSubmissionUploadRenewal: Sendable, Equatable { 16 let priorRevision: UInt64 17 let priorAttempt: String 18 let nativeFailed: Bool 19 } 20 21 struct TeraSubmissionStatus: Sendable, Equatable, Identifiable, CustomStringConvertible, CustomDebugStringConvertible { 22 let request: TeraSubmissionRequest 23 let intentID: String 24 let operationID: String 25 let revision: UInt64 26 let captured: TeraComposerDraft 27 let state: TeraOutboxState 28 let committedAtUnixMilliseconds: UInt64 29 let updatedAtUnixMilliseconds: UInt64 30 let media: [TeraSubmissionMedia] 31 let settlement: TeraOperationSettlement 32 let delivery: TeraPublicationEvidence 33 let targetDetails: TeraPublicationTargets 34 let retry: TeraPublicationRetry 35 36 var id: String { 37 operationID 38 } 39 40 var description: String { 41 "TeraSubmissionStatus(\(state.rawValue))" 42 } 43 44 var debugDescription: String { 45 description 46 } 47 48 var summary: String { 49 if delivery.isStopped { 50 return delivery.stoppedSummary 51 } 52 if media.contains(where: \.progress.possibleOrphan) { 53 return "Photo delivery needs attention. A remote copy may exist." 54 } 55 if delivery.state == .unknown { 56 return "Relay delivery outcome is unknown. Check the original request before continuing." 57 } 58 if delivery.state == .partiallyAccepted { 59 return "Some saved relays accepted this publication. Remaining delivery is not confirmed." 60 } 61 if case .needsAction = retry, delivery.state != .accepted { 62 return "Saved publication needs attention." 63 } 64 return state.label 65 } 66 67 var canOfferStop: Bool { 68 !delivery.isStopped && (state.canCancel || delivery.unresolvedClaims) 69 } 70 71 /// Presentation of an explicit action, never authority to bypass Rust policy. 72 var canOfferContinuation: Bool { 73 retry.mayStart && !delivery.isStopped && state != .complete && state != .cancelled && state != .terminal 74 } 75 76 var mediaSummary: String { 77 let verified = media.filter { $0.progress.stage == .verified }.count 78 let orphans = media.filter(\.progress.possibleOrphan).count 79 let uncertain = media.filter { $0.progress.stage == .uploading }.count 80 let summary = "\(verified) of \(media.count) photos verified" 81 if delivery.isStopped, uncertain > 0 { 82 return "\(summary); \(uncertain) upload outcome remains uncertain. Remote copies may exist." 83 } 84 return orphans > 0 ? "\(summary); \(orphans) possible orphan" : summary 85 } 86 87 /// Translation only: these URLs come from the validated Rust intent. 88 var preparedMedia: [TeraPreparedMedia] { 89 zip(captured.form.editingValue.media, media).map { source, state in 90 TeraPreparedMedia(opaqueReference: source.opaqueReference, remoteURL: state.progress.url, 91 sha256: source.sha256, mediaType: source.mediaType, byteSize: source.byteSize, 92 width: source.width, height: source.height, alt: source.alt, 93 preparedAtUnixSeconds: source.preparedAtUnixSeconds) 94 } 95 } 96 } 97 98 enum TeraSubmissionSummaryState: Sendable, Equatable { 99 case reserved 100 case operation(intentID: String, operationID: String, revision: UInt64, state: TeraOutboxState) 101 } 102 103 enum TeraSubmissionRepairReason: Sendable, Equatable { 104 case unsupportedSchema 105 case corruptRecord 106 case needsAttention 107 } 108 109 struct TeraSubmissionSummary: Sendable, Equatable, Identifiable { 110 let request: TeraSubmissionRequest 111 let reservationID: String 112 let reservedAtUnixMilliseconds: UInt64 113 let state: TeraSubmissionSummaryState 114 var id: String { 115 reservationID 116 } 117 } 118 119 enum TeraSubmissionEntry: Sendable, Equatable, Identifiable { 120 case submission(TeraSubmissionSummary) 121 case repair(key: String, revision: UInt64, reason: TeraSubmissionRepairReason) 122 123 var id: String { 124 switch self { 125 case let .submission(summary): summary.id 126 case let .repair(key, _, _): key 127 } 128 } 129 } 130 131 struct TeraSubmissionPage: Sendable, Equatable { 132 let scope: TeraComposerScope 133 let entries: [TeraSubmissionEntry] 134 let nextCursor: String? 135 } 136 137 struct TeraSubmissionMediaRequest: Sendable, Equatable { 138 let request: TeraSubmissionRequest 139 let expectedRevision: UInt64 140 let media: TeraPreparedMediaHandle 141 } 142 143 struct TeraSubmissionUploadJob: Sendable, Equatable { 144 let submission: TeraSubmissionStatus 145 let transfer: TeraNativeTransferJob 146 }