feat: 暂提

This commit is contained in:
jinyaqiu 2025-08-25 15:17:23 +08:00
parent 2a9ef552fa
commit e2c4f5d882

View File

@ -98,15 +98,22 @@ struct MediaUploadView: View {
selectedMedia: $uploadManager.selectedMedia,
imageSelectionLimit: 20,
videoSelectionLimit: 5,
onDismiss: handleMediaPickerDismiss,
onDismiss: {
//
showMediaPicker = false
},
onUploadProgress: { index, progress in
//
print("File \(index) upload progress: \(progress * 100)%")
}
)
}
.onChange(of: uploadManager.selectedMedia) { newMedia in
handleMediaChange(newMedia)
print("onChange1111111", uploadManager.selectedMedia)
//
if !newMedia.isEmpty {
//
uploadManager.startUpload()
}
}
}
@ -114,6 +121,8 @@ struct MediaUploadView: View {
private func handleMediaPickerDismiss() {
self.uploadManager.startUpload()
print("handleMediaPickerDismiss1111111", uploadManager.selectedMedia)
// showMediaPicker = false
// //
@ -174,28 +183,88 @@ struct MainUploadArea: View {
.fontWeight(.bold)
.foregroundColor(.black)
.multilineTextAlignment(.center)
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
if let media = selectedMedia {
MediaPreview(media: media, uploadManager: uploadManager)
.frame(width: 225, height: 225)
.onTapGesture { showMediaPicker = true }
.padding(.bottom, 10)
.padding(.horizontal)
if !uploadManager.selectedMedia.isEmpty {
//
ScrollView {
LazyVGrid(columns: [
GridItem(.flexible(), spacing: 16),
GridItem(.flexible(), spacing: 16)
], spacing: 16) {
ForEach(0..<uploadManager.selectedMedia.count, id: \.self) { index in
VStack(spacing: 8) {
//
MediaPreview(
media: uploadManager.selectedMedia[index],
uploadManager: uploadManager
)
.frame(height: 150)
.cornerRadius(12)
.shadow(radius: 2)
//
if let status = uploadManager.uploadStatus["\(index)"] {
switch status {
case .uploading(let progress):
VStack(alignment: .leading, spacing: 4) {
Text("Uploading: \(Int(progress * 100))%")
.font(.caption)
.foregroundColor(.gray)
ProgressView(value: progress, total: 1.0)
.progressViewStyle(LinearProgressViewStyle())
.tint(Color.themePrimary)
}
.padding(.horizontal, 8)
case .completed:
HStack {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
Text("Uploaded")
.font(.caption)
.foregroundColor(.gray)
}
case .failed:
HStack {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(.red)
Text("Upload failed")
.font(.caption)
.foregroundColor(.red)
}
default:
EmptyView()
}
}
}
}
}
.padding()
}
.frame(maxHeight: 400)
} else {
//
UploadPromptView(showMediaPicker: $showMediaPicker)
}
if !uploadManager.selectedMedia.isEmpty {
ThumbnailScrollView(
uploadManager: uploadManager,
selectedIndices: $selectedIndices,
selectedMedia: $selectedMedia
)
.id("thumbnailScroll\(uploadManager.selectedMedia.count)")
//
Button(action: { showMediaPicker = true }) {
HStack {
Image(systemName: "plus.circle.fill")
Text("Add More")
}
.font(.headline)
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(Color.themePrimary)
.cornerRadius(10)
.padding(.horizontal)
}
}
.background(Color.white)
.cornerRadius(16)
.padding()
}
}
@ -243,13 +312,14 @@ struct ThumbnailScrollView: View {
ThumbnailView(
media: media,
isSelected: selectedIndex == index,
showCheckmark: true
) {
// Directly update the selection without animation for immediate response
selectedIndex = index
selectedMedia = media
selectedIndices = [index]
}
showCheckmark: true,
uploadManager: uploadManager,
onTap: {
selectedIndex = index
selectedMedia = media
selectedIndices = [index]
}
)
}
AddMoreButton(showMediaPicker: .constant(true))
@ -274,11 +344,14 @@ struct ThumbnailView: View {
let media: MediaType
let isSelected: Bool
let showCheckmark: Bool
let uploadManager: MediaUploadManager?
let onTap: () -> Void
var body: some View {
Button(action: onTap) {
ZStack(alignment: .topTrailing) {
Button(action: onTap) {
ZStack(alignment: .topTrailing) {
// Main thumbnail content
ZStack {
Group {
if let thumbnail = media.thumbnail {
Image(uiImage: thumbnail)
@ -297,34 +370,64 @@ struct ThumbnailView: View {
.stroke(isSelected ? Color.themePrimary : Color.clear, lineWidth: 2)
)
// Selection checkmark
if isSelected && showCheckmark {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 20))
.foregroundColor(.white)
.background(Circle().fill(Color.themePrimary))
.offset(x: 6, y: -6) // Adjusted offset to ensure visibility
.zIndex(1) // Ensure checkmark is above video icon
}
// Video icon
if media.isVideo {
Image(systemName: "video.fill")
.font(.caption)
.foregroundColor(.white)
.padding(4)
.background(Color.black.opacity(0.6))
.clipShape(Circle())
.padding(4)
.offset(x: -4, y: 4) // Slight adjustment for better positioning
// Upload progress border
if let uploadManager = uploadManager,
let index = uploadManager.selectedMedia.firstIndex(where: { $0 == media }) {
let status = uploadManager.uploadStatus["\(index)"]
if case .uploading(let progress) = status, progress > 0 && progress < 1 {
ZStack {
Circle()
.stroke(
Color.themePrimary.opacity(0.3),
style: StrokeStyle(lineWidth: 2, lineCap: .round)
)
.rotationEffect(.degrees(-90))
.padding(2)
Circle()
.trim(from: 0, to: progress)
.stroke(
Color.themePrimary,
style: StrokeStyle(lineWidth: 2, lineCap: .round)
)
.rotationEffect(.degrees(-90))
.animation(.linear, value: progress)
.padding(2)
}
.frame(width: 20, height: 20)
.offset(x: 30, y: -30)
}
}
}
.frame(width: 80, height: 80)
.contentShape(Rectangle())
.padding(4) // Add padding to prevent clipping
// Selection checkmark
if isSelected && showCheckmark {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 20))
.foregroundColor(.white)
.background(Circle().fill(Color.themePrimary))
.offset(x: 6, y: -6)
.zIndex(1)
}
// Video icon
if media.isVideo {
Image(systemName: "video.fill")
.font(.caption)
.foregroundColor(.white)
.padding(4)
.background(Color.black.opacity(0.6))
.clipShape(Circle())
.padding(4)
.offset(x: -4, y: 4)
}
}
.buttonStyle(PlainButtonStyle())
.frame(width: 80, height: 80)
.contentShape(Rectangle())
.padding(4)
}
.buttonStyle(PlainButtonStyle())
}
}
// MARK: - Add More Button
@ -357,83 +460,46 @@ struct MediaPreview: View {
@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
}
guard let index = uploadManager.selectedMedia.firstIndex(where: { $0 == media }),
case .uploading(let progress) = uploadManager.uploadStatus["\(index)"] else {
return 0
}
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
return progress
}
var body: some View {
ZStack {
// Main media content
//
Group {
switch media {
case .image(let uiImage):
Image(uiImage: uiImage)
.resizable()
.scaledToFit()
.cornerRadius(12)
.drawingGroup()
.scaledToFill()
case .video(_, let thumbnail):
if let thumbnail = thumbnail {
Image(uiImage: thumbnail)
.resizable()
.scaledToFit()
.scaledToFill()
.overlay(
Image(systemName: "play.circle.fill")
.font(.system(size: 48))
.font(.system(size: 36))
.foregroundColor(.white)
.shadow(radius: 10)
.shadow(radius: 8)
)
.cornerRadius(12)
.drawingGroup()
} else {
Color.gray
}
}
}
// 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)
}
.aspectRatio(1, contentMode: .fill)
.clipped()
.cornerRadius(8)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color.themePrimary.opacity(0.3), lineWidth: 1)
)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.themeTextWhiteSecondary)
.cornerRadius(16)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(Color.themePrimary, lineWidth: 2)
)
.contentShape(Rectangle())
}
}