diff --git a/yana/Features/FeedListFeature.swift b/yana/Features/FeedListFeature.swift index 456c7f8..0b5a326 100644 --- a/yana/Features/FeedListFeature.swift +++ b/yana/Features/FeedListFeature.swift @@ -13,12 +13,17 @@ struct FeedListFeature { var moments: [MomentsInfo] = [] // 新增:只加载一次标志 var isLoaded: Bool = false + // 分页相关 + var currentPage: Int = 1 + var hasMore: Bool = true + var isLoadingMore: Bool = false } enum Action: Equatable { case onAppear case reload case loadMore + case loadMoreResponse(TaskResult) case editFeedButtonTapped // 新增:点击 add 按钮 case editFeedDismissed // 新增:关闭编辑页 // 新增:动态内容相关 @@ -34,6 +39,57 @@ struct FeedListFeature { guard !state.isLoaded else { return .none } state.isLoaded = true return .send(.fetchFeeds) + case .reload: + // 下拉刷新,重置状态并请求第一页 + state.isLoading = true + state.error = nil + state.currentPage = 1 + state.hasMore = true + state.isLoaded = true + return .run { [apiService] send in + await send(.fetchFeedsResponse(TaskResult { + let request = LatestDynamicsRequest(dynamicId: "", pageSize: 20, types: [.text, .picture]) + return try await apiService.request(request) + })) + } + case .loadMore: + // 上拉加载更多 + guard state.hasMore, !state.isLoadingMore, !state.isLoading else { return .none } + state.isLoadingMore = true + let lastDynamicId: String = { + if let last = state.moments.last { + return String(last.dynamicId) + } else { + return "" + } + }() + return .run { [apiService] send in + await send(.loadMoreResponse(TaskResult { + let request = LatestDynamicsRequest(dynamicId: lastDynamicId, pageSize: 20, types: [.text, .picture]) + return try await apiService.request(request) + })) + } + case let .loadMoreResponse(.success(response)): + state.isLoadingMore = false + if let list = response.data?.dynamicList { + if list.isEmpty { + state.hasMore = false + } else { + state.moments.append(contentsOf: list) + state.currentPage += 1 + state.hasMore = (list.count >= 20) + } + state.error = nil + } else { + state.hasMore = false + state.error = response.message + } + return .none + case let .loadMoreResponse(.failure(error)): + state.isLoadingMore = false + state.hasMore = false + state.error = error.localizedDescription + return .none case .fetchFeeds: state.isLoading = true state.error = nil @@ -49,21 +105,19 @@ struct FeedListFeature { if let list = response.data?.dynamicList { state.moments = list state.error = nil + state.currentPage = 1 + state.hasMore = (list.count >= 20) } else { state.moments = [] state.error = response.message + state.hasMore = false } return .none case let .fetchFeedsResponse(.failure(error)): state.isLoading = false state.moments = [] state.error = error.localizedDescription - return .none - case .reload: - // 预留刷新逻辑 - return .none - case .loadMore: - // 预留分页加载逻辑 + state.hasMore = false return .none case .editFeedButtonTapped: state.isEditFeedPresented = true diff --git a/yana/Views/FeedListView.swift b/yana/Views/FeedListView.swift index 6494e2b..6ce00b9 100644 --- a/yana/Views/FeedListView.swift +++ b/yana/Views/FeedListView.swift @@ -69,12 +69,29 @@ struct FeedListView: View { LazyVStack(spacing: 16) { ForEach(Array(viewStore.moments.enumerated()), id: \ .element.dynamicId) { index, moment in OptimizedDynamicCardView(moment: moment, allMoments: viewStore.moments, currentIndex: index) + // 上拉加载更多触发点 + if index == viewStore.moments.count - 1, viewStore.hasMore, !viewStore.isLoadingMore { + Color.clear + .frame(height: 1) + .onAppear { + viewStore.send(.loadMore) + } + } + } + // 加载更多指示器 + if viewStore.isLoadingMore { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .white)) + .padding(.vertical, 8) } } .padding(.horizontal, 16) .padding(.top, 10) .padding(.bottom, 20) } + .refreshable { + viewStore.send(.reload) + } } Spacer() }