wake-ios/wake/View/Examples/MediaUpload.swift
2025-08-21 19:39:44 +08:00

217 lines
8.2 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
import os.log
///
struct ExampleView: View {
///
@State private var showMediaPicker = false
///
@State private var selectedMedia: [MediaType] = []
///
@State private var uploadStatus: [String: UploadStatus] = [:]
///
private let uploader = ImageUploadService()
///
private enum UploadStatus: Equatable {
case pending
case uploading(progress: Double)
case completed(fileId: String)
case failed(Error)
static func == (lhs: UploadStatus, rhs: UploadStatus) -> Bool {
switch (lhs, rhs) {
case (.pending, .pending):
return true
case (.uploading(let lhsProgress), .uploading(let rhsProgress)):
return lhsProgress == rhsProgress
case (.completed(let lhsId), .completed(let rhsId)):
return lhsId == rhsId
default:
return false
}
}
var description: String {
switch self {
case .pending: return "等待上传"
case .uploading(let progress): return "上传中 \(Int(progress * 100))%"
case .completed(let fileId): return "上传完成 (ID: \(fileId.prefix(8))...)"
case .failed(let error): return "上传失败: \(error.localizedDescription)"
}
}
}
var body: some View {
NavigationView {
VStack(spacing: 20) {
//
Button(action: {
showMediaPicker = true
}) {
Label("选择媒体", systemImage: "photo.on.rectangle")
.font(.headline)
.frame(maxWidth: .infinity)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.padding(.horizontal)
//
if !selectedMedia.isEmpty {
VStack(spacing: 10) {
Text("已选择 \(selectedMedia.count) 个媒体文件")
.font(.headline)
//
List {
ForEach(0..<selectedMedia.count, id: \.self) { index in
let media = selectedMedia[index]
let mediaId = "\(index)"
let status = uploadStatus[mediaId] ?? .pending
HStack {
//
MediaThumbnailView(media: media, onDelete: nil)
.frame(width: 60, height: 60)
VStack(alignment: .leading, spacing: 4) {
Text(media.isVideo ? "视频" : "图片")
.font(.subheadline)
//
Text(status.description)
.font(.caption)
.foregroundColor(statusColor(status))
//
if case .uploading(let progress) = status {
ProgressView(value: progress, total: 1.0)
.progressViewStyle(LinearProgressViewStyle())
.frame(height: 4)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
.padding(.vertical, 8)
}
}
.frame(height: 300)
}
.padding(.top)
} else {
Text("未选择任何媒体")
.foregroundColor(.secondary)
.padding(.top, 50)
}
Spacer()
}
.navigationTitle("媒体上传")
.sheet(isPresented: $showMediaPicker) {
MediaPickerWithLogging(
selectedMedia: $selectedMedia,
selectionLimit: 5,
onDismiss: {
//
showMediaPicker = false
//
uploadAllMedia()
}
)
}
.onChange(of: selectedMedia) { _ in
// selectedMedia
uploadStatus.removeAll()
}
}
}
//
private func uploadMedia(_ media: ImageUploadService.MediaType, id: String) {
print("🔄 开始处理媒体: \(id)")
uploadStatus[id] = .uploading(progress: 0)
uploader.uploadMedia(
media,
progress: { progress in
print("📊 上传进度 (\(id)): \(progress.current)%")
uploadStatus[id] = .uploading(progress: progress.progress)
},
completion: { result in
switch result {
case .success(let uploadResult):
print("✅ 上传成功 (\(id)): \(uploadResult.fileId)")
uploadStatus[id] = .completed(fileId: uploadResult.fileId)
case .failure(let error):
print("❌ 上传失败 (\(id)): \(error.localizedDescription)")
uploadStatus[id] = .failed(error)
}
}
)
}
//
private func uploadAllMedia() {
print("🔄 开始批量上传 \(selectedMedia.count) 个文件")
for (index, media) in selectedMedia.enumerated() {
let id = "\(index)"
if case .pending = uploadStatus[id] ?? .pending {
// Convert MediaType to ImageUploadService.MediaType
let uploadMediaType: ImageUploadService.MediaType
switch media {
case .image(let image):
uploadMediaType = .image(image)
case .video(let url, let thumbnail):
uploadMediaType = .video(url, thumbnail)
}
uploadMedia(uploadMediaType, id: id)
}
}
}
//
private func statusColor(_ status: UploadStatus) -> Color {
switch status {
case .pending: return .secondary
case .uploading: return .blue
case .completed: return .green
case .failed: return .red
}
}
}
///
private struct MediaThumbnailView: View {
let media: MediaType
let onDelete: (() -> Void)?
var body: some View {
ZStack(alignment: .topTrailing) {
if let thumbnail = media.thumbnail {
Image(uiImage: thumbnail)
.resizable()
.scaledToFill()
.frame(width: 80, height: 80)
.cornerRadius(8)
.clipped()
if media.isVideo {
Image(systemName: "video.fill")
.foregroundColor(.white)
.padding(4)
.background(Color.black.opacity(0.6))
.clipShape(Circle())
.padding(4)
}
}
}
}
}
#Preview {
ExampleView()
.environmentObject(AuthState.shared)
}