3
This commit is contained in:
@@ -35,23 +35,23 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
|
||||
@property(nonatomic, strong) NSMutableArray<KBAICommentModel *> *comments;
|
||||
|
||||
/// 分页参数
|
||||
/// Pagination params
|
||||
@property(nonatomic, assign) NSInteger currentPage;
|
||||
@property(nonatomic, assign) NSInteger pageSize;
|
||||
@property(nonatomic, assign) BOOL isLoading;
|
||||
@property(nonatomic, assign) BOOL hasMoreData;
|
||||
|
||||
/// 键盘高度
|
||||
/// Keyboard height
|
||||
@property(nonatomic, assign) CGFloat keyboardHeight;
|
||||
/// 输入框底部约束
|
||||
/// Bottom constraint for input view
|
||||
@property(nonatomic, strong) MASConstraint *inputBottomConstraint;
|
||||
|
||||
/// 当前回复的目标(一级评论)
|
||||
/// Current reply target (top-level comment)
|
||||
@property(nonatomic, weak) KBAICommentModel *replyToComment;
|
||||
/// 当前回复的目标(二级评论)
|
||||
/// Current reply target (reply)
|
||||
@property(nonatomic, weak) KBAIReplyModel *replyToReply;
|
||||
|
||||
/// AiVM 实例
|
||||
/// AiVM instance
|
||||
@property(nonatomic, strong) AiVM *aiVM;
|
||||
|
||||
@end
|
||||
@@ -65,7 +65,7 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
if (user.nickName.length > 0) {
|
||||
return user.nickName;
|
||||
}
|
||||
return @"我";
|
||||
return KBLocalized(@"Me");
|
||||
}
|
||||
|
||||
- (NSString *)currentUserId {
|
||||
@@ -80,7 +80,7 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
|
||||
- (NSString *)generateTempIdString {
|
||||
long long ms = (long long)([[NSDate date] timeIntervalSince1970] * 1000.0);
|
||||
// 使用负数避免与后端 ID 冲突
|
||||
// Use negative values to avoid colliding with server IDs
|
||||
long long tmp = -ms;
|
||||
return [NSString stringWithFormat:@"%lld", tmp];
|
||||
}
|
||||
@@ -155,13 +155,13 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
#pragma mark - UI Setup
|
||||
|
||||
- (void)setupUI {
|
||||
// 设置背景为透明,让模糊效果可见
|
||||
// Make background transparent so blur effect is visible
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.layer.cornerRadius = 12;
|
||||
self.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner;
|
||||
self.clipsToBounds = YES;
|
||||
|
||||
// 添加模糊背景(最底层)
|
||||
// Add blur background (bottom-most layer)
|
||||
[self addSubview:self.blurBackgroundView];
|
||||
[self.blurBackgroundView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(self);
|
||||
@@ -201,7 +201,7 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
make.bottom.equalTo(self.inputView.mas_top);
|
||||
}];
|
||||
|
||||
// 上拉加载更多
|
||||
// Load more on pull-up
|
||||
__weak typeof(self) weakSelf = self;
|
||||
MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
@@ -302,7 +302,7 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
|
||||
- (void)fetchCommentsAtPage:(NSInteger)page append:(BOOL)append {
|
||||
if (self.companionId <= 0) {
|
||||
NSLog(@"[KBAICommentView] companionId 未设置,无法加载评论");
|
||||
NSLog(@"[KBAICommentView] companionId is not set, cannot load comments");
|
||||
[self showEmptyState];
|
||||
[self.tableView.mj_footer endRefreshing];
|
||||
return;
|
||||
@@ -323,7 +323,7 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
strongSelf.isLoading = NO;
|
||||
|
||||
if (error) {
|
||||
NSLog(@"[KBAICommentView] 加载评论失败:%@", error.localizedDescription);
|
||||
NSLog(@"[KBAICommentView] Failed to load comments: %@", error.localizedDescription);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (append) {
|
||||
[strongSelf.tableView.mj_footer endRefreshing];
|
||||
@@ -340,11 +340,11 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
}];
|
||||
}
|
||||
|
||||
/// 更新评论数据(从后端返回的 KBCommentPageModel 转换为 UI 层的 KBAICommentModel)
|
||||
/// Update comments (convert backend KBCommentPageModel to UI KBAICommentModel)
|
||||
- (void)updateCommentsWithPageModel:(KBCommentPageModel *)pageModel append:(BOOL)append {
|
||||
if (!pageModel) {
|
||||
NSLog(@"[KBAICommentView] pageModel 为空");
|
||||
// 数据为空,显示空态
|
||||
NSLog(@"[KBAICommentView] pageModel is nil");
|
||||
// Data is empty, show empty state
|
||||
[self showEmptyState];
|
||||
[self.tableView.mj_footer endRefreshing];
|
||||
return;
|
||||
@@ -356,19 +356,20 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
[self.comments removeAllObjects];
|
||||
}
|
||||
|
||||
// 获取 tableView 宽度用于计算高度
|
||||
// Get tableView width for height calculation
|
||||
CGFloat tableWidth = self.tableView.bounds.size.width;
|
||||
if (tableWidth <= 0) {
|
||||
tableWidth = [UIScreen mainScreen].bounds.size.width;
|
||||
}
|
||||
|
||||
NSLog(@"[KBAICommentView] 加载到 %ld 条评论,共 %ld 条,页码:%ld/%ld", (long)pageModel.records.count, (long)pageModel.total, (long)pageModel.current, (long)pageModel.pages);
|
||||
NSLog(@"[KBAICommentView] Loaded %ld comments, total %ld, page: %ld/%ld", (long)pageModel.records.count, (long)pageModel.total, (long)pageModel.current, (long)pageModel.pages);
|
||||
|
||||
for (KBCommentItem *item in pageModel.records) {
|
||||
// 转换为 KBAICommentModel(使用 MJExtension)
|
||||
// 注意:KBCommentItem 通过 MJExtension 将后端字段 id 映射为了 commentId。
|
||||
// 这里如果直接用 mj_keyValues,会导致字典里只有 commentId,KBAICommentModel/KBAIReplyModel
|
||||
// 的映射(commentId/replyId -> id)拿不到值,最终 commentId/replyId 为空,进而影响发送回复时的 parentId/rootId。
|
||||
// Convert to KBAICommentModel (via MJExtension)
|
||||
// Note: KBCommentItem maps backend field id to commentId via MJExtension.
|
||||
// If we directly use mj_keyValues, the dictionary only has commentId and
|
||||
// KBAICommentModel/KBAIReplyModel mapping (commentId/replyId -> id) misses it.
|
||||
// That would make commentId/replyId empty and break parentId/rootId when replying.
|
||||
NSMutableDictionary *itemKV = [[item mj_keyValues] mutableCopy];
|
||||
id commentIdVal = itemKV[@"commentId"];
|
||||
if (commentIdVal) {
|
||||
@@ -397,10 +398,10 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
|
||||
KBAICommentModel *comment = [KBAICommentModel mj_objectWithKeyValues:[itemKV copy]];
|
||||
|
||||
// 预先计算并缓存 Header 高度
|
||||
// Precompute and cache header height
|
||||
comment.cachedHeaderHeight = [comment calculateHeaderHeightWithMaxWidth:tableWidth];
|
||||
|
||||
// 预先计算并缓存所有 Reply 高度
|
||||
// Precompute and cache all reply heights
|
||||
for (KBAIReplyModel *reply in comment.replies) {
|
||||
reply.cachedCellHeight = [reply calculateCellHeightWithMaxWidth:tableWidth];
|
||||
}
|
||||
@@ -411,7 +412,7 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
[self updateTitle];
|
||||
[self.tableView reloadData];
|
||||
|
||||
// 更新分页状态
|
||||
// Update pagination state
|
||||
self.currentPage = pageModel.current > 0 ? pageModel.current : self.currentPage;
|
||||
if (pageModel.pages > 0) {
|
||||
self.hasMoreData = pageModel.current < pageModel.pages;
|
||||
@@ -425,7 +426,7 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
[self.tableView.mj_footer endRefreshingWithNoMoreData];
|
||||
}
|
||||
|
||||
// 根据数据是否为空,动态控制空态显示
|
||||
// Toggle empty state based on data
|
||||
if (self.comments.count == 0) {
|
||||
[self showEmptyState];
|
||||
} else {
|
||||
@@ -433,25 +434,25 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
}
|
||||
}
|
||||
|
||||
/// 显示空态视图
|
||||
/// Show empty state view
|
||||
- (void)showEmptyState {
|
||||
self.tableView.useEmptyDataSet = YES;
|
||||
self.tableView.emptyTitleText = @"暂无评论";
|
||||
self.tableView.emptyDescriptionText = @"快来抢沙发吧~";
|
||||
self.tableView.emptyImage = nil; // 可选:设置空态图片
|
||||
self.tableView.emptyVerticalOffset = -50; // 向上偏移一点
|
||||
self.tableView.emptyTitleText = KBLocalized(@"No comments yet");
|
||||
self.tableView.emptyDescriptionText = KBLocalized(@"Be the first to comment");
|
||||
self.tableView.emptyImage = nil; // Optional: set empty-state image
|
||||
self.tableView.emptyVerticalOffset = -50; // Slight upward offset
|
||||
[self.tableView kb_reloadEmptyDataSet];
|
||||
}
|
||||
|
||||
/// 显示错误空态视图
|
||||
/// Show error empty state view
|
||||
- (void)showEmptyStateWithError {
|
||||
self.tableView.useEmptyDataSet = YES;
|
||||
self.tableView.emptyTitleText = @"加载失败";
|
||||
self.tableView.emptyDescriptionText = @"点击重新加载";
|
||||
self.tableView.emptyTitleText = KBLocalized(@"Load failed");
|
||||
self.tableView.emptyDescriptionText = KBLocalized(@"Tap to retry");
|
||||
self.tableView.emptyImage = nil;
|
||||
self.tableView.emptyVerticalOffset = -50;
|
||||
|
||||
// 点击重新加载
|
||||
// Tap to reload
|
||||
__weak typeof(self) weakSelf = self;
|
||||
self.tableView.emptyDidTapView = ^{
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
@@ -463,7 +464,7 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
[self.tableView kb_reloadEmptyDataSet];
|
||||
}
|
||||
|
||||
/// 隐藏空态视图
|
||||
/// Hide empty state view
|
||||
- (void)hideEmptyState {
|
||||
self.tableView.useEmptyDataSet = NO;
|
||||
[self.tableView kb_reloadEmptyDataSet];
|
||||
@@ -473,10 +474,10 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
NSString *countText;
|
||||
if (self.totalCommentCount >= 10000) {
|
||||
countText = [NSString
|
||||
stringWithFormat:@"%.1fw条评论", self.totalCommentCount / 10000.0];
|
||||
stringWithFormat:KBLocalized(@"%.1fw comments"), self.totalCommentCount / 10000.0];
|
||||
} else {
|
||||
countText =
|
||||
[NSString stringWithFormat:@"%ld条评论", (long)self.totalCommentCount];
|
||||
[NSString stringWithFormat:KBLocalized(@"%ld comments"), (long)self.totalCommentCount];
|
||||
}
|
||||
self.titleLabel.text = countText;
|
||||
}
|
||||
@@ -510,41 +511,41 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取评论 ID(需要转换为 NSInteger)
|
||||
// Get comment ID (convert to NSInteger)
|
||||
NSInteger commentId = [reply.replyId integerValue];
|
||||
|
||||
// 调用点赞接口
|
||||
// Call like API
|
||||
[strongSelf.aiVM likeCommentWithCommentId:commentId completion:^(KBCommentLikeResponse * _Nullable response, NSError * _Nullable error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error) {
|
||||
NSLog(@"[KBAICommentView] 二级评论点赞失败:%@", error.localizedDescription);
|
||||
// TODO: 显示错误提示
|
||||
NSLog(@"[KBAICommentView] Failed to like reply: %@", error.localizedDescription);
|
||||
// TODO: Show error message
|
||||
return;
|
||||
}
|
||||
|
||||
if (response && response.code == 0) {
|
||||
// data = true: 点赞成功,data = false: 取消点赞成功
|
||||
// data = true: liked, data = false: unliked
|
||||
BOOL isNowLiked = response.data;
|
||||
|
||||
// 更新模型状态
|
||||
// Update model state
|
||||
if (isNowLiked) {
|
||||
// 点赞成功:喜欢数+1
|
||||
// Like succeeded: like count +1
|
||||
reply.liked = YES;
|
||||
reply.likeCount = MAX(0, reply.likeCount + 1);
|
||||
NSLog(@"[KBAICommentView] 二级评论点赞成功,ID: %ld", (long)commentId);
|
||||
NSLog(@"[KBAICommentView] Reply liked successfully, ID: %ld", (long)commentId);
|
||||
} else {
|
||||
// 取消点赞成功:喜欢数-1
|
||||
// Unlike succeeded: like count -1
|
||||
reply.liked = NO;
|
||||
reply.likeCount = MAX(0, reply.likeCount - 1);
|
||||
NSLog(@"[KBAICommentView] 二级评论取消点赞成功,ID: %ld", (long)commentId);
|
||||
NSLog(@"[KBAICommentView] Reply unliked successfully, ID: %ld", (long)commentId);
|
||||
}
|
||||
|
||||
// 刷新对应的行
|
||||
// Refresh target row
|
||||
[strongSelf.tableView reloadRowsAtIndexPaths:@[ indexPath ]
|
||||
withRowAnimation:UITableViewRowAnimationNone];
|
||||
} else {
|
||||
NSLog(@"[KBAICommentView] 二级评论点赞失败:%@", response.message ?: @"未知错误");
|
||||
// TODO: 显示错误提示
|
||||
NSLog(@"[KBAICommentView] Failed to like reply: %@", response.message ?: @"Unknown error");
|
||||
// TODO: Show error message
|
||||
}
|
||||
});
|
||||
}];
|
||||
@@ -574,41 +575,41 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取评论 ID(需要转换为 NSInteger)
|
||||
// Get comment ID (convert to NSInteger)
|
||||
NSInteger commentId = [comment.commentId integerValue];
|
||||
|
||||
// 调用点赞接口
|
||||
// Call like API
|
||||
[strongSelf.aiVM likeCommentWithCommentId:commentId completion:^(KBCommentLikeResponse * _Nullable response, NSError * _Nullable error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error) {
|
||||
NSLog(@"[KBAICommentView] 一级评论点赞失败:%@", error.localizedDescription);
|
||||
// TODO: 显示错误提示
|
||||
NSLog(@"[KBAICommentView] Failed to like top-level comment: %@", error.localizedDescription);
|
||||
// TODO: Show error message
|
||||
return;
|
||||
}
|
||||
|
||||
if (response && response.code == 0) {
|
||||
// data = true: 点赞成功,data = false: 取消点赞成功
|
||||
// data = true: liked, data = false: unliked
|
||||
BOOL isNowLiked = response.data;
|
||||
|
||||
// 更新模型状态
|
||||
// Update model state
|
||||
if (isNowLiked) {
|
||||
// 点赞成功:喜欢数+1
|
||||
// Like succeeded: like count +1
|
||||
comment.liked = YES;
|
||||
comment.likeCount = MAX(0, comment.likeCount + 1);
|
||||
NSLog(@"[KBAICommentView] 一级评论点赞成功,ID: %ld", (long)commentId);
|
||||
NSLog(@"[KBAICommentView] Top-level comment liked successfully, ID: %ld", (long)commentId);
|
||||
} else {
|
||||
// 取消点赞成功:喜欢数-1
|
||||
// Unlike succeeded: like count -1
|
||||
comment.liked = NO;
|
||||
comment.likeCount = MAX(0, comment.likeCount - 1);
|
||||
NSLog(@"[KBAICommentView] 一级评论取消点赞成功,ID: %ld", (long)commentId);
|
||||
NSLog(@"[KBAICommentView] Top-level comment unliked successfully, ID: %ld", (long)commentId);
|
||||
}
|
||||
|
||||
// 刷新对应的 section
|
||||
// Refresh target section
|
||||
[strongSelf.tableView reloadSections:[NSIndexSet indexSetWithIndex:section]
|
||||
withRowAnimation:UITableViewRowAnimationNone];
|
||||
} else {
|
||||
NSLog(@"[KBAICommentView] 一级评论点赞失败:%@", response.message ?: @"未知错误");
|
||||
// TODO: 显示错误提示
|
||||
NSLog(@"[KBAICommentView] Failed to like top-level comment: %@", response.message ?: @"Unknown error");
|
||||
// TODO: Show error message
|
||||
}
|
||||
});
|
||||
}];
|
||||
@@ -626,7 +627,7 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
KBAICommentModel *comment = self.comments[section];
|
||||
KBAIReplyFooterState state = [comment footerState];
|
||||
|
||||
// 无二级评论时返回空视图
|
||||
// Return empty view when there are no replies
|
||||
if (state == KBAIReplyFooterStateHidden) {
|
||||
return nil;
|
||||
}
|
||||
@@ -669,7 +670,7 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
|
||||
#pragma mark - Footer Actions
|
||||
|
||||
/// 每次加载的回复数量
|
||||
/// Number of replies loaded each time
|
||||
static NSInteger const kRepliesLoadCount = 5;
|
||||
|
||||
- (void)handleFooterActionForSection:(NSInteger)section {
|
||||
@@ -695,10 +696,10 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
KBAICommentModel *comment = self.comments[section];
|
||||
NSInteger currentCount = comment.displayedReplies.count;
|
||||
|
||||
// 加载更多回复
|
||||
// Load more replies
|
||||
[comment loadMoreReplies:kRepliesLoadCount];
|
||||
|
||||
// 计算新增的行
|
||||
// Calculate newly inserted rows
|
||||
NSInteger newCount = comment.displayedReplies.count;
|
||||
NSMutableArray *insertIndexPaths = [NSMutableArray array];
|
||||
for (NSInteger i = currentCount; i < newCount; i++) {
|
||||
@@ -706,7 +707,7 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
inSection:section]];
|
||||
}
|
||||
|
||||
// 插入行(不刷新 Header,避免头像闪烁)
|
||||
// Insert rows (do not refresh header to avoid avatar flicker)
|
||||
[self.tableView beginUpdates];
|
||||
if (insertIndexPaths.count > 0) {
|
||||
[self.tableView insertRowsAtIndexPaths:insertIndexPaths
|
||||
@@ -714,7 +715,7 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
}
|
||||
[self.tableView endUpdates];
|
||||
|
||||
// 手动刷新 Footer
|
||||
// Manually refresh footer
|
||||
KBAICommentFooterView *footerView =
|
||||
(KBAICommentFooterView *)[self.tableView footerViewForSection:section];
|
||||
if (footerView) {
|
||||
@@ -726,17 +727,17 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
KBAICommentModel *comment = self.comments[section];
|
||||
NSInteger rowCount = comment.displayedReplies.count;
|
||||
|
||||
// 计算要删除的行
|
||||
// Calculate rows to delete
|
||||
NSMutableArray *deleteIndexPaths = [NSMutableArray array];
|
||||
for (NSInteger i = 0; i < rowCount; i++) {
|
||||
[deleteIndexPaths addObject:[NSIndexPath indexPathForRow:i
|
||||
inSection:section]];
|
||||
}
|
||||
|
||||
// 收起全部回复
|
||||
// Collapse all replies
|
||||
[comment collapseReplies];
|
||||
|
||||
// 删除行(不刷新 Header,避免头像闪烁)
|
||||
// Delete rows (do not refresh header to avoid avatar flicker)
|
||||
[self.tableView beginUpdates];
|
||||
if (deleteIndexPaths.count > 0) {
|
||||
[self.tableView deleteRowsAtIndexPaths:deleteIndexPaths
|
||||
@@ -744,7 +745,7 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
}
|
||||
[self.tableView endUpdates];
|
||||
|
||||
// 手动刷新 Footer
|
||||
// Manually refresh footer
|
||||
KBAICommentFooterView *footerView =
|
||||
(KBAICommentFooterView *)[self.tableView footerViewForSection:section];
|
||||
if (footerView) {
|
||||
@@ -756,7 +757,7 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
|
||||
- (void)closeButtonTapped {
|
||||
[self.popView dismiss];
|
||||
// 关闭评论视图(由外部处理)
|
||||
// Close comment view (handled by outside)
|
||||
// [[NSNotificationCenter defaultCenter]
|
||||
// postNotificationName:@"KBAICommentViewCloseNotification"
|
||||
// object:nil];
|
||||
@@ -766,13 +767,13 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
|
||||
- (UIVisualEffectView *)blurBackgroundView {
|
||||
if (!_blurBackgroundView) {
|
||||
// 创建模糊效果(43pt 的模糊半径)
|
||||
// iOS 的 UIBlurEffect 没有直接设置模糊半径的 API,使用系统预设的 dark 效果
|
||||
// Create blur effect (43pt blur radius in design)
|
||||
// iOS UIBlurEffect has no direct blur-radius API; use system dark style
|
||||
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
|
||||
_blurBackgroundView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
|
||||
|
||||
// 在模糊效果上叠加一个半透明黑色遮罩来调整透明度和颜色
|
||||
// 颜色:#000000,透明度:0.31
|
||||
// Overlay a semi-transparent black layer to tune tone and opacity
|
||||
// Color: #000000, alpha: 0.31
|
||||
UIView *darkOverlay = [[UIView alloc] init];
|
||||
darkOverlay.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.31];
|
||||
[_blurBackgroundView.contentView addSubview:darkOverlay];
|
||||
@@ -796,7 +797,7 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
_titleLabel = [[UILabel alloc] init];
|
||||
_titleLabel.font = [UIFont systemFontOfSize:15 weight:UIFontWeightMedium];
|
||||
_titleLabel.textColor = [UIColor whiteColor];
|
||||
_titleLabel.text = @"0条评论";
|
||||
_titleLabel.text = [NSString stringWithFormat:KBLocalized(@"%ld comments"), (long)0];
|
||||
}
|
||||
return _titleLabel;
|
||||
}
|
||||
@@ -824,10 +825,10 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
_tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
|
||||
_tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1, 0.01)];
|
||||
|
||||
// 关闭空数据占位,避免加载时显示"暂无数据"
|
||||
// Disable empty placeholder by default to avoid showing "No data" while loading
|
||||
_tableView.useEmptyDataSet = NO;
|
||||
|
||||
// 注册 Header/Cell/Footer
|
||||
// Register Header/Cell/Footer
|
||||
[_tableView registerClass:[KBAICommentHeaderView class]
|
||||
forHeaderFooterViewReuseIdentifier:kCommentHeaderIdentifier];
|
||||
[_tableView registerClass:[KBAIReplyCell class]
|
||||
@@ -835,7 +836,7 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
[_tableView registerClass:[KBAICommentFooterView class]
|
||||
forHeaderFooterViewReuseIdentifier:kCommentFooterIdentifier];
|
||||
|
||||
// 去掉顶部间距
|
||||
// Remove top padding
|
||||
if (@available(iOS 15.0, *)) {
|
||||
_tableView.sectionHeaderTopPadding = 0;
|
||||
}
|
||||
@@ -846,7 +847,7 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
- (KBAICommentInputView *)inputView {
|
||||
if (!_inputView) {
|
||||
_inputView = [[KBAICommentInputView alloc] init];
|
||||
_inputView.placeholder = @"Send A Message";
|
||||
_inputView.placeholder = KBLocalized(@"Send A Message");
|
||||
_inputView.layer.cornerRadius = 26;
|
||||
_inputView.clipsToBounds = true;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
@@ -865,26 +866,26 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
self.replyToReply = reply;
|
||||
|
||||
if (reply) {
|
||||
// 回复二级评论
|
||||
// Reply to a second-level comment
|
||||
self.inputView.placeholder =
|
||||
[NSString stringWithFormat:@"回复 @%@", reply.userName];
|
||||
[NSString stringWithFormat:KBLocalized(@"Reply to @%@"), reply.userName];
|
||||
} else if (comment) {
|
||||
// 回复一级评论
|
||||
// Reply to a top-level comment
|
||||
self.inputView.placeholder =
|
||||
[NSString stringWithFormat:@"回复 @%@", comment.userName];
|
||||
[NSString stringWithFormat:KBLocalized(@"Reply to @%@"), comment.userName];
|
||||
} else {
|
||||
// 普通评论
|
||||
self.inputView.placeholder = @"说点什么...";
|
||||
// New comment
|
||||
self.inputView.placeholder = KBLocalized(@"Say something...");
|
||||
}
|
||||
|
||||
// 弹起键盘
|
||||
// Show keyboard
|
||||
[self.inputView showKeyboard];
|
||||
}
|
||||
|
||||
- (void)clearReplyTarget {
|
||||
self.replyToComment = nil;
|
||||
self.replyToReply = nil;
|
||||
self.inputView.placeholder = @"说点什么...";
|
||||
self.inputView.placeholder = KBLocalized(@"Say something...");
|
||||
}
|
||||
|
||||
#pragma mark - Send Comment
|
||||
@@ -899,20 +900,20 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
}
|
||||
|
||||
if (self.replyToComment) {
|
||||
// 回复评论(添加二级评论)
|
||||
// Send reply (add second-level comment)
|
||||
[self sendReplyWithText:text tableWidth:tableWidth];
|
||||
} else {
|
||||
// 发送一级评论
|
||||
// Send top-level comment
|
||||
[self sendNewCommentWithText:text tableWidth:tableWidth];
|
||||
}
|
||||
|
||||
// 清空输入框和回复目标
|
||||
// Clear input and reply target
|
||||
[self.inputView clearText];
|
||||
[self clearReplyTarget];
|
||||
}
|
||||
|
||||
- (void)sendNewCommentWithText:(NSString *)text tableWidth:(CGFloat)tableWidth {
|
||||
NSLog(@"[KBAICommentView] 发送一级评论:%@", text);
|
||||
NSLog(@"[KBAICommentView] Send top-level comment: %@", text);
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self.aiVM addCommentWithCompanionId:self.companionId
|
||||
@@ -927,11 +928,11 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error || code != 0) {
|
||||
NSLog(@"[KBAICommentView] 发送一级评论失败:%@", error.localizedDescription ?: @"");
|
||||
NSLog(@"[KBAICommentView] Failed to send top-level comment: %@", error.localizedDescription ?: @"");
|
||||
return;
|
||||
}
|
||||
|
||||
// 本地插入新评论到第一条,不再全量刷新
|
||||
// Insert new comment locally at first position; avoid full reload
|
||||
KBAICommentModel *localComment =
|
||||
[strongSelf buildLocalNewCommentWithText:text
|
||||
serverItem:newItem
|
||||
@@ -954,29 +955,29 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
});
|
||||
}];
|
||||
|
||||
// 示例代码:
|
||||
// Example code:
|
||||
// [self.aiVM sendCommentWithCompanionId:self.companionId
|
||||
// content:text
|
||||
// completion:^(KBCommentItem *newItem, NSError *error) {
|
||||
// if (error) {
|
||||
// NSLog(@"[KBAICommentView] 发送评论失败:%@", error.localizedDescription);
|
||||
// NSLog(@"[KBAICommentView] Failed to send comment: %@", error.localizedDescription);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 转换为 KBAICommentModel
|
||||
// // Convert to KBAICommentModel
|
||||
// KBAICommentModel *comment = [KBAICommentModel mj_objectWithKeyValues:[newItem mj_keyValues]];
|
||||
// comment.cachedHeaderHeight = [comment calculateHeaderHeightWithMaxWidth:tableWidth];
|
||||
//
|
||||
// // 插入到数组第一个
|
||||
// // Insert into array at index 0
|
||||
// [self.comments insertObject:comment atIndex:0];
|
||||
// self.totalCommentCount++;
|
||||
// [self updateTitle];
|
||||
//
|
||||
// // 插入新 section
|
||||
// // Insert new section
|
||||
// [self.tableView insertSections:[NSIndexSet indexSetWithIndex:0]
|
||||
// withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
//
|
||||
// // 滚动到顶部
|
||||
// // Scroll to top
|
||||
// [self.tableView setContentOffset:CGPointZero animated:YES];
|
||||
// }];
|
||||
}
|
||||
@@ -986,7 +987,7 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
if (!comment)
|
||||
return;
|
||||
|
||||
NSLog(@"[KBAICommentView] 回复评论 %@:%@", comment.commentId, text);
|
||||
NSLog(@"[KBAICommentView] Reply to comment %@: %@", comment.commentId, text);
|
||||
|
||||
NSInteger root = [comment.commentId integerValue];
|
||||
NSNumber *rootId = @(root);
|
||||
@@ -1011,7 +1012,7 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error || code != 0) {
|
||||
NSLog(@"[KBAICommentView] 回复评论失败:%@", error.localizedDescription ?: @"");
|
||||
NSLog(@"[KBAICommentView] Failed to send reply: %@", error.localizedDescription ?: @"");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1051,7 +1052,7 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
[strongSelf.delegate commentView:strongSelf didUpdateTotalCommentCount:strongSelf.totalCommentCount];
|
||||
}
|
||||
|
||||
// 若当前已完整展开,则直接插入新行;否则保持 displayedReplies 为前缀,避免破坏 loadMoreReplies 逻辑
|
||||
// If fully expanded, insert new row directly; otherwise keep displayedReplies as prefix to preserve loadMoreReplies behavior
|
||||
if (wasFullyExpanded) {
|
||||
[comment.displayedReplies addObject:localReply];
|
||||
NSInteger newRowIndex = comment.displayedReplies.count - 1;
|
||||
@@ -1085,31 +1086,31 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
});
|
||||
}];
|
||||
|
||||
// 示例代码:
|
||||
// Example code:
|
||||
// NSInteger parentId = [comment.commentId integerValue];
|
||||
// [self.aiVM replyCommentWithParentId:parentId
|
||||
// content:text
|
||||
// completion:^(KBCommentItem *newItem, NSError *error) {
|
||||
// if (error) {
|
||||
// NSLog(@"[KBAICommentView] 回复评论失败:%@", error.localizedDescription);
|
||||
// NSLog(@"[KBAICommentView] Failed to reply comment: %@", error.localizedDescription);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // 转换为 KBAIReplyModel
|
||||
// // Convert to KBAIReplyModel
|
||||
// KBAIReplyModel *newReply = [KBAIReplyModel mj_objectWithKeyValues:[newItem mj_keyValues]];
|
||||
// newReply.cachedCellHeight = [newReply calculateCellHeightWithMaxWidth:tableWidth];
|
||||
//
|
||||
// // 添加到 replies 数组
|
||||
// // Append to replies array
|
||||
// NSMutableArray *newReplies = [NSMutableArray arrayWithArray:comment.replies];
|
||||
// [newReplies addObject:newReply];
|
||||
// comment.replies = newReplies;
|
||||
// comment.totalReplyCount = newReplies.count;
|
||||
//
|
||||
// // 找到该评论的 section
|
||||
// // Find section for this comment
|
||||
// NSInteger section = [self.comments indexOfObject:comment];
|
||||
// if (section == NSNotFound) return;
|
||||
//
|
||||
// // 如果已展开,添加到 displayedReplies 并插入行
|
||||
// // If expanded, append to displayedReplies and insert row
|
||||
// if (comment.isRepliesExpanded) {
|
||||
// NSInteger newRowIndex = comment.displayedReplies.count;
|
||||
// [comment.displayedReplies addObject:newReply];
|
||||
@@ -1118,18 +1119,18 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
// [self.tableView insertRowsAtIndexPaths:@[indexPath]
|
||||
// withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
//
|
||||
// // 刷新 Footer
|
||||
// // Refresh footer
|
||||
// KBAICommentFooterView *footerView = (KBAICommentFooterView *)[self.tableView footerViewForSection:section];
|
||||
// if (footerView) {
|
||||
// [footerView configureWithComment:comment];
|
||||
// }
|
||||
//
|
||||
// // 滚动到新回复
|
||||
// // Scroll to new reply
|
||||
// [self.tableView scrollToRowAtIndexPath:indexPath
|
||||
// atScrollPosition:UITableViewScrollPositionBottom
|
||||
// animated:YES];
|
||||
// } else {
|
||||
// // 未展开,刷新 Footer 显示新的回复数
|
||||
// // If not expanded, refresh footer to show updated reply count
|
||||
// KBAICommentFooterView *footerView = (KBAICommentFooterView *)[self.tableView footerViewForSection:section];
|
||||
// if (footerView) {
|
||||
// [footerView configureWithComment:comment];
|
||||
|
||||
Reference in New Issue
Block a user