import SwiftUI import UniformTypeIdentifiers public struct DropZoneView: View { @State private var isDragging = false @State private var uploadResult: UploadResult? @State private var error: String? @State private var isUploading = false @State private var uploadProgress: Double = 0 public let uploadManager: UploadManager public init(uploadManager: UploadManager) { self.uploadManager = uploadManager } public var body: some View { VStack(spacing: 20) { Image(systemName: "arrow.up.doc.fill") .font(.system(size: 48)) .foregroundColor(isDragging ? .blue : .secondary) Text("Drop files here") .font(.title2) if isUploading { ProgressView(value: uploadProgress) .frame(width: 180) Text("\(Int(uploadProgress * 100))%") .font(.caption) .foregroundColor(.secondary) } else if let result = uploadResult { VStack(spacing: 8) { Image(systemName: "checkmark.circle.fill") .font(.system(size: 32)) .foregroundColor(.green) Text("Uploaded \(result.filename)") .font(.headline) Text("URL copied to clipboard") .font(.caption) .foregroundColor(.secondary) Text(result.url.absoluteString) .font(.caption2) .foregroundColor(.blue) .lineLimit(1) .truncationMode(.middle) .padding(.horizontal) } } else if let error = error { VStack(spacing: 8) { Image(systemName: "xmark.circle.fill") .font(.system(size: 32)) .foregroundColor(.red) Text("Upload failed") .font(.headline) Text(error) .font(.caption) .foregroundColor(.secondary) } } } .frame(width: 300, height: 250) .background( RoundedRectangle(cornerRadius: 12) .fill(isDragging ? Color.blue.opacity(0.1) : Color.secondary.opacity(0.1)) .overlay( RoundedRectangle(cornerRadius: 12) .strokeBorder(isDragging ? Color.blue : Color.secondary.opacity(0.3), style: StrokeStyle(lineWidth: 2, dash: [8])) ) ) .onDrop(of: [.fileURL], isTargeted: $isDragging) { providers in guard let provider = providers.first else { return false } _ = provider.loadObject(ofClass: URL.self) { url, error in guard let url = url else { return } Task { await MainActor.run { isUploading = true uploadProgress = 0 self.error = nil self.uploadResult = nil } do { let result = try await uploadFileResolvingDuplicates(url, onProgress: { progress in Task { @MainActor in uploadProgress = min(max(progress, 0), 1) } }) await MainActor.run { uploadResult = result isUploading = false uploadProgress = 0 } } catch { await MainActor.run { self.error = error.localizedDescription isUploading = false uploadProgress = 0 } } } } return true } } private func uploadFileResolvingDuplicates( _ url: URL, onProgress: UploadProgressHandler? ) async throws -> UploadResult { var duplicateAction: UploadDuplicateAction? while true { do { return try await uploadManager.uploadFile( url, duplicateAction: duplicateAction, onProgress: onProgress ) } catch let error as APIError where error.isUploadConflict { guard let action = promptForUploadDuplicate(error, filename: url.lastPathComponent) else { throw UploadCancelledError() } duplicateAction = action } } } } #Preview { // This preview won't work without a real UploadManager Text("DropZoneView Preview") }