Files
e-party-iOS/yana/Views/FeedListView.swift
edwinQQQ 86fcb96d50 feat: 优化FeedListFeature和视图结构
- 在FeedListFeature中添加返回语句,确保认证信息准备好后立即获取动态。
- 移除FeedListView中的冗余注释,提升代码整洁性。
- 更新MainView中的方法名称,从contentView更改为mainContentView,增强代码可读性。
2025-08-05 16:05:07 +08:00

301 lines
10 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
// 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 CustomEmptyView: View {
let onRetry: () -> Void
var body: some View {
EmptyStateView(onRetry: onRetry)
}
}
// 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,
onAvatarTap: nil, // FeedListView
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<Int>
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<FeedListFeature>
@Binding var previewItem: PreviewItem?
@Binding var previewCurrentIndex: Int
var body: some View {
//
let _ = debugInfoSync("📱 FeedListContentView 状态:")
let _ = debugInfoSync(" isLoading: \(store.isLoading)")
let _ = debugInfoSync(" error: \(store.error ?? "nil")")
let _ = debugInfoSync(" moments.count: \(store.moments.count)")
let _ = debugInfoSync(" hasMore: \(store.hasMore)")
if store.isLoading {
FeedListLoadingView()
} else if let error = store.error {
ErrorView(error: error)
} else if store.moments.isEmpty {
CustomEmptyView(onRetry: {
store.send(.reload)
})
} 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<FeedListFeature>
@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
}
}
}
}
}