// Created by AI on 2025-10-10. #import #import "EPMomentListView.h" #import "EPMomentCell.h" #import #import "YuMi-Swift.h" #import "EPEmotionColorStorage.h" @interface EPMomentListView () @property (nonatomic, strong) UITableView *tableView; @property (nonatomic, strong) UIRefreshControl *refreshControl; @property (nonatomic, strong) NSMutableArray *mutableRawList; @property (nonatomic, strong) EPMomentAPISwiftHelper *api; @property (nonatomic, assign) BOOL isLoading; @property (nonatomic, copy) NSString *nextID; @property (nonatomic, assign) BOOL isLocalMode; @property (nonatomic, copy) void (^refreshCallback)(void); @end @implementation EPMomentListView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.backgroundColor = [UIColor clearColor]; _api = [[EPMomentAPISwiftHelper alloc] init]; _mutableRawList = [NSMutableArray array]; _sourceType = EPMomentListSourceTypeRecommend; _isLocalMode = NO; [self addSubview:self.tableView]; [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self); }]; } return self; } - (NSArray *)rawList { return [self.mutableRawList copy]; } - (void)reloadFirstPage { NSLog(@"[EPMomentListView] 📄 开始刷新第一页,isLocalMode=%d", self.isLocalMode); if (self.isLocalMode) { if (self.refreshCallback) { self.refreshCallback(); } [self.refreshControl endRefreshing]; return; } self.nextID = @""; [self.mutableRawList removeAllObjects]; [self.tableView reloadData]; [self.tableView.mj_footer resetNoMoreData]; [self requestNextPage]; } - (void)loadWithDynamicInfo:(NSArray *)dynamicInfo refreshCallback:(void (^)(void))refreshCallback { self.isLocalMode = YES; self.refreshCallback = refreshCallback; [self.mutableRawList removeAllObjects]; if (dynamicInfo.count > 0) { [self.mutableRawList addObjectsFromArray:dynamicInfo]; } self.tableView.mj_footer.hidden = YES; [self.tableView reloadData]; [self.refreshControl endRefreshing]; } - (void)requestNextPage { if (self.isLoading) { NSLog(@"[EPMomentListView] ⚠️ 已有加载任务进行中,跳过本次请求"); return; } NSLog(@"[EPMomentListView] 🌐 发起网络请求,nextID=%@", self.nextID.length > 0 ? self.nextID : @"(首页)"); self.isLoading = YES; @kWeakify(self); [self.api fetchLatestMomentsWithNextID:self.nextID completion:^(NSArray * _Nonnull list, NSString * _Nonnull nextMomentID) { @kStrongify(self); NSLog(@"[EPMomentListView] ✅ 请求成功,获得 %lu 条数据", (unsigned long)list.count); [self endLoading]; if (list.count > 0) { [self processEmotionColors:list isFirstPage:(self.nextID.length == 0)]; self.nextID = nextMomentID; [self.mutableRawList addObjectsFromArray:list]; [self removeEmptyState]; [self.tableView reloadData]; if (nextMomentID.length > 0) { [self.tableView.mj_footer endRefreshing]; } else { [self.tableView.mj_footer endRefreshingWithNoMoreData]; } } else { NSLog(@"[EPMomentListView] ⚠️ 返回数据为空"); if (self.mutableRawList.count == 0) { [self showEmptyStateWithMessage:YMLocalizedString(@"common.no_data")]; } [self.tableView.mj_footer endRefreshingWithNoMoreData]; } } failure:^(NSInteger code, NSString * _Nonnull msg) { @kStrongify(self); NSLog(@"[EPMomentListView] ❌ 请求失败,code=%ld, msg=%@", (long)code, msg); [self endLoading]; if (self.mutableRawList.count == 0) { [self showEmptyStateWithMessage:msg ?: YMLocalizedString(@"error.request_failed")]; } [self.tableView.mj_footer endRefreshing]; }]; } - (void)endLoading { self.isLoading = NO; [self.refreshControl endRefreshing]; } - (void)showEmptyStateWithMessage:(NSString *)message { UILabel *emptyLabel = [[UILabel alloc] initWithFrame:CGRectZero]; emptyLabel.text = [NSString stringWithFormat:@"%@\n\n%@", message, YMLocalizedString(@"common.pull_to_retry")]; emptyLabel.textColor = [UIColor whiteColor]; emptyLabel.textAlignment = NSTextAlignmentCenter; emptyLabel.numberOfLines = 0; emptyLabel.font = [UIFont systemFontOfSize:15]; emptyLabel.tag = 9999; [[self.tableView viewWithTag:9999] removeFromSuperview]; [self.tableView addSubview:emptyLabel]; [emptyLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(self.tableView); make.leading.trailing.equalTo(self.tableView).inset(40); }]; } - (void)removeEmptyState { [[self.tableView viewWithTag:9999] removeFromSuperview]; } - (void)processEmotionColors:(NSArray *)list isFirstPage:(BOOL)isFirstPage { NSString *pendingColor = [[NSUserDefaults standardUserDefaults] stringForKey:@"EP_Pending_Emotion_Color"]; NSNumber *pendingTimestamp = [[NSUserDefaults standardUserDefaults] objectForKey:@"EP_Pending_Emotion_Timestamp"]; for (NSInteger i = 0; i < list.count; i++) { MomentsInfoModel *model = list[i]; if (isFirstPage && i == 0 && pendingColor && pendingTimestamp) { NSTimeInterval now = [[NSDate date] timeIntervalSince1970]; NSTimeInterval pending = pendingTimestamp.doubleValue; if ((now - pending) < 5.0) { model.emotionColor = pendingColor; [EPEmotionColorStorage saveColor:pendingColor forDynamicId:model.dynamicId]; [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"EP_Pending_Emotion_Color"]; [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"EP_Pending_Emotion_Timestamp"]; [[NSUserDefaults standardUserDefaults] synchronize]; continue; } } NSString *savedColor = [EPEmotionColorStorage colorForDynamicId:model.dynamicId]; if (savedColor) { model.emotionColor = savedColor; } else { NSString *randomColor = [EPEmotionColorStorage randomEmotionColor]; model.emotionColor = randomColor; [EPEmotionColorStorage saveColor:randomColor forDynamicId:model.dynamicId]; NSLog(@"[EPMomentListView] 为动态 %@ 分配随机颜色: %@", model.dynamicId, randomColor); } } } #pragma mark - UITableView - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.mutableRawList.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { EPMomentCell *cell = [tableView dequeueReusableCellWithIdentifier:@"NewMomentCell" forIndexPath:indexPath]; if (indexPath.row < self.mutableRawList.count) { MomentsInfoModel *model = [self.mutableRawList xpSafeObjectAtIndex:indexPath.row]; [cell configureWithModel:model]; } return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return UITableViewAutomaticDimension; } - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { return 200; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; if (self.onSelectMoment) self.onSelectMoment(indexPath.row); } - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (self.isLocalMode) return; CGFloat offsetY = scrollView.contentOffset.y; CGFloat contentHeight = scrollView.contentSize.height; CGFloat screenHeight = scrollView.frame.size.height; if (offsetY > contentHeight - screenHeight - 100 && !self.isLoading) { [self requestNextPage]; } } #pragma mark - Lazy - (UITableView *)tableView { if (!_tableView) { _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; _tableView.delegate = self; _tableView.dataSource = self; _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; _tableView.backgroundColor = [UIColor clearColor]; _tableView.estimatedRowHeight = 200; _tableView.rowHeight = UITableViewAutomaticDimension; _tableView.showsVerticalScrollIndicator = NO; _tableView.contentInset = UIEdgeInsetsMake(10, 0, 120, 0); _tableView.scrollIndicatorInsets = UIEdgeInsetsMake(10, 0, 120, 0); [_tableView registerClass:[EPMomentCell class] forCellReuseIdentifier:@"NewMomentCell"]; _tableView.refreshControl = self.refreshControl; __weak typeof(self) weakSelf = self; MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{ __strong typeof(weakSelf) self = weakSelf; if (!self.isLoading && self.nextID.length > 0) { [self requestNextPage]; } else if (self.nextID.length == 0) { [self.tableView.mj_footer endRefreshingWithNoMoreData]; } else { [self.tableView.mj_footer endRefreshing]; } }]; footer.stateLabel.textColor = [UIColor whiteColor]; footer.loadingView.color = [UIColor whiteColor]; _tableView.mj_footer = footer; } return _tableView; } - (UIRefreshControl *)refreshControl { if (!_refreshControl) { _refreshControl = [[UIRefreshControl alloc] init]; _refreshControl.tintColor = [UIColor whiteColor]; [_refreshControl addTarget:self action:@selector(reloadFirstPage) forControlEvents:UIControlEventValueChanged]; } return _refreshControl; } @end