import SwiftUI import ComposableArchitecture import PhotosUI struct EditFeedView: View { let onDismiss: () -> Void let store: StoreOf @State private var isKeyboardVisible = false private let maxCount = 500 private func hideKeyboard() { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) } var body: some View { GeometryReader { geometry in ZStack { backgroundView mainContent(geometry: geometry) if store.isUploadingImages { uploadingImagesOverlay(progress: store.imageUploadProgress) } else if store.isLoading { loadingOverlay } } .contentShape(Rectangle()) .onTapGesture { if isKeyboardVisible { hideKeyboard() } } .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { _ in withAnimation(.easeInOut(duration: 0.3)) { isKeyboardVisible = true } } .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in withAnimation(.easeInOut(duration: 0.3)) { isKeyboardVisible = false } } } .navigationBarHidden(true) .onAppear { store.send(.clearError) } .onChange(of: store.shouldDismiss) { if store.shouldDismiss { onDismiss() } } .photosPicker( isPresented: Binding( get: { store.showPhotosPicker }, set: { _ in store.send(.photosPickerDismissed) } ), selection: Binding( get: { store.selectedPhotoItems }, set: { store.send(.photosPickerItemsChanged($0)) } ), maxSelectionCount: 9, matching: .images ) .alert("删除图片", isPresented: Binding( get: { store.showDeleteImageAlert }, set: { _ in store.send(.deleteImageAlertDismissed) } )) { Button("删除", role: .destructive) { if let indexToDelete = store.imageToDeleteIndex { store.send(.removeImage(indexToDelete)) } } Button("取消", role: .cancel) { store.send(.deleteImageAlertDismissed) } } message: { Text("确定要删除这张图片吗?") } } private var backgroundView: some View { Color(hex: 0x0C0527) .ignoresSafeArea() } private func mainContent(geometry: GeometryProxy) -> some View { VStack(spacing: 0) { topNavigationBar ScrollView { VStack(spacing: 20) { textInputSection imageSelectionSection publishButton } .padding(.horizontal, 20) .padding(.bottom, 40) } } } private var topNavigationBar: some View { HStack { Button(action: { store.send(.clearDismissFlag) onDismiss() }) { Image(systemName: "xmark") .font(.system(size: 20, weight: .medium)) .foregroundColor(.white) .frame(width: 44, height: 44) } Spacer() Text("编辑动态") .font(.system(size: 18, weight: .medium)) .foregroundColor(.white) Spacer() Color.clear .frame(width: 44, height: 44) } .padding(.horizontal, 16) .padding(.top, 8) .padding(.bottom, 16) } private var textInputSection: some View { VStack(alignment: .leading, spacing: 12) { Text("分享你的想法...") .font(.system(size: 16, weight: .medium)) .foregroundColor(.white) TextEditor(text: Binding( get: { store.content }, set: { store.send(.contentChanged($0)) } )) .font(.system(size: 16)) .foregroundColor(.white) .background(Color.clear) .frame(minHeight: 120) .padding(12) .background(Color.white.opacity(0.1)) .cornerRadius(12) .overlay( RoundedRectangle(cornerRadius: 12) .stroke(Color.white.opacity(0.2), lineWidth: 1) ) HStack { Spacer() Text("\(store.content.count)/\(maxCount)") .font(.system(size: 12)) .foregroundColor(.white.opacity(0.6)) } } } private var imageSelectionSection: some View { VStack(alignment: .leading, spacing: 12) { Text("添加图片") .font(.system(size: 16, weight: .medium)) .foregroundColor(.white) ImageGrid( images: store.processedImages, onRemoveImage: { index in store.send(.showDeleteImageAlert(index)) }, onAddImage: { store.send(.addImageButtonTapped) } ) } } private var publishButton: some View { Button(action: { store.send(.publishButtonTapped) }) { if store.isLoading { ProgressView() .progressViewStyle(CircularProgressViewStyle(tint: .white)) .scaleEffect(1.2) } else { Text("发布") .font(.system(size: 16, weight: .medium)) .foregroundColor(.white) } } .frame(maxWidth: .infinity) .padding(.vertical, 16) .background(store.content.isEmpty ? Color.gray : Color.blue) .cornerRadius(12) .disabled(store.isLoading || store.content.isEmpty) } private func uploadingImagesOverlay(progress: Double) -> some View { WithPerceptionTracking { ZStack { Color.black.opacity(0.5) .ignoresSafeArea() VStack(spacing: 16) { ProgressView(value: progress) .progressViewStyle(LinearProgressViewStyle(tint: .white)) .frame(width: 200) Text("上传图片中... \(Int(progress * 100))%") .font(.system(size: 16)) .foregroundColor(.white) } .padding(24) .background(Color.black.opacity(0.8)) .cornerRadius(12) } } } private var loadingOverlay: some View { WithPerceptionTracking { ZStack { Color.black.opacity(0.5) .ignoresSafeArea() ProgressView() .progressViewStyle(CircularProgressViewStyle(tint: .white)) .scaleEffect(1.5) } } } } // MARK: - 简化的图片网格组件 struct ImageGrid: View { let images: [UIImage] let onRemoveImage: (Int) -> Void let onAddImage: () -> Void private let columns = Array(repeating: GridItem(.flexible(), spacing: 8), count: 3) var body: some View { LazyVGrid(columns: columns, spacing: 8) { ForEach(Array(images.enumerated()), id: \.offset) { index, image in ImageGridItem( image: image, onRemove: { onRemoveImage(index) } ) } if images.count < 9 { AddImageButton(onTap: onAddImage) } } } } // MARK: - 图片网格项组件 struct ImageGridItem: View { let image: UIImage let onRemove: () -> Void var body: some View { ZStack(alignment: .topTrailing) { Image(uiImage: image) .resizable() .aspectRatio(contentMode: .fill) .frame(height: 100) .clipped() .cornerRadius(8) Button(action: onRemove) { Image(systemName: "xmark.circle.fill") .font(.system(size: 20)) .foregroundColor(.white) .background(Color.black.opacity(0.5)) .clipShape(Circle()) } .padding(4) } } } // MARK: - 添加图片按钮组件 struct AddImageButton: View { let onTap: () -> Void var body: some View { Button(action: onTap) { VStack { Image(systemName: "plus") .font(.system(size: 24)) .foregroundColor(.white.opacity(0.7)) Text("添加") .font(.system(size: 12)) .foregroundColor(.white.opacity(0.7)) } .frame(height: 100) .frame(maxWidth: .infinity) .background(Color.white.opacity(0.1)) .cornerRadius(8) .overlay( RoundedRectangle(cornerRadius: 8) .stroke(Color.white.opacity(0.2), lineWidth: 1) ) } } }