1
This commit is contained in:
@@ -14,6 +14,8 @@
|
||||
#import "KBAIReplyModel.h"
|
||||
#import "KBCommentModel.h"
|
||||
#import "AiVM.h"
|
||||
#import "KBUserSessionManager.h"
|
||||
#import "KBUser.h"
|
||||
#import <MJExtension/MJExtension.h>
|
||||
#import <Masonry/Masonry.h>
|
||||
#import <MJRefresh/MJRefresh.h>
|
||||
@@ -56,6 +58,81 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
|
||||
@implementation KBAICommentView
|
||||
|
||||
#pragma mark - Local Model Builders
|
||||
|
||||
- (NSString *)currentUserName {
|
||||
KBUser *user = [KBUserSessionManager shared].currentUser;
|
||||
if (user.nickName.length > 0) {
|
||||
return user.nickName;
|
||||
}
|
||||
return @"我";
|
||||
}
|
||||
|
||||
- (NSString *)currentUserId {
|
||||
KBUser *user = [KBUserSessionManager shared].currentUser;
|
||||
return user.userId ?: @"";
|
||||
}
|
||||
|
||||
- (NSString *)currentUserAvatarUrl {
|
||||
KBUser *user = [KBUserSessionManager shared].currentUser;
|
||||
return user.avatarUrl ?: @"";
|
||||
}
|
||||
|
||||
- (NSString *)generateTempIdString {
|
||||
long long ms = (long long)([[NSDate date] timeIntervalSince1970] * 1000.0);
|
||||
// 使用负数避免与后端 ID 冲突
|
||||
long long tmp = -ms;
|
||||
return [NSString stringWithFormat:@"%lld", tmp];
|
||||
}
|
||||
|
||||
- (KBAICommentModel *)buildLocalNewCommentWithText:(NSString *)text
|
||||
serverItem:(KBCommentItem *_Nullable)serverItem
|
||||
tableWidth:(CGFloat)tableWidth {
|
||||
KBAICommentModel *comment = [[KBAICommentModel alloc] init];
|
||||
NSString *cid = nil;
|
||||
if (serverItem && serverItem.commentId > 0) {
|
||||
cid = [NSString stringWithFormat:@"%ld", (long)serverItem.commentId];
|
||||
} else {
|
||||
cid = [self generateTempIdString];
|
||||
}
|
||||
comment.commentId = cid;
|
||||
comment.userId = [self currentUserId];
|
||||
comment.userName = [self currentUserName];
|
||||
comment.avatarUrl = [self currentUserAvatarUrl];
|
||||
comment.content = text ?: @"";
|
||||
comment.likeCount = 0;
|
||||
comment.liked = NO;
|
||||
comment.createTime = [[NSDate date] timeIntervalSince1970];
|
||||
comment.replies = @[];
|
||||
comment.cachedHeaderHeight =
|
||||
[comment calculateHeaderHeightWithMaxWidth:tableWidth];
|
||||
return comment;
|
||||
}
|
||||
|
||||
- (KBAIReplyModel *)buildLocalNewReplyWithText:(NSString *)text
|
||||
serverItem:(KBCommentItem *_Nullable)serverItem
|
||||
replyToUserName:(NSString *)replyToUserName
|
||||
tableWidth:(CGFloat)tableWidth {
|
||||
KBAIReplyModel *reply = [[KBAIReplyModel alloc] init];
|
||||
NSString *rid = nil;
|
||||
if (serverItem && serverItem.commentId > 0) {
|
||||
rid = [NSString stringWithFormat:@"%ld", (long)serverItem.commentId];
|
||||
} else {
|
||||
rid = [self generateTempIdString];
|
||||
}
|
||||
reply.replyId = rid;
|
||||
reply.userId = [self currentUserId];
|
||||
reply.userName = [self currentUserName];
|
||||
reply.avatarUrl = [self currentUserAvatarUrl];
|
||||
reply.content = text ?: @"";
|
||||
reply.replyToUserName = replyToUserName ?: @"";
|
||||
reply.likeCount = 0;
|
||||
reply.liked = NO;
|
||||
reply.createTime = [[NSDate date] timeIntervalSince1970];
|
||||
reply.cachedCellHeight = [reply calculateCellHeightWithMaxWidth:tableWidth];
|
||||
return reply;
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
@@ -289,7 +366,36 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
||||
|
||||
for (KBCommentItem *item in pageModel.records) {
|
||||
// 转换为 KBAICommentModel(使用 MJExtension)
|
||||
KBAICommentModel *comment = [KBAICommentModel mj_objectWithKeyValues:[item mj_keyValues]];
|
||||
// 注意:KBCommentItem 通过 MJExtension 将后端字段 id 映射为了 commentId。
|
||||
// 这里如果直接用 mj_keyValues,会导致字典里只有 commentId,KBAICommentModel/KBAIReplyModel
|
||||
// 的映射(commentId/replyId -> id)拿不到值,最终 commentId/replyId 为空,进而影响发送回复时的 parentId/rootId。
|
||||
NSMutableDictionary *itemKV = [[item mj_keyValues] mutableCopy];
|
||||
id commentIdVal = itemKV[@"commentId"];
|
||||
if (commentIdVal) {
|
||||
itemKV[@"id"] = commentIdVal;
|
||||
[itemKV removeObjectForKey:@"commentId"];
|
||||
}
|
||||
|
||||
id repliesObj = itemKV[@"replies"];
|
||||
if ([repliesObj isKindOfClass:[NSArray class]]) {
|
||||
NSArray *replies = (NSArray *)repliesObj;
|
||||
NSMutableArray *fixedReplies = [NSMutableArray arrayWithCapacity:replies.count];
|
||||
for (id obj in replies) {
|
||||
if (![obj isKindOfClass:[NSDictionary class]]) {
|
||||
continue;
|
||||
}
|
||||
NSMutableDictionary *replyKV = [((NSDictionary *)obj) mutableCopy];
|
||||
id replyCommentIdVal = replyKV[@"commentId"];
|
||||
if (replyCommentIdVal) {
|
||||
replyKV[@"id"] = replyCommentIdVal;
|
||||
[replyKV removeObjectForKey:@"commentId"];
|
||||
}
|
||||
[fixedReplies addObject:[replyKV copy]];
|
||||
}
|
||||
itemKV[@"replies"] = [fixedReplies copy];
|
||||
}
|
||||
|
||||
KBAICommentModel *comment = [KBAICommentModel mj_objectWithKeyValues:[itemKV copy]];
|
||||
|
||||
// 预先计算并缓存 Header 高度
|
||||
comment.cachedHeaderHeight = [comment calculateHeaderHeightWithMaxWidth:tableWidth];
|
||||
@@ -805,8 +911,44 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
}
|
||||
|
||||
- (void)sendNewCommentWithText:(NSString *)text tableWidth:(CGFloat)tableWidth {
|
||||
// TODO: 调用网络接口发送一级评论
|
||||
NSLog(@"[KBAICommentView] 发送一级评论:%@", text);
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self.aiVM addCommentWithCompanionId:self.companionId
|
||||
content:text
|
||||
parentId:nil
|
||||
rootId:nil
|
||||
completion:^(KBCommentItem * _Nullable newItem, NSInteger code, NSError * _Nullable error) {
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error || code != 0) {
|
||||
NSLog(@"[KBAICommentView] 发送一级评论失败:%@", error.localizedDescription ?: @"");
|
||||
return;
|
||||
}
|
||||
|
||||
// 本地插入新评论到第一条,不再全量刷新
|
||||
KBAICommentModel *localComment =
|
||||
[strongSelf buildLocalNewCommentWithText:text
|
||||
serverItem:newItem
|
||||
tableWidth:tableWidth];
|
||||
[strongSelf.comments insertObject:localComment atIndex:0];
|
||||
strongSelf.totalCommentCount += 1;
|
||||
[strongSelf updateTitle];
|
||||
[strongSelf hideEmptyState];
|
||||
|
||||
[strongSelf.tableView beginUpdates];
|
||||
[strongSelf.tableView
|
||||
insertSections:[NSIndexSet indexSetWithIndex:0]
|
||||
withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
[strongSelf.tableView endUpdates];
|
||||
|
||||
[strongSelf.tableView setContentOffset:CGPointZero animated:YES];
|
||||
});
|
||||
}];
|
||||
|
||||
// 示例代码:
|
||||
// [self.aiVM sendCommentWithCompanionId:self.companionId
|
||||
@@ -840,8 +982,101 @@ static NSInteger const kRepliesLoadCount = 5;
|
||||
if (!comment)
|
||||
return;
|
||||
|
||||
// TODO: 调用网络接口发送二级评论(回复)
|
||||
NSLog(@"[KBAICommentView] 回复评论 %@:%@", comment.commentId, text);
|
||||
|
||||
NSInteger root = [comment.commentId integerValue];
|
||||
NSNumber *rootId = @(root);
|
||||
NSNumber *parentId = nil;
|
||||
|
||||
if (self.replyToReply && self.replyToReply.replyId.length > 0) {
|
||||
parentId = @([self.replyToReply.replyId integerValue]);
|
||||
} else {
|
||||
parentId = @(root);
|
||||
}
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self.aiVM addCommentWithCompanionId:self.companionId
|
||||
content:text
|
||||
parentId:parentId
|
||||
rootId:rootId
|
||||
completion:^(KBCommentItem * _Nullable newItem, NSInteger code, NSError * _Nullable error) {
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (error || code != 0) {
|
||||
NSLog(@"[KBAICommentView] 回复评论失败:%@", error.localizedDescription ?: @"");
|
||||
return;
|
||||
}
|
||||
|
||||
NSInteger section = [strongSelf.comments indexOfObject:comment];
|
||||
if (section == NSNotFound) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSInteger oldTotalReplyCount = comment.totalReplyCount;
|
||||
BOOL wasFooterHidden = (oldTotalReplyCount == 0);
|
||||
BOOL wasFullyExpanded =
|
||||
(comment.isRepliesExpanded &&
|
||||
comment.displayedReplies.count == oldTotalReplyCount);
|
||||
|
||||
NSString *replyToUserName = @"";
|
||||
if (strongSelf.replyToReply && strongSelf.replyToReply.userName.length > 0) {
|
||||
replyToUserName = strongSelf.replyToReply.userName;
|
||||
} else if (comment.userName.length > 0) {
|
||||
replyToUserName = comment.userName;
|
||||
}
|
||||
|
||||
KBAIReplyModel *localReply =
|
||||
[strongSelf buildLocalNewReplyWithText:text
|
||||
serverItem:newItem
|
||||
replyToUserName:replyToUserName
|
||||
tableWidth:tableWidth];
|
||||
|
||||
NSArray<KBAIReplyModel *> *oldReplies = comment.replies ?: @[];
|
||||
NSMutableArray<KBAIReplyModel *> *newReplies =
|
||||
[NSMutableArray arrayWithArray:oldReplies];
|
||||
[newReplies addObject:localReply];
|
||||
comment.replies = [newReplies copy];
|
||||
|
||||
strongSelf.totalCommentCount += 1;
|
||||
[strongSelf updateTitle];
|
||||
|
||||
// 若当前已完整展开,则直接插入新行;否则保持 displayedReplies 为前缀,避免破坏 loadMoreReplies 逻辑
|
||||
if (wasFullyExpanded) {
|
||||
[comment.displayedReplies addObject:localReply];
|
||||
NSInteger newRowIndex = comment.displayedReplies.count - 1;
|
||||
NSIndexPath *indexPath =
|
||||
[NSIndexPath indexPathForRow:newRowIndex inSection:section];
|
||||
[strongSelf.tableView beginUpdates];
|
||||
[strongSelf.tableView insertRowsAtIndexPaths:@[ indexPath ]
|
||||
withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
[strongSelf.tableView endUpdates];
|
||||
|
||||
KBAICommentFooterView *footerView =
|
||||
(KBAICommentFooterView *)[strongSelf.tableView footerViewForSection:section];
|
||||
if (footerView) {
|
||||
[footerView configureWithComment:comment];
|
||||
}
|
||||
} else {
|
||||
if (wasFooterHidden) {
|
||||
[strongSelf.tableView reloadSections:[NSIndexSet indexSetWithIndex:section]
|
||||
withRowAnimation:UITableViewRowAnimationNone];
|
||||
} else {
|
||||
KBAICommentFooterView *footerView =
|
||||
(KBAICommentFooterView *)[strongSelf.tableView footerViewForSection:section];
|
||||
if (footerView) {
|
||||
[footerView configureWithComment:comment];
|
||||
} else {
|
||||
[strongSelf.tableView reloadSections:[NSIndexSet indexSetWithIndex:section]
|
||||
withRowAnimation:UITableViewRowAnimationNone];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}];
|
||||
|
||||
// 示例代码:
|
||||
// NSInteger parentId = [comment.commentId integerValue];
|
||||
|
||||
Reference in New Issue
Block a user