import Foundation import ComposableArchitecture @Reducer struct FeedListFeature { @Dependency(\.apiService) var apiService @ObservableState struct State: Equatable { var isFirstLoad: Bool = true var feeds: [Feed] = [] // 预留 feed 内容 var isLoading: Bool = false var error: String? = nil var isEditFeedPresented: Bool = false // 新增:控制 CreateFeedView 弹窗 // 新增:动态内容 var moments: [MomentsInfo] = [] // 新增:只加载一次标志 var isLoaded: Bool = false // 分页相关 var currentPage: Int = 1 var hasMore: Bool = true var isLoadingMore: Bool = false // 新增:DetailView相关状态 var showDetail: Bool = false var selectedMoment: MomentsInfo? // 新增:点赞相关状态 var likeLoadingDynamicIds: Set = [] init() { // 默认初始化 } } enum Action: Equatable { case onAppear case reload case loadMore case loadMoreResponse(TaskResult) case editFeedButtonTapped // 新增:点击 add 按钮 case editFeedDismissed // 新增:关闭编辑页 case testButtonTapped // 新增:点击测试按钮 // 新增:动态内容相关 case fetchFeeds case fetchFeedsResponse(TaskResult) // 新增:DetailView相关Action case showDetail(MomentsInfo) case detailDismissed // 新增:点赞相关Action case likeDynamic(Int, Int, Int, Int) // dynamicId, uid, likedUid, worldId case likeResponse(TaskResult, dynamicId: Int, loadingId: UUID?) // 新增:CreateFeed发布成功通知 case createFeedPublishSuccess // 预留后续 Action case checkAuthAndLoad } func reduce(into state: inout State, action: Action) -> Effect { switch action { case .onAppear: guard state.isFirstLoad else { return .none } state.isFirstLoad = false debugInfoSync("📱 FeedListFeature onAppear") // 直接触发认证检查和数据加载 return .send(.checkAuthAndLoad) case .checkAuthAndLoad: // 新增:认证检查和数据加载 return .run { send in // 检查认证信息是否已保存 let accountModel = await UserInfoManager.getAccountModel() if accountModel?.uid != nil { debugInfoSync("✅ FeedListFeature: 认证信息已准备好,开始获取动态") await send(.fetchFeeds) } else { debugInfoSync("⏳ FeedListFeature: 认证信息未准备好,等待...") // 增加等待时间和重试次数 for attempt in 1...3 { try? await Task.sleep(nanoseconds: 500_000_000) // 0.5秒 let retryAccountModel = await UserInfoManager.getAccountModel() if retryAccountModel?.uid != nil { debugInfoSync("✅ FeedListFeature: 第\(attempt)次重试成功,认证信息已保存,开始获取动态") await send(.fetchFeeds) return } else { debugInfoSync("⏳ FeedListFeature: 第\(attempt)次重试,认证信息仍未准备好") } } debugInfoSync("❌ FeedListFeature: 多次重试后认证信息仍未准备好") } } 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 debugInfoSync("🔄 FeedListFeature: 开始获取动态") // 发起 API 请求 return .run { [apiService] send in await send(.fetchFeedsResponse(TaskResult { let request = LatestDynamicsRequest(dynamicId: "", pageSize: 20, types: [.text, .picture]) debugInfoSync("📡 FeedListFeature: 发送请求: \(request.endpoint)") debugInfoSync(" 参数: dynamicId=\(request.dynamicId), pageSize=\(request.pageSize)") return try await apiService.request(request) })) } case let .fetchFeedsResponse(.success(response)): state.isLoading = false debugInfoSync("✅ FeedListFeature: API 请求成功") debugInfoSync(" 响应码: \(response.code)") debugInfoSync(" 消息: \(response.message)") debugInfoSync(" 数据数量: \(response.data?.dynamicList.count ?? 0)") if let list = response.data?.dynamicList { state.moments = list state.error = nil state.currentPage = 1 state.hasMore = (list.count >= 20) debugInfoSync("✅ FeedListFeature: 数据加载成功") debugInfoSync(" 动态数量: \(list.count)") debugInfoSync(" 是否有更多: \(state.hasMore)") } else { state.moments = [] state.error = response.message state.hasMore = false debugErrorSync("❌ FeedListFeature: 数据为空") debugErrorSync(" 错误消息: \(response.message)") } return .none case let .fetchFeedsResponse(.failure(error)): state.isLoading = false state.moments = [] state.error = error.localizedDescription state.hasMore = false debugErrorSync("❌ FeedListFeature: API 请求失败") debugErrorSync(" 错误: \(error.localizedDescription)") return .none case .editFeedButtonTapped: state.isEditFeedPresented = true return .none case .editFeedDismissed: state.isEditFeedPresented = false return .none case .createFeedPublishSuccess: // CreateFeed发布成功,触发刷新并关闭编辑页面 return .merge( .send(.reload), .send(.editFeedDismissed) ) case .testButtonTapped: debugInfoSync("[LOG] FeedListFeature testButtonTapped") return .none case let .showDetail(moment): state.selectedMoment = moment state.showDetail = true return .none case .detailDismissed: state.showDetail = false state.selectedMoment = nil return .none case let .likeDynamic(dynamicId, uid, likedUid, worldId): // 添加loading状态 state.likeLoadingDynamicIds.insert(dynamicId) // 找到对应的动态并获取当前点赞状态 guard let index = state.moments.firstIndex(where: { $0.dynamicId == dynamicId }) else { // 找不到对应的动态,显示错误信息 setAPILoadingErrorSync(UUID(), errorMessage: "找不到对应的动态") state.likeLoadingDynamicIds.remove(dynamicId) return .none } let currentMoment = state.moments[index] let status = currentMoment.isLike ? 0 : 1 // 0: 取消点赞, 1: 点赞 let request = LikeDynamicRequest( dynamicId: dynamicId, uid: uid, status: status, likedUid: likedUid, worldId: worldId ) return .run { [apiService] send in let loadingId = await APILoadingManager.shared.startLoading( shouldShowLoading: request.shouldShowLoading, shouldShowError: request.shouldShowError ) do { let response: LikeDynamicResponse = try await apiService.request(request) await send(.likeResponse(.success(response), dynamicId: dynamicId, loadingId: loadingId)) } catch { await send(.likeResponse(.failure(error), dynamicId: dynamicId, loadingId: loadingId)) } } case let .likeResponse(.success(response), dynamicId, loadingId): state.likeLoadingDynamicIds.remove(dynamicId) if let loadingId = loadingId { if let data = response.data, let success = data.success, success { Task { @MainActor in APILoadingManager.shared.finishLoading(loadingId) } if let index = state.moments.firstIndex(where: { $0.dynamicId == dynamicId }) { let currentMoment = state.moments[index] let newLikeState = !currentMoment.isLike let updatedMoment = MomentsInfo( dynamicId: currentMoment.dynamicId, uid: currentMoment.uid, nick: currentMoment.nick, avatar: currentMoment.avatar, type: currentMoment.type, content: currentMoment.content, likeCount: data.likeCount ?? currentMoment.likeCount, isLike: newLikeState, commentCount: currentMoment.commentCount, publishTime: currentMoment.publishTime, worldId: currentMoment.worldId, status: currentMoment.status, playCount: currentMoment.playCount, dynamicResList: currentMoment.dynamicResList, gender: currentMoment.gender, squareTop: currentMoment.squareTop, topicTop: currentMoment.topicTop, newUser: currentMoment.newUser, defUser: currentMoment.defUser, scene: currentMoment.scene, userVipInfoVO: currentMoment.userVipInfoVO, headwearPic: currentMoment.headwearPic, headwearEffect: currentMoment.headwearEffect, headwearType: currentMoment.headwearType, headwearName: currentMoment.headwearName, headwearId: currentMoment.headwearId, experLevelPic: currentMoment.experLevelPic, charmLevelPic: currentMoment.charmLevelPic, isCustomWord: currentMoment.isCustomWord, labelList: currentMoment.labelList ) state.moments[index] = updatedMoment } } else { let errorMessage = response.message.isEmpty ? "点赞失败,请重试" : response.message setAPILoadingErrorSync(loadingId, errorMessage: errorMessage) } } return .none case let .likeResponse(.failure(error), dynamicId, loadingId): state.likeLoadingDynamicIds.remove(dynamicId) if let loadingId = loadingId { setAPILoadingErrorSync(loadingId, errorMessage: error.localizedDescription) } return .none } } } // Feed 数据模型占位,后续可替换为真实模型 enum Feed: Equatable, Identifiable { case placeholder(id: UUID = UUID()) var id: UUID { switch self { case .placeholder(let id): return id } } }