feat: 暂提
This commit is contained in:
parent
2a9ef552fa
commit
e2c4f5d882
@ -98,15 +98,22 @@ struct MediaUploadView: View {
|
|||||||
selectedMedia: $uploadManager.selectedMedia,
|
selectedMedia: $uploadManager.selectedMedia,
|
||||||
imageSelectionLimit: 20,
|
imageSelectionLimit: 20,
|
||||||
videoSelectionLimit: 5,
|
videoSelectionLimit: 5,
|
||||||
onDismiss: handleMediaPickerDismiss,
|
onDismiss: {
|
||||||
|
// 只处理界面相关的逻辑
|
||||||
|
showMediaPicker = false
|
||||||
|
},
|
||||||
onUploadProgress: { index, progress in
|
onUploadProgress: { index, progress in
|
||||||
// 更新单个文件的上传进度
|
|
||||||
print("File \(index) upload progress: \(progress * 100)%")
|
print("File \(index) upload progress: \(progress * 100)%")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.onChange(of: uploadManager.selectedMedia) { newMedia in
|
.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() {
|
private func handleMediaPickerDismiss() {
|
||||||
self.uploadManager.startUpload()
|
self.uploadManager.startUpload()
|
||||||
|
print("handleMediaPickerDismiss1111111", uploadManager.selectedMedia)
|
||||||
|
|
||||||
// showMediaPicker = false
|
// showMediaPicker = false
|
||||||
|
|
||||||
// // 确保选择器完全关闭后再开始上传
|
// // 确保选择器完全关闭后再开始上传
|
||||||
@ -174,28 +183,88 @@ struct MainUploadArea: View {
|
|||||||
.fontWeight(.bold)
|
.fontWeight(.bold)
|
||||||
.foregroundColor(.black)
|
.foregroundColor(.black)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.padding(.horizontal)
|
||||||
.padding()
|
|
||||||
|
if !uploadManager.selectedMedia.isEmpty {
|
||||||
if let media = selectedMedia {
|
// 显示媒体预览网格
|
||||||
MediaPreview(media: media, uploadManager: uploadManager)
|
ScrollView {
|
||||||
.frame(width: 225, height: 225)
|
LazyVGrid(columns: [
|
||||||
.onTapGesture { showMediaPicker = true }
|
GridItem(.flexible(), spacing: 16),
|
||||||
.padding(.bottom, 10)
|
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 {
|
} else {
|
||||||
|
// 没有选择媒体时显示上传提示
|
||||||
UploadPromptView(showMediaPicker: $showMediaPicker)
|
UploadPromptView(showMediaPicker: $showMediaPicker)
|
||||||
}
|
}
|
||||||
if !uploadManager.selectedMedia.isEmpty {
|
|
||||||
ThumbnailScrollView(
|
// 添加更多按钮
|
||||||
uploadManager: uploadManager,
|
Button(action: { showMediaPicker = true }) {
|
||||||
selectedIndices: $selectedIndices,
|
HStack {
|
||||||
selectedMedia: $selectedMedia
|
Image(systemName: "plus.circle.fill")
|
||||||
)
|
Text("Add More")
|
||||||
.id("thumbnailScroll\(uploadManager.selectedMedia.count)")
|
}
|
||||||
|
.font(.headline)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.padding()
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.background(Color.themePrimary)
|
||||||
|
.cornerRadius(10)
|
||||||
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.background(Color.white)
|
.background(Color.white)
|
||||||
.cornerRadius(16)
|
.cornerRadius(16)
|
||||||
|
.padding()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,13 +312,14 @@ struct ThumbnailScrollView: View {
|
|||||||
ThumbnailView(
|
ThumbnailView(
|
||||||
media: media,
|
media: media,
|
||||||
isSelected: selectedIndex == index,
|
isSelected: selectedIndex == index,
|
||||||
showCheckmark: true
|
showCheckmark: true,
|
||||||
) {
|
uploadManager: uploadManager,
|
||||||
// Directly update the selection without animation for immediate response
|
onTap: {
|
||||||
selectedIndex = index
|
selectedIndex = index
|
||||||
selectedMedia = media
|
selectedMedia = media
|
||||||
selectedIndices = [index]
|
selectedIndices = [index]
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
AddMoreButton(showMediaPicker: .constant(true))
|
AddMoreButton(showMediaPicker: .constant(true))
|
||||||
@ -274,11 +344,14 @@ struct ThumbnailView: View {
|
|||||||
let media: MediaType
|
let media: MediaType
|
||||||
let isSelected: Bool
|
let isSelected: Bool
|
||||||
let showCheckmark: Bool
|
let showCheckmark: Bool
|
||||||
|
let uploadManager: MediaUploadManager?
|
||||||
let onTap: () -> Void
|
let onTap: () -> Void
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Button(action: onTap) {
|
Button(action: onTap) {
|
||||||
ZStack(alignment: .topTrailing) {
|
ZStack(alignment: .topTrailing) {
|
||||||
|
// Main thumbnail content
|
||||||
|
ZStack {
|
||||||
Group {
|
Group {
|
||||||
if let thumbnail = media.thumbnail {
|
if let thumbnail = media.thumbnail {
|
||||||
Image(uiImage: thumbnail)
|
Image(uiImage: thumbnail)
|
||||||
@ -297,34 +370,64 @@ struct ThumbnailView: View {
|
|||||||
.stroke(isSelected ? Color.themePrimary : Color.clear, lineWidth: 2)
|
.stroke(isSelected ? Color.themePrimary : Color.clear, lineWidth: 2)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Selection checkmark
|
// Upload progress border
|
||||||
if isSelected && showCheckmark {
|
if let uploadManager = uploadManager,
|
||||||
Image(systemName: "checkmark.circle.fill")
|
let index = uploadManager.selectedMedia.firstIndex(where: { $0 == media }) {
|
||||||
.font(.system(size: 20))
|
let status = uploadManager.uploadStatus["\(index)"]
|
||||||
.foregroundColor(.white)
|
if case .uploading(let progress) = status, progress > 0 && progress < 1 {
|
||||||
.background(Circle().fill(Color.themePrimary))
|
ZStack {
|
||||||
.offset(x: 6, y: -6) // Adjusted offset to ensure visibility
|
Circle()
|
||||||
.zIndex(1) // Ensure checkmark is above video icon
|
.stroke(
|
||||||
}
|
Color.themePrimary.opacity(0.3),
|
||||||
|
style: StrokeStyle(lineWidth: 2, lineCap: .round)
|
||||||
// Video icon
|
)
|
||||||
if media.isVideo {
|
.rotationEffect(.degrees(-90))
|
||||||
Image(systemName: "video.fill")
|
.padding(2)
|
||||||
.font(.caption)
|
|
||||||
.foregroundColor(.white)
|
Circle()
|
||||||
.padding(4)
|
.trim(from: 0, to: progress)
|
||||||
.background(Color.black.opacity(0.6))
|
.stroke(
|
||||||
.clipShape(Circle())
|
Color.themePrimary,
|
||||||
.padding(4)
|
style: StrokeStyle(lineWidth: 2, lineCap: .round)
|
||||||
.offset(x: -4, y: 4) // Slight adjustment for better positioning
|
)
|
||||||
|
.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())
|
// Selection checkmark
|
||||||
.padding(4) // Add padding to prevent clipping
|
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
|
// MARK: - Add More Button
|
||||||
@ -357,83 +460,46 @@ struct MediaPreview: View {
|
|||||||
@ObservedObject var uploadManager: MediaUploadManager
|
@ObservedObject var uploadManager: MediaUploadManager
|
||||||
|
|
||||||
private var uploadProgress: Double {
|
private var uploadProgress: Double {
|
||||||
// Get the upload progress for this media item using its index
|
guard let index = uploadManager.selectedMedia.firstIndex(where: { $0 == media }),
|
||||||
if let index = uploadManager.selectedMedia.firstIndex(where: { $0 == media }) {
|
case .uploading(let progress) = uploadManager.uploadStatus["\(index)"] else {
|
||||||
let status = uploadManager.uploadStatus["\(index)"]
|
return 0
|
||||||
if case .uploading(let progress) = status {
|
|
||||||
return progress
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0
|
return progress
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
// Main media content
|
// 媒体内容
|
||||||
Group {
|
Group {
|
||||||
switch media {
|
switch media {
|
||||||
case .image(let uiImage):
|
case .image(let uiImage):
|
||||||
Image(uiImage: uiImage)
|
Image(uiImage: uiImage)
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit()
|
.scaledToFill()
|
||||||
.cornerRadius(12)
|
|
||||||
.drawingGroup()
|
|
||||||
case .video(_, let thumbnail):
|
case .video(_, let thumbnail):
|
||||||
if let thumbnail = thumbnail {
|
if let thumbnail = thumbnail {
|
||||||
Image(uiImage: thumbnail)
|
Image(uiImage: thumbnail)
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit()
|
.scaledToFill()
|
||||||
.overlay(
|
.overlay(
|
||||||
Image(systemName: "play.circle.fill")
|
Image(systemName: "play.circle.fill")
|
||||||
.font(.system(size: 48))
|
.font(.system(size: 36))
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
.shadow(radius: 10)
|
.shadow(radius: 8)
|
||||||
)
|
)
|
||||||
.cornerRadius(12)
|
} else {
|
||||||
.drawingGroup()
|
Color.gray
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.aspectRatio(1, contentMode: .fill)
|
||||||
// Upload progress border
|
.clipped()
|
||||||
if isUploading {
|
.cornerRadius(8)
|
||||||
Circle()
|
.overlay(
|
||||||
.stroke(
|
RoundedRectangle(cornerRadius: 8)
|
||||||
Color.themePrimary.opacity(0.3),
|
.stroke(Color.themePrimary.opacity(0.3), lineWidth: 1)
|
||||||
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)
|
|
||||||
.cornerRadius(16)
|
|
||||||
.overlay(
|
|
||||||
RoundedRectangle(cornerRadius: 16)
|
|
||||||
.stroke(Color.themePrimary, lineWidth: 2)
|
|
||||||
)
|
|
||||||
.contentShape(Rectangle())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user