This commit is contained in:
2026-01-29 15:53:26 +08:00
parent 07a77149fc
commit 766c62f3c0
10 changed files with 890 additions and 18 deletions

View File

@@ -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 commentIdKBAICommentModel/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];