21 Commits

Author SHA1 Message Date
750b391100 处理键盘语音svip权限弹窗,跳转主app充值 2026-02-05 14:10:24 +08:00
faccf6f10f 1 2026-02-04 23:40:06 +08:00
35b1fc0f1e 处理vip逻辑 2026-02-04 21:49:28 +08:00
b73f225d15 语音vip限制ai弹窗 2026-02-04 20:23:20 +08:00
dd59094a16 处理了评论新增评论,在聊天里的评论数也一同改变 2026-02-04 19:31:30 +08:00
bacaf537f3 调用删除接口 2026-02-04 19:01:58 +08:00
619d356d31 新增接口 2026-02-04 18:45:57 +08:00
db9f07d199 优化长按聊天记录 如果是自己 不要显示举报 2026-02-04 18:39:25 +08:00
3ed120106e 处理二级评论回复用户名称显示问题 2026-02-04 18:29:12 +08:00
ff4edab820 1 2026-02-04 18:10:39 +08:00
3e30f619b9 新增删除弹窗,修改bug 2026-02-04 16:57:19 +08:00
533e23ebfe 1 2026-02-04 16:03:39 +08:00
85fb407717 处理支付 2026-02-04 15:57:22 +08:00
c1b50b407d 2 2026-02-04 15:40:45 +08:00
7c7e2477cb 1 2026-02-04 15:20:53 +08:00
faae0297cb 1 2026-02-04 15:09:03 +08:00
e50eaecbd9 1 2026-02-04 14:59:02 +08:00
879dbb860c 2 2026-02-04 13:14:26 +08:00
b4e4b7b606 处理svip 2026-02-04 12:48:18 +08:00
68a610e0a8 处理kbpayvip 2026-02-04 12:33:01 +08:00
305326aa9a 添加placehold颜色 2026-02-03 20:51:48 +08:00
121 changed files with 2931 additions and 399 deletions

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ai_limit_close@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ai_limit_close@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ai_limit_goto@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ai_limit_goto@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ai_limit_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ai_limit_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 KiB

View File

@@ -20,6 +20,7 @@
#import "KBSettingView.h"
#import "KBChatMessage.h"
#import "KBChatPanelView.h"
#import "KBChatLimitPopView.h"
#import "KBSkinInstallBridge.h"
#import "KBSkinManager.h"
#import "KBSuggestionEngine.h"
@@ -66,7 +67,8 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
@interface KeyboardViewController () <KBKeyBoardMainViewDelegate,
KBFunctionViewDelegate,
KBKeyboardSubscriptionViewDelegate,
KBChatPanelViewDelegate>
KBChatPanelViewDelegate,
KBChatLimitPopViewDelegate>
@property(nonatomic, strong)
UIButton *nextKeyboardButton; //
@property(nonatomic, strong) UIView *contentView;
@@ -85,6 +87,7 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
@property(nonatomic, strong) KBSuggestionEngine *suggestionEngine;
@property(nonatomic, copy) NSString *currentWord;
@property(nonatomic, assign) BOOL suppressSuggestions;
@property(nonatomic, strong) UIControl *chatLimitMaskView;
@property(nonatomic, strong) MASConstraint *contentWidthConstraint;
@property(nonatomic, strong) MASConstraint *contentHeightConstraint;
@property(nonatomic, strong) MASConstraint *keyBoardMainHeightConstraint;
@@ -867,14 +870,66 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
[KBHUD showInfo:KBLocalized(@"请开启完全访问后使用")];
return;
}
// loading
[self.chatPanelView kb_addLoadingAssistantMessage];
//
[self kb_requestChatMessageWithContent:text];
}
#pragma mark - Chat Limit Pop
- (void)kb_showChatLimitPopWithMessage:(NSString *)message {
[self kb_dismissChatLimitPop];
UIControl *mask = [[UIControl alloc] init];
mask.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
mask.alpha = 0.0;
[mask addTarget:self
action:@selector(kb_dismissChatLimitPop)
forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:mask];
[mask mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.contentView);
}];
CGFloat width = 252.0;
CGFloat height = 252.0 + 18.0 + 53.0 + 18.0 + 28.0;
KBChatLimitPopView *content =
[[KBChatLimitPopView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
content.message = message ?: @"";
content.delegate = self;
[mask addSubview:content];
[content mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(mask);
make.width.mas_equalTo(width);
make.height.mas_equalTo(height);
}];
self.chatLimitMaskView = mask;
[self.contentView bringSubviewToFront:mask];
[UIView animateWithDuration:0.18
animations:^{
mask.alpha = 1.0;
}];
}
- (void)kb_dismissChatLimitPop {
if (!self.chatLimitMaskView) {
return;
}
UIControl *mask = self.chatLimitMaskView;
self.chatLimitMaskView = nil;
[UIView animateWithDuration:0.15
animations:^{
mask.alpha = 0.0;
}
completion:^(__unused BOOL finished) {
[mask removeFromSuperview];
}];
}
- (void)kb_clearHostInputForText:(NSString *)text {
if (text.length == 0) {
return;
@@ -1019,9 +1074,15 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
if (!self) return;
if (!response.success) {
NSLog(@"[KB] ❌ 请求失败: %@", response.errorMessage);
if (response.code == 50030) {
NSLog(@"[KB] ⚠️ 次数用尽: %@", response.message);
[self.chatPanelView kb_removeLoadingAssistantMessage];
[self kb_showChatLimitPopWithMessage:response.message];
return;
}
NSLog(@"[KB] ❌ 请求失败: %@", response.message);
[self.chatPanelView kb_removeLoadingAssistantMessage];
[KBHUD showInfo:response.errorMessage ?: KBLocalized(@"请求失败")];
[KBHUD showInfo:response.message ?: KBLocalized(@"请求失败")];
return;
}
@@ -1352,6 +1413,25 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
[self kb_openRechargeForProduct:product];
}
#pragma mark - KBChatLimitPopViewDelegate
- (void)chatLimitPopViewDidTapCancel:(KBChatLimitPopView *)view {
[self kb_dismissChatLimitPop];
}
- (void)chatLimitPopViewDidTapRecharge:(KBChatLimitPopView *)view {
[self kb_dismissChatLimitPop];
NSString *urlString =
[NSString stringWithFormat:@"%@://recharge?src=keyboard&vipType=svip",
KB_APP_SCHEME];
NSURL *scheme = [NSURL URLWithString:urlString];
BOOL success = [KBHostAppLauncher openHostAppURL:scheme
fromResponder:self.view];
if (!success) {
[KBHUD showInfo:KBLocalized(@"Please open the App to finish purchase")];
}
}
#pragma mark - lazy
- (KBKeyBoardMainView *)keyBoardMainView {
if (!_keyBoardMainView) {

View File

@@ -14,8 +14,10 @@ NS_ASSUME_NONNULL_BEGIN
@interface KBChatResponse : NSObject
@property (nonatomic, copy, nullable) NSString *text;
@property (nonatomic, copy, nullable) NSString *audioId;
@property (nonatomic, copy, nullable) NSString *errorMessage;
@property (nonatomic, copy, nullable) NSString *message;
@property (nonatomic, assign) BOOL success;
@property (nonatomic, assign) NSInteger code;
@end
/// 音频响应模型

View File

@@ -7,6 +7,7 @@
#import "KBNetworkManager.h"
#import "KBConfig.h"
#import <AVFoundation/AVFoundation.h>
#import <MJExtension/MJExtension.h>
@implementation KBChatResponse
@end
@@ -46,7 +47,7 @@
if (completion) {
KBChatResponse *response = [[KBChatResponse alloc] init];
response.success = NO;
response.errorMessage = @"内容为空";
response.message = @"内容为空";
completion(response);
}
return;
@@ -66,24 +67,24 @@
headers:nil
completion:^(NSDictionary *json, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
KBChatResponse *chatResponse = [[KBChatResponse alloc] init];
KBChatResponse *chatResponse = [KBChatResponse mj_objectWithKeyValues:json];
if (error) {
chatResponse.success = NO;
chatResponse.errorMessage = error.localizedDescription ?: @"请求失败";
// chatResponse.errorMessage = error.localizedDescription ?: @"请求失败";
if (completion) completion(chatResponse);
return;
}
//
chatResponse.text = [self p_parseTextFromJSON:json];
// audioId
chatResponse.audioId = [self p_parseAudioIdFromJSON:json];
// //
// chatResponse.text = [self p_parseTextFromJSON:json];
// // audioId
// chatResponse.audioId = [self p_parseAudioIdFromJSON:json];
chatResponse.success = (chatResponse.text.length > 0);
if (!chatResponse.success) {
chatResponse.errorMessage = @"未获取到回复内容";
}
// chatResponse.success = (chatResponse.text.length > 0);
// if (!chatResponse.success) {
// chatResponse.errorMessage = @"未获取到回复内容";
// }
if (completion) completion(chatResponse);
});

View File

@@ -54,7 +54,6 @@
0459D1B72EBA287900F2D189 /* KBSkinManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0459D1B62EBA287900F2D189 /* KBSkinManager.m */; };
0459D1B82EBA287900F2D189 /* KBSkinManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0459D1B62EBA287900F2D189 /* KBSkinManager.m */; };
0460866B2F18D75500757C95 /* ai_test.m4a in Resources */ = {isa = PBXBuildFile; fileRef = 0460866A2F18D75500757C95 /* ai_test.m4a */; };
046086732F191B6900757C95 /* KBAiMainVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086722F191B6900757C95 /* KBAiMainVC.m */; };
046086752F191CC700757C95 /* AI技术分析.txt in Resources */ = {isa = PBXBuildFile; fileRef = 046086742F191CC700757C95 /* AI技术分析.txt */; };
0460869A2F19238500757C95 /* KBAiWaveformView.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086992F19238500757C95 /* KBAiWaveformView.m */; };
0460869C2F19238500757C95 /* KBAiRecordButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 046086972F19238500757C95 /* KBAiRecordButton.m */; };
@@ -141,13 +140,14 @@
048FFD112F27432D005D62AE /* KBPersonaPageModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD0F2F27432D005D62AE /* KBPersonaPageModel.m */; };
048FFD142F274342005D62AE /* KBPersonaChatCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD132F274342005D62AE /* KBPersonaChatCell.m */; };
048FFD182F2763A5005D62AE /* KBVoiceInputBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD172F2763A5005D62AE /* KBVoiceInputBar.m */; };
048FFD1D2F277486005D62AE /* KBChatHistoryPageModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD1C2F277486005D62AE /* KBChatHistoryPageModel.m */; };
048FFD1E2F277486005D62AE /* KBChatHistoryModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD1A2F277486005D62AE /* KBChatHistoryModel.m */; };
048FFD242F28A836005D62AE /* KBChatLimitPopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD232F28A836005D62AE /* KBChatLimitPopView.m */; };
048FFD272F28C6CF005D62AE /* KBImagePositionButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD262F28C6CF005D62AE /* KBImagePositionButton.m */; };
048FFD2A2F28E99A005D62AE /* KBCommentModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD292F28E99A005D62AE /* KBCommentModel.m */; };
048FFD2D2F29F356005D62AE /* KBAIMessageVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD2C2F29F356005D62AE /* KBAIMessageVC.m */; };
048FFD302F29F3C3005D62AE /* KBAIMessageZanVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD2F2F29F3C3005D62AE /* KBAIMessageZanVC.m */; };
048FFD1D2F277486005D62AE /* KBChatHistoryPageModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD1C2F277486005D62AE /* KBChatHistoryPageModel.m */; };
048FFD1E2F277486005D62AE /* KBChatHistoryModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD1A2F277486005D62AE /* KBChatHistoryModel.m */; };
048FFD242F28A836005D62AE /* KBChatLimitPopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD232F28A836005D62AE /* KBChatLimitPopView.m */; };
A1B2C9302FCA000100000001 /* KBChatLimitPopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD232F28A836005D62AE /* KBChatLimitPopView.m */; };
048FFD272F28C6CF005D62AE /* KBImagePositionButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD262F28C6CF005D62AE /* KBImagePositionButton.m */; };
048FFD2A2F28E99A005D62AE /* KBCommentModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD292F28E99A005D62AE /* KBCommentModel.m */; };
048FFD2D2F29F356005D62AE /* KBAIMessageVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD2C2F29F356005D62AE /* KBAIMessageVC.m */; };
048FFD302F29F3C3005D62AE /* KBAIMessageZanVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD2F2F29F3C3005D62AE /* KBAIMessageZanVC.m */; };
048FFD332F29F3D2005D62AE /* KBAIMessageChatingVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD322F29F3D2005D62AE /* KBAIMessageChatingVC.m */; };
048FFD342F29F400005D62AE /* KBAIMessageListVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD362F29F400005D62AE /* KBAIMessageListVC.m */; };
048FFD362F29F88E005D62AE /* AIMessageVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 048FFD352F29F88E005D62AE /* AIMessageVM.m */; };
@@ -229,8 +229,6 @@
04E038E92F20E877002CA5A0 /* DeepgramStreamingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E038E52F20E877002CA5A0 /* DeepgramStreamingManager.m */; };
04E038EF2F21F0EC002CA5A0 /* AiVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E038EE2F21F0EC002CA5A0 /* AiVM.m */; };
04E0394B2F236E75002CA5A0 /* KBChatUserMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E0394A2F236E75002CA5A0 /* KBChatUserMessageCell.m */; };
0F2A10032F3C0001002CA5A0 /* KBChatMessageActionPopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F2A10022F3C0001002CA5A0 /* KBChatMessageActionPopView.m */; };
0F2A10132F3C0002002CA5A0 /* KBAIPersonaSidebarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F2A10122F3C0002002CA5A0 /* KBAIPersonaSidebarView.m */; };
04E0394C2F236E75002CA5A0 /* KBChatTimeCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E039482F236E75002CA5A0 /* KBChatTimeCell.m */; };
04E0394D2F236E75002CA5A0 /* KBChatAssistantMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E039432F236E75002CA5A0 /* KBChatAssistantMessageCell.m */; };
04E0394E2F236E75002CA5A0 /* KBChatTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E039452F236E75002CA5A0 /* KBChatTableView.m */; };
@@ -240,6 +238,14 @@
04E0B2022F300002002CA5A0 /* KBVoiceRecordManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 04E0B2012F300002002CA5A0 /* KBVoiceRecordManager.m */; };
04E161832F10E6470022C23B /* normal_hei_them.zip in Resources */ = {isa = PBXBuildFile; fileRef = 04E161812F10E6470022C23B /* normal_hei_them.zip */; };
04E161842F10E6470022C23B /* normal_them.zip in Resources */ = {isa = PBXBuildFile; fileRef = 04E161822F10E6470022C23B /* normal_them.zip */; };
04F4C0AA2F32274000E8F08C /* KBPayMainVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0A92F32274000E8F08C /* KBPayMainVC.m */; };
04F4C0AD2F32288600E8F08C /* KBPaySvipVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0AC2F32288600E8F08C /* KBPaySvipVC.m */; };
04F4C0B02F322EF200E8F08C /* PagingViewTableHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0AF2F322EF200E8F08C /* PagingViewTableHeaderView.m */; };
04F4C0B52F33053800E8F08C /* KBSvipBenefitCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0B22F33053800E8F08C /* KBSvipBenefitCell.m */; };
04F4C0B62F33053800E8F08C /* KBSvipSubscribeCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0B42F33053800E8F08C /* KBSvipSubscribeCell.m */; };
04F4C0BB2F3306CF00E8F08C /* KBSvipFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0BA2F3306CF00E8F08C /* KBSvipFlowLayout.m */; };
04F4C0BC2F3306CF00E8F08C /* KBSvipBenefitBgView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0B82F3306CF00E8F08C /* KBSvipBenefitBgView.m */; };
04F4C0BF2F33369900E8F08C /* KBAIChatDeleteConfirmView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C0BE2F33369900E8F08C /* KBAIChatDeleteConfirmView.m */; };
04FC95672EB0546C007BD342 /* KBKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95652EB0546C007BD342 /* KBKey.m */; };
04FC956A2EB05497007BD342 /* KBKeyButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC95692EB05497007BD342 /* KBKeyButton.m */; };
04FC956D2EB054B7007BD342 /* KBKeyboardView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FC956C2EB054B7007BD342 /* KBKeyboardView.m */; };
@@ -273,6 +279,8 @@
04FEDC422F00040000999999 /* KBKeyboardSubscriptionFeatureMarqueeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 04FEDC412F00040000999999 /* KBKeyboardSubscriptionFeatureMarqueeView.m */; };
05A1B2D12F5B1A2B3C4D5E60 /* KBSearchVM.m in Sources */ = {isa = PBXBuildFile; fileRef = 05A1B2C52F5B1A2B3C4D5E60 /* KBSearchVM.m */; };
05A1B2D22F5B1A2B3C4D5E60 /* KBSearchThemeModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 05A1B2C72F5B1A2B3C4D5E60 /* KBSearchThemeModel.m */; };
0F2A10032F3C0001002CA5A0 /* KBChatMessageActionPopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F2A10022F3C0001002CA5A0 /* KBChatMessageActionPopView.m */; };
0F2A10132F3C0002002CA5A0 /* KBAIPersonaSidebarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F2A10122F3C0002002CA5A0 /* KBAIPersonaSidebarView.m */; };
471CAD3574798685B72ADD55 /* KBMyTheme.m in Sources */ = {isa = PBXBuildFile; fileRef = 180D662EC4DB3A7FFF83FF18 /* KBMyTheme.m */; };
49B63DBAEE9076C591E13D68 /* KBShopThemeTagModel.m in Sources */ = {isa = PBXBuildFile; fileRef = E2A844CD2D8584596DBE6316 /* KBShopThemeTagModel.m */; };
550CB2630FA4A7B4B9782EFA /* KBMyTheme.m in Sources */ = {isa = PBXBuildFile; fileRef = 180D662EC4DB3A7FFF83FF18 /* KBMyTheme.m */; };
@@ -396,8 +404,6 @@
0459D1B52EBA287900F2D189 /* KBSkinManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSkinManager.h; sourceTree = "<group>"; };
0459D1B62EBA287900F2D189 /* KBSkinManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSkinManager.m; sourceTree = "<group>"; };
0460866A2F18D75500757C95 /* ai_test.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = ai_test.m4a; sourceTree = "<group>"; };
046086712F191B6900757C95 /* KBAiMainVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAiMainVC.h; sourceTree = "<group>"; };
046086722F191B6900757C95 /* KBAiMainVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAiMainVC.m; sourceTree = "<group>"; };
046086742F191CC700757C95 /* AI技术分析.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "AI技术分析.txt"; sourceTree = "<group>"; };
046086962F19238500757C95 /* KBAiRecordButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAiRecordButton.h; sourceTree = "<group>"; };
046086972F19238500757C95 /* KBAiRecordButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAiRecordButton.m; sourceTree = "<group>"; };
@@ -564,8 +570,6 @@
048FFD1C2F277486005D62AE /* KBChatHistoryPageModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBChatHistoryPageModel.m; sourceTree = "<group>"; };
048FFD222F28A836005D62AE /* KBChatLimitPopView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBChatLimitPopView.h; sourceTree = "<group>"; };
048FFD232F28A836005D62AE /* KBChatLimitPopView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBChatLimitPopView.m; sourceTree = "<group>"; };
0F2A10112F3C0002002CA5A0 /* KBAIPersonaSidebarView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAIPersonaSidebarView.h; sourceTree = "<group>"; };
0F2A10122F3C0002002CA5A0 /* KBAIPersonaSidebarView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAIPersonaSidebarView.m; sourceTree = "<group>"; };
048FFD252F28C6CF005D62AE /* KBImagePositionButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBImagePositionButton.h; sourceTree = "<group>"; };
048FFD262F28C6CF005D62AE /* KBImagePositionButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBImagePositionButton.m; sourceTree = "<group>"; };
048FFD282F28E99A005D62AE /* KBCommentModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBCommentModel.h; sourceTree = "<group>"; };
@@ -724,8 +728,6 @@
04E039482F236E75002CA5A0 /* KBChatTimeCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBChatTimeCell.m; sourceTree = "<group>"; };
04E039492F236E75002CA5A0 /* KBChatUserMessageCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBChatUserMessageCell.h; sourceTree = "<group>"; };
04E0394A2F236E75002CA5A0 /* KBChatUserMessageCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBChatUserMessageCell.m; sourceTree = "<group>"; };
0F2A10012F3C0001002CA5A0 /* KBChatMessageActionPopView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBChatMessageActionPopView.h; sourceTree = "<group>"; };
0F2A10022F3C0001002CA5A0 /* KBChatMessageActionPopView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBChatMessageActionPopView.m; sourceTree = "<group>"; };
04E039502F2387D2002CA5A0 /* KBAiChatMessage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAiChatMessage.h; sourceTree = "<group>"; };
04E039512F2387D2002CA5A0 /* KBAiChatMessage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAiChatMessage.m; sourceTree = "<group>"; };
04E0B1002F300001002CA5A0 /* KBVoiceToTextManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBVoiceToTextManager.h; sourceTree = "<group>"; };
@@ -734,6 +736,22 @@
04E0B2012F300002002CA5A0 /* KBVoiceRecordManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBVoiceRecordManager.m; sourceTree = "<group>"; };
04E161812F10E6470022C23B /* normal_hei_them.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = normal_hei_them.zip; sourceTree = "<group>"; };
04E161822F10E6470022C23B /* normal_them.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = normal_them.zip; sourceTree = "<group>"; };
04F4C0A82F32274000E8F08C /* KBPayMainVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBPayMainVC.h; sourceTree = "<group>"; };
04F4C0A92F32274000E8F08C /* KBPayMainVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBPayMainVC.m; sourceTree = "<group>"; };
04F4C0AB2F32288600E8F08C /* KBPaySvipVC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBPaySvipVC.h; sourceTree = "<group>"; };
04F4C0AC2F32288600E8F08C /* KBPaySvipVC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBPaySvipVC.m; sourceTree = "<group>"; };
04F4C0AE2F322EF200E8F08C /* PagingViewTableHeaderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PagingViewTableHeaderView.h; sourceTree = "<group>"; };
04F4C0AF2F322EF200E8F08C /* PagingViewTableHeaderView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PagingViewTableHeaderView.m; sourceTree = "<group>"; };
04F4C0B12F33053800E8F08C /* KBSvipBenefitCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSvipBenefitCell.h; sourceTree = "<group>"; };
04F4C0B22F33053800E8F08C /* KBSvipBenefitCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSvipBenefitCell.m; sourceTree = "<group>"; };
04F4C0B32F33053800E8F08C /* KBSvipSubscribeCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSvipSubscribeCell.h; sourceTree = "<group>"; };
04F4C0B42F33053800E8F08C /* KBSvipSubscribeCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSvipSubscribeCell.m; sourceTree = "<group>"; };
04F4C0B72F3306CF00E8F08C /* KBSvipBenefitBgView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSvipBenefitBgView.h; sourceTree = "<group>"; };
04F4C0B82F3306CF00E8F08C /* KBSvipBenefitBgView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSvipBenefitBgView.m; sourceTree = "<group>"; };
04F4C0B92F3306CF00E8F08C /* KBSvipFlowLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSvipFlowLayout.h; sourceTree = "<group>"; };
04F4C0BA2F3306CF00E8F08C /* KBSvipFlowLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSvipFlowLayout.m; sourceTree = "<group>"; };
04F4C0BD2F33369900E8F08C /* KBAIChatDeleteConfirmView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAIChatDeleteConfirmView.h; sourceTree = "<group>"; };
04F4C0BE2F33369900E8F08C /* KBAIChatDeleteConfirmView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAIChatDeleteConfirmView.m; sourceTree = "<group>"; };
04FC953A2EAFAE56007BD342 /* KeyBoardPrefixHeader.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyBoardPrefixHeader.pch; sourceTree = "<group>"; };
04FC95642EB0546C007BD342 /* KBKey.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBKey.h; sourceTree = "<group>"; };
04FC95652EB0546C007BD342 /* KBKey.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBKey.m; sourceTree = "<group>"; };
@@ -803,6 +821,10 @@
05A1B2C52F5B1A2B3C4D5E60 /* KBSearchVM.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSearchVM.m; sourceTree = "<group>"; };
05A1B2C62F5B1A2B3C4D5E60 /* KBSearchThemeModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBSearchThemeModel.h; sourceTree = "<group>"; };
05A1B2C72F5B1A2B3C4D5E60 /* KBSearchThemeModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBSearchThemeModel.m; sourceTree = "<group>"; };
0F2A10012F3C0001002CA5A0 /* KBChatMessageActionPopView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBChatMessageActionPopView.h; sourceTree = "<group>"; };
0F2A10022F3C0001002CA5A0 /* KBChatMessageActionPopView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBChatMessageActionPopView.m; sourceTree = "<group>"; };
0F2A10112F3C0002002CA5A0 /* KBAIPersonaSidebarView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBAIPersonaSidebarView.h; sourceTree = "<group>"; };
0F2A10122F3C0002002CA5A0 /* KBAIPersonaSidebarView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBAIPersonaSidebarView.m; sourceTree = "<group>"; };
180D662EC4DB3A7FFF83FF18 /* KBMyTheme.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBMyTheme.m; sourceTree = "<group>"; };
2C1092FB2B452F95B15D4263 /* Pods_CustomKeyboard.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CustomKeyboard.framework; sourceTree = BUILT_PRODUCTS_DIR; };
35E2B1C590E060D912A4E7F4 /* KBShopThemeTagModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBShopThemeTagModel.h; sourceTree = "<group>"; };
@@ -957,6 +979,16 @@
04122FAF2EC73C0100EF7AB3 /* KBVipReviewItemCell.m */,
04122FB12EC73C0100EF7AB3 /* KBVipReviewListCell.h */,
04122FB22EC73C0100EF7AB3 /* KBVipReviewListCell.m */,
04F4C0AE2F322EF200E8F08C /* PagingViewTableHeaderView.h */,
04F4C0AF2F322EF200E8F08C /* PagingViewTableHeaderView.m */,
04F4C0B12F33053800E8F08C /* KBSvipBenefitCell.h */,
04F4C0B22F33053800E8F08C /* KBSvipBenefitCell.m */,
04F4C0B32F33053800E8F08C /* KBSvipSubscribeCell.h */,
04F4C0B42F33053800E8F08C /* KBSvipSubscribeCell.m */,
04F4C0B72F3306CF00E8F08C /* KBSvipBenefitBgView.h */,
04F4C0B82F3306CF00E8F08C /* KBSvipBenefitBgView.m */,
04F4C0B92F3306CF00E8F08C /* KBSvipFlowLayout.h */,
04F4C0BA2F3306CF00E8F08C /* KBSvipFlowLayout.m */,
);
path = V;
sourceTree = "<group>";
@@ -968,6 +1000,10 @@
04122F802EC5FC6F00EF7AB3 /* KBJfPay.m */,
04122F8F2EC73AF700EF7AB3 /* KBVipPay.h */,
04122F902EC73AF700EF7AB3 /* KBVipPay.m */,
04F4C0A82F32274000E8F08C /* KBPayMainVC.h */,
04F4C0A92F32274000E8F08C /* KBPayMainVC.m */,
04F4C0AB2F32288600E8F08C /* KBPaySvipVC.h */,
04F4C0AC2F32288600E8F08C /* KBPaySvipVC.m */,
);
path = VC;
sourceTree = "<group>";
@@ -1120,8 +1156,6 @@
0460866E2F191A5100757C95 /* VC */ = {
isa = PBXGroup;
children = (
046086712F191B6900757C95 /* KBAiMainVC.h */,
046086722F191B6900757C95 /* KBAiMainVC.m */,
048FFD092F273BFC005D62AE /* KBAIHomeVC.h */,
048FFD0A2F273BFC005D62AE /* KBAIHomeVC.m */,
048FFD2B2F29F356005D62AE /* KBAIMessageVC.h */,
@@ -1454,6 +1488,8 @@
048FFD232F28A836005D62AE /* KBChatLimitPopView.m */,
0F2A10112F3C0002002CA5A0 /* KBAIPersonaSidebarView.h */,
0F2A10122F3C0002002CA5A0 /* KBAIPersonaSidebarView.m */,
04F4C0BD2F33369900E8F08C /* KBAIChatDeleteConfirmView.h */,
04F4C0BE2F33369900E8F08C /* KBAIChatDeleteConfirmView.m */,
);
path = PopView;
sourceTree = "<group>";
@@ -2350,14 +2386,15 @@
0498BD862EE1BEC9006CC1D5 /* KBSignUtils.m in Sources */,
04791FFC2ED71D17004E8522 /* UIColor+Extension.m in Sources */,
0450AC4A2EF2C3ED00B6AF06 /* KBKeyboardSubscriptionOptionCell.m in Sources */,
04A9FE0F2EB481100020DB6D /* KBHUD.m in Sources */,
048FFD562F2B9C3D005D62AE /* KBChatAssistantCell.m in Sources */,
048FFD572F2B9C3D005D62AE /* KBChatUserCell.m in Sources */,
04C6EADD2EAF8CEB0089C901 /* KBToolBar.m in Sources */,
A1B2C9262FC9000100000001 /* KBChatMessage.m in Sources */,
A1B2C9272FC9000100000001 /* KBChatMessageCell.m in Sources */,
A1B2C9282FC9000100000001 /* KBChatPanelView.m in Sources */,
A1B2C3EB2F20000000000001 /* KBSuggestionBarView.m in Sources */,
04A9FE0F2EB481100020DB6D /* KBHUD.m in Sources */,
048FFD562F2B9C3D005D62AE /* KBChatAssistantCell.m in Sources */,
048FFD572F2B9C3D005D62AE /* KBChatUserCell.m in Sources */,
A1B2C9302FCA000100000001 /* KBChatLimitPopView.m in Sources */,
04C6EADD2EAF8CEB0089C901 /* KBToolBar.m in Sources */,
A1B2C9262FC9000100000001 /* KBChatMessage.m in Sources */,
A1B2C9272FC9000100000001 /* KBChatMessageCell.m in Sources */,
A1B2C9282FC9000100000001 /* KBChatPanelView.m in Sources */,
A1B2C3EB2F20000000000001 /* KBSuggestionBarView.m in Sources */,
0419C9662F2C7693002E86D3 /* KBVM.m in Sources */,
048FFD512F2B68F7005D62AE /* KBPersonaModel.m in Sources */,
04FC95792EB09BC8007BD342 /* KBKeyBoardMainView.m in Sources */,
@@ -2418,6 +2455,7 @@
04122F622EC5F41D00EF7AB3 /* KBUser.m in Sources */,
04122F8B2EC6F7C800EF7AB3 /* IAPVerifyTransactionObj.m in Sources */,
04B5A1A22EEFA12300AAAAAA /* KBPayProductModel.m in Sources */,
04F4C0AD2F32288600E8F08C /* KBPaySvipVC.m in Sources */,
046086D62F1A093400757C95 /* KBAICommentInputView.m in Sources */,
046086D72F1A093400757C95 /* KBAICommentFooterView.m in Sources */,
046086D82F1A093400757C95 /* KBAIReplyCell.m in Sources */,
@@ -2433,7 +2471,6 @@
04122FAD2EC73C0100EF7AB3 /* KBVipSubscribeCell.m in Sources */,
049FB31D2EC21BCD00FAB05D /* KBMyKeyboardCell.m in Sources */,
048909F62EC0AAAA00FABA60 /* KBCategoryTitleCell.m in Sources */,
046086732F191B6900757C95 /* KBAiMainVC.m in Sources */,
048909F72EC0AAAA00FABA60 /* KBCategoryTitleView.m in Sources */,
048FFD332F29F3D2005D62AE /* KBAIMessageChatingVC.m in Sources */,
048FFD342F29F400005D62AE /* KBAIMessageListVC.m in Sources */,
@@ -2452,6 +2489,7 @@
046086CC2F1A092500757C95 /* KBAIReplyModel.m in Sources */,
046086CD2F1A092500757C95 /* KBAICommentModel.m in Sources */,
04791F8F2ED469C0004E8522 /* KBHostAppLauncher.m in Sources */,
04F4C0BF2F33369900E8F08C /* KBAIChatDeleteConfirmView.m in Sources */,
047C65582EBCC06D0035E841 /* HomeRankCardCell.m in Sources */,
04D1F6B32EDFF10A00B12345 /* KBSkinInstallBridge.m in Sources */,
04122F912EC73AF700EF7AB3 /* KBVipPay.m in Sources */,
@@ -2487,6 +2525,7 @@
048908E92EBF843000FABA60 /* KBSkinDetailHeaderCell.m in Sources */,
04A9FE1B2EB892460020DB6D /* KBLocalizationManager.m in Sources */,
048908BC2EBE1FCB00FABA60 /* BaseViewController.m in Sources */,
04F4C0B02F322EF200E8F08C /* PagingViewTableHeaderView.m in Sources */,
0498BD902EE6A3BD006CC1D5 /* KBMyMainModel.m in Sources */,
A1F0C1C32FABCDEF12345678 /* KBInviteCodeModel.m in Sources */,
A1F0C1D32FACAD0012345678 /* KBMaiPointReporter.m in Sources */,
@@ -2523,6 +2562,8 @@
047C655C2EBCD0F80035E841 /* UIView+KBShadow.m in Sources */,
04E038DD2F20C420002CA5A0 /* VoiceChatStreamingManager.m in Sources */,
04E038DE2F20C420002CA5A0 /* VoiceChatWebSocketClient.m in Sources */,
04F4C0B52F33053800E8F08C /* KBSvipBenefitCell.m in Sources */,
04F4C0B62F33053800E8F08C /* KBSvipSubscribeCell.m in Sources */,
049FB2262EC3136D00FAB05D /* KBPersonInfoItemCell.m in Sources */,
048908C32EBE32B800FABA60 /* KBSearchVC.m in Sources */,
049FB20B2EC1C13800FAB05D /* KBSkinBottomActionView.m in Sources */,
@@ -2530,6 +2571,7 @@
04FC95DD2EB202A3007BD342 /* KBGuideVC.m in Sources */,
04FC95E52EB220B5007BD342 /* UIColor+Extension.m in Sources */,
048908E02EBF73DC00FABA60 /* MySkinVC.m in Sources */,
04F4C0AA2F32274000E8F08C /* KBPayMainVC.m in Sources */,
048FFD362F29F88E005D62AE /* AIMessageVM.m in Sources */,
048908F22EC047FD00FABA60 /* KBShopHeadView.m in Sources */,
0498BD742EE02E3D006CC1D5 /* KBRegistVerEmailVC.m in Sources */,
@@ -2591,6 +2633,8 @@
047C65502EBCBA9E0035E841 /* KBShopVC.m in Sources */,
0477BE042EBC83130055D639 /* HomeMainVC.m in Sources */,
0477BDFD2EBC6A170055D639 /* HomeHotVC.m in Sources */,
04F4C0BB2F3306CF00E8F08C /* KBSvipFlowLayout.m in Sources */,
04F4C0BC2F3306CF00E8F08C /* KBSvipBenefitBgView.m in Sources */,
04E039522F2387D2002CA5A0 /* KBAiChatMessage.m in Sources */,
0460869A2F19238500757C95 /* KBAiWaveformView.m in Sources */,
0460869C2F19238500757C95 /* KBAiRecordButton.m in Sources */,

View File

@@ -21,7 +21,7 @@
#import "KBLoginPopView.h"
#import "KBSexSelVC.h"
#import "KBKeyboardPermissionManager.h"
#import "KBVipPay.h"
#import "KBPayMainVC.h"
#import "KBUserSessionManager.h"
#import "KBLoginVC.h"
#import "KBConfig.h"
@@ -192,6 +192,9 @@ static NSTimeInterval const kKBSubscriptionPrefillTTL = 10 * 60.0;
return YES;
} else if ([host isEqualToString:@"recharge"]) { // kbkeyboard://recharge
NSDictionary<NSString *, NSString *> *params = [self kb_queryParametersFromURL:url];
NSString *vipType = [params[@"vipType"] lowercaseString];
BOOL preferSvip = ([vipType isKindOfClass:NSString.class] && vipType.length > 0 &&
([vipType isEqualToString:@"svip"] || [vipType isEqualToString:@"1"]));
NSString *productId = params[@"productId"];
BOOL autoPay = NO;
NSString *autoFlag = params[@"autoPay"];
@@ -203,34 +206,37 @@ static NSTimeInterval const kKBSubscriptionPrefillTTL = 10 * 60.0;
autoPay = YES;
}
BOOL wantsPrefill = NO;
NSString *prefillFlag = params[@"prefill"];
if ([prefillFlag respondsToSelector:@selector(boolValue)] && prefillFlag.boolValue) {
wantsPrefill = YES;
}
NSString *src = params[@"src"];
if ([src isKindOfClass:NSString.class] && [src.lowercaseString isEqualToString:@"keyboard"]) {
wantsPrefill = YES;
}
NSDictionary *prefillPayload = wantsPrefill ? [self kb_consumeSubscriptionPrefillPayloadIfValid] : nil;
if ([prefillPayload isKindOfClass:NSDictionary.class]) {
NSString *payloadProductId = prefillPayload[@"productId"];
if (productId.length == 0 && [payloadProductId isKindOfClass:NSString.class]) {
productId = payloadProductId;
KBPayMainVC *vc = [[KBPayMainVC alloc] init];
vc.initialSelectedIndex = preferSvip ? 1 : 0;
if (!preferSvip) {
BOOL wantsPrefill = NO;
NSString *prefillFlag = params[@"prefill"];
if ([prefillFlag respondsToSelector:@selector(boolValue)] && prefillFlag.boolValue) {
wantsPrefill = YES;
}
NSString *src = params[@"src"];
if ([src isKindOfClass:NSString.class] && [src.lowercaseString isEqualToString:@"keyboard"]) {
wantsPrefill = YES;
}
NSDictionary *prefillPayload = wantsPrefill ? [self kb_consumeSubscriptionPrefillPayloadIfValid] : nil;
if ([prefillPayload isKindOfClass:NSDictionary.class]) {
NSString *payloadProductId = prefillPayload[@"productId"];
if (productId.length == 0 && [payloadProductId isKindOfClass:NSString.class]) {
productId = payloadProductId;
}
}
}
KBVipPay *vc = [[KBVipPay alloc] init];
if ([prefillPayload isKindOfClass:NSDictionary.class]) {
NSArray *productsJSON = prefillPayload[@"products"];
NSNumber *selectedIndexNumber = prefillPayload[@"selectedIndex"];
NSInteger selectedIndex = [selectedIndexNumber respondsToSelector:@selector(integerValue)] ? selectedIndexNumber.integerValue : NSNotFound;
[vc configureWithProductId:productId
autoPurchase:autoPay
prefillProductsJSON:productsJSON
selectedIndex:selectedIndex];
} else {
[vc configureWithProductId:productId autoPurchase:autoPay];
if ([prefillPayload isKindOfClass:NSDictionary.class]) {
NSArray *productsJSON = prefillPayload[@"products"];
NSNumber *selectedIndexNumber = prefillPayload[@"selectedIndex"];
NSInteger selectedIndex = [selectedIndexNumber respondsToSelector:@selector(integerValue)] ? selectedIndexNumber.integerValue : NSNotFound;
[vc configureWithProductId:productId
autoPurchase:autoPay
prefillProductsJSON:productsJSON
selectedIndex:selectedIndex];
} else {
[vc configureWithProductId:productId autoPurchase:autoPay];
}
}
[KB_CURRENT_NAV pushViewController:vc animated:true];
return YES;

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ai_limit_close@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ai_limit_close@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ai_limit_goto@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ai_limit_goto@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "ai_limit_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "ai_limit_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "chat_del_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "chat_del_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "SVIP_Normal_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "SVIP_Normal_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "SVIP_Selected_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "SVIP_Selected_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "VIP_Normal_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "VIP_Normal_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "VIP_Selected_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "VIP_Selected_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pay_ai_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pay_ai_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pay_chat_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pay_chat_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pay_colorbg_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pay_colorbg_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pay_graybg_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pay_graybg_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pay_history_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pay_history_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pay_keyboard_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pay_keyboard_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pay_leftline_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pay_leftline_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pay_oks_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pay_oks_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 999 B

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pay_person_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pay_person_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pay_phone_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pay_phone_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pay_rightline_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pay_rightline_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pay_soon_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pay_soon_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "pay_speed_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "pay_speed_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -43,6 +43,10 @@ typedef NS_ENUM(NSInteger, KBAIReplyFooterState) {
/// 是否已点赞
@property(nonatomic, assign) BOOL liked;
@property(nonatomic, assign) NSString *replyToUserId;
@property(nonatomic, assign) NSString *replyToUserName;
/// 创建时间(时间戳)
@property(nonatomic, assign) NSTimeInterval createTime;

View File

@@ -59,6 +59,9 @@
if (!_content) {
_content = @"";
}
if (!_replyToUserName) {
_replyToUserName = @"";
}
}
- (instancetype)init {

View File

@@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic, copy) NSString *content;
/// 被回复的用户名(@xxx
@property(nonatomic, copy, nullable) NSString *replyToUserName;
@property(nonatomic, copy) NSString *replyToUserName;
/// 点赞数
@property(nonatomic, assign) NSInteger likeCount;

View File

@@ -55,6 +55,9 @@
if (!_content) {
_content = @"";
}
// if ([_content isEqualToString:@"78910"]) {
// NSLog(@"===");
// }
if (!_replyToUserName) {
_replyToUserName = @"";
}

View File

@@ -22,6 +22,9 @@ typedef NS_ENUM(NSInteger, KBAiChatMessageType) {
/// 消息类型
@property (nonatomic, assign) KBAiChatMessageType type;
/// 聊天记录 ID服务端返回的 id用于删除等操作0 表示本地临时消息)
@property (nonatomic, assign) NSInteger historyId;
/// 文本内容
@property (nonatomic, copy) NSString *text;

View File

@@ -33,6 +33,10 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, strong, nullable) NSArray<KBCommentItem *> *replies;
/// 回复数量
@property (nonatomic, assign) NSInteger replyCount;
/// 被回复的用户 ID仅二级回复场景可能存在
@property (nonatomic, strong, nullable) NSNumber *replyToUserId;
/// 被回复的用户名(@xxx仅二级回复场景可能存在
@property (nonatomic, copy, nullable) NSString *replyToUserName;
/// 用户头像
@property (nonatomic, copy, nullable) NSString *userAvatar;
/// 用户 ID

View File

@@ -28,6 +28,13 @@ typedef NS_ENUM(NSInteger, KBChatMessageActionType) {
@property (nonatomic, weak) id<KBChatMessageActionPopViewDelegate> delegate;
/// 是否显示 “Report” 操作(默认 YES
/// - 当长按的是右侧用户消息KBChatUserMessageCell可设置为 NO。
@property (nonatomic, assign) BOOL showsReportAction;
/// 根据是否显示 Report 计算推荐高度
+ (CGFloat)preferredHeightWithShowsReportAction:(BOOL)showsReportAction;
@end
NS_ASSUME_NONNULL_END

View File

@@ -22,10 +22,20 @@ static CGFloat const kKBChatActionRowHeight = 52.0;
@implementation KBChatMessageActionPopView
+ (CGFloat)preferredHeightWithShowsReportAction:(BOOL)showsReportAction {
// 2 Copy + Delete
// 3 Copy + Delete + Report
NSInteger rows = showsReportAction ? 3 : 2;
NSInteger lines = showsReportAction ? 2 : 1;
return kKBChatActionRowHeight * rows + 0.5 * lines;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_showsReportAction = YES;
[self setupUI];
[self applyReportVisibility];
}
return self;
}
@@ -73,6 +83,32 @@ static CGFloat const kKBChatActionRowHeight = 52.0;
}];
}
- (void)setShowsReportAction:(BOOL)showsReportAction {
if (_showsReportAction == showsReportAction) {
return;
}
_showsReportAction = showsReportAction;
[self applyReportVisibility];
}
- (void)applyReportVisibility {
BOOL show = self.showsReportAction;
self.reportRow.hidden = !show;
self.reportRow.userInteractionEnabled = show;
self.line2.hidden = !show;
// frame
[self.line2 mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(show ? 0.5 : 0.0);
}];
[self.reportRow mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(show ? kKBChatActionRowHeight : 0.0);
}];
[self setNeedsLayout];
[self layoutIfNeeded];
}
- (UIControl *)buildRowWithTitle:(NSString *)title
iconName:(NSString *)iconName
action:(KBChatMessageActionType)action {

View File

@@ -22,7 +22,7 @@
///
static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidResetNotification";
@interface KBPersonaChatCell () <KBChatTableViewDelegate, KBChatMessageActionPopViewDelegate>
@interface KBPersonaChatCell () <KBChatTableViewDelegate, KBChatMessageActionPopViewDelegate, KBAICommentViewDelegate>
///
@property (nonatomic, strong) UIImageView *backgroundImageView;
@@ -354,6 +354,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
message.isComplete = YES;
message.needsTypewriterEffect = NO;
message.historyId = item.messageId;
[newMessages addObject:message];
// [newMessages insertObject:message atIndex:0];
}
@@ -931,7 +932,13 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
} break;
case KBChatMessageActionTypeDelete: {
NSInteger idx = [self.messages indexOfObjectIdenticalTo:message];
if (idx != NSNotFound) {
if (idx == NSNotFound) {
return;
}
NSInteger historyId = message.historyId;
// id /
if (historyId <= 0) {
[self.messages removeObjectAtIndex:idx];
[self.chatView reloadWithMessages:self.messages
keepOffset:YES
@@ -944,7 +951,44 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
[[KBAIChatMessageCacheManager shared] clearMessagesForCompanionId:self.persona.personaId];
}
}
return;
}
[KBHUD show];
__weak typeof(self) weakSelf = self;
[self.aiVM deleteChatHistoryWithId:historyId completion:^(BOOL success, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
[KBHUD dismiss];
if (!success || error) {
NSString *msg = error.localizedDescription ?: KBLocalized(@"删除失败,请重试");
[KBHUD showError:msg];
return;
}
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) {
return;
}
NSInteger idx2 = [strongSelf.messages indexOfObjectIdenticalTo:message];
if (idx2 == NSNotFound) {
return;
}
[strongSelf.messages removeObjectAtIndex:idx2];
[strongSelf.chatView reloadWithMessages:strongSelf.messages
keepOffset:YES
scrollToBottom:NO];
if (strongSelf.persona.personaId > 0) {
if (strongSelf.messages.count > 0) {
[[KBAIChatMessageCacheManager shared] saveMessages:strongSelf.messages
forCompanionId:strongSelf.persona.personaId];
} else {
[[KBAIChatMessageCacheManager shared] clearMessagesForCompanionId:strongSelf.persona.personaId];
}
}
});
}];
} break;
case KBChatMessageActionTypeReport: {
if (self.persona.personaId <= 0) {
@@ -970,10 +1014,13 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
self.selectedActionMessage = message;
CGFloat width = 240;
CGFloat height = 156;
BOOL isUserMessage = (message.type == KBAiChatMessageTypeUser);
BOOL showsReport = !isUserMessage;
CGFloat height = [KBChatMessageActionPopView preferredHeightWithShowsReportAction:showsReport];
KBChatMessageActionPopView *content = [[KBChatMessageActionPopView alloc]
initWithFrame:CGRectMake(0, 0, width, height)];
content.delegate = self;
content.showsReportAction = showsReport;
UIWindow *window = [UIApplication sharedApplication].keyWindow;
if (!window) {
@@ -988,7 +1035,6 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
[window addSubview:mask];
self.messageActionMaskView = mask;
BOOL isUserMessage = (message.type == KBAiChatMessageTypeUser);
CGFloat margin = 12.0;
CGFloat spacing = 8.0;
CGFloat topSafe = 0.0;
@@ -1204,6 +1250,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
CGFloat customViewHeight = KB_SCREEN_HEIGHT * 0.75;
KBAICommentView *customView = [[KBAICommentView alloc]
initWithFrame:CGRectMake(0, 0, KB_SCREEN_WIDTH, customViewHeight)];
customView.delegate = self;
NSString *commentCount = self.persona.commentCount;
NSInteger totalCommentCount = [commentCount integerValue];;
@@ -1235,6 +1282,18 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
[popView pop];
}
#pragma mark - KBAICommentViewDelegate
- (void)commentView:(KBAICommentView *)view didUpdateTotalCommentCount:(NSInteger)totalCommentCount {
// cell
if (view.companionId <= 0 || view.companionId != self.persona.personaId) {
return;
}
self.persona.commentCount = [NSString stringWithFormat:@"%ld", (long)totalCommentCount];
[self.commentButton setTitle:self.persona.commentCount forState:UIControlStateNormal];
}
- (AiVM *)aiVM{

View File

@@ -99,8 +99,14 @@
- (void)sendButtonTapped {
NSString *text = self.textField.text;
if (text.length > 0 && self.onSend) {
self.onSend(text);
if (text.length > 0) {
if (self.onSend) {
self.onSend(text);
}
//
[self clearText];
//
[self.textField resignFirstResponder];
}
}

View File

@@ -10,9 +10,19 @@
NS_ASSUME_NONNULL_BEGIN
@class KBAICommentView;
@protocol KBAICommentViewDelegate <NSObject>
@optional
/// 评论总数变化回调(例如发送评论/回复成功后)
- (void)commentView:(KBAICommentView *)view didUpdateTotalCommentCount:(NSInteger)totalCommentCount;
@end
/// 抖音风格评论视图
@interface KBAICommentView : UIView
@property(nonatomic, weak, nullable) id<KBAICommentViewDelegate> delegate;
/// AI 陪聊角色 ID
@property(nonatomic, assign) NSInteger companionId;

View File

@@ -940,6 +940,9 @@ static NSInteger const kRepliesLoadCount = 5;
strongSelf.totalCommentCount += 1;
[strongSelf updateTitle];
[strongSelf hideEmptyState];
if ([strongSelf.delegate respondsToSelector:@selector(commentView:didUpdateTotalCommentCount:)]) {
[strongSelf.delegate commentView:strongSelf didUpdateTotalCommentCount:strongSelf.totalCommentCount];
}
[strongSelf.tableView beginUpdates];
[strongSelf.tableView
@@ -1044,6 +1047,9 @@ static NSInteger const kRepliesLoadCount = 5;
strongSelf.totalCommentCount += 1;
[strongSelf updateTitle];
if ([strongSelf.delegate respondsToSelector:@selector(commentView:didUpdateTotalCommentCount:)]) {
[strongSelf.delegate commentView:strongSelf didUpdateTotalCommentCount:strongSelf.totalCommentCount];
}
// displayedReplies loadMoreReplies
if (wasFullyExpanded) {

View File

@@ -98,7 +98,7 @@
NSDictionary *nameAttrs = @{
NSFontAttributeName : [UIFont systemFontOfSize:13 weight:UIFontWeightMedium],
NSForegroundColorAttributeName : [UIColor secondaryLabelColor]
NSForegroundColorAttributeName : [UIColor whiteColor]
};
[attrName appendAttributedString:[[NSAttributedString alloc]
initWithString:reply.userName
@@ -106,7 +106,7 @@
NSDictionary *replyAttrs = @{
NSFontAttributeName : [UIFont systemFontOfSize:13],
NSForegroundColorAttributeName : [UIColor secondaryLabelColor]
NSForegroundColorAttributeName : [UIColor whiteColor]
};
[attrName appendAttributedString:[[NSAttributedString alloc]
initWithString:@" 回复 "
@@ -114,7 +114,7 @@
NSDictionary *toUserAttrs = @{
NSFontAttributeName : [UIFont systemFontOfSize:13],
NSForegroundColorAttributeName : [UIColor systemBlueColor]
NSForegroundColorAttributeName : [UIColor whiteColor]
};
[attrName appendAttributedString:[[NSAttributedString alloc]
initWithString:[NSString stringWithFormat:@"@%@", reply.replyToUserName]

View File

@@ -0,0 +1,27 @@
//
// KBAIChatDeleteConfirmView.h
// keyBoard
//
// Created by Codex on 2026/02/03.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class KBAIChatDeleteConfirmView;
@protocol KBAIChatDeleteConfirmViewDelegate <NSObject>
- (void)chatDeleteConfirmViewDidTapDelete:(KBAIChatDeleteConfirmView *)view;
- (void)chatDeleteConfirmViewDidTapCancel:(KBAIChatDeleteConfirmView *)view;
@end
@interface KBAIChatDeleteConfirmView : UIView
@property (nonatomic, weak) id<KBAIChatDeleteConfirmViewDelegate> delegate;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,172 @@
//
// KBAIChatDeleteConfirmView.m
// keyBoard
//
// Created by Codex on 2026/02/03.
//
#import "KBAIChatDeleteConfirmView.h"
#import <Masonry/Masonry.h>
@interface KBAIChatDeleteConfirmView ()
@property (nonatomic, strong) UIView *coverView;
@property (nonatomic, strong) UIImageView *iconView;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UILabel *messageLabel;
@property (nonatomic, strong) UIButton *deleteButton;
@property (nonatomic, strong) UIButton *cancelButton;
@end
@implementation KBAIChatDeleteConfirmView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor clearColor];
self.layer.cornerRadius = 16.0;
self.layer.masksToBounds = YES;
[self setupUI];
}
return self;
}
#pragma mark - UI
- (void)setupUI {
[self addSubview:self.coverView];
[self addSubview:self.iconView];
[self.coverView addSubview:self.titleLabel];
[self.coverView addSubview:self.messageLabel];
[self.coverView addSubview:self.deleteButton];
[self.coverView addSubview:self.cancelButton];
[self.coverView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(216);
make.bottom.left.right.bottom.equalTo(self);
}];
[self.iconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self).offset(5);
make.centerX.equalTo(self);
make.width.height.mas_equalTo(64);
}];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.coverView).offset(50);
make.left.equalTo(self.coverView).offset(16);
make.right.equalTo(self.coverView).offset(-16);
}];
[self.messageLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.titleLabel.mas_bottom).offset(16);
make.left.equalTo(self.coverView).offset(20);
make.right.equalTo(self.coverView).offset(-20);
}];
[self.deleteButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.coverView).offset(24);
make.bottom.equalTo(self.coverView).offset(-20);
make.height.mas_equalTo(40);
make.right.equalTo(self.coverView.mas_centerX).offset(-15);
}];
[self.cancelButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.coverView).offset(-24);
make.bottom.equalTo(self.coverView).offset(-20);
make.height.mas_equalTo(40);
make.left.equalTo(self.coverView.mas_centerX).offset(15);
}];
}
#pragma mark - Actions
- (void)onTapDelete {
if ([self.delegate respondsToSelector:@selector(chatDeleteConfirmViewDidTapDelete:)]) {
[self.delegate chatDeleteConfirmViewDidTapDelete:self];
}
}
- (void)onTapCancel {
if ([self.delegate respondsToSelector:@selector(chatDeleteConfirmViewDidTapCancel:)]) {
[self.delegate chatDeleteConfirmViewDidTapCancel:self];
}
}
#pragma mark - Lazy
- (UIImageView *)iconView {
if (!_iconView) {
_iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"chat_del_icon"]];
_iconView.contentMode = UIViewContentModeScaleAspectFit;
}
return _iconView;
}
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.text = KBLocalized(@"Are you sure to delete?");
_titleLabel.font = [UIFont boldSystemFontOfSize:18];
_titleLabel.textColor = [UIColor colorWithWhite:0.1 alpha:1.0];
_titleLabel.textAlignment = NSTextAlignmentCenter;
}
return _titleLabel;
}
- (UILabel *)messageLabel {
if (!_messageLabel) {
_messageLabel = [[UILabel alloc] init];
_messageLabel.text = KBLocalized(@"The existing conversation history will be cleared.");
_messageLabel.font = [UIFont systemFontOfSize:14];
_messageLabel.textColor = [UIColor colorWithWhite:0.45 alpha:1.0];
_messageLabel.textAlignment = NSTextAlignmentCenter;
_messageLabel.numberOfLines = 0;
}
return _messageLabel;
}
- (UIButton *)deleteButton {
if (!_deleteButton) {
_deleteButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_deleteButton setTitle:KBLocalized(@"Delete") forState:UIControlStateNormal];
[_deleteButton setTitleColor:[UIColor colorWithWhite:0.2 alpha:1.0] forState:UIControlStateNormal];
_deleteButton.titleLabel.font = [UIFont systemFontOfSize:15 weight:UIFontWeightSemibold];
_deleteButton.backgroundColor = [UIColor colorWithWhite:0.93 alpha:1.0];
_deleteButton.layer.cornerRadius = 20;
_deleteButton.layer.masksToBounds = YES;
[_deleteButton addTarget:self action:@selector(onTapDelete) forControlEvents:UIControlEventTouchUpInside];
}
return _deleteButton;
}
- (UIButton *)cancelButton {
if (!_cancelButton) {
_cancelButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_cancelButton setTitle:KBLocalized(@"Think Again") forState:UIControlStateNormal];
[_cancelButton setTitleColor:[UIColor colorWithWhite:0.15 alpha:1.0] forState:UIControlStateNormal];
_cancelButton.titleLabel.font = [UIFont systemFontOfSize:15 weight:UIFontWeightSemibold];
_cancelButton.backgroundColor = [UIColor colorWithRed:0.80 green:0.94 blue:0.72 alpha:1.0];
_cancelButton.layer.cornerRadius = 20;
_cancelButton.layer.masksToBounds = YES;
[_cancelButton addTarget:self action:@selector(onTapCancel) forControlEvents:UIControlEventTouchUpInside];
}
return _cancelButton;
}
- (UIView *)coverView{
if (!_coverView) {
_coverView = [[UIView alloc] init];
_coverView.backgroundColor = [UIColor whiteColor];
_coverView.layer.cornerRadius = 30;
_coverView.layer.masksToBounds = true;
}
return _coverView;
}
@end

View File

@@ -458,6 +458,8 @@
if (!_searchField) {
_searchField = [[UITextField alloc] init];
_searchField.placeholder = KBLocalized(@"Search Role");
_searchField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:KBLocalized(@"Search Role")
attributes:@{NSForegroundColorAttributeName: [UIColor whiteColor]}];
_searchField.textColor = [UIColor whiteColor];
_searchField.font = [UIFont systemFontOfSize:14];
_searchField.clearButtonMode = UITextFieldViewModeWhileEditing;

View File

@@ -8,21 +8,25 @@
#import "KBChatLimitPopView.h"
#import <Masonry/Masonry.h>
static CGFloat const kKBChatLimitIconSize = 252.0;
static CGFloat const kKBChatLimitGotoWidth = 251.0;
static CGFloat const kKBChatLimitGotoHeight = 53.0;
static CGFloat const kKBChatLimitCloseSize = 28.0;
static CGFloat const kKBChatLimitSpacing1 = 18.0;
static CGFloat const kKBChatLimitSpacing2 = 18.0;
@interface KBChatLimitPopView ()
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIImageView *iconImageView;
@property (nonatomic, strong) UILabel *messageLabel;
@property (nonatomic, strong) UIButton *cancelButton;
@property (nonatomic, strong) UIButton *rechargeButton;
@property (nonatomic, strong) UIView *buttonDivider;
@property (nonatomic, strong) UIButton *gotoButton;
@property (nonatomic, strong) UIButton *closeButton;
@end
@implementation KBChatLimitPopView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor whiteColor];
self.layer.cornerRadius = 16.0;
self.layer.masksToBounds = YES;
self.backgroundColor = [UIColor clearColor];
[self setupUI];
}
return self;
@@ -31,51 +35,37 @@
#pragma mark - UI
- (void)setupUI {
[self addSubview:self.titleLabel];
[self addSubview:self.messageLabel];
[self addSubview:self.buttonDivider];
[self addSubview:self.cancelButton];
[self addSubview:self.rechargeButton];
[self addSubview:self.iconImageView];
[self.iconImageView addSubview:self.messageLabel];
[self addSubview:self.gotoButton];
[self addSubview:self.closeButton];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self).offset(20);
make.left.equalTo(self).offset(20);
make.right.equalTo(self).offset(-20);
}];
[self.messageLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.titleLabel.mas_bottom).offset(8);
make.left.equalTo(self).offset(20);
make.right.equalTo(self).offset(-20);
}];
[self.buttonDivider mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self);
make.height.mas_equalTo(1);
make.top.greaterThanOrEqualTo(self.messageLabel.mas_bottom).offset(16);
make.bottom.equalTo(self).offset(-48);
}];
[self.cancelButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.bottom.equalTo(self);
make.top.equalTo(self.buttonDivider.mas_bottom);
make.right.equalTo(self.mas_centerX);
}];
[self.rechargeButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.bottom.equalTo(self);
make.top.equalTo(self.buttonDivider.mas_bottom);
make.left.equalTo(self.mas_centerX);
}];
UIView *verticalLine = [[UIView alloc] init];
verticalLine.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1.0];
[self addSubview:verticalLine];
[verticalLine mas_makeConstraints:^(MASConstraintMaker *make) {
[self.iconImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self);
make.centerX.equalTo(self);
make.top.equalTo(self.buttonDivider.mas_bottom);
make.width.height.mas_equalTo(kKBChatLimitIconSize);
}];
//
[self.messageLabel mas_makeConstraints:^(MASConstraintMaker *make) {
// make.centerX.equalTo(self.iconImageView);
make.centerY.equalTo(self.iconImageView).offset(10);
make.left.equalTo(self.iconImageView).offset(26);
make.right.equalTo(self.iconImageView).offset(-46);
}];
[self.gotoButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.iconImageView.mas_bottom).offset(kKBChatLimitSpacing1);
make.centerX.equalTo(self);
make.width.mas_equalTo(kKBChatLimitGotoWidth);
make.height.mas_equalTo(kKBChatLimitGotoHeight);
}];
[self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.gotoButton.mas_bottom).offset(kKBChatLimitSpacing2);
make.centerX.equalTo(self);
make.width.height.mas_equalTo(kKBChatLimitCloseSize);
make.bottom.equalTo(self);
make.width.mas_equalTo(1);
}];
}
@@ -102,56 +92,46 @@
#pragma mark - Lazy
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] init];
_titleLabel.text = KBLocalized(@"提示");
_titleLabel.font = [UIFont boldSystemFontOfSize:18];
_titleLabel.textColor = [UIColor blackColor];
_titleLabel.textAlignment = NSTextAlignmentCenter;
- (UIImageView *)iconImageView {
if (!_iconImageView) {
_iconImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"ai_limit_icon"]];
_iconImageView.contentMode = UIViewContentModeScaleAspectFit;
_iconImageView.userInteractionEnabled = YES;
}
return _titleLabel;
return _iconImageView;
}
- (UILabel *)messageLabel {
if (!_messageLabel) {
_messageLabel = [[UILabel alloc] init];
_messageLabel.font = [UIFont systemFontOfSize:14];
_messageLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1.0];
_messageLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
_messageLabel.textColor = [UIColor colorWithWhite:0.18 alpha:1.0];
_messageLabel.textAlignment = NSTextAlignmentCenter;
_messageLabel.numberOfLines = 0;
}
return _messageLabel;
}
- (UIButton *)cancelButton {
if (!_cancelButton) {
_cancelButton = [UIButton buttonWithType:UIButtonTypeSystem];
[_cancelButton setTitle:KBLocalized(@"取消") forState:UIControlStateNormal];
_cancelButton.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
[_cancelButton setTitleColor:[UIColor colorWithWhite:0.2 alpha:1.0] forState:UIControlStateNormal];
[_cancelButton addTarget:self action:@selector(onTapCancel) forControlEvents:UIControlEventTouchUpInside];
- (UIButton *)gotoButton {
if (!_gotoButton) {
_gotoButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_gotoButton setBackgroundImage:[UIImage imageNamed:@"ai_limit_goto"] forState:UIControlStateNormal];
[_gotoButton setTitle:KBLocalized(@"Go To Recharge") forState:UIControlStateNormal];
[_gotoButton setTitleColor:[UIColor colorWithWhite:0.1 alpha:1.0] forState:UIControlStateNormal];
_gotoButton.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
[_gotoButton addTarget:self action:@selector(onTapRecharge) forControlEvents:UIControlEventTouchUpInside];
}
return _cancelButton;
return _gotoButton;
}
- (UIButton *)rechargeButton {
if (!_rechargeButton) {
_rechargeButton = [UIButton buttonWithType:UIButtonTypeSystem];
[_rechargeButton setTitle:KBLocalized(@"去充值") forState:UIControlStateNormal];
_rechargeButton.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
[_rechargeButton setTitleColor:[UIColor colorWithRed:0.28 green:0.45 blue:0.94 alpha:1.0] forState:UIControlStateNormal];
[_rechargeButton addTarget:self action:@selector(onTapRecharge) forControlEvents:UIControlEventTouchUpInside];
- (UIButton *)closeButton {
if (!_closeButton) {
_closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_closeButton setImage:[UIImage imageNamed:@"ai_limit_close"] forState:UIControlStateNormal];
[_closeButton addTarget:self action:@selector(onTapCancel) forControlEvents:UIControlEventTouchUpInside];
}
return _rechargeButton;
}
- (UIView *)buttonDivider {
if (!_buttonDivider) {
_buttonDivider = [[UIView alloc] init];
_buttonDivider.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1.0];
}
return _buttonDivider;
return _closeButton;
}
@end

View File

@@ -14,7 +14,7 @@
#import "AiVM.h"
#import "KBHUD.h"
#import "KBChatLimitPopView.h"
#import "KBVipPay.h"
#import "KBPayMainVC.h"
#import "KBUserSessionManager.h"
#import "LSTPopView.h"
#import "KBAIMessageVC.h"
@@ -764,8 +764,9 @@ static NSString * const KBAISelectedPersonaIdKey = @"KBAISelectedPersonaId";
[self.chatLimitPopView dismiss];
}
CGFloat width = KB_SCREEN_WIDTH - 60;
KBChatLimitPopView *content = [[KBChatLimitPopView alloc] initWithFrame:CGRectMake(0, 0, width, 180)];
CGFloat width = 252.0;
CGFloat height = 252.0 + 18.0 + 53.0 + 18.0 + 28.0;
KBChatLimitPopView *content = [[KBChatLimitPopView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
content.message = message;
content.delegate = self;
@@ -841,7 +842,8 @@ static NSString * const KBAISelectedPersonaIdKey = @"KBAISelectedPersonaId";
[[KBUserSessionManager shared] goLoginVC];
return;
}
KBVipPay *vc = [[KBVipPay alloc] init];
KBPayMainVC *vc = [[KBPayMainVC alloc] init];
vc.initialSelectedIndex = 1; // SVIP
[KB_CURRENT_NAV pushViewController:vc animated:true];
}
@@ -1051,8 +1053,21 @@ static NSString * const KBAISelectedPersonaIdKey = @"KBAISelectedPersonaId";
dispatch_async(dispatch_get_main_queue(), ^{
KBPersonaChatCell *cell = [strongSelf currentPersonaCell];
//
if (error) {
NSInteger bizCode = [error.userInfo[@"code"] integerValue];
NSString *messageError = error.localizedDescription;
if (bizCode == 50030) {
if (cell) {
[cell removeLoadingUserMessage];
}
NSString *message = messageError ?: @"";
strongSelf.isVoiceProcessing = NO;
[strongSelf updateCollectionViewScrollState];
[strongSelf showChatLimitPopWithMessage:message];
return;
}
NSLog(@"[KBAIHomeVC] 语音转文字失败:%@", error.localizedDescription);
[KBHUD showError:KBLocalized(@"语音转文字失败,请重试")];
if (cell) {

View File

@@ -10,12 +10,14 @@
#import "KBChattedCompanionModel.h"
#import "KBHUD.h"
#import "KBAIChatMessageCacheManager.h"
#import "KBAIChatDeleteConfirmView.h"
#import "LSTPopView.h"
#import <Masonry/Masonry.h>
///
static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidResetNotification";
@interface KBAIMessageChatingVC ()
@interface KBAIMessageChatingVC () <KBAIChatDeleteConfirmViewDelegate, UIGestureRecognizerDelegate>
@property (nonatomic, strong) AiVM *viewModel;
@property (nonatomic, strong) NSMutableArray<KBChattedCompanionModel *> *chattedList;
@@ -24,6 +26,16 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
@property (nonatomic, strong) UIButton *deleteButton;
/// indexPath
@property (nonatomic, strong) NSIndexPath *longPressIndexPath;
///
@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;
@end
@@ -39,23 +51,53 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
[self setupLongPressGesture];
//
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
tapGesture.cancelsTouchesInView = NO; // cell
[self.tableView addGestureRecognizer:tapGesture];
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;
}
#pragma mark -
- (void)setupLongPressGesture {
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
longPress.minimumPressDuration = 0.5; // 0.5
[self.tableView addGestureRecognizer:longPress];
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];
}
- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture {
CGPoint point = [gesture locationInView:self.tableView];
NSLog(@"[KBAIMessageChatingVC] longPress state=%ld point=(%.1f, %.1f)",
(long)gesture.state, point.x, point.y);
if (gesture.state == UIGestureRecognizerStateBegan) {
CGPoint point = [gesture locationInView:self.tableView];
// tap tap
if (self.tapGesture.enabled) {
self.tapGesture.enabled = NO;
NSLog(@"[KBAIMessageChatingVC] tapGesture disabled for longPress");
}
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:point];
NSLog(@"[KBAIMessageChatingVC] longPress began indexPath=%@",
indexPath);
if (indexPath) {
//
@@ -64,12 +106,23 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
// showDeleteButtonAtPoint hideDeleteButton
self.longPressIndexPath = indexPath;
}
} 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");
}
}
}
- (void)handleTapGesture:(UITapGestureRecognizer *)gesture {
//
if (self.deleteButton && !self.deleteButton.hidden) {
CGPoint pointInView = [gesture locationInView:self.tableView];
NSLog(@"[KBAIMessageChatingVC] tap state=%ld point=(%.1f, %.1f)",
(long)gesture.state, pointInView.x, pointInView.y);
CGPoint pointInButton = [gesture locationInView:self.deleteButton];
//
@@ -83,8 +136,10 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
}
- (void)showDeleteButtonAtPoint:(CGPoint)point {
//
[self hideDeleteButton];
// 使 completion
self.deleteButtonGeneration += 1;
NSLog(@"[KBAIMessageChatingVC] showDeleteButtonAtPoint=(%.1f, %.1f) generation=%ld",
point.x, point.y, (long)self.deleteButtonGeneration);
//
if (!self.deleteButton) {
@@ -105,7 +160,14 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
}
//
[self.view addSubview:self.deleteButton];
if (self.deleteButton.superview != self.view) {
[self.view addSubview:self.deleteButton];
} else {
[self.view bringSubviewToFront:self.deleteButton];
}
//
[self.deleteButton.layer removeAllAnimations];
//
CGSize buttonSize = CGSizeMake(110, 40);
@@ -138,6 +200,9 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
}];
self.deleteButton.hidden = NO;
self.deleteButton.alpha = 1.0;
self.deleteButton.transform = CGAffineTransformIdentity;
NSLog(@"[KBAIMessageChatingVC] deleteButton shown");
//
self.deleteButton.transform = CGAffineTransformMakeScale(0.3, 0.3);
@@ -150,10 +215,20 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
- (void)hideDeleteButton {
if (self.deleteButton) {
NSInteger generation = self.deleteButtonGeneration;
NSLog(@"[KBAIMessageChatingVC] hideDeleteButton generation=%ld", (long)generation);
//
[self.deleteButton.layer removeAllAnimations];
[UIView animateWithDuration:0.15 animations:^{
self.deleteButton.alpha = 0;
self.deleteButton.transform = CGAffineTransformMakeScale(0.8, 0.8);
} completion:^(BOOL finished) {
//
if (generation != self.deleteButtonGeneration) {
NSLog(@"[KBAIMessageChatingVC] hide completion ignored (generation changed: %ld -> %ld)",
(long)generation, (long)self.deleteButtonGeneration);
return;
}
self.deleteButton.hidden = YES;
[self.deleteButton removeFromSuperview];
}];
@@ -166,26 +241,69 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
return;
}
// indexPath
NSIndexPath *indexPath = self.longPressIndexPath;
// 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;
}
//
if (indexPath.row >= self.chattedList.count) {
NSLog(@"[KBAIMessageChatingVC] 错误索引越界row=%ld, count=%ld",
NSLog(@"[KBAIMessageChatingVC] 错误索引越界row=%ld, count=%ld",
(long)indexPath.row, (long)self.chattedList.count);
[self hideDeleteButton];
return;
}
KBChattedCompanionModel *model = self.chattedList[indexPath.row];
NSInteger companionId = model.companionId;
NSLog(@"[KBAIMessageChatingVC] 开始删除聊天记录companionId=%ld, name=%@",
NSLog(@"[KBAIMessageChatingVC] 开始删除聊天记录companionId=%ld, name=%@",
(long)companionId, model.name);
//
[self hideDeleteButton];
//
[KBHUD show];
@@ -214,7 +332,7 @@ static NSString * const KBChatSessionDidResetNotification = @"KBChatSessionDidRe
}
// 2. TableView
[weakSelf.tableView deleteRowsAtIndexPaths:@[indexPath]
[weakSelf.tableView deleteRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationLeft];
// 3.

View File

@@ -15,7 +15,7 @@
#import "KBAiRecordButton.h"
#import "KBHUD.h"
#import "KBChatLimitPopView.h"
#import "KBVipPay.h"
#import "KBPayMainVC.h"
#import "LSTPopView.h"
#import "VoiceChatStreamingManager.h"
#import "KBUserSessionManager.h"
@@ -430,9 +430,10 @@
[self.limitPopView dismiss];
}
CGFloat width = KB_SCREEN_WIDTH - 60;
CGFloat width = 252.0;
CGFloat height = 252.0 + 18.0 + 53.0 + 18.0 + 28.0;
KBChatLimitPopView *content =
[[KBChatLimitPopView alloc] initWithFrame:CGRectMake(0, 0, width, 180)];
[[KBChatLimitPopView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
content.message = message;
content.delegate = self;
@@ -461,7 +462,8 @@
[[KBUserSessionManager shared] goLoginVC];
return;
}
KBVipPay *vc = [[KBVipPay alloc] init];
KBPayMainVC *vc = [[KBPayMainVC alloc] init];
vc.initialSelectedIndex = 1; // SVIP
[KB_CURRENT_NAV pushViewController:vc animated:true];
}

View File

@@ -111,6 +111,12 @@ typedef void (^AiVMSpeechTranscribeCompletion)(KBAiSpeechTranscribeResponse *_Nu
pageSize:(NSInteger)pageSize
completion:(void(^)(KBChatHistoryPageModel * _Nullable pageModel, NSError * _Nullable error))completion;
/// 删除聊天记录(根据聊天记录 ID 逻辑删除聊天消息)
/// @param historyId 聊天记录 ID接口字段为 id
/// @param completion 完成回调
- (void)deleteChatHistoryWithId:(NSInteger)historyId
completion:(void(^)(BOOL success, NSError * _Nullable error))completion;
#pragma mark - 评论相关接口
/// 发表评论

View File

@@ -426,6 +426,68 @@ autoShowBusinessError:NO
}];
}
- (void)deleteChatHistoryWithId:(NSInteger)historyId
completion:(void (^)(BOOL, NSError * _Nullable))completion {
if (historyId <= 0) {
NSError *error = [NSError errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey : @"invalid historyId"}];
if (completion) {
completion(NO, error);
}
return;
}
NSDictionary *params = @{
@"id": @(historyId)
};
NSLog(@"[AiVM] /chat/history/delete request: %@", params);
[[KBNetworkManager shared]
POST:@"/chat/history/delete"
jsonBody:params
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *_Nullable json,
NSURLResponse *_Nullable response,
NSError *_Nullable error) {
if (error) {
NSLog(@"[AiVM] /chat/history/delete failed: %@", error.localizedDescription ?: @"");
if (completion) {
completion(NO, error);
}
return;
}
NSLog(@"[AiVM] /chat/history/delete response: %@", json);
if (![json isKindOfClass:[NSDictionary class]]) {
NSError *parseError = [NSError errorWithDomain:@"AiVM"
code:-1
userInfo:@{NSLocalizedDescriptionKey : @"数据格式错误"}];
if (completion) {
completion(NO, parseError);
}
return;
}
NSInteger code = [json[@"code"] integerValue];
if (code != 0) {
NSString *message = json[@"message"] ?: @"请求失败";
NSError *bizError = [NSError errorWithDomain:@"AiVM"
code:code
userInfo:@{NSLocalizedDescriptionKey : message}];
if (completion) {
completion(NO, bizError);
}
return;
}
if (completion) {
completion(YES, nil);
}
}];
}
#pragma mark -
- (void)addCommentWithCompanionId:(NSInteger)companionId

View File

@@ -7,7 +7,7 @@
#import "BaseTabBarController.h"
#import "HomeMainVC.h"
#import "KBAiMainVC.h"
//#import "KBAiMainVC.h"
#import "KBShopVC.h"
#import "MyVC.h"
#import "KBAIHomeVC.h"

Some files were not shown because too many files have changed in this diff Show More