2026-01-28 16:35:47 +08:00
|
|
|
|
//
|
|
|
|
|
|
// KBAIMessageChatingVC.m
|
|
|
|
|
|
// keyBoard
|
|
|
|
|
|
//
|
|
|
|
|
|
// Created by Mac on 2026/1/28.
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
#import "KBAIMessageChatingVC.h"
|
|
|
|
|
|
#import "AiVM.h"
|
|
|
|
|
|
#import "KBChattedCompanionModel.h"
|
|
|
|
|
|
#import "KBHUD.h"
|
2026-01-28 19:31:27 +08:00
|
|
|
|
#import "KBAIChatMessageCacheManager.h"
|
2026-02-04 16:57:19 +08:00
|
|
|
|
#import "KBAIChatDeleteConfirmView.h"
|
|
|
|
|
|
#import "LSTPopView.h"
|
2026-01-28 18:15:58 +08:00
|
|
|
|
#import <Masonry/Masonry.h>
|
2026-01-28 16:35:47 +08:00
|
|
|
|
|
2026-01-28 19:31:27 +08:00
|
|
|
|
/// 聊天会话被重置的通知
|
|
|
|
|
|
static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidResetNotification";
|
|
|
|
|
|
|
2026-02-04 16:57:19 +08:00
|
|
|
|
@interface KBAIMessageChatingVC () <KBAIChatDeleteConfirmViewDelegate, UIGestureRecognizerDelegate>
|
2026-01-28 16:35:47 +08:00
|
|
|
|
|
|
|
|
|
|
@property (nonatomic, strong) AiVM *viewModel;
|
|
|
|
|
|
@property (nonatomic, strong) NSMutableArray<KBChattedCompanionModel *> *chattedList;
|
|
|
|
|
|
|
2026-01-28 18:15:58 +08:00
|
|
|
|
/// 删除按钮
|
|
|
|
|
|
@property (nonatomic, strong) UIButton *deleteButton;
|
|
|
|
|
|
/// 当前长按的 indexPath
|
|
|
|
|
|
@property (nonatomic, strong) NSIndexPath *longPressIndexPath;
|
2026-02-04 16:57:19 +08:00
|
|
|
|
/// 长按手势
|
|
|
|
|
|
@property (nonatomic, strong) UILongPressGestureRecognizer *longPressGesture;
|
|
|
|
|
|
/// 单击手势(用于隐藏删除按钮)
|
|
|
|
|
|
@property (nonatomic, strong) UITapGestureRecognizer *tapGesture;
|
|
|
|
|
|
/// 删除按钮显示代数(用于避免旧的隐藏动画 completion 误删新按钮)
|
|
|
|
|
|
@property (nonatomic, assign) NSInteger deleteButtonGeneration;
|
|
|
|
|
|
/// 待删除的 indexPath(弹窗确认使用)
|
|
|
|
|
|
@property (nonatomic, strong) NSIndexPath *pendingDeleteIndexPath;
|
|
|
|
|
|
/// 删除确认弹窗
|
|
|
|
|
|
@property (nonatomic, weak) LSTPopView *deleteConfirmPopView;
|
2026-01-28 18:15:58 +08:00
|
|
|
|
|
2026-01-28 16:35:47 +08:00
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
@implementation KBAIMessageChatingVC
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - Lifecycle
|
|
|
|
|
|
|
|
|
|
|
|
- (void)viewDidLoad {
|
|
|
|
|
|
self.listType = 1; // Chatting
|
|
|
|
|
|
[super viewDidLoad];
|
2026-01-28 18:15:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 添加长按手势
|
|
|
|
|
|
[self setupLongPressGesture];
|
|
|
|
|
|
|
|
|
|
|
|
// 添加点击手势,用于隐藏删除按钮
|
2026-02-04 16:57:19 +08:00
|
|
|
|
self.tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
|
|
|
|
|
|
self.tapGesture.cancelsTouchesInView = NO; // 不影响 cell 的点击
|
|
|
|
|
|
self.tapGesture.delegate = self;
|
|
|
|
|
|
if (self.longPressGesture) {
|
|
|
|
|
|
[self.tapGesture requireGestureRecognizerToFail:self.longPressGesture];
|
|
|
|
|
|
}
|
|
|
|
|
|
[self.tableView addGestureRecognizer:self.tapGesture];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - UIGestureRecognizerDelegate
|
|
|
|
|
|
|
|
|
|
|
|
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
|
|
|
|
|
|
// 删除按钮显示时,点击按钮本身不走 tableView 的 tap 逻辑
|
|
|
|
|
|
if (gestureRecognizer == self.tapGesture) {
|
|
|
|
|
|
if (self.deleteButton && !self.deleteButton.hidden) {
|
|
|
|
|
|
CGPoint p = [touch locationInView:self.deleteButton];
|
|
|
|
|
|
if (CGRectContainsPoint(self.deleteButton.bounds, p)) {
|
|
|
|
|
|
return NO;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return YES;
|
2026-01-28 18:15:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - 手势处理
|
|
|
|
|
|
|
|
|
|
|
|
- (void)setupLongPressGesture {
|
2026-02-04 16:57:19 +08:00
|
|
|
|
self.longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
|
|
|
|
|
|
self.longPressGesture.minimumPressDuration = 0.5; // 长按0.5秒
|
|
|
|
|
|
self.longPressGesture.delegate = self;
|
|
|
|
|
|
[self.tableView addGestureRecognizer:self.longPressGesture];
|
2026-01-28 18:15:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture {
|
2026-02-04 16:57:19 +08:00
|
|
|
|
CGPoint point = [gesture locationInView:self.tableView];
|
|
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] longPress state=%ld point=(%.1f, %.1f)",
|
|
|
|
|
|
(long)gesture.state, point.x, point.y);
|
|
|
|
|
|
|
2026-01-28 18:15:58 +08:00
|
|
|
|
if (gesture.state == UIGestureRecognizerStateBegan) {
|
2026-02-04 16:57:19 +08:00
|
|
|
|
// 关键:长按期间禁用 tap,避免同一轮触摸结束被识别为 tap 导致“闪一下就隐藏”
|
|
|
|
|
|
if (self.tapGesture.enabled) {
|
|
|
|
|
|
self.tapGesture.enabled = NO;
|
|
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] tapGesture disabled for longPress");
|
|
|
|
|
|
}
|
2026-01-28 18:15:58 +08:00
|
|
|
|
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:point];
|
2026-02-04 16:57:19 +08:00
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] longPress began indexPath=%@",
|
|
|
|
|
|
indexPath);
|
2026-01-28 18:15:58 +08:00
|
|
|
|
|
|
|
|
|
|
if (indexPath) {
|
|
|
|
|
|
// 在手指位置显示删除按钮
|
|
|
|
|
|
[self showDeleteButtonAtPoint:point];
|
2026-01-28 19:31:27 +08:00
|
|
|
|
|
|
|
|
|
|
// 在 showDeleteButtonAtPoint 之后再设置,避免被 hideDeleteButton 清空
|
|
|
|
|
|
self.longPressIndexPath = indexPath;
|
2026-01-28 18:15:58 +08:00
|
|
|
|
}
|
2026-02-04 16:57:19 +08:00
|
|
|
|
} else if (gesture.state == UIGestureRecognizerStateEnded ||
|
|
|
|
|
|
gesture.state == UIGestureRecognizerStateCancelled ||
|
|
|
|
|
|
gesture.state == UIGestureRecognizerStateFailed) {
|
|
|
|
|
|
if (!self.tapGesture.enabled) {
|
|
|
|
|
|
self.tapGesture.enabled = YES;
|
|
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] tapGesture re-enabled after longPress");
|
|
|
|
|
|
}
|
2026-01-28 18:15:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)handleTapGesture:(UITapGestureRecognizer *)gesture {
|
|
|
|
|
|
// 点击其他地方隐藏删除按钮
|
|
|
|
|
|
if (self.deleteButton && !self.deleteButton.hidden) {
|
2026-02-04 16:57:19 +08:00
|
|
|
|
CGPoint pointInView = [gesture locationInView:self.tableView];
|
|
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] tap state=%ld point=(%.1f, %.1f)",
|
|
|
|
|
|
(long)gesture.state, pointInView.x, pointInView.y);
|
|
|
|
|
|
|
2026-01-28 19:31:27 +08:00
|
|
|
|
CGPoint pointInButton = [gesture locationInView:self.deleteButton];
|
2026-01-28 18:15:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 如果点击的不是删除按钮,则隐藏
|
2026-01-28 19:31:27 +08:00
|
|
|
|
if (!CGRectContainsPoint(self.deleteButton.bounds, pointInButton)) {
|
|
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] 点击了删除按钮外部,隐藏按钮");
|
2026-01-28 18:15:58 +08:00
|
|
|
|
[self hideDeleteButton];
|
2026-01-28 19:31:27 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] 点击了删除按钮内部,不隐藏");
|
2026-01-28 18:15:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)showDeleteButtonAtPoint:(CGPoint)point {
|
2026-02-04 16:57:19 +08:00
|
|
|
|
// 新一轮展示:使之前的隐藏动画 completion 失效
|
|
|
|
|
|
self.deleteButtonGeneration += 1;
|
|
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] showDeleteButtonAtPoint=(%.1f, %.1f) generation=%ld",
|
|
|
|
|
|
point.x, point.y, (long)self.deleteButtonGeneration);
|
2026-01-28 18:15:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 创建删除按钮
|
|
|
|
|
|
if (!self.deleteButton) {
|
|
|
|
|
|
self.deleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
|
|
|
[self.deleteButton setTitle:@"删除此记录" forState:UIControlStateNormal];
|
|
|
|
|
|
[self.deleteButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
|
|
|
|
|
self.deleteButton.titleLabel.font = [UIFont systemFontOfSize:14];
|
|
|
|
|
|
self.deleteButton.backgroundColor = [UIColor colorWithRed:244/255.0 green:67/255.0 blue:54/255.0 alpha:1.0]; // #F44336
|
|
|
|
|
|
self.deleteButton.layer.cornerRadius = 6;
|
|
|
|
|
|
self.deleteButton.layer.masksToBounds = YES;
|
|
|
|
|
|
[self.deleteButton addTarget:self action:@selector(deleteButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
|
|
|
|
|
|
|
|
|
|
|
// 添加阴影效果
|
|
|
|
|
|
self.deleteButton.layer.shadowColor = [UIColor blackColor].CGColor;
|
|
|
|
|
|
self.deleteButton.layer.shadowOffset = CGSizeMake(0, 2);
|
|
|
|
|
|
self.deleteButton.layer.shadowOpacity = 0.3;
|
|
|
|
|
|
self.deleteButton.layer.shadowRadius = 4;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加到父视图(确保在最上层)
|
2026-02-04 16:57:19 +08:00
|
|
|
|
if (self.deleteButton.superview != self.view) {
|
|
|
|
|
|
[self.view addSubview:self.deleteButton];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
[self.view bringSubviewToFront:self.deleteButton];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 取消隐藏动画,避免闪一下
|
|
|
|
|
|
[self.deleteButton.layer removeAllAnimations];
|
2026-01-28 18:15:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 设置按钮大小和位置
|
|
|
|
|
|
CGSize buttonSize = CGSizeMake(110, 40);
|
|
|
|
|
|
|
|
|
|
|
|
// 将 tableView 的坐标转换为父视图坐标
|
|
|
|
|
|
CGPoint pointInView = [self.tableView convertPoint:point toView:self.view];
|
|
|
|
|
|
|
|
|
|
|
|
// 计算按钮位置,确保不超出屏幕
|
|
|
|
|
|
CGFloat buttonX = pointInView.x - buttonSize.width / 2;
|
|
|
|
|
|
CGFloat buttonY = pointInView.y - buttonSize.height - 10; // 在手指上方10px
|
|
|
|
|
|
|
|
|
|
|
|
// 边界检查
|
|
|
|
|
|
CGFloat margin = 10;
|
|
|
|
|
|
if (buttonX < margin) {
|
|
|
|
|
|
buttonX = margin;
|
|
|
|
|
|
} else if (buttonX + buttonSize.width > self.view.bounds.size.width - margin) {
|
|
|
|
|
|
buttonX = self.view.bounds.size.width - buttonSize.width - margin;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (buttonY < KB_NAV_TOTAL_HEIGHT + margin) {
|
|
|
|
|
|
// 如果上方空间不够,显示在手指下方
|
|
|
|
|
|
buttonY = pointInView.y + 10;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[self.deleteButton mas_remakeConstraints:^(MASConstraintMaker *make) {
|
|
|
|
|
|
make.left.equalTo(self.view).offset(buttonX);
|
|
|
|
|
|
make.top.equalTo(self.view).offset(buttonY);
|
|
|
|
|
|
make.width.mas_equalTo(buttonSize.width);
|
|
|
|
|
|
make.height.mas_equalTo(buttonSize.height);
|
|
|
|
|
|
}];
|
|
|
|
|
|
|
|
|
|
|
|
self.deleteButton.hidden = NO;
|
2026-02-04 16:57:19 +08:00
|
|
|
|
self.deleteButton.alpha = 1.0;
|
|
|
|
|
|
self.deleteButton.transform = CGAffineTransformIdentity;
|
|
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] deleteButton shown");
|
2026-01-28 18:15:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 添加弹出动画
|
|
|
|
|
|
self.deleteButton.transform = CGAffineTransformMakeScale(0.3, 0.3);
|
|
|
|
|
|
self.deleteButton.alpha = 0;
|
|
|
|
|
|
[UIView animateWithDuration:0.2 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:0.5 options:UIViewAnimationOptionCurveEaseOut animations:^{
|
|
|
|
|
|
self.deleteButton.transform = CGAffineTransformIdentity;
|
|
|
|
|
|
self.deleteButton.alpha = 1.0;
|
|
|
|
|
|
} completion:nil];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)hideDeleteButton {
|
|
|
|
|
|
if (self.deleteButton) {
|
2026-02-04 16:57:19 +08:00
|
|
|
|
NSInteger generation = self.deleteButtonGeneration;
|
|
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] hideDeleteButton generation=%ld", (long)generation);
|
|
|
|
|
|
// 取消之前的动画,避免叠加
|
|
|
|
|
|
[self.deleteButton.layer removeAllAnimations];
|
2026-01-28 18:15:58 +08:00
|
|
|
|
[UIView animateWithDuration:0.15 animations:^{
|
|
|
|
|
|
self.deleteButton.alpha = 0;
|
|
|
|
|
|
self.deleteButton.transform = CGAffineTransformMakeScale(0.8, 0.8);
|
|
|
|
|
|
} completion:^(BOOL finished) {
|
2026-02-04 16:57:19 +08:00
|
|
|
|
// 如果期间又展示了新一轮按钮,则不执行移除(避免“闪一下”)
|
|
|
|
|
|
if (generation != self.deleteButtonGeneration) {
|
|
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] hide completion ignored (generation changed: %ld -> %ld)",
|
|
|
|
|
|
(long)generation, (long)self.deleteButtonGeneration);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-01-28 18:15:58 +08:00
|
|
|
|
self.deleteButton.hidden = YES;
|
|
|
|
|
|
[self.deleteButton removeFromSuperview];
|
|
|
|
|
|
}];
|
|
|
|
|
|
}
|
|
|
|
|
|
self.longPressIndexPath = nil;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)deleteButtonTapped {
|
2026-01-28 19:31:27 +08:00
|
|
|
|
if (!self.longPressIndexPath) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-04 16:57:19 +08:00
|
|
|
|
// 保存 indexPath,因为 hideDeleteButton 会清空
|
|
|
|
|
|
self.pendingDeleteIndexPath = self.longPressIndexPath;
|
|
|
|
|
|
[self hideDeleteButton];
|
|
|
|
|
|
[self showDeleteConfirmPopView];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - 删除确认弹窗
|
|
|
|
|
|
|
|
|
|
|
|
- (void)showDeleteConfirmPopView {
|
|
|
|
|
|
if (self.deleteConfirmPopView) {
|
|
|
|
|
|
[self.deleteConfirmPopView dismiss];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CGFloat width = KB_SCREEN_WIDTH - 80;
|
|
|
|
|
|
KBAIChatDeleteConfirmView *content = [[KBAIChatDeleteConfirmView alloc] initWithFrame:CGRectMake(0, 0, width, 260)];
|
|
|
|
|
|
content.delegate = self;
|
|
|
|
|
|
|
|
|
|
|
|
LSTPopView *popView = [LSTPopView initWithCustomView:content
|
|
|
|
|
|
parentView:nil
|
|
|
|
|
|
popStyle:LSTPopStyleFade
|
|
|
|
|
|
dismissStyle:LSTDismissStyleFade];
|
|
|
|
|
|
popView.bgColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
|
|
|
|
|
|
popView.hemStyle = LSTHemStyleCenter;
|
|
|
|
|
|
popView.isClickBgDismiss = YES;
|
|
|
|
|
|
popView.isAvoidKeyboard = NO;
|
|
|
|
|
|
self.deleteConfirmPopView = popView;
|
|
|
|
|
|
[popView pop];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - KBAIChatDeleteConfirmViewDelegate
|
|
|
|
|
|
|
|
|
|
|
|
- (void)chatDeleteConfirmViewDidTapDelete:(KBAIChatDeleteConfirmView *)view {
|
|
|
|
|
|
[self.deleteConfirmPopView dismiss];
|
|
|
|
|
|
NSIndexPath *indexPath = self.pendingDeleteIndexPath;
|
|
|
|
|
|
self.pendingDeleteIndexPath = nil;
|
|
|
|
|
|
[self performDeleteAtIndexPath:indexPath];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (void)chatDeleteConfirmViewDidTapCancel:(KBAIChatDeleteConfirmView *)view {
|
|
|
|
|
|
[self.deleteConfirmPopView dismiss];
|
|
|
|
|
|
self.pendingDeleteIndexPath = nil;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - 删除逻辑
|
|
|
|
|
|
|
|
|
|
|
|
- (void)performDeleteAtIndexPath:(NSIndexPath *)indexPath {
|
|
|
|
|
|
if (!indexPath) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-01-28 19:31:27 +08:00
|
|
|
|
|
|
|
|
|
|
// 获取要删除的数据
|
|
|
|
|
|
if (indexPath.row >= self.chattedList.count) {
|
2026-02-04 16:57:19 +08:00
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] 错误:索引越界,row=%ld, count=%ld",
|
2026-01-28 19:31:27 +08:00
|
|
|
|
(long)indexPath.row, (long)self.chattedList.count);
|
|
|
|
|
|
return;
|
2026-01-28 18:15:58 +08:00
|
|
|
|
}
|
2026-01-28 19:31:27 +08:00
|
|
|
|
|
|
|
|
|
|
KBChattedCompanionModel *model = self.chattedList[indexPath.row];
|
|
|
|
|
|
NSInteger companionId = model.companionId;
|
|
|
|
|
|
|
2026-02-04 16:57:19 +08:00
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] 开始删除聊天记录:companionId=%ld, name=%@",
|
2026-01-28 19:31:27 +08:00
|
|
|
|
(long)companionId, model.name);
|
|
|
|
|
|
|
|
|
|
|
|
// 显示加载提示
|
|
|
|
|
|
[KBHUD show];
|
|
|
|
|
|
|
|
|
|
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
|
|
|
|
|
|
|
|
// 调用清空聊天会话的 API
|
|
|
|
|
|
[self.viewModel resetChatSessionWithCompanionId:companionId
|
|
|
|
|
|
completion:^(KBChatSessionResetResponse * _Nullable response, NSError * _Nullable error) {
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
|
[KBHUD dismiss];
|
|
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] 删除失败:%@", error.localizedDescription);
|
|
|
|
|
|
[KBHUD showError:@"删除失败,请重试"];
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] ✅ API 调用成功,开始清理本地数据");
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 删除本地列表数据
|
|
|
|
|
|
if (indexPath.row < weakSelf.chattedList.count) {
|
|
|
|
|
|
[weakSelf.chattedList removeObjectAtIndex:indexPath.row];
|
|
|
|
|
|
}
|
|
|
|
|
|
if (indexPath.row < weakSelf.dataArray.count) {
|
|
|
|
|
|
[weakSelf.dataArray removeObjectAtIndex:indexPath.row];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 更新 TableView(带动画)
|
2026-02-04 16:57:19 +08:00
|
|
|
|
[weakSelf.tableView deleteRowsAtIndexPaths:@[indexPath]
|
2026-01-28 19:31:27 +08:00
|
|
|
|
withRowAnimation:UITableViewRowAnimationLeft];
|
|
|
|
|
|
|
|
|
|
|
|
// 3. ✅ 清除缓存管理器中的聊天记录(关键!)
|
|
|
|
|
|
[[KBAIChatMessageCacheManager shared] clearMessagesForCompanionId:companionId];
|
|
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] ✅ 已清除缓存:companionId=%ld", (long)companionId);
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 发送通知,通知其他页面(主页)刷新
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:KBChatSessionDidResetNotification
|
|
|
|
|
|
object:nil
|
|
|
|
|
|
userInfo:@{@"companionId": @(companionId)}];
|
|
|
|
|
|
NSLog(@"[KBAIMessageChatingVC] ✅ 已发送重置通知:companionId=%ld", (long)companionId);
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 显示成功提示
|
|
|
|
|
|
[KBHUD showSuccess:@"已删除"];
|
|
|
|
|
|
});
|
|
|
|
|
|
}];
|
2026-01-28 16:35:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - 2:数据加载
|
|
|
|
|
|
|
|
|
|
|
|
- (void)loadData {
|
|
|
|
|
|
[KBHUD show];
|
|
|
|
|
|
__weak typeof(self) weakSelf = self;
|
|
|
|
|
|
[self.viewModel fetchChattedCompanionsWithCompletion:^(NSArray<KBChattedCompanionModel *> * _Nullable list, NSError * _Nullable error) {
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
|
|
[KBHUD dismiss];
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
[KBHUD showError:error.localizedDescription];
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[weakSelf.chattedList removeAllObjects];
|
|
|
|
|
|
if (list.count > 0) {
|
|
|
|
|
|
[weakSelf.chattedList addObjectsFromArray:list];
|
|
|
|
|
|
}
|
|
|
|
|
|
[weakSelf.dataArray removeAllObjects];
|
|
|
|
|
|
|
|
|
|
|
|
// 转换为通用数据格式
|
|
|
|
|
|
for (KBChattedCompanionModel *model in weakSelf.chattedList) {
|
|
|
|
|
|
NSMutableDictionary *item = [NSMutableDictionary dictionary];
|
|
|
|
|
|
item[@"avatar"] = model.avatarUrl ?: @"";
|
|
|
|
|
|
item[@"name"] = model.name ?: @"";
|
|
|
|
|
|
item[@"content"] = model.shortDesc ?: @"";
|
|
|
|
|
|
item[@"time"] = model.createdAt ?: @"";
|
|
|
|
|
|
item[@"isPinned"] = @NO;
|
|
|
|
|
|
item[@"companionId"] = @(model.companionId);
|
|
|
|
|
|
[weakSelf.dataArray addObject:item];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[weakSelf.tableView reloadData];
|
|
|
|
|
|
});
|
|
|
|
|
|
}];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark - Lazy Load
|
|
|
|
|
|
|
|
|
|
|
|
- (AiVM *)viewModel {
|
|
|
|
|
|
if (!_viewModel) {
|
|
|
|
|
|
_viewModel = [[AiVM alloc] init];
|
|
|
|
|
|
}
|
|
|
|
|
|
return _viewModel;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSMutableArray<KBChattedCompanionModel *> *)chattedList {
|
|
|
|
|
|
if (!_chattedList) {
|
|
|
|
|
|
_chattedList = [NSMutableArray array];
|
|
|
|
|
|
}
|
|
|
|
|
|
return _chattedList;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@end
|