@@ -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 ] ] ;
// 预 先 计 算 并 缓 存 H eader 高 度
// Precompute and cache h eader height
comment . cachedHeaderHeight = [ comment calculateHeaderHeightWithMaxWidth : tableWidth ] ;
// 预 先 计 算 并 缓 存 所 有 R eply 高 度
// Precompute and cache all r eply 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 ] ] ;
}
// 插 入 行 ( 不 刷 新 H eader, 避 免 头 像 闪 烁 )
// Insert rows ( do not refresh h eader 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 ] ;
// 手 动 刷 新 F ooter
// Manually refresh f ooter
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 ] ;
// 删 除 行 ( 不 刷 新 H eader, 避 免 头 像 闪 烁 )
// Delete rows ( do not refresh h eader 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 ] ;
// 手 动 刷 新 F ooter
// Manually refresh f ooter
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 ) {
// 创 建 模 糊 效 果 ( 43 pt 的 模 糊 半 径 )
// iOS 的 UIBlurEffect 没 有 直 接 设 置 模 糊 半 径 的 API , 使 用 系 统 预 设 的 dark 效 果
// Create blur effect ( 43 pt 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 ] ;
//
// // 刷 新 F ooter
// // Refresh f ooter
// 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 {
// // 未 展 开 , 刷 新 F ooter 显 示 新 的 回 复 数
// // If not expanded , refresh f ooter to show updated reply count
// KBAICommentFooterView * footerView = ( KBAICommentFooterView * ) [ self . tableView footerViewForSection : section ] ;
// if ( footerView ) {
// [ footerView configureWithComment : comment ] ;