diff --git a/wake/View/Components/Upload/MediaPicker.swift b/wake/View/Components/Upload/MediaPicker.swift index e12aa8e..9c4aee1 100644 --- a/wake/View/Components/Upload/MediaPicker.swift +++ b/wake/View/Components/Upload/MediaPicker.swift @@ -57,6 +57,7 @@ struct MediaPicker: UIViewControllerRepresentable { let onDismiss: (() -> Void)? let allowedMediaTypes: MediaTypeFilter let selectionMode: SelectionMode + let onUploadProgress: ((Int, Double) -> Void)? /// 选择模式 enum SelectionMode { @@ -72,18 +73,21 @@ struct MediaPicker: UIViewControllerRepresentable { } init(selectedMedia: Binding<[MediaType]>, - imageSelectionLimit: Int = 10, - videoSelectionLimit: Int = 10, - allowedMediaTypes: MediaTypeFilter = .all, - selectionMode: SelectionMode = .multiple, - onDismiss: (() -> Void)? = nil) { + imageSelectionLimit: Int = 10, + videoSelectionLimit: Int = 10, + allowedMediaTypes: MediaTypeFilter = .all, + selectionMode: SelectionMode = .multiple, + onDismiss: (() -> Void)? = nil, + onUploadProgress: ((Int, Double) -> Void)? = nil) { self._selectedMedia = selectedMedia self.imageSelectionLimit = imageSelectionLimit self.videoSelectionLimit = videoSelectionLimit self.allowedMediaTypes = allowedMediaTypes self.selectionMode = selectionMode self.onDismiss = onDismiss + self.onUploadProgress = onUploadProgress } + func makeUIViewController(context: Context) -> PHPickerViewController { var configuration = PHPickerConfiguration(photoLibrary: PHPhotoLibrary.shared()) @@ -182,17 +186,26 @@ struct MediaPicker: UIViewControllerRepresentable { return } - // 处理选择的媒体 - processSelectedMedia(results: results, picker: picker, processedMedia: &processedMedia) + // 先关闭选择器 + picker.dismiss(animated: true) { [weak self] in + guard let self = self else { return } + + // 处理选中的媒体 + var processedMedia = self.parent.selectionMode == .single ? [] : self.parent.selectedMedia + self.processSelectedMedia(results: results, picker: picker, processedMedia: &processedMedia) + + // 调用 onDismiss 通知外部选择器已关闭 + self.parent.onDismiss?() + } } private func processSelectedMedia(results: [PHPickerResult], - picker: PHPickerViewController, - processedMedia: inout [MediaType]) { + picker: PHPickerViewController, + processedMedia: inout [MediaType]) { let group = DispatchGroup() let mediaCollector = MediaCollector() - for result in results { + for (index, result) in results.enumerated() { let itemProvider = result.itemProvider if itemProvider.hasItemConformingToTypeIdentifier(UTType.image.identifier) { @@ -202,6 +215,10 @@ struct MediaPicker: UIViewControllerRepresentable { processImage(itemProvider: itemProvider) { media in if let media = media { mediaCollector.add(media: media) + // 更新上传进度 + DispatchQueue.main.async { + self.parent.onUploadProgress?(index, 1.0) // 图片直接完成 + } } group.leave() } @@ -212,21 +229,19 @@ struct MediaPicker: UIViewControllerRepresentable { processVideo(itemProvider: itemProvider) { media in if let media = media { mediaCollector.add(media: media) + // 更新上传进度 + DispatchQueue.main.async { + self.parent.onUploadProgress?(index, 1.0) // 视频直接完成 + } } group.leave() } } } - - // Create a local copy of the parent reference - let parent = self.parent - + group.notify(queue: .main) { let finalMedia = mediaCollector.mediaItems - parent.selectedMedia = finalMedia - picker.dismiss(animated: true) { - parent.onDismiss?() - } + self.parent.selectedMedia = finalMedia } } diff --git a/wake/View/Upload/MediaUploadView.swift b/wake/View/Upload/MediaUploadView.swift index da7d876..099c1fe 100644 --- a/wake/View/Upload/MediaUploadView.swift +++ b/wake/View/Upload/MediaUploadView.swift @@ -35,10 +35,11 @@ struct MediaUploadView: View { .padding(.trailing, 16) } .background(Color.themeTextWhiteSecondary) - .padding(.bottom, -24) + .padding(.horizontal) + .padding(.bottom, -20) .zIndex(1) // 确保导航栏在最上层 - // 主体内容 - HStack(spacing: 20) { + + HStack() { Text("The upload process will take approximately 2 minutes. Thank you for your patience. ") .font(Typography.font(for: .caption)) .foregroundColor(.black) @@ -95,9 +96,13 @@ struct MediaUploadView: View { .sheet(isPresented: $showMediaPicker) { MediaPicker( selectedMedia: $uploadManager.selectedMedia, - imageSelectionLimit: 10, - videoSelectionLimit: 10, - onDismiss: handleMediaPickerDismiss + imageSelectionLimit: 20, + videoSelectionLimit: 5, + onDismiss: handleMediaPickerDismiss, + onUploadProgress: { index, progress in + // 更新单个文件的上传进度 + print("File \(index) upload progress: \(progress * 100)%") + } ) } .onChange(of: uploadManager.selectedMedia) { newMedia in @@ -108,13 +113,18 @@ struct MediaUploadView: View { // MARK: - Private Methods private func handleMediaPickerDismiss() { - showMediaPicker = false - if !uploadManager.selectedMedia.isEmpty && selectedMedia == nil { - selectedMedia = uploadManager.selectedMedia.first - selectedIndices = [0] - // Start upload when media picker is dismissed with new media - uploadManager.startUpload() - } + self.uploadManager.startUpload() + // showMediaPicker = false + + // // 确保选择器完全关闭后再开始上传 + // DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + // if !self.uploadManager.selectedMedia.isEmpty { + // self.selectedMedia = self.uploadManager.selectedMedia.first + // self.selectedIndices = [0] + // // 在选择器完全关闭后再开始上传 + // self.uploadManager.startUpload() + // } + // } } private func handleMediaChange(_ newMedia: [MediaType]) { @@ -124,7 +134,7 @@ struct MediaUploadView: View { return } - // Only update if needed + // 只在需要时更新 if selectedIndices.isEmpty || selectedIndices.first! >= newMedia.count { selectedMedia = newMedia.first selectedIndices = [0] @@ -132,11 +142,21 @@ struct MediaUploadView: View { selectedMedia = newMedia[selectedIndex] } - // Auto-upload when new media is added - if !newMedia.isEmpty && !uploadManager.isUploading { + // 当有新媒体时自动开始上传 + if !newMedia.isEmpty && !isUploading() { uploadManager.startUpload() } } + + // 添加辅助方法检查上传状态 + private func isUploading() -> Bool { + return uploadManager.uploadStatus.values.contains { status in + if case .uploading = status { + return true + } + return false + } + } } // MARK: - Main Upload Area @@ -158,9 +178,10 @@ struct MainUploadArea: View { .padding() if let media = selectedMedia { - MediaPreview(media: media) + MediaPreview(media: media, uploadManager: uploadManager) .frame(width: 225, height: 225) .onTapGesture { showMediaPicker = true } + .padding(.bottom, 10) } else { UploadPromptView(showMediaPicker: $showMediaPicker) } @@ -333,31 +354,77 @@ struct AddMoreButton: View { struct MediaPreview: View { let media: MediaType + @ObservedObject var uploadManager: MediaUploadManager + + private var uploadProgress: Double { + // Get the upload progress for this media item using its index + if let index = uploadManager.selectedMedia.firstIndex(where: { $0 == media }) { + let status = uploadManager.uploadStatus["\(index)"] + if case .uploading(let progress) = status { + return progress + } + } + return 0 + } + + private var isUploading: Bool { + if let index = uploadManager.selectedMedia.firstIndex(where: { $0 == media }) { + let status = uploadManager.uploadStatus["\(index)"] + if case .uploading = status { + return true + } + } + return false + } var body: some View { - Group { - switch media { - case .image(let uiImage): - Image(uiImage: uiImage) - .resizable() - .scaledToFit() - .cornerRadius(12) - .drawingGroup() // Improves performance for large images - case .video(_, let thumbnail): - if let thumbnail = thumbnail { - Image(uiImage: thumbnail) + ZStack { + // Main media content + Group { + switch media { + case .image(let uiImage): + Image(uiImage: uiImage) .resizable() .scaledToFit() - .overlay( - Image(systemName: "play.circle.fill") - .font(.system(size: 48)) - .foregroundColor(.white) - .shadow(radius: 10) - ) .cornerRadius(12) - .drawingGroup() // Improves performance for thumbnails + .drawingGroup() + case .video(_, let thumbnail): + if let thumbnail = thumbnail { + Image(uiImage: thumbnail) + .resizable() + .scaledToFit() + .overlay( + Image(systemName: "play.circle.fill") + .font(.system(size: 48)) + .foregroundColor(.white) + .shadow(radius: 10) + ) + .cornerRadius(12) + .drawingGroup() + } } } + + // Upload progress border + if isUploading { + Circle() + .stroke( + Color.themePrimary.opacity(0.3), + style: StrokeStyle(lineWidth: 4, lineCap: .round) + ) + .rotationEffect(.degrees(-90)) + .padding(4) + + Circle() + .trim(from: 0, to: uploadProgress) + .stroke( + Color.themePrimary, + style: StrokeStyle(lineWidth: 4, lineCap: .round) + ) + .rotationEffect(.degrees(-90)) + .animation(.linear, value: uploadProgress) + .padding(4) + } } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.themeTextWhiteSecondary) @@ -366,7 +433,7 @@ struct MediaPreview: View { RoundedRectangle(cornerRadius: 16) .stroke(Color.themePrimary, lineWidth: 2) ) - .contentShape(Rectangle()) // Better tap target + .contentShape(Rectangle()) } }