1
This commit is contained in:
@@ -40,12 +40,12 @@
|
|||||||
make.height.mas_equalTo(24);
|
make.height.mas_equalTo(24);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self.lineView mas_makeConstraints:^(MASConstraintMaker *make) {
|
// [self.lineView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.left.equalTo(self.contentView).offset(16);
|
// make.left.equalTo(self.contentView).offset(16);
|
||||||
make.right.equalTo(self.contentView).offset(-16);
|
// make.right.equalTo(self.contentView).offset(-16);
|
||||||
make.bottom.equalTo(self.contentView);
|
// make.bottom.equalTo(self.contentView);
|
||||||
make.height.mas_equalTo(0.5);
|
// make.height.mas_equalTo(0.5);
|
||||||
}];
|
// }];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Configuration
|
#pragma mark - Configuration
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#import "AiVM.h"
|
#import "AiVM.h"
|
||||||
#import <MJExtension/MJExtension.h>
|
#import <MJExtension/MJExtension.h>
|
||||||
#import <Masonry/Masonry.h>
|
#import <Masonry/Masonry.h>
|
||||||
|
#import <MJRefresh/MJRefresh.h>
|
||||||
|
|
||||||
static NSString *const kCommentHeaderIdentifier = @"CommentHeader";
|
static NSString *const kCommentHeaderIdentifier = @"CommentHeader";
|
||||||
static NSString *const kReplyCellIdentifier = @"ReplyCell";
|
static NSString *const kReplyCellIdentifier = @"ReplyCell";
|
||||||
@@ -33,6 +34,12 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
|||||||
@property(nonatomic, strong) NSMutableArray<KBAICommentModel *> *comments;
|
@property(nonatomic, strong) NSMutableArray<KBAICommentModel *> *comments;
|
||||||
@property(nonatomic, assign) NSInteger totalCommentCount;
|
@property(nonatomic, assign) NSInteger totalCommentCount;
|
||||||
|
|
||||||
|
/// 分页参数
|
||||||
|
@property(nonatomic, assign) NSInteger currentPage;
|
||||||
|
@property(nonatomic, assign) NSInteger pageSize;
|
||||||
|
@property(nonatomic, assign) BOOL isLoading;
|
||||||
|
@property(nonatomic, assign) BOOL hasMoreData;
|
||||||
|
|
||||||
/// 键盘高度
|
/// 键盘高度
|
||||||
@property(nonatomic, assign) CGFloat keyboardHeight;
|
@property(nonatomic, assign) CGFloat keyboardHeight;
|
||||||
/// 输入框底部约束
|
/// 输入框底部约束
|
||||||
@@ -56,6 +63,9 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
|||||||
self = [super initWithFrame:frame];
|
self = [super initWithFrame:frame];
|
||||||
if (self) {
|
if (self) {
|
||||||
self.comments = [NSMutableArray array];
|
self.comments = [NSMutableArray array];
|
||||||
|
self.currentPage = 1;
|
||||||
|
self.pageSize = 20;
|
||||||
|
self.hasMoreData = YES;
|
||||||
[self setupUI];
|
[self setupUI];
|
||||||
[self setupKeyboardObservers];
|
[self setupKeyboardObservers];
|
||||||
}
|
}
|
||||||
@@ -114,6 +124,20 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
|||||||
make.left.right.equalTo(self);
|
make.left.right.equalTo(self);
|
||||||
make.bottom.equalTo(self.inputView.mas_top);
|
make.bottom.equalTo(self.inputView.mas_top);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
// 上拉加载更多
|
||||||
|
__weak typeof(self) weakSelf = self;
|
||||||
|
MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
|
||||||
|
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||||
|
if (!strongSelf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[strongSelf loadMoreComments];
|
||||||
|
}];
|
||||||
|
footer.stateLabel.hidden = YES;
|
||||||
|
footer.backgroundColor = [UIColor clearColor];
|
||||||
|
footer.automaticallyHidden = YES;
|
||||||
|
self.tableView.mj_footer = footer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Keyboard Observers
|
#pragma mark - Keyboard Observers
|
||||||
@@ -174,49 +198,87 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
|||||||
#pragma mark - Data Loading
|
#pragma mark - Data Loading
|
||||||
|
|
||||||
- (void)loadComments {
|
- (void)loadComments {
|
||||||
if (self.companionId <= 0) {
|
if (self.isLoading) {
|
||||||
NSLog(@"[KBAICommentView] companionId 未设置,无法加载评论");
|
|
||||||
[self showEmptyState];
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.currentPage = 1;
|
||||||
|
self.hasMoreData = YES;
|
||||||
|
[self.tableView.mj_footer resetNoMoreData];
|
||||||
|
|
||||||
|
[self fetchCommentsAtPage:self.currentPage append:NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)loadMoreComments {
|
||||||
|
if (self.isLoading) {
|
||||||
|
[self.tableView.mj_footer endRefreshing];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self.hasMoreData) {
|
||||||
|
[self.tableView.mj_footer endRefreshingWithNoMoreData];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSInteger nextPage = self.currentPage + 1;
|
||||||
|
[self fetchCommentsAtPage:nextPage append:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)fetchCommentsAtPage:(NSInteger)page append:(BOOL)append {
|
||||||
|
if (self.companionId <= 0) {
|
||||||
|
NSLog(@"[KBAICommentView] companionId 未设置,无法加载评论");
|
||||||
|
[self showEmptyState];
|
||||||
|
[self.tableView.mj_footer endRefreshing];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.isLoading = YES;
|
||||||
|
|
||||||
__weak typeof(self) weakSelf = self;
|
__weak typeof(self) weakSelf = self;
|
||||||
[self.aiVM fetchCommentsWithCompanionId:self.companionId
|
[self.aiVM fetchCommentsWithCompanionId:self.companionId
|
||||||
pageNum:1
|
pageNum:page
|
||||||
pageSize:20
|
pageSize:self.pageSize
|
||||||
completion:^(KBCommentPageModel *pageModel, NSError *error) {
|
completion:^(KBCommentPageModel *pageModel, NSError *error) {
|
||||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||||
if (!strongSelf) {
|
if (!strongSelf) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strongSelf.isLoading = NO;
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
NSLog(@"[KBAICommentView] 加载评论失败:%@", error.localizedDescription);
|
NSLog(@"[KBAICommentView] 加载评论失败:%@", error.localizedDescription);
|
||||||
// 加载失败也显示空态
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[strongSelf showEmptyStateWithError];
|
if (append) {
|
||||||
|
[strongSelf.tableView.mj_footer endRefreshing];
|
||||||
|
} else {
|
||||||
|
[strongSelf showEmptyStateWithError];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[strongSelf updateCommentsWithPageModel:pageModel];
|
[strongSelf updateCommentsWithPageModel:pageModel append:append];
|
||||||
});
|
});
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 更新评论数据(从后端返回的 KBCommentPageModel 转换为 UI 层的 KBAICommentModel)
|
/// 更新评论数据(从后端返回的 KBCommentPageModel 转换为 UI 层的 KBAICommentModel)
|
||||||
- (void)updateCommentsWithPageModel:(KBCommentPageModel *)pageModel {
|
- (void)updateCommentsWithPageModel:(KBCommentPageModel *)pageModel append:(BOOL)append {
|
||||||
if (!pageModel) {
|
if (!pageModel) {
|
||||||
NSLog(@"[KBAICommentView] pageModel 为空");
|
NSLog(@"[KBAICommentView] pageModel 为空");
|
||||||
// 数据为空,显示空态
|
// 数据为空,显示空态
|
||||||
[self showEmptyState];
|
[self showEmptyState];
|
||||||
|
[self.tableView.mj_footer endRefreshing];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.totalCommentCount = pageModel.total;
|
self.totalCommentCount = pageModel.total;
|
||||||
|
|
||||||
[self.comments removeAllObjects];
|
if (!append) {
|
||||||
|
[self.comments removeAllObjects];
|
||||||
|
}
|
||||||
|
|
||||||
// 获取 tableView 宽度用于计算高度
|
// 获取 tableView 宽度用于计算高度
|
||||||
CGFloat tableWidth = self.tableView.bounds.size.width;
|
CGFloat tableWidth = self.tableView.bounds.size.width;
|
||||||
@@ -224,7 +286,7 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
|||||||
tableWidth = [UIScreen mainScreen].bounds.size.width;
|
tableWidth = [UIScreen mainScreen].bounds.size.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSLog(@"[KBAICommentView] 加载到 %ld 条评论,共 %ld 条", (long)pageModel.records.count, (long)pageModel.total);
|
NSLog(@"[KBAICommentView] 加载到 %ld 条评论,共 %ld 条,页码:%ld/%ld", (long)pageModel.records.count, (long)pageModel.total, (long)pageModel.current, (long)pageModel.pages);
|
||||||
|
|
||||||
for (KBCommentItem *item in pageModel.records) {
|
for (KBCommentItem *item in pageModel.records) {
|
||||||
// 转换为 KBAICommentModel(使用 MJExtension)
|
// 转换为 KBAICommentModel(使用 MJExtension)
|
||||||
@@ -244,6 +306,20 @@ static NSString *const kCommentFooterIdentifier = @"CommentFooter";
|
|||||||
[self updateTitle];
|
[self updateTitle];
|
||||||
[self.tableView reloadData];
|
[self.tableView reloadData];
|
||||||
|
|
||||||
|
// 更新分页状态
|
||||||
|
self.currentPage = pageModel.current > 0 ? pageModel.current : self.currentPage;
|
||||||
|
if (pageModel.pages > 0) {
|
||||||
|
self.hasMoreData = pageModel.current < pageModel.pages;
|
||||||
|
} else {
|
||||||
|
self.hasMoreData = pageModel.records.count >= self.pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.hasMoreData) {
|
||||||
|
[self.tableView.mj_footer endRefreshing];
|
||||||
|
} else {
|
||||||
|
[self.tableView.mj_footer endRefreshingWithNoMoreData];
|
||||||
|
}
|
||||||
|
|
||||||
// 根据数据是否为空,动态控制空态显示
|
// 根据数据是否为空,动态控制空态显示
|
||||||
if (self.comments.count == 0) {
|
if (self.comments.count == 0) {
|
||||||
[self showEmptyState];
|
[self showEmptyState];
|
||||||
@@ -641,6 +717,7 @@ static NSInteger const kRepliesLoadCount = 5;
|
|||||||
_tableView.backgroundColor = [UIColor clearColor];
|
_tableView.backgroundColor = [UIColor clearColor];
|
||||||
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||||
_tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
|
_tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
|
||||||
|
_tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1, 0.01)];
|
||||||
|
|
||||||
// 关闭空数据占位,避免加载时显示"暂无数据"
|
// 关闭空数据占位,避免加载时显示"暂无数据"
|
||||||
_tableView.useEmptyDataSet = NO;
|
_tableView.useEmptyDataSet = NO;
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
[self.avatarImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.avatarImageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.left.equalTo(self.contentView).offset(68); // 16 + 40 + 12 = 68
|
make.left.equalTo(self.contentView).offset(68); // 16 + 40 + 12 = 68
|
||||||
make.top.equalTo(self.contentView).offset(8);
|
make.top.equalTo(self.contentView).offset(8);
|
||||||
make.width.height.mas_equalTo(28);
|
make.width.height.mas_equalTo(26);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self.userNameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.userNameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
@@ -171,7 +171,7 @@
|
|||||||
if (!_avatarImageView) {
|
if (!_avatarImageView) {
|
||||||
_avatarImageView = [[UIImageView alloc] init];
|
_avatarImageView = [[UIImageView alloc] init];
|
||||||
_avatarImageView.contentMode = UIViewContentModeScaleAspectFill;
|
_avatarImageView.contentMode = UIViewContentModeScaleAspectFill;
|
||||||
_avatarImageView.layer.cornerRadius = 14;
|
_avatarImageView.layer.cornerRadius = 13;
|
||||||
_avatarImageView.layer.masksToBounds = YES;
|
_avatarImageView.layer.masksToBounds = YES;
|
||||||
_avatarImageView.backgroundColor = [UIColor systemGray5Color];
|
_avatarImageView.backgroundColor = [UIColor systemGray5Color];
|
||||||
}
|
}
|
||||||
@@ -182,7 +182,7 @@
|
|||||||
if (!_userNameLabel) {
|
if (!_userNameLabel) {
|
||||||
_userNameLabel = [[UILabel alloc] init];
|
_userNameLabel = [[UILabel alloc] init];
|
||||||
_userNameLabel.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium];
|
_userNameLabel.font = [UIFont systemFontOfSize:13 weight:UIFontWeightMedium];
|
||||||
_userNameLabel.textColor = [UIColor secondaryLabelColor];
|
_userNameLabel.textColor = [UIColor colorWithHex:0x9F9F9F];
|
||||||
_userNameLabel.numberOfLines = 0;
|
_userNameLabel.numberOfLines = 0;
|
||||||
}
|
}
|
||||||
return _userNameLabel;
|
return _userNameLabel;
|
||||||
@@ -191,8 +191,8 @@
|
|||||||
- (UILabel *)contentLabel {
|
- (UILabel *)contentLabel {
|
||||||
if (!_contentLabel) {
|
if (!_contentLabel) {
|
||||||
_contentLabel = [[UILabel alloc] init];
|
_contentLabel = [[UILabel alloc] init];
|
||||||
_contentLabel.font = [UIFont systemFontOfSize:14];
|
_contentLabel.font = [UIFont systemFontOfSize:12];
|
||||||
_contentLabel.textColor = [UIColor labelColor];
|
_contentLabel.textColor = [UIColor whiteColor];
|
||||||
_contentLabel.numberOfLines = 0;
|
_contentLabel.numberOfLines = 0;
|
||||||
}
|
}
|
||||||
return _contentLabel;
|
return _contentLabel;
|
||||||
@@ -201,8 +201,8 @@
|
|||||||
- (UILabel *)timeLabel {
|
- (UILabel *)timeLabel {
|
||||||
if (!_timeLabel) {
|
if (!_timeLabel) {
|
||||||
_timeLabel = [[UILabel alloc] init];
|
_timeLabel = [[UILabel alloc] init];
|
||||||
_timeLabel.font = [UIFont systemFontOfSize:11];
|
_timeLabel.font = [UIFont systemFontOfSize:12];
|
||||||
_timeLabel.textColor = [UIColor secondaryLabelColor];
|
_timeLabel.textColor = [UIColor colorWithHex:0x9F9F9F];
|
||||||
}
|
}
|
||||||
return _timeLabel;
|
return _timeLabel;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,8 @@
|
|||||||
[self.voiceButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.voiceButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.left.equalTo(self.contentView).offset(16);
|
make.left.equalTo(self.contentView).offset(16);
|
||||||
make.top.equalTo(self.contentView).offset(8);
|
make.top.equalTo(self.contentView).offset(8);
|
||||||
make.width.height.mas_equalTo(24);
|
// make.width.height.mas_equalTo(24);
|
||||||
|
make.width.height.mas_equalTo(0);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self.durationLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.durationLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
@@ -105,8 +106,8 @@
|
|||||||
|
|
||||||
// 关键修复:messageLabel 约束必须完整,让 AutoLayout 能推导出 bubbleView 的高度
|
// 关键修复:messageLabel 约束必须完整,让 AutoLayout 能推导出 bubbleView 的高度
|
||||||
[self.messageLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.messageLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.top.equalTo(self.bubbleView).offset(10);
|
make.top.equalTo(self.bubbleView).offset(5);
|
||||||
make.bottom.equalTo(self.bubbleView).offset(-10).priority(999); // 降低优先级
|
make.bottom.equalTo(self.bubbleView).offset(-5).priority(999); // 降低优先级
|
||||||
make.left.equalTo(self.bubbleView).offset(12);
|
make.left.equalTo(self.bubbleView).offset(12);
|
||||||
make.right.equalTo(self.bubbleView).offset(-12);
|
make.right.equalTo(self.bubbleView).offset(-12);
|
||||||
// 关键修复:给 messageLabel 一个最小高度,防止高度为 0
|
// 关键修复:给 messageLabel 一个最小高度,防止高度为 0
|
||||||
|
|||||||
@@ -38,8 +38,8 @@
|
|||||||
|
|
||||||
// 布局约束
|
// 布局约束
|
||||||
[self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.top.equalTo(self.contentView).offset(8);
|
make.top.equalTo(self.contentView).offset(1);
|
||||||
make.bottom.equalTo(self.contentView).offset(-8);
|
make.bottom.equalTo(self.contentView).offset(-1);
|
||||||
make.centerX.equalTo(self.contentView);
|
make.centerX.equalTo(self.contentView);
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,8 +202,14 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
|
|||||||
// 关键修复:清空消息时停止音频播放,避免状态混乱
|
// 关键修复:清空消息时停止音频播放,避免状态混乱
|
||||||
[self.chatView stopPlayingAudio];
|
[self.chatView stopPlayingAudio];
|
||||||
|
|
||||||
// 如果有缓存,直接显示
|
// 确保开场白在第一条
|
||||||
|
[self ensureOpeningMessageAtTop];
|
||||||
|
|
||||||
|
// 如果有消息,直接显示(包含开场白)
|
||||||
if (self.messages.count > 0) {
|
if (self.messages.count > 0) {
|
||||||
|
// 同步缓存,避免下次从缓存缺少开场白
|
||||||
|
[[KBAIChatMessageCacheManager shared] saveMessages:self.messages
|
||||||
|
forCompanionId:persona.personaId];
|
||||||
[self.chatView reloadWithMessages:self.messages
|
[self.chatView reloadWithMessages:self.messages
|
||||||
keepOffset:NO
|
keepOffset:NO
|
||||||
scrollToBottom:YES];
|
scrollToBottom:YES];
|
||||||
@@ -287,14 +293,19 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
|
|||||||
[newMessages addObject:message];
|
[newMessages addObject:message];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 插入到顶部(历史消息)
|
// 插入历史消息(确保开场白始终是第一条)
|
||||||
if (weakSelf.currentPage == 1) {
|
if (weakSelf.currentPage == 1) {
|
||||||
// 第一页,直接赋值
|
// 第一页,直接赋值
|
||||||
weakSelf.messages = newMessages;
|
weakSelf.messages = newMessages;
|
||||||
|
[weakSelf ensureOpeningMessageAtTop];
|
||||||
} else {
|
} else {
|
||||||
// 后续页,插入到顶部
|
// 后续页,插入到开场白之后
|
||||||
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, newMessages.count)];
|
[weakSelf ensureOpeningMessageAtTop];
|
||||||
[weakSelf.messages insertObjects:newMessages atIndexes:indexSet];
|
if (newMessages.count > 0) {
|
||||||
|
NSUInteger insertIndex = [weakSelf hasOpeningMessageAtTop] ? 1 : 0;
|
||||||
|
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(insertIndex, newMessages.count)];
|
||||||
|
[weakSelf.messages insertObjects:newMessages atIndexes:indexSet];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新 UI
|
// 刷新 UI
|
||||||
@@ -306,7 +317,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
|
|||||||
scrollToBottom:scrollToBottom];
|
scrollToBottom:scrollToBottom];
|
||||||
[weakSelf.chatView endLoadMoreWithHasMoreData:weakSelf.hasMoreHistory];
|
[weakSelf.chatView endLoadMoreWithHasMoreData:weakSelf.hasMoreHistory];
|
||||||
|
|
||||||
// ✅ 保存到缓存
|
// ✅ 保存到缓存(包含开场白)
|
||||||
[[KBAIChatMessageCacheManager shared] saveMessages:weakSelf.messages
|
[[KBAIChatMessageCacheManager shared] saveMessages:weakSelf.messages
|
||||||
forCompanionId:companionId];
|
forCompanionId:companionId];
|
||||||
});
|
});
|
||||||
@@ -332,10 +343,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
|
|||||||
|
|
||||||
- (void)showOpeningMessage {
|
- (void)showOpeningMessage {
|
||||||
// 显示开场白作为第一条消息
|
// 显示开场白作为第一条消息
|
||||||
KBAiChatMessage *openingMsg = [KBAiChatMessage assistantMessageWithText:self.persona.introText];
|
[self ensureOpeningMessageAtTop];
|
||||||
openingMsg.isComplete = YES;
|
|
||||||
openingMsg.needsTypewriterEffect = NO;
|
|
||||||
[self.messages addObject:openingMsg];
|
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.chatView reloadWithMessages:self.messages
|
[self.chatView reloadWithMessages:self.messages
|
||||||
@@ -344,6 +352,41 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)hasOpeningMessageAtTop {
|
||||||
|
if (self.messages.count == 0) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
return [self isOpeningMessage:self.messages.firstObject];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isOpeningMessage:(KBAiChatMessage *)message {
|
||||||
|
if (!message) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
NSString *introText = self.persona.introText ?: @"";
|
||||||
|
if (introText.length == 0) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
return (message.type == KBAiChatMessageTypeAssistant) && [message.text isEqualToString:introText];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)ensureOpeningMessageAtTop {
|
||||||
|
NSString *introText = self.persona.introText ?: @"";
|
||||||
|
if (introText.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!self.messages) {
|
||||||
|
self.messages = [NSMutableArray array];
|
||||||
|
}
|
||||||
|
if ([self hasOpeningMessageAtTop]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
KBAiChatMessage *openingMsg = [KBAiChatMessage assistantMessageWithText:introText];
|
||||||
|
openingMsg.isComplete = YES;
|
||||||
|
openingMsg.needsTypewriterEffect = NO;
|
||||||
|
[self.messages insertObject:openingMsg atIndex:0];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - 通知处理
|
#pragma mark - 通知处理
|
||||||
|
|
||||||
/// 处理聊天会话被重置的通知
|
/// 处理聊天会话被重置的通知
|
||||||
@@ -368,10 +411,8 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
|
|||||||
// 清空聊天视图
|
// 清空聊天视图
|
||||||
[self.chatView clearMessages];
|
[self.chatView clearMessages];
|
||||||
|
|
||||||
// 显示开场白
|
// 显示开场白(始终保持第一条)
|
||||||
if (self.persona.introText.length > 0) {
|
[self showOpeningMessage];
|
||||||
[self showOpeningMessage];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,13 +427,14 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
|
|||||||
self.messages = [NSMutableArray array];
|
self.messages = [NSMutableArray array];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[self ensureOpeningMessageAtTop];
|
||||||
KBAiChatMessage *message = [KBAiChatMessage userMessageWithText:text];
|
KBAiChatMessage *message = [KBAiChatMessage userMessageWithText:text];
|
||||||
[self.messages addObject:message];
|
[self.messages addObject:message];
|
||||||
[self.chatView addMessage:message autoScroll:YES];
|
[self.chatView addMessage:message autoScroll:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)appendAssistantMessage:(NSString *)text
|
- (void)appendAssistantMessage:(NSString *)text
|
||||||
audioId:(NSString *)audioId {
|
audioId:(NSString *)audioId {
|
||||||
if (text.length == 0) {
|
if (text.length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -401,6 +443,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
|
|||||||
self.messages = [NSMutableArray array];
|
self.messages = [NSMutableArray array];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[self ensureOpeningMessageAtTop];
|
||||||
KBAiChatMessage *message = [KBAiChatMessage assistantMessageWithText:text
|
KBAiChatMessage *message = [KBAiChatMessage assistantMessageWithText:text
|
||||||
audioId:audioId];
|
audioId:audioId];
|
||||||
message.needsTypewriterEffect = YES;
|
message.needsTypewriterEffect = YES;
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ autoShowBusinessError:NO
|
|||||||
[[KBNetworkManager shared] uploadFile:API_AI_AUDIO_UPLOAD
|
[[KBNetworkManager shared] uploadFile:API_AI_AUDIO_UPLOAD
|
||||||
fileURL:fileURL
|
fileURL:fileURL
|
||||||
name:@"file"
|
name:@"file"
|
||||||
mimeType:@"audio/mp4"
|
mimeType:@"audio/m4a"
|
||||||
parameters:nil
|
parameters:nil
|
||||||
headers:nil
|
headers:nil
|
||||||
completion:^(NSDictionary *_Nullable json,
|
completion:^(NSDictionary *_Nullable json,
|
||||||
|
|||||||
Reference in New Issue
Block a user