Files
e-party-iOS/yana/Views/DetailView.swift
edwinQQQ 99a53d7274 feat: 新增我的动态信息结构和相关API请求逻辑
- 在DynamicsModels.swift中新增MyMomentInfo结构,专门用于处理/dynamic/getMyDynamic接口的响应数据。
- 更新MyMomentsResponse结构以使用MyMomentInfo,确保数据类型一致性。
- 在LoginModels.swift中重构IDLoginAPIRequest和EmailLoginRequest,优化queryParameters的实现方式,提升代码可读性。
- 在RecoverPasswordFeature中重构ResetPasswordRequest,优化queryParameters的实现方式,确保一致性。
- 在多个视图中添加调试信息,增强调试能力和用户体验。
- 更新Localizable.strings文件,新增动态列表为空时的提示信息,提升用户交互体验。
2025-08-04 19:12:31 +08:00

251 lines
9.8 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 ComposableArchitecture
struct DetailView: View {
@State var store: StoreOf<DetailFeature>
let onLikeSuccess: ((Int, Bool) -> Void)?
init(store: StoreOf<DetailFeature>, onLikeSuccess: ((Int, Bool) -> Void)? = nil) {
self.store = store
self.onLikeSuccess = onLikeSuccess
}
var body: some View {
ZStack {
//
Image("bg")
.resizable()
.aspectRatio(contentMode: .fill)
.ignoresSafeArea()
VStack(spacing: 0) {
//
WithPerceptionTracking {
CustomNavigationBar(
title: LocalizedString("detail.title", comment: "Detail page title"),
showDeleteButton: false,
isDeleteLoading: store.isDeleteLoading,
onBack: {
// onDismiss?() 使 dismiss()
},
onDelete: {
store.send(.deleteDynamic)
}
)
}
.padding(.top, 24)
//
ScrollView {
VStack(spacing: 0) {
// 使OptimizedDynamicCardView
WithPerceptionTracking {
OptimizedDynamicCardView(
moment: store.moment,
allMoments: [store.moment], //
currentIndex: 0,
onImageTap: { images, index in
store.send(.showImagePreview(images, index))
},
onLikeTap: { dynamicId, uid, likedUid, worldId in
store.send(.likeDynamic(dynamicId, uid, likedUid, worldId))
},
onCardTap: nil, //
onAvatarTap: {
//
if !isCurrentUserDynamic {
store.send(.showUserProfile(store.moment.uid))
}
},
isDetailMode: true, //
isLikeLoading: store.isLikeLoading
)
.padding(.horizontal, 16)
.padding(.top, 16)
.padding(.bottom, 116)
}
}
}
}
}
.navigationBarHidden(true)
.onAppear {
debugInfoSync("🔍 DetailView: onAppear - moment.uid: \(store.moment.uid)")
store.send(.onAppear)
}
.onChange(of: store.shouldDismiss) { _, shouldDismiss in
if shouldDismiss {
debugInfoSync("🔍 DetailView: shouldDismiss = true, 调用onDismiss")
// onDismiss?() 使 dismiss
}
}
.fullScreenCover(isPresented: Binding(
get: { store.showImagePreview },
set: { _ in store.send(.hideImagePreview) }
)) {
WithPerceptionTracking {
ImagePreviewPager(
images: store.selectedImages,
currentIndex: Binding(
get: { store.selectedImageIndex },
set: { newIndex in
store.send(.showImagePreview(store.selectedImages, newIndex))
}
),
onClose: {
store.send(.imagePreviewDismissed)
}
)
}
}
.sheet(isPresented: Binding(
get: { store.showUserProfile },
set: { _ in store.send(.hideUserProfile) }
)) {
WithPerceptionTracking {
let meStore = Store(
initialState: MeFeature.State(displayUID: store.targetUserId)
) {
MeFeature()
}
MeView(store: meStore, showCloseButton: true)
.presentationDetents([.large])
.presentationDragIndicator(.visible)
}
}
}
//
private var isCurrentUserDynamic: Bool {
// 使storeID
guard let currentUserId = store.currentUserId,
let currentUserIdInt = Int(currentUserId) else {
debugInfoSync("🔍 DetailView: 无法获取当前用户ID - currentUserId: \(store.currentUserId ?? "nil")")
return false
}
let isCurrentUser = store.moment.uid == currentUserIdInt
debugInfoSync("🔍 DetailView: 动态用户判断 - moment.uid: \(store.moment.uid), currentUserId: \(currentUserIdInt), isCurrentUser: \(isCurrentUser)")
return isCurrentUser
}
}
// MARK: - CustomNavigationBar
struct CustomNavigationBar: View {
let title: String
let showDeleteButton: Bool
let isDeleteLoading: Bool
let onBack: () -> Void
let onDelete: () -> Void
init(title: String, showDeleteButton: Bool, isDeleteLoading: Bool, onBack: @escaping () -> Void, onDelete: @escaping () -> Void) {
self.title = title
self.showDeleteButton = showDeleteButton
self.isDeleteLoading = isDeleteLoading
self.onBack = onBack
self.onDelete = onDelete
}
@SwiftUI.Environment(\.dismiss) private var dismiss: SwiftUI.DismissAction
var body: some View {
HStack {
//
Button(action: {
onBack()
dismiss() // 使 dismiss
}) {
Image(systemName: "chevron.left")
.font(.system(size: 20, weight: .medium))
.foregroundColor(.white)
.frame(width: 44, height: 44)
.background(Color.black.opacity(0.3))
.clipShape(Circle())
}
Spacer()
//
Text(title)
.font(.system(size: 18, weight: .semibold))
.foregroundColor(.white)
.shadow(color: .black.opacity(0.5), radius: 2, x: 0, y: 1)
Spacer()
//
WithPerceptionTracking {
if showDeleteButton {
Button(action: onDelete) {
if isDeleteLoading {
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: .white))
.scaleEffect(0.8)
.frame(width: 44, height: 44)
.background(Color.red.opacity(0.8))
.clipShape(Circle())
} else {
Image(systemName: "trash")
.font(.system(size: 16, weight: .medium))
.foregroundColor(.white)
.frame(width: 44, height: 44)
.background(Color.red.opacity(0.8))
.clipShape(Circle())
}
}
.disabled(isDeleteLoading)
} else {
//
Color.clear
.frame(width: 44, height: 44)
}
}
}
.padding(.horizontal, 16)
.padding(.top, 8)
.padding(.bottom, 12)
.background(
LinearGradient(
gradient: Gradient(colors: [
Color.black.opacity(0.4),
Color.black.opacity(0.2),
Color.clear
]),
startPoint: .top,
endPoint: .bottom
)
)
}
}
//#Preview {
// DetailView(
// store: Store(
// initialState: DetailFeature.State(
// moment: MomentsInfo(
// dynamicId: 1,
// uid: 123,
// nick: "Test User",
// avatar: "https://example.com/avatar.jpg",
// type: 1,
// content: "This is a test dynamic content",
// publishTime: Int(Date().timeIntervalSince1970 * 1000),
// likeCount: 10,
// isLike: false,
// worldId: 1,
// dynamicResList: [
// MomentsPicture(
// id: 1,
// resUrl: "https://example.com/image1.jpg",
// format: "jpg",
// width: 800,
// height: 600,
// resDuration: nil
// )
// ]
// )
// )
// ) {
// DetailFeature()
// }
// )
//}