import SwiftUI import ComposableArchitecture // MARK: - BackgroundView struct BackgroundView: View { var body: some View { Image("bg") .resizable() .aspectRatio(contentMode: .fill) .frame(maxWidth: .infinity, maxHeight: .infinity) .clipped() .ignoresSafeArea(.all) } } // MARK: - TopBarView struct TopBarView: View { let onEditTapped: () -> Void var body: some View { ZStack { HStack { Spacer(minLength: 0) Text(LocalizedString("feedList.title", comment: "Enjoy your Life Time")) .font(.system(size: 22, weight: .semibold)) .foregroundColor(.white) .frame(maxWidth: .infinity, alignment: .center) Spacer(minLength: 0) } HStack { Spacer(minLength: 0) Button(action: onEditTapped) { Image("add icon") .resizable() .frame(width: 40, height: 40) } } } .padding(.horizontal, 20) } } // MARK: - LoadingView private struct FeedListLoadingView: View { var body: some View { ProgressView() .progressViewStyle(CircularProgressViewStyle(tint: .white)) .padding(.top, 20) } } // MARK: - ErrorView struct ErrorView: View { let error: String var body: some View { Text(error) .font(.system(size: 14)) .foregroundColor(.red) .multilineTextAlignment(.center) .padding(.horizontal, 20) .padding(.top, 20) } } // MARK: - EmptyView struct EmptyView: View { var body: some View { Text(LocalizedString("feedList.empty", comment: "暂无动态")) .font(.system(size: 16)) .foregroundColor(.white.opacity(0.7)) .padding(.top, 20) } } // MARK: - MomentCardView struct MomentCardView: View { let moment: MomentsInfo let allMoments: [MomentsInfo] let index: Int let onImageTap: ([String], Int) -> Void let onTap: () -> Void let onLikeTap: (Int, Int, Int, Int) -> Void let onLoadMore: () -> Void let isLastItem: Bool let hasMore: Bool let isLoadingMore: Bool let isLikeLoading: Bool var body: some View { VStack(spacing: 16) { OptimizedDynamicCardView( moment: moment, allMoments: allMoments, currentIndex: index, onImageTap: onImageTap, onLikeTap: onLikeTap, onCardTap: onTap, isDetailMode: false, isLikeLoading: isLikeLoading ) // 上拉加载更多触发点 if isLastItem && hasMore && !isLoadingMore { Color.clear .frame(height: 1) .onAppear { onLoadMore() } } } } } // MARK: - MomentsListView struct MomentsListView: View { let moments: [MomentsInfo] let hasMore: Bool let isLoadingMore: Bool let onImageTap: ([String], Int) -> Void let onMomentTap: (MomentsInfo) -> Void let onLikeTap: (Int, Int, Int, Int) -> Void let onLoadMore: () -> Void let onRefresh: () -> Void let likeLoadingDynamicIds: Set var body: some View { ScrollView { LazyVStack(spacing: 16) { ForEach(Array(moments.enumerated()), id: \.element.dynamicId) { index, moment in MomentCardView( moment: moment, allMoments: moments, index: index, onImageTap: onImageTap, onTap: { onMomentTap(moment) }, onLikeTap: onLikeTap, onLoadMore: onLoadMore, isLastItem: index == moments.count - 1, hasMore: hasMore, isLoadingMore: isLoadingMore, isLikeLoading: likeLoadingDynamicIds.contains(moment.dynamicId) ) } // 加载更多指示器 if isLoadingMore { ProgressView() .progressViewStyle(CircularProgressViewStyle(tint: .white)) .padding(.vertical, 8) } // 新增底部间距 Color.clear.frame(height: 120) } .padding(.horizontal, 16) .padding(.top, 10) .padding(.bottom, 20) } .refreshable { onRefresh() } } } // MARK: - FeedListContentView struct FeedListContentView: View { let store: StoreOf @Binding var previewItem: PreviewItem? @Binding var previewCurrentIndex: Int var body: some View { if store.isLoading { FeedListLoadingView() } else if let error = store.error { ErrorView(error: error) } else if store.moments.isEmpty { EmptyView() } else { MomentsListView( moments: store.moments, hasMore: store.hasMore, isLoadingMore: store.isLoadingMore, onImageTap: { images, tappedIndex in previewCurrentIndex = tappedIndex previewItem = PreviewItem(images: images, index: tappedIndex) }, onMomentTap: { moment in store.send(.showDetail(moment)) }, onLikeTap: { dynamicId, uid, likedUid, worldId in store.send(.likeDynamic(dynamicId, uid, likedUid, worldId)) }, onLoadMore: { store.send(.loadMore) }, onRefresh: { store.send(.reload) }, likeLoadingDynamicIds: store.likeLoadingDynamicIds ) } } } struct FeedListView: View { let store: StoreOf // 新增:图片预览状态 @State private var previewItem: PreviewItem? = nil @State private var previewCurrentIndex: Int = 0 var body: some View { WithViewStore(store, observe: { $0 }) { viewStore in GeometryReader { geometry in ZStack { // 背景 BackgroundView() VStack(alignment: .center, spacing: 0) { // 顶部栏 TopBarView { store.send(.editFeedButtonTapped) } // 其他内容 Image("Volume") .frame(width: 56, height: 41) .padding(.top, 16) Text(LocalizedString("feedList.slogan", comment: "The disease is like a cruel ruler,\nand time is our most precious treasure.\nEvery moment we live is a victory\nagainst the inevitable.")) .font(.system(size: 16)) .multilineTextAlignment(.leading) .foregroundColor(.white.opacity(0.9)) .padding(.horizontal, 30) .padding(.bottom, 30) // 动态内容列表 FeedListContentView( store: store, previewItem: $previewItem, previewCurrentIndex: $previewCurrentIndex ) Spacer() } } } .onAppear { store.send(.onAppear) } .refreshable { store.send(.reload) } // 新增:编辑动态页面 .sheet(isPresented: viewStore.binding(get: \.isEditFeedPresented, send: { _ in .editFeedDismissed })) { let createFeedStore = Store( initialState: CreateFeedFeature.State() ) { CreateFeedFeature() } CreateFeedView(store: createFeedStore) .onReceive(NotificationCenter.default.publisher(for: .init("CreateFeedPublishSuccess"))) { _ in store.send(.createFeedPublishSuccess) } .onReceive(NotificationCenter.default.publisher(for: .init("CreateFeedDismiss"))) { _ in store.send(.editFeedDismissed) } } // 新增:详情页导航 .navigationDestination(isPresented: viewStore.binding(get: \.showDetail, send: { _ in .detailDismissed })) { if let selectedMoment = viewStore.selectedMoment { DetailView( store: Store( initialState: DetailFeature.State(moment: selectedMoment) ) { DetailFeature() } ) } } // 新增:图片预览弹窗 .fullScreenCover(item: $previewItem) { item in ImagePreviewPager(images: item.images as [String], currentIndex: $previewCurrentIndex) { previewItem = nil } } } } }