增加换肤功能

This commit is contained in:
启星
2025-08-14 10:07:49 +08:00
parent f6964c1e89
commit 4f9318d98e
8789 changed files with 978530 additions and 2 deletions

View File

@@ -0,0 +1,36 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <Foundation/Foundation.h>
#import <TIMCommon/TIMDefine.h>
#import <TIMCommon/TIMInputViewMoreActionProtocol.h>
#import "TUIChatBaseDataProvider.h"
#import "TUIChatConversationModel.h"
#import "TUIInputMoreCellData.h"
#import "TUIVideoMessageCellData.h"
@class TUIChatDataProvider;
@class TUICustomActionSheetItem;
NS_ASSUME_NONNULL_BEGIN
@interface TUIChatDataProvider : TUIChatBaseDataProvider
#pragma mark - CellData
// For Classic Edition.
- (NSMutableArray<TUIInputMoreCellData *> *)getMoreMenuCellDataArray:(NSString *)groupID
userID:(NSString *)userID
conversationModel:(TUIChatConversationModel *)conversationModel
actionController:(id<TIMInputViewMoreActionProtocol>)actionController;
// For Minimalist Edition.
- (NSArray<TUICustomActionSheetItem *> *)getInputMoreActionItemList:(nullable NSString *)userID
groupID:(nullable NSString *)groupID
conversationModel:(TUIChatConversationModel *)conversationModel
pushVC:(nullable UINavigationController *)pushVC
actionController:(id<TIMInputViewMoreActionProtocol>)actionController;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,503 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
@import ImSDK_Plus;
#import <objc/runtime.h>
#import <TUICore/NSDictionary+TUISafe.h>
#import <TUICore/TUICore.h>
#import <TUICore/TUIThemeManager.h>
#import "UIAlertController+TUICustomStyle.h"
#import "TUIChatConfig.h"
#import "TUIChatDataProvider.h"
#import "TUIMessageDataProvider.h"
#import "TUIVideoMessageCellData.h"
#import "TUIChatConversationModel.h"
#import <TIMCommon/TIMCommonMediator.h>
#import <TIMCommon/TUIEmojiMeditorProtocol.h>
#define Input_SendBtn_Key @"Input_SendBtn_Key"
#define Input_SendBtn_Title @"Input_SendBtn_Title"
#define Input_SendBtn_ImageName @"Input_SendBtn_ImageName"
@interface TUISplitEmojiData : NSObject
@property (nonatomic, assign) NSInteger start;
@property (nonatomic, assign) NSInteger end;
@end
@implementation TUISplitEmojiData
@end
@interface TUIChatDataProvider ()
@property(nonatomic, strong) TUIInputMoreCellData *welcomeInputMoreMenu;
@property(nonatomic, strong) NSMutableArray<TUIInputMoreCellData *> *customInputMoreMenus;
@property(nonatomic, strong) NSArray<TUIInputMoreCellData *> *builtInInputMoreMenus;
@property(nonatomic, strong) NSArray<TUICustomActionSheetItem *> *customInputMoreActionItemList;
@property(nonatomic, strong) NSMutableArray<TUICustomActionSheetItem *> *builtInInputMoreActionItemList;
@end
@implementation TUIChatDataProvider
- (instancetype)init {
if (self = [super init]) {
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(onChangeLanguage) name:TUIChangeLanguageNotification object:nil];
}
return self;
}
#pragma mark - Public
// For Classic Edition.
- (NSMutableArray<TUIInputMoreCellData *> *)getMoreMenuCellDataArray:(NSString *)groupID
userID:(NSString *)userID
conversationModel:(TUIChatConversationModel *)conversationModel
actionController:(id<TIMInputViewMoreActionProtocol>)actionController {
self.builtInInputMoreMenus = [self createBuiltInInputMoreMenusWithConversationModel:conversationModel];
NSMutableArray *moreMenus = [NSMutableArray array];
[moreMenus addObjectsFromArray:self.builtInInputMoreMenus];
BOOL isNeedWelcomeCustomMessage = [TUIChatConfig defaultConfig].enableWelcomeCustomMessage && conversationModel.enableWelcomeCustomMessage;
if (isNeedWelcomeCustomMessage) {
if (![self.customInputMoreMenus containsObject:self.welcomeInputMoreMenu]) {
[self.customInputMoreMenus addObject:self.welcomeInputMoreMenu];
}
}
[moreMenus addObjectsFromArray:self.customInputMoreMenus];
// Extension items
BOOL isNeedVideoCall = [TUIChatConfig defaultConfig].enableVideoCall && conversationModel.enableVideoCall;
BOOL isNeedAudioCall = [TUIChatConfig defaultConfig].enableAudioCall && conversationModel.enableAudioCall;
BOOL isNeedRoom = [TUIChatConfig defaultConfig].showRoomButton && conversationModel.enableRoom;
BOOL isNeedPoll = [TUIChatConfig defaultConfig].showPollButton && conversationModel.enablePoll;
BOOL isNeedGroupNote = [TUIChatConfig defaultConfig].showGroupNoteButton && conversationModel.enableGroupNote;
NSMutableDictionary *extensionParam = [NSMutableDictionary dictionary];
if (userID.length > 0) {
extensionParam[TUICore_TUIChatExtension_InputViewMoreItem_UserID] = userID;
} else if (groupID.length > 0) {
extensionParam[TUICore_TUIChatExtension_InputViewMoreItem_GroupID] = groupID;
}
extensionParam[TUICore_TUIChatExtension_InputViewMoreItem_FilterVideoCall] = @(!isNeedVideoCall);
extensionParam[TUICore_TUIChatExtension_InputViewMoreItem_FilterAudioCall] = @(!isNeedAudioCall);
extensionParam[TUICore_TUIChatExtension_InputViewMoreItem_FilterRoom] = @(!isNeedRoom);
extensionParam[TUICore_TUIChatExtension_InputViewMoreItem_FilterPoll] = @(!isNeedPoll);
extensionParam[TUICore_TUIChatExtension_InputViewMoreItem_FilterGroupNote] = @(!isNeedGroupNote);
extensionParam[TUICore_TUIChatExtension_InputViewMoreItem_ActionVC] = actionController;
NSArray *extensionList = [TUICore getExtensionList:TUICore_TUIChatExtension_InputViewMoreItem_ClassicExtensionID param:extensionParam];
for (TUIExtensionInfo *info in extensionList) {
NSAssert(info.icon && info.text && info.onClicked, @"extension for input view is invalid, check icon/text/onclick");
if (info.icon && info.text && info.onClicked) {
TUIInputMoreCellData *data = [[TUIInputMoreCellData alloc] init];
data.priority = info.weight;
data.image = info.icon;
data.title = info.text;
data.onClicked = info.onClicked;
[moreMenus addObject:data];
}
}
// Customized items
if (conversationModel.customizedNewItemsInMoreMenu.count > 0) {
[moreMenus addObjectsFromArray:conversationModel.customizedNewItemsInMoreMenu];
}
// Sort with priority
NSArray *sortedMenus = [moreMenus sortedArrayUsingComparator:^NSComparisonResult(TUIInputMoreCellData *obj1, TUIInputMoreCellData *obj2) {
return obj1.priority > obj2.priority ? NSOrderedAscending : NSOrderedDescending;
}];
return [NSMutableArray arrayWithArray:sortedMenus];
}
// For Minimalist Edition.
- (NSArray<TUICustomActionSheetItem *> *)getInputMoreActionItemList:(NSString *)userID
groupID:(NSString *)groupID
conversationModel:(TUIChatConversationModel *)conversationModel
pushVC:(UINavigationController *)pushVC
actionController:(id<TIMInputViewMoreActionProtocol>)actionController {
NSMutableArray *result = [NSMutableArray array];
[result addObjectsFromArray:[self createBuiltInInputMoreActionItemList:conversationModel]];
[result addObjectsFromArray:[self createCustomInputMoreActionItemList:conversationModel]];
// Extension items
NSMutableArray<TUICustomActionSheetItem *> *items = [NSMutableArray array];
NSMutableDictionary *param = [NSMutableDictionary dictionary];
if (userID.length > 0) {
param[TUICore_TUIChatExtension_InputViewMoreItem_UserID] = userID;
} else if (groupID.length > 0) {
param[TUICore_TUIChatExtension_InputViewMoreItem_GroupID] = groupID;
}
param[TUICore_TUIChatExtension_InputViewMoreItem_FilterVideoCall] = @(!TUIChatConfig.defaultConfig.enableVideoCall);
param[TUICore_TUIChatExtension_InputViewMoreItem_FilterAudioCall] = @(!TUIChatConfig.defaultConfig.enableAudioCall);
if (pushVC) {
param[TUICore_TUIChatExtension_InputViewMoreItem_PushVC] = pushVC;
}
param[TUICore_TUIChatExtension_InputViewMoreItem_ActionVC] = actionController;
NSArray *extensionList = [TUICore getExtensionList:TUICore_TUIChatExtension_InputViewMoreItem_MinimalistExtensionID param:param];
for (TUIExtensionInfo *info in extensionList) {
if (info.icon && info.text && info.onClicked) {
TUICustomActionSheetItem *item = [[TUICustomActionSheetItem alloc] initWithTitle:info.text
leftMark:info.icon
withActionHandler:^(UIAlertAction *_Nonnull action) {
info.onClicked(param);
}];
item.priority = info.weight;
[items addObject:item];
}
}
if (items.count > 0) {
[result addObjectsFromArray:items];
}
// Sort with priority
NSArray *sorted = [result sortedArrayUsingComparator:^NSComparisonResult(TUICustomActionSheetItem *obj1, TUICustomActionSheetItem *obj2) {
return obj1.priority > obj2.priority ? NSOrderedAscending : NSOrderedDescending;
}];
return sorted;
}
#pragma mark - Private
- (void)onChangeLanguage {
self.customInputMoreActionItemList = nil;
self.builtInInputMoreActionItemList = nil;
}
#pragma mark -- Classic
- (NSArray<TUIInputMoreCellData *> *)createBuiltInInputMoreMenusWithConversationModel:(TUIChatConversationModel *)conversationModel {
BOOL isNeedRecordVideo = [TUIChatConfig defaultConfig].showRecordVideoButton && conversationModel.enableRecordVideo;
BOOL isNeedTakePhoto = [TUIChatConfig defaultConfig].showTakePhotoButton && conversationModel.enableTakePhoto;
BOOL isNeedAlbum = [TUIChatConfig defaultConfig].showAlbumButton && conversationModel.enableAlbum;
BOOL isNeedFile = [TUIChatConfig defaultConfig].showFileButton && conversationModel.enableFile;
__weak typeof(self) weakSelf = self;
TUIInputMoreCellData *albumData = [[TUIInputMoreCellData alloc] init];
albumData.priority = 1000;
albumData.title = TIMCommonLocalizableString(TUIKitMorePhoto);
albumData.image = TUIChatBundleThemeImage(@"chat_more_picture_img", @"more_picture");
albumData.onClicked = ^(NSDictionary *actionParam) {
if ([weakSelf.delegate respondsToSelector:@selector(onSelectPhotoMoreCellData)]) {
[weakSelf.delegate onSelectPhotoMoreCellData];
}
};
TUIInputMoreCellData *takePictureData = [[TUIInputMoreCellData alloc] init];
takePictureData.priority = 900;
takePictureData.title = TIMCommonLocalizableString(TUIKitMoreCamera);
takePictureData.image = TUIChatBundleThemeImage(@"chat_more_camera_img", @"more_camera");
takePictureData.onClicked = ^(NSDictionary *actionParam) {
if ([weakSelf.delegate respondsToSelector:@selector(onTakePictureMoreCellData)]) {
[weakSelf.delegate onTakePictureMoreCellData];
}
};
TUIInputMoreCellData *videoData = [[TUIInputMoreCellData alloc] init];
videoData.priority = 800;
videoData.title = TIMCommonLocalizableString(TUIKitMoreVideo);
videoData.image = TUIChatBundleThemeImage(@"chat_more_video_img", @"more_video");
videoData.onClicked = ^(NSDictionary *actionParam) {
if ([weakSelf.delegate respondsToSelector:@selector(onTakeVideoMoreCellData)]) {
[weakSelf.delegate onTakeVideoMoreCellData];
}
};
TUIInputMoreCellData *fileData = [[TUIInputMoreCellData alloc] init];
fileData.priority = 700;
fileData.title = TIMCommonLocalizableString(TUIKitMoreFile);
fileData.image = TUIChatBundleThemeImage(@"chat_more_file_img", @"more_file");
fileData.onClicked = ^(NSDictionary *actionParam) {
if ([weakSelf.delegate respondsToSelector:@selector(onSelectFileMoreCellData)]) {
[weakSelf.delegate onSelectFileMoreCellData];
}
};
NSMutableArray *formatArray = [NSMutableArray array];
if (isNeedAlbum) {
[formatArray addObject:albumData];
}
if (isNeedTakePhoto) {
[formatArray addObject:takePictureData];
}
if (isNeedRecordVideo) {
[formatArray addObject:videoData];
}
if (isNeedFile) {
[formatArray addObject:fileData];
}
_builtInInputMoreMenus = [NSArray arrayWithArray:formatArray];
return _builtInInputMoreMenus;
}
#pragma mark -- Minimalist
- (NSArray<TUICustomActionSheetItem *> *)createBuiltInInputMoreActionItemList:(TUIChatConversationModel *)model {
if (_builtInInputMoreActionItemList == nil) {
self.builtInInputMoreActionItemList = [NSMutableArray array];
BOOL showTakePhoto = [TUIChatConfig defaultConfig].showTakePhotoButton && model.enableTakePhoto;
BOOL showAlbum = [TUIChatConfig defaultConfig].showAlbumButton && model.enableAlbum;
BOOL showRecordVideo = [TUIChatConfig defaultConfig].showRecordVideoButton && model.enableRecordVideo;
BOOL showFile = [TUIChatConfig defaultConfig].showFileButton && model.enableFile;
__weak typeof(self) weakSelf = self;
if (showAlbum) {
TUICustomActionSheetItem *album =
[[TUICustomActionSheetItem alloc] initWithTitle:TIMCommonLocalizableString(TUIKitMorePhoto)
leftMark:[UIImage imageNamed:TUIChatImagePath_Minimalist(@"icon_more_photo")]
withActionHandler:^(UIAlertAction *_Nonnull action) {
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(onSelectPhotoMoreCellData)]) {
[weakSelf.delegate onSelectPhotoMoreCellData];
}
}];
album.priority = 1000;
[self.builtInInputMoreActionItemList addObject:album];
}
if (showTakePhoto) {
TUICustomActionSheetItem *takePhoto =
[[TUICustomActionSheetItem alloc] initWithTitle:TIMCommonLocalizableString(TUIKitMoreCamera)
leftMark:[UIImage imageNamed:TUIChatImagePath_Minimalist(@"icon_more_camera")]
withActionHandler:^(UIAlertAction *_Nonnull action) {
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(onTakePictureMoreCellData)]) {
[weakSelf.delegate onTakePictureMoreCellData];
}
}];
takePhoto.priority = 900;
[self.builtInInputMoreActionItemList addObject:takePhoto];
}
if (showRecordVideo) {
TUICustomActionSheetItem *recordVideo =
[[TUICustomActionSheetItem alloc] initWithTitle:TIMCommonLocalizableString(TUIKitMoreVideo)
leftMark:[UIImage imageNamed:TUIChatImagePath_Minimalist(@"icon_more_video")]
withActionHandler:^(UIAlertAction *_Nonnull action) {
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(onTakeVideoMoreCellData)]) {
[weakSelf.delegate onTakeVideoMoreCellData];
}
}];
recordVideo.priority = 800;
[self.builtInInputMoreActionItemList addObject:recordVideo];
}
if (showFile) {
TUICustomActionSheetItem *file =
[[TUICustomActionSheetItem alloc] initWithTitle:TIMCommonLocalizableString(TUIKitMoreFile)
leftMark:[UIImage imageNamed:TUIChatImagePath_Minimalist(@"icon_more_document")]
withActionHandler:^(UIAlertAction *_Nonnull action) {
if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(onSelectFileMoreCellData)]) {
[weakSelf.delegate onSelectFileMoreCellData];
}
}];
file.priority = 700;
[self.builtInInputMoreActionItemList addObject:file];
}
}
return self.builtInInputMoreActionItemList;
}
- (NSArray<TUICustomActionSheetItem *> *)createCustomInputMoreActionItemList:(TUIChatConversationModel *)model {
if (_customInputMoreActionItemList == nil) {
NSMutableArray *arrayM = [NSMutableArray array];
BOOL showCustom = [TUIChatConfig defaultConfig].enableWelcomeCustomMessage && model.enableWelcomeCustomMessage;
if (showCustom) {
__weak typeof(self) weakSelf = self;
TUICustomActionSheetItem *link =
[[TUICustomActionSheetItem alloc] initWithTitle:TIMCommonLocalizableString(TUIKitMoreLink)
leftMark:[UIImage imageNamed:TUIChatImagePath_Minimalist(@"icon_more_custom")]
withActionHandler:^(UIAlertAction *_Nonnull action) {
link.priority = 100;
NSString *text = TIMCommonLocalizableString(TUIKitWelcome);
NSString *link = TUITencentCloudHomePageEN;
NSString *language = [TUIGlobalization tk_localizableLanguageKey];
if ([language tui_containsString:@"zh-"]) {
link = TUITencentCloudHomePageCN;
}
NSError *error = nil;
NSDictionary *param = @{BussinessID : BussinessID_TextLink, @"text" : text, @"link" : link};
NSData *data = [NSJSONSerialization dataWithJSONObject:param options:0 error:&error];
if (error) {
NSLog(@"[%@] Post Json Error", [self class]);
return;
}
V2TIMMessage *message = [TUIMessageDataProvider getCustomMessageWithJsonData:data desc:text extension:text];
if ([weakSelf.delegate respondsToSelector:@selector(dataProvider:sendMessage:)]) {
[weakSelf.delegate dataProvider:weakSelf sendMessage:message];
}
}];
[arrayM addObject:link];
}
if (model.customizedNewItemsInMoreMenu.count > 0) {
[arrayM addObjectsFromArray:model.customizedNewItemsInMoreMenu];
}
_customInputMoreActionItemList = [NSArray arrayWithArray:arrayM];
}
return _customInputMoreActionItemList;
}
#pragma mark -- Override
- (NSString *)abstractDisplayWithMessage:(V2TIMMessage *)msg {
NSString *desc = @"";
if (msg.nickName.length > 0) {
desc = msg.nickName;
} else if (msg.sender.length > 0) {
desc = msg.sender;
}
NSString *display = [self.delegate dataProvider:self mergeForwardMsgAbstactForMessage:msg];
if (display.length == 0) {
display = [self.class parseAbstractDisplayWStringFromMessageElement:msg];
}
NSString * splitStr = @":";
splitStr = @"\u202C:";
NSString *nameFormat = [desc stringByAppendingFormat:@"%@", splitStr];
return [self.class alignEmojiStringWithUserName:nameFormat
text:display];
}
+ (nullable NSString *)parseAbstractDisplayWStringFromMessageElement:(V2TIMMessage *)message {
NSString *str = nil;
if (message.elemType == V2TIM_ELEM_TYPE_TEXT) {
NSString *content = message.textElem.text;
str = content;
}
else {
str = [TUIMessageDataProvider getDisplayString:message];
}
return str;
}
+ (NSString *)alignEmojiStringWithUserName:(NSString *)userName text:(NSString *)text {
NSArray *textList = [self.class splitEmojiText:text];
NSInteger forwardMsgLength = 98;
NSMutableString *sb = [NSMutableString string];
[sb appendString:userName];
NSInteger length = userName.length;
for (NSString *textItem in textList) {
BOOL isFaceChar = [self.class isFaceStrKey:textItem];
if (isFaceChar) {
if (length + textItem.length < forwardMsgLength) {
[sb appendString:textItem];
length += textItem.length;
} else {
[sb appendString:@"..."];
break;
}
} else {
if (length + textItem.length < forwardMsgLength) {
[sb appendString:textItem];
length += textItem.length;
} else {
[sb appendString:textItem];
break;
}
}
}
return sb;
}
+ (BOOL)isFaceStrKey:(NSString*) strkey {
id<TUIEmojiMeditorProtocol> service = [[TIMCommonMediator share] getObject:@protocol(TUIEmojiMeditorProtocol)];
NSArray <TUIFaceGroup *> * groups = service.getFaceGroup;
if ([groups.firstObject.facesMap objectForKey:strkey] != nil) {
return YES;
} else {
return NO;
}
}
+ (NSArray<NSString *> *)splitEmojiText:(NSString *)text {
NSString *regex = @"\\[(\\S+?)\\]";
NSRegularExpression *regexExp = [NSRegularExpression regularExpressionWithPattern:regex options:0 error:nil];
NSArray<NSTextCheckingResult *> *matches = [regexExp matchesInString:text options:0 range:NSMakeRange(0, text.length)];
NSMutableArray<TUISplitEmojiData *> *emojiDataList = [NSMutableArray array];
NSInteger lastMentionIndex = -1;
for (NSTextCheckingResult *match in matches) {
NSString *emojiKey = [text substringWithRange:match.range];
NSInteger start;
if (lastMentionIndex != -1) {
start = [text rangeOfString:emojiKey options:0 range:NSMakeRange(lastMentionIndex, text.length - lastMentionIndex)].location;
} else {
start = [text rangeOfString:emojiKey].location;
}
NSInteger end = start + emojiKey.length;
lastMentionIndex = end;
if (![self.class isFaceStrKey:emojiKey]) {
continue;
}
TUISplitEmojiData *emojiData = [[TUISplitEmojiData alloc] init];
emojiData.start = start;
emojiData.end = end;
[emojiDataList addObject:emojiData];
}
NSMutableArray<NSString *> *stringList = [NSMutableArray array];
NSInteger offset = 0;
for (TUISplitEmojiData *emojiData in emojiDataList) {
NSInteger start = emojiData.start - offset;
NSInteger end = emojiData.end - offset;
NSString *startStr = [text substringToIndex:start];
NSString *middleStr = [text substringWithRange:NSMakeRange(start, end - start)];
text = [text substringFromIndex:end];
if (startStr.length > 0) {
[stringList addObject:startStr];
}
[stringList addObject:middleStr];
offset += startStr.length + middleStr.length;
}
if (text.length > 0) {
[stringList addObject:text];
}
return stringList;
}
#pragma mark - Getter
- (TUIInputMoreCellData *)welcomeInputMoreMenu {
if (!_welcomeInputMoreMenu) {
__weak typeof(self) weakSelf = self;
_welcomeInputMoreMenu = [[TUIInputMoreCellData alloc] init];
_welcomeInputMoreMenu.priority = 0;
_welcomeInputMoreMenu.title = TIMCommonLocalizableString(TUIKitMoreLink);
_welcomeInputMoreMenu.image = TUIChatBundleThemeImage(@"chat_more_link_img", @"chat_more_link_img");
_welcomeInputMoreMenu.onClicked = ^(NSDictionary *actionParam) {
NSString *text = TIMCommonLocalizableString(TUIKitWelcome);
NSString *link = TUITencentCloudHomePageEN;
NSString *language = [TUIGlobalization tk_localizableLanguageKey];
if ([language tui_containsString:@"zh-"]) {
link = TUITencentCloudHomePageCN;
}
NSError *error = nil;
NSDictionary *param = @{BussinessID : BussinessID_TextLink, @"text" : text, @"link" : link};
NSData *data = [NSJSONSerialization dataWithJSONObject:param options:0 error:&error];
if (error) {
NSLog(@"[%@] Post Json Error", [weakSelf class]);
return;
}
V2TIMMessage *message = [TUIMessageDataProvider getCustomMessageWithJsonData:data desc:text extension:text];
if ([weakSelf.delegate respondsToSelector:@selector(dataProvider:sendMessage:)]) {
[weakSelf.delegate dataProvider:weakSelf sendMessage:message];
}
};
}
return _welcomeInputMoreMenu;
}
- (NSMutableArray<TUIInputMoreCellData *> *)customInputMoreMenus {
if (!_customInputMoreMenus) {
_customInputMoreMenus = [NSMutableArray array];
}
return _customInputMoreMenus;
}
- (NSArray<TUIInputMoreCellData *> *)builtInInputMoreMenus {
if (_builtInInputMoreMenus == nil) {
return [self createBuiltInInputMoreMenusWithConversationModel:nil];
}
return _builtInInputMoreMenus;
}
@end

View File

@@ -0,0 +1,25 @@
//
// TUIGroupNoticeDataProvider.h
// TUIGroup
//
// Created by harvy on 2022/1/12.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <ImSDK_Plus/ImSDK_Plus.h>
NS_ASSUME_NONNULL_BEGIN
@interface TUIGroupNoticeDataProvider : NSObject
@property(nonatomic, strong, readonly) V2TIMGroupInfo *groupInfo;
@property(nonatomic, copy) NSString *groupID;
- (void)getGroupInfo:(dispatch_block_t)callback;
- (BOOL)canEditNotice;
- (void)updateNotice:(NSString *)notice callback:(void (^)(int, NSString *))callback;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,76 @@
//
// TUIGroupNoticeDataProvider.m
// TUIGroup
//
// Created by harvy on 2022/1/12.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIGroupNoticeDataProvider.h"
@interface TUIGroupNoticeDataProvider ()
@property(nonatomic, strong) V2TIMGroupInfo *groupInfo;
@end
@implementation TUIGroupNoticeDataProvider
- (void)getGroupInfo:(dispatch_block_t)callback {
if (self.groupInfo && [self.groupInfo.groupID isEqual:self.groupID]) {
if (callback) {
callback();
}
return;
}
__weak typeof(self) weakSelf = self;
[V2TIMManager.sharedInstance getGroupsInfo:@[ self.groupID ?: @"" ]
succ:^(NSArray<V2TIMGroupInfoResult *> *groupResultList) {
V2TIMGroupInfoResult *result = groupResultList.firstObject;
if (result && result.resultCode == 0) {
weakSelf.groupInfo = result.info;
}
if (callback) {
callback();
}
}
fail:^(int code, NSString *desc) {
if (callback) {
callback();
}
}];
}
- (BOOL)canEditNotice {
return self.groupInfo.role == V2TIM_GROUP_MEMBER_ROLE_ADMIN || self.groupInfo.role == V2TIM_GROUP_MEMBER_ROLE_SUPER;
}
- (void)updateNotice:(NSString *)notice callback:(void (^)(int, NSString *))callback {
V2TIMGroupInfo *info = [[V2TIMGroupInfo alloc] init];
info.groupID = self.groupID;
info.notification = notice;
__weak typeof(self) weakSelf = self;
[V2TIMManager.sharedInstance setGroupInfo:info
succ:^{
if (callback) {
callback(0, nil);
}
[weakSelf sendNoticeMessage:notice];
}
fail:^(int code, NSString *desc) {
if (callback) {
callback(code, desc);
}
}];
}
- (void)sendNoticeMessage:(NSString *)notice {
if (notice.length == 0) {
return;
}
}
@end

View File

@@ -0,0 +1,18 @@
//
// TUIMessageDataProvider+MessageDeal.h
// TUIChat
//
// Created by wyl on 2022/3/22.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIMessageDataProvider.h"
#import "TUIReplyMessageCellData.h"
NS_ASSUME_NONNULL_BEGIN
@interface TUIMessageDataProvider (MessageDeal)
- (void)loadOriginMessageFromReplyData:(TUIReplyMessageCellData *)replycellData dealCallback:(void (^)(void))callback;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,69 @@
//
// TUIMessageDataProvider+MessageDeal.m
// TUIChat
//
// Created by wyl on 2022/3/22.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <TIMCommon/TUIMessageCellData.h>
#import "TUIChatDataProvider.h"
#import "TUIImageMessageCellData.h"
#import "TUIMessageDataProvider+MessageDeal.h"
#import "TUIMessageDataProvider.h"
@implementation TUIMessageDataProvider (MessageDeal)
- (void)loadOriginMessageFromReplyData:(TUIReplyMessageCellData *)replycellData dealCallback:(void (^)(void))callback {
if (replycellData.originMsgID.length == 0) {
if (callback) {
callback();
}
return;
}
@weakify(replycellData)[TUIChatDataProvider findMessages:@[ replycellData.originMsgID ]
callback:^(BOOL succ, NSString *_Nonnull error_message, NSArray *_Nonnull msgs) {
@strongify(replycellData) if (!succ) {
replycellData.quoteData = [replycellData getQuoteData:nil];
replycellData.originMessage = nil;
if (callback) {
callback();
}
return;
}
V2TIMMessage *originMessage = msgs.firstObject;
if (originMessage == nil) {
replycellData.quoteData = [replycellData getQuoteData:nil];
if (callback) {
callback();
}
return;
}
TUIMessageCellData *cellData = [TUIMessageDataProvider getCellData:originMessage];
replycellData.originCellData = cellData;
if ([cellData isKindOfClass:TUIImageMessageCellData.class]) {
TUIImageMessageCellData *imageData = (TUIImageMessageCellData *)cellData;
[imageData downloadImage:TImage_Type_Thumb];
replycellData.quoteData = [replycellData getQuoteData:imageData];
replycellData.originMessage = originMessage;
if (callback) {
callback();
}
} else if ([cellData isKindOfClass:TUIVideoMessageCellData.class]) {
TUIVideoMessageCellData *videoData = (TUIVideoMessageCellData *)cellData;
[videoData downloadThumb];
replycellData.quoteData = [replycellData getQuoteData:videoData];
replycellData.originMessage = originMessage;
if (callback) {
callback();
}
} else {
replycellData.quoteData = [replycellData getQuoteData:cellData];
replycellData.originMessage = originMessage;
if (callback) {
callback();
}
}
}];
}
@end

View File

@@ -0,0 +1,46 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import <Foundation/Foundation.h>
#import "TUIMessageBaseDataProvider.h"
NS_ASSUME_NONNULL_BEGIN
@class TUITextMessageCellData;
@class TUIFaceMessageCellData;
@class TUIImageMessageCellData;
@class TUIVoiceMessageCellData;
@class TUIVideoMessageCellData;
@class TUIFileMessageCellData;
@class TUISystemMessageCellData;
@class TUIChatCallingDataProvider;
@protocol TUIMessageDataProviderDataSource <TUIMessageBaseDataProviderDataSource>
+ (nullable Class)onGetCustomMessageCellDataClass:(NSString *)businessID;
@end
@interface TUIMessageDataProvider : TUIMessageBaseDataProvider
+ (void)setDataSourceClass:(Class<TUIMessageDataProviderDataSource>)dataSourceClass;
#pragma mark - TUIMessageCellData parser
+ (nullable TUIMessageCellData *)getCellData:(V2TIMMessage *)message;
#pragma mark - Last message parser
+ (void)asyncGetDisplayString:(NSArray<V2TIMMessage *> *)messageList callback:(void(^)(NSDictionary<NSString *, NSString *> *))callback;
+ (nullable NSString *)getDisplayString:(V2TIMMessage *)message;
#pragma mark - Data source operate
- (void)processQuoteMessage:(NSArray<TUIMessageCellData *> *)uiMsgs;
- (void)deleteUIMsgs:(NSArray<TUIMessageCellData *> *)uiMsgs SuccBlock:(nullable V2TIMSucc)succ FailBlock:(nullable V2TIMFail)fail;
- (void)removeUIMsgList:(NSArray<TUIMessageCellData *> *)cellDatas;
+ (TUIChatCallingDataProvider *)callingDataProvider;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,754 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import "TUIMessageDataProvider.h"
#import <AVFoundation/AVFoundation.h>
#import <TIMCommon/TUIRelationUserModel.h>
#import <TIMCommon/TUISystemMessageCellData.h>
#import <TUICore/TUILogin.h>
#import "TUIChatCallingDataProvider.h"
#import "TUICloudCustomDataTypeCenter.h"
#import "TUIFaceMessageCellData.h"
#import "TUIFileMessageCellData.h"
#import "TUIImageMessageCellData.h"
#import "TUIJoinGroupMessageCellData.h"
#import "TUIMergeMessageCellData.h"
#import "TUIMessageDataProvider+MessageDeal.h"
#import "TUIMessageProgressManager.h"
#import "TUIReplyMessageCellData.h"
#import "TUITextMessageCellData.h"
#import "TUIVideoMessageCellData.h"
#import "TUIVoiceMessageCellData.h"
/**
* The maximum editable time after the message is recalled, default is (2 * 60)
*/
#define MaxReEditMessageDelay 2 * 60
#define kIsCustomMessageFromPlugin @"kIsCustomMessageFromPlugin"
static Class<TUIMessageDataProviderDataSource> gDataSourceClass = nil;
@implementation TUIMessageDataProvider
#pragma mark - Life cycle
- (void)dealloc {
gCallingDataProvider = nil;
}
+ (void)setDataSourceClass:(Class<TUIMessageDataProviderDataSource>)dataSourceClass {
gDataSourceClass = dataSourceClass;
}
#pragma mark - TUIMessageCellData parser
+ (nullable TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
// 1 Parse cell data
TUIMessageCellData *data = [self parseMessageCellDataFromMessageStatus:message];
if (data == nil) {
data = [self parseMessageCellDataFromMessageCustomData:message];
}
if (data == nil) {
data = [self parseMessageCellDataFromMessageElement:message];
}
// 2 Fill in property if needed
if (data) {
[self fillPropertyToCellData:data ofMessage:message];
} else {
NSLog(@"current message will be ignored in chat page, msg:%@", message);
}
return data;
}
+ (nullable TUIMessageCellData *)parseMessageCellDataFromMessageStatus:(V2TIMMessage *)message {
TUIMessageCellData *data = nil;
if (message.status == V2TIM_MSG_STATUS_LOCAL_REVOKED) {
data = [TUIMessageDataProvider getRevokeCellData:message];
}
return data;
}
+ (nullable TUIMessageCellData *)parseMessageCellDataFromMessageCustomData:(V2TIMMessage *)message {
TUIMessageCellData *data = nil;
if ([message isContainsCloudCustomOfDataType:TUICloudCustomDataType_MessageReply]) {
/**
* Determine whether to include "reply-message"
*/
data = [TUIReplyMessageCellData getCellData:message];
} else if ([message isContainsCloudCustomOfDataType:TUICloudCustomDataType_MessageReference]) {
/**
* Determine whether to include "quote-message"
*/
data = [TUIReferenceMessageCellData getCellData:message];
}
return data;
}
+ (nullable TUIMessageCellData *)parseMessageCellDataFromMessageElement:(V2TIMMessage *)message {
TUIMessageCellData *data = nil;
switch (message.elemType) {
case V2TIM_ELEM_TYPE_TEXT: {
data = [TUITextMessageCellData getCellData:message];
} break;
case V2TIM_ELEM_TYPE_IMAGE: {
data = [TUIImageMessageCellData getCellData:message];
} break;
case V2TIM_ELEM_TYPE_SOUND: {
data = [TUIVoiceMessageCellData getCellData:message];
} break;
case V2TIM_ELEM_TYPE_VIDEO: {
data = [TUIVideoMessageCellData getCellData:message];
} break;
case V2TIM_ELEM_TYPE_FILE: {
data = [TUIFileMessageCellData getCellData:message];
} break;
case V2TIM_ELEM_TYPE_FACE: {
data = [TUIFaceMessageCellData getCellData:message];
} break;
case V2TIM_ELEM_TYPE_GROUP_TIPS: {
data = [self getSystemCellData:message];
} break;
case V2TIM_ELEM_TYPE_MERGER: {
data = [TUIMergeMessageCellData getCellData:message];
} break;
case V2TIM_ELEM_TYPE_CUSTOM: {
data = [self getCustomMessageCellData:message];
} break;
default:
data = [self getUnsupportedCellData:message];
break;
}
return data;
}
+ (void)fillPropertyToCellData:(TUIMessageCellData *)data ofMessage:(V2TIMMessage *)message {
data.innerMessage = message;
if (message.groupID.length > 0 && !message.isSelf && ![data isKindOfClass:[TUISystemMessageCellData class]]) {
data.showName = YES;
}
switch (message.status) {
case V2TIM_MSG_STATUS_SEND_SUCC:
data.status = Msg_Status_Succ;
break;
case V2TIM_MSG_STATUS_SEND_FAIL:
data.status = Msg_Status_Fail;
break;
case V2TIM_MSG_STATUS_SENDING:
data.status = Msg_Status_Sending_2;
break;
default:
break;
}
/**
* Update progress of message uploading/downloading
*/
{
NSInteger uploadProgress = [TUIMessageProgressManager.shareManager uploadProgressForMessage:message.msgID];
NSInteger downloadProgress = [TUIMessageProgressManager.shareManager downloadProgressForMessage:message.msgID];
if ([data conformsToProtocol:@protocol(TUIMessageCellDataFileUploadProtocol)]) {
((id<TUIMessageCellDataFileUploadProtocol>)data).uploadProgress = uploadProgress;
}
if ([data conformsToProtocol:@protocol(TUIMessageCellDataFileDownloadProtocol)]) {
((id<TUIMessageCellDataFileDownloadProtocol>)data).downladProgress = downloadProgress;
((id<TUIMessageCellDataFileDownloadProtocol>)data).isDownloading = (downloadProgress != 0) && (downloadProgress != 100);
}
}
/**
* Determine whether to include "replies-message"
*/
if ([message isContainsCloudCustomOfDataType:TUICloudCustomDataType_MessageReplies]) {
[message doThingsInContainsCloudCustomOfDataType:TUICloudCustomDataType_MessageReplies
callback:^(BOOL isContains, id obj) {
if (isContains) {
if ([data isKindOfClass:TUISystemMessageCellData.class] ||
[data isKindOfClass:TUIJoinGroupMessageCellData.class]) {
data.showMessageModifyReplies = NO;
} else {
data.showMessageModifyReplies = YES;
}
if (obj && [obj isKindOfClass:NSDictionary.class]) {
NSDictionary *dic = (NSDictionary *)obj;
NSString *typeStr =
[TUICloudCustomDataTypeCenter convertType2String:TUICloudCustomDataType_MessageReplies];
NSDictionary *messageReplies = [dic valueForKey:typeStr];
NSArray *repliesArr = [messageReplies valueForKey:@"replies"];
if ([repliesArr isKindOfClass:NSArray.class]) {
data.messageModifyReplies = repliesArr.copy;
}
}
}
}];
}
}
+ (nullable TUIMessageCellData *)getCustomMessageCellData:(V2TIMMessage *)message {
// ************************************************************************************
// ************************************************************************************
// **The compatible processing logic of TUICallKit will be removed after***************
// **TUICallKit is connected according to the standard process. ***********************
// ************************************************************************************
// ************************************************************************************
TUIMessageCellData *data = nil;
id<TUIChatCallingInfoProtocol> callingInfo = nil;
if ([self.callingDataProvider isCallingMessage:message callingInfo:&callingInfo]) {
// Voice-video-call message
if (callingInfo) {
// Supported voice-video-call message
if (callingInfo.excludeFromHistory) {
// This message will be ignore in chat page
data = nil;
} else {
data = [self getCallingCellData:callingInfo];
if (data == nil) {
data = [self getUnsupportedCellData:message];
}
}
} else {
// Unsupported voice-video-call message
data = [self getUnsupportedCellData:message];
}
return data;
}
// ************************************************************************************
// ************************************************************************************
// ************************************************************************************
// ************************************************************************************
NSString *businessID = nil;
BOOL excludeFromHistory = NO;
V2TIMSignalingInfo *signalingInfo = [V2TIMManager.sharedInstance getSignallingInfo:message];
if (signalingInfo) {
// This message is signaling message
BOOL isOnlineOnly = NO;
@try {
isOnlineOnly = [[message valueForKeyPath:@"message.IsOnlineOnly"] boolValue];
} @catch (NSException *exception) {
isOnlineOnly = NO;
}
excludeFromHistory = isOnlineOnly || (message.isExcludedFromLastMessage && message.isExcludedFromUnreadCount);
businessID = [self getSignalingBusinessID:signalingInfo];
} else {
// This message is normal custom message
excludeFromHistory = NO;
businessID = [self getCustomBusinessID:message];
}
if (excludeFromHistory) {
// Return nil means not display in the chat page
return nil;
}
if (businessID.length > 0) {
Class cellDataClass = nil;
if (gDataSourceClass && [gDataSourceClass respondsToSelector:@selector(onGetCustomMessageCellDataClass:)]) {
cellDataClass = [gDataSourceClass onGetCustomMessageCellDataClass:businessID];
}
if (cellDataClass && [cellDataClass respondsToSelector:@selector(getCellData:)]) {
TUIMessageCellData *data = [cellDataClass getCellData:message];
if (data.shouldHide) {
return nil;
} else {
data.reuseId = businessID;
return data;
}
}
// In CustomerService scenarios, unsupported messages are not displayed directly.
if ([businessID tui_containsString:BussinessID_CustomerService]) {
return nil;
}
return [self getUnsupportedCellData:message];
} else {
return [self getUnsupportedCellData:message];
}
}
+ (TUIMessageCellData *)getUnsupportedCellData:(V2TIMMessage *)message {
TUITextMessageCellData *cellData = [[TUITextMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
cellData.content = TIMCommonLocalizableString(TUIKitNotSupportThisMessage);
cellData.reuseId = TTextMessageCell_ReuseId;
return cellData;
}
+ (nullable TUISystemMessageCellData *)getSystemCellData:(V2TIMMessage *)message {
V2TIMGroupTipsElem *tip = message.groupTipsElem;
NSString *opUserName = [self getOpUserName:tip.opMember];
NSMutableArray<NSString *> *userNameList = [self getUserNameList:tip.memberList];
NSMutableArray<NSString *> *userIDList = [self getUserIDList:tip.memberList];
if (tip.type == V2TIM_GROUP_TIPS_TYPE_JOIN || tip.type == V2TIM_GROUP_TIPS_TYPE_INVITE || tip.type == V2TIM_GROUP_TIPS_TYPE_KICKED ||
tip.type == V2TIM_GROUP_TIPS_TYPE_GROUP_INFO_CHANGE || tip.type == V2TIM_GROUP_TIPS_TYPE_QUIT ||
tip.type == V2TIM_GROUP_TIPS_TYPE_PINNED_MESSAGE_ADDED || tip.type == V2TIM_GROUP_TIPS_TYPE_PINNED_MESSAGE_DELETED) {
TUIJoinGroupMessageCellData *joinGroupData = [[TUIJoinGroupMessageCellData alloc] initWithDirection:MsgDirectionIncoming];
joinGroupData.content = [self getDisplayString:message];
joinGroupData.opUserName = opUserName;
joinGroupData.opUserID = tip.opMember.userID;
joinGroupData.userNameList = userNameList;
joinGroupData.userIDList = userIDList;
joinGroupData.reuseId = TJoinGroupMessageCell_ReuseId;
return joinGroupData;
} else {
TUISystemMessageCellData *sysdata = [[TUISystemMessageCellData alloc] initWithDirection:MsgDirectionIncoming];
sysdata.content = [self getDisplayString:message];
sysdata.reuseId = TSystemMessageCell_ReuseId;
if (sysdata.content.length) {
return sysdata;
}
}
return nil;
}
+ (nullable TUISystemMessageCellData *)getRevokeCellData:(V2TIMMessage *)message {
TUISystemMessageCellData *revoke = [[TUISystemMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
revoke.reuseId = TSystemMessageCell_ReuseId;
revoke.content = [self getRevokeDispayString:message];
revoke.innerMessage = message;
V2TIMUserFullInfo *revokerInfo = message.revokerInfo;
if (message.isSelf) {
if (message.elemType == V2TIM_ELEM_TYPE_TEXT && fabs([[NSDate date] timeIntervalSinceDate:message.timestamp]) < MaxReEditMessageDelay) {
if (revokerInfo && ![revokerInfo.userID isEqualToString:message.sender]) {
// Super User revoke
revoke.supportReEdit = NO;
} else {
revoke.supportReEdit = YES;
}
}
} else if (message.groupID.length > 0) {
/**
* For the name display of group messages, the group business card is displayed first, the nickname has the second priority, and the user ID has the
* lowest priority.
*/
NSString *userName = [TUIMessageDataProvider getShowName:message];
TUIJoinGroupMessageCellData *joinGroupData = [[TUIJoinGroupMessageCellData alloc] initWithDirection:MsgDirectionIncoming];
joinGroupData.content = [self getRevokeDispayString:message];
joinGroupData.opUserID = message.sender;
joinGroupData.opUserName = userName;
joinGroupData.reuseId = TJoinGroupMessageCell_ReuseId;
return joinGroupData;
}
return revoke;
}
+ (nullable TUISystemMessageCellData *)getSystemMsgFromDate:(NSDate *)date {
TUISystemMessageCellData *system = [[TUISystemMessageCellData alloc] initWithDirection:MsgDirectionOutgoing];
system.content = [TUITool convertDateToStr:date];
system.reuseId = TSystemMessageCell_ReuseId;
system.type = TUISystemMessageTypeDate;
return system;
}
#pragma mark - Last message parser
+ (void)asyncGetDisplayString:(NSArray<V2TIMMessage *> *)messageList callback:(void (^)(NSDictionary<NSString *, NSString *> *))callback {
if (!callback) {
return;
}
NSMutableDictionary *originDisplayMap = [NSMutableDictionary dictionary];
NSMutableArray *cellDataList = [NSMutableArray array];
for (V2TIMMessage *message in messageList) {
TUIMessageCellData *cellData = [self getCellData:message];
if (cellData) {
[cellDataList addObject:cellData];
}
NSString *displayString = [self getDisplayString:message];
if (displayString && message.msgID) {
originDisplayMap[message.msgID] = displayString;
}
}
if (cellDataList.count == 0) {
callback(@{});
return;
}
TUIMessageDataProvider *provider = [[TUIMessageDataProvider alloc] init];
NSArray *additionUserIDList = [provider getUserIDListForAdditionalUserInfo:cellDataList];
if (additionUserIDList.count == 0) {
callback(@{});
return;
}
NSMutableDictionary *result = [NSMutableDictionary dictionary];
[provider
requestForAdditionalUserInfo:cellDataList
callback:^{
for (TUIMessageCellData *cellData in cellDataList) {
[cellData.additionalUserInfoResult
enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull key, TUIRelationUserModel *_Nonnull obj, BOOL *_Nonnull stop) {
NSString *str = [NSString stringWithFormat:@"{%@}", key];
NSString *showName = obj.userID;
if (obj.nameCard.length > 0) {
showName = obj.nameCard;
} else if (obj.friendRemark.length > 0) {
showName = obj.friendRemark;
} else if (obj.nickName.length > 0) {
showName = obj.nickName;
}
NSString *displayString = [originDisplayMap objectForKey:cellData.msgID];
if (displayString && [displayString containsString:str]) {
displayString = [displayString stringByReplacingOccurrencesOfString:str withString:showName];
result[cellData.msgID] = displayString;
}
callback(result);
}];
}
}];
}
+ (nullable NSString *)getDisplayString:(V2TIMMessage *)message {
BOOL hasRiskContent = message.hasRiskContent;
BOOL isRevoked = (message.status == V2TIM_MSG_STATUS_LOCAL_REVOKED);
if (hasRiskContent && !isRevoked) {
return TIMCommonLocalizableString(TUIKitMessageDisplayRiskContent);
}
NSString *str = [self parseDisplayStringFromMessageStatus:message];
if (str == nil) {
str = [self parseDisplayStringFromMessageElement:message];
}
if (str == nil) {
NSLog(@"current message will be ignored in chat page or conversation list page, msg:%@", message);
}
return str;
}
+ (nullable NSString *)parseDisplayStringFromMessageStatus:(V2TIMMessage *)message {
NSString *str = nil;
if (message.status == V2TIM_MSG_STATUS_LOCAL_REVOKED) {
str = [self getRevokeDispayString:message];
}
return str;
}
+ (nullable NSString *)parseDisplayStringFromMessageElement:(V2TIMMessage *)message {
NSString *str = nil;
switch (message.elemType) {
case V2TIM_ELEM_TYPE_TEXT: {
str = [TUITextMessageCellData getDisplayString:message];
} break;
case V2TIM_ELEM_TYPE_IMAGE: {
str = [TUIImageMessageCellData getDisplayString:message];
} break;
case V2TIM_ELEM_TYPE_SOUND: {
str = [TUIVoiceMessageCellData getDisplayString:message];
} break;
case V2TIM_ELEM_TYPE_VIDEO: {
str = [TUIVideoMessageCellData getDisplayString:message];
} break;
case V2TIM_ELEM_TYPE_FILE: {
str = [TUIFileMessageCellData getDisplayString:message];
} break;
case V2TIM_ELEM_TYPE_FACE: {
str = [TUIFaceMessageCellData getDisplayString:message];
} break;
case V2TIM_ELEM_TYPE_MERGER: {
str = [TUIMergeMessageCellData getDisplayString:message];
} break;
case V2TIM_ELEM_TYPE_GROUP_TIPS: {
str = [self getGroupTipsDisplayString:message];
} break;
case V2TIM_ELEM_TYPE_CUSTOM: {
str = [self getCustomDisplayString:message];
} break;
default:
str = TIMCommonLocalizableString(TUIKitMessageTipsUnsupportCustomMessage);
break;
}
return str;
}
+ (nullable NSString *)getCustomDisplayString:(V2TIMMessage *)message {
// ************************************************************************************
// ************************************************************************************
// ************** TUICallKit TUICallKit *************
// ************************************************************************************
// ************************************************************************************
NSString *str = nil;
id<TUIChatCallingInfoProtocol> callingInfo = nil;
if ([self.callingDataProvider isCallingMessage:message callingInfo:&callingInfo]) {
// Voice-video-call message
if (callingInfo) {
// Supported voice-video-call message
if (callingInfo.excludeFromHistory) {
// This message will be ignore in chat page
str = nil;
} else {
// Get display text
str = callingInfo.content ?: TIMCommonLocalizableString(TUIKitMessageTipsUnsupportCustomMessage);
}
} else {
// Unsupported voice-video-call message
str = TIMCommonLocalizableString(TUIKitMessageTipsUnsupportCustomMessage);
}
return str;
}
// ************************************************************************************
// ************************************************************************************
// ************************************************************************************
// ************************************************************************************
NSString *businessID = nil;
BOOL excludeFromHistory = NO;
V2TIMSignalingInfo *signalingInfo = [V2TIMManager.sharedInstance getSignallingInfo:message];
if (signalingInfo) {
// This message is signaling message
excludeFromHistory = message.isExcludedFromLastMessage && message.isExcludedFromUnreadCount;
businessID = [self getSignalingBusinessID:signalingInfo];
} else {
// This message is normal custom message
excludeFromHistory = NO;
businessID = [self getCustomBusinessID:message];
}
if (excludeFromHistory) {
// Return nil means not display int the chat page
return nil;
}
if (businessID.length > 0) {
Class cellDataClass = nil;
if (gDataSourceClass && [gDataSourceClass respondsToSelector:@selector(onGetCustomMessageCellDataClass:)]) {
cellDataClass = [gDataSourceClass onGetCustomMessageCellDataClass:businessID];
}
if (cellDataClass && [cellDataClass respondsToSelector:@selector(getDisplayString:)]) {
return [cellDataClass getDisplayString:message];
}
// In CustomerService scenarios, unsupported messages are not displayed directly.
if ([businessID tui_containsString:BussinessID_CustomerService]) {
return nil;
}
return TIMCommonLocalizableString(TUIKitMessageTipsUnsupportCustomMessage);
} else {
return TIMCommonLocalizableString(TUIKitMessageTipsUnsupportCustomMessage);
}
}
#pragma mark - Data source operate
- (void)processQuoteMessage:(NSArray<TUIMessageCellData *> *)uiMsgs {
if (uiMsgs.count == 0) {
return;
}
@weakify(self);
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, concurrentQueue, ^{
for (TUIMessageCellData *cellData in uiMsgs) {
if (![cellData isKindOfClass:TUIReplyMessageCellData.class]) {
continue;
}
TUIReplyMessageCellData *myData = (TUIReplyMessageCellData *)cellData;
__weak typeof(myData) weakMyData = myData;
myData.onFinish = ^{
dispatch_async(dispatch_get_main_queue(), ^{
NSUInteger index = [self.uiMsgs indexOfObject:weakMyData];
if (index != NSNotFound) {
// if messageData exist In datasource, reload this data.
[UIView performWithoutAnimation:^{
@strongify(self);
[self.dataSource dataProviderDataSourceWillChange:self];
[self.dataSource dataProviderDataSourceChange:self
withType:TUIMessageBaseDataProviderDataSourceChangeTypeReload
atIndex:index
animation:NO];
[self.dataSource dataProviderDataSourceDidChange:self];
}];
}
});
};
dispatch_group_enter(group);
[self loadOriginMessageFromReplyData:myData
dealCallback:^{
dispatch_group_leave(group);
dispatch_async(dispatch_get_main_queue(), ^{
NSUInteger index = [self.uiMsgs indexOfObject:weakMyData];
if (index != NSNotFound) {
// if messageData exist In datasource, reload this data.
[UIView performWithoutAnimation:^{
@strongify(self);
[self.dataSource dataProviderDataSourceWillChange:self];
[self.dataSource dataProvider:self onRemoveHeightCache:weakMyData];
[self.dataSource dataProviderDataSourceChange:self
withType:TUIMessageBaseDataProviderDataSourceChangeTypeReload
atIndex:index
animation:NO];
[self.dataSource dataProviderDataSourceDidChange:self];
}];
}
});
}];
}
});
dispatch_group_notify(group, dispatch_get_main_queue(),
^{
// complete
});
}
- (void)deleteUIMsgs:(NSArray<TUIMessageCellData *> *)uiMsgs SuccBlock:(nullable V2TIMSucc)succ FailBlock:(nullable V2TIMFail)fail {
NSMutableArray *uiMsgList = [NSMutableArray array];
NSMutableArray *imMsgList = [NSMutableArray array];
for (TUIMessageCellData *uiMsg in uiMsgs) {
if ([self.uiMsgs containsObject:uiMsg]) {
// Check content cell
[uiMsgList addObject:uiMsg];
[imMsgList addObject:uiMsg.innerMessage];
// Check time cell which also need to be deleted
NSInteger index = [self.uiMsgs indexOfObject:uiMsg];
index--;
if (index >= 0 && index < self.uiMsgs.count && [[self.uiMsgs objectAtIndex:index] isKindOfClass:TUISystemMessageCellData.class]) {
TUISystemMessageCellData *systemCellData = (TUISystemMessageCellData *)[self.uiMsgs objectAtIndex:index];
if (systemCellData.type == TUISystemMessageTypeDate) {
[uiMsgList addObject:systemCellData];
}
}
}
}
if (imMsgList.count == 0) {
if (fail) {
fail(ERR_INVALID_PARAMETERS, @"not found uiMsgs");
}
return;
}
@weakify(self);
[self.class deleteMessages:imMsgList
succ:^{
@strongify(self);
[self.dataSource dataProviderDataSourceWillChange:self];
for (TUIMessageCellData *uiMsg in uiMsgList) {
NSInteger index = [self.uiMsgs indexOfObject:uiMsg];
[self.dataSource dataProviderDataSourceChange:self
withType:TUIMessageBaseDataProviderDataSourceChangeTypeDelete
atIndex:index
animation:YES];
}
[self removeUIMsgList:uiMsgList];
[self.dataSource dataProviderDataSourceDidChange:self];
if (succ) {
succ();
}
}
fail:fail];
}
- (void)removeUIMsgList:(NSArray<TUIMessageCellData *> *)cellDatas {
for (TUIMessageCellData *uiMsg in cellDatas) {
[self removeUIMsg:uiMsg];
}
}
#pragma mark - Utils
+ (nullable NSString *)getCustomBusinessID:(V2TIMMessage *)message {
if (message == nil || message.customElem.data == nil) {
return nil;
}
NSError *error = nil;
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:&error];
if (error) {
NSLog(@"parse customElem data error: %@", error);
return nil;
}
if (!param || ![param isKindOfClass:[NSDictionary class]]) {
return nil;
}
NSString *businessID = param[BussinessID];
if ([businessID isKindOfClass:[NSString class]] && businessID.length > 0) {
return businessID;
} else {
if ([param.allKeys containsObject:BussinessID_CustomerService]) {
NSString *src = param[BussinessID_Src_CustomerService];
if (src.length > 0 && [src isKindOfClass:[NSString class]]) {
return [NSString stringWithFormat:@"%@%@", BussinessID_CustomerService, src];
}
}
return nil;
}
}
+ (nullable NSString *)getSignalingBusinessID:(V2TIMSignalingInfo *)signalInfo {
if (signalInfo.data == nil) {
return nil;
}
NSError *error = nil;
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:[signalInfo.data dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingAllowFragments
error:&error];
if (error) {
NSLog(@"parse customElem data error: %@", error);
return nil;
}
if (!param || ![param isKindOfClass:[NSDictionary class]]) {
return nil;
}
NSString *businessID = param[BussinessID];
if (!businessID || ![businessID isKindOfClass:[NSString class]]) {
return nil;
}
return businessID;
}
#pragma mark - TUICallKit
static TUIChatCallingDataProvider *gCallingDataProvider;
+ (TUIChatCallingDataProvider *)callingDataProvider {
if (gCallingDataProvider == nil) {
gCallingDataProvider = [[TUIChatCallingDataProvider alloc] init];
}
return gCallingDataProvider;
}
+ (TUIMessageCellData *)getCallingCellData:(id<TUIChatCallingInfoProtocol>)callingInfo {
TMsgDirection direction = MsgDirectionIncoming;
if (callingInfo.direction == TUICallMessageDirectionIncoming) {
direction = MsgDirectionIncoming;
} else if (callingInfo.direction == TUICallMessageDirectionOutgoing) {
direction = MsgDirectionOutgoing;
}
if (callingInfo.participantType == TUICallParticipantTypeC2C) {
TUITextMessageCellData *cellData = [[TUITextMessageCellData alloc] initWithDirection:direction];
if (callingInfo.streamMediaType == TUICallStreamMediaTypeVoice) {
cellData.isAudioCall = YES;
} else if (callingInfo.streamMediaType == TUICallStreamMediaTypeVideo) {
cellData.isVideoCall = YES;
} else {
cellData.isAudioCall = NO;
cellData.isVideoCall = NO;
}
cellData.content = callingInfo.content;
cellData.isCaller = (callingInfo.participantRole == TUICallParticipantRoleCaller);
cellData.showUnreadPoint = callingInfo.showUnreadPoint;
cellData.isUseMsgReceiverAvatar = callingInfo.isUseReceiverAvatar;
cellData.reuseId = TTextMessageCell_ReuseId;
return cellData;
} else if (callingInfo.participantType == TUICallParticipantTypeGroup) {
TUISystemMessageCellData *cellData = [[TUISystemMessageCellData alloc] initWithDirection:direction];
cellData.content = callingInfo.content;
cellData.replacedUserIDList = callingInfo.participantIDList;
cellData.reuseId = TSystemMessageCell_ReuseId;
return cellData;
} else {
return nil;
}
}
@end

View File

@@ -0,0 +1,13 @@
// Created by Tencent on 2023/06/09.
// Copyright © 2023 Tencent. All rights reserved.
#import "TUIMessageBaseMediaDataProvider.h"
NS_ASSUME_NONNULL_BEGIN
@interface TUIMessageMediaDataProvider : TUIMessageBaseMediaDataProvider
+ (TUIMessageCellData *)getMediaCellData:(V2TIMMessage *)message;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,32 @@
//
// TUIMessageSearchDataProvider.m
// TXIMSDK_TUIKit_iOS
//
// Created by kayev on 2021/7/8.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIMessageMediaDataProvider.h"
#import "TUIImageMessageCellData.h"
#import "TUIMessageBaseDataProvider+ProtectedAPI.h"
#import "TUIVideoMessageCellData.h"
@implementation TUIMessageMediaDataProvider
+ (TUIMessageCellData *)getMediaCellData:(V2TIMMessage *)message {
if (message.status == V2TIM_MSG_STATUS_HAS_DELETED || message.status == V2TIM_MSG_STATUS_LOCAL_REVOKED) {
return nil;
}
TUIMessageCellData *data = nil;
if (message.elemType == V2TIM_ELEM_TYPE_IMAGE) {
data = [TUIImageMessageCellData getCellData:message];
} else if (message.elemType == V2TIM_ELEM_TYPE_VIDEO) {
data = [TUIVideoMessageCellData getCellData:message];
}
if (data) {
data.innerMessage = message;
}
return data;
}
@end

View File

@@ -0,0 +1,39 @@
//
// TUIMessageSearchDataProvider.h
// TXIMSDK_TUIKit_iOS
//
// Created by kayev on 2021/7/8.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIMessageDataProvider.h"
NS_ASSUME_NONNULL_BEGIN
@interface TUIMessageSearchDataProvider : TUIMessageDataProvider
@property(nonatomic) BOOL isOlderNoMoreMsg;
@property(nonatomic) BOOL isNewerNoMoreMsg;
@property(nonatomic) V2TIMMessage *msgForOlderGet;
@property(nonatomic) V2TIMMessage *msgForNewerGet;
- (void)loadMessageWithSearchMsg:(V2TIMMessage *)searchMsg
SearchMsgSeq:(uint64_t)searchSeq
ConversationInfo:(TUIChatConversationModel *)conversation
SucceedBlock:(void (^)(BOOL isOlderNoMoreMsg, BOOL isNewerNoMoreMsg, NSArray<TUIMessageCellData *> *newMsgs))succeedBlock
FailBlock:(V2TIMFail)failBlock;
- (void)loadMessageWithIsRequestOlderMsg:(BOOL)orderType
ConversationInfo:(TUIChatConversationModel *)conversation
SucceedBlock:(void (^)(BOOL isOlderNoMoreMsg, BOOL isNewerNoMoreMsg, BOOL isFirstLoad,
NSArray<TUIMessageCellData *> *newUIMsgs))succeedBlock
FailBlock:(V2TIMFail)failBlock;
- (void)removeAllSearchData;
- (void)findMessages:(NSArray<NSString *> *)msgIDs callback:(void (^)(BOOL success, NSString *desc, NSArray<V2TIMMessage *> *messages))callback;
- (void)preProcessMessage:(NSArray *)uiMsgs;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,361 @@
//
// TUIMessageSearchDataProvider.m
// TXIMSDK_TUIKit_iOS
//
// Created by kayev on 2021/7/8.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIMessageSearchDataProvider.h"
#import "TUIMessageBaseDataProvider+ProtectedAPI.h"
#import "TUIChatMediaSendingManager.h"
typedef void (^LoadSearchMsgSucceedBlock)(BOOL isOlderNoMoreMsg, BOOL isNewerNoMoreMsg, NSArray<TUIMessageCellData *> *newMsgs);
typedef void (^LoadMsgSucceedBlock)(BOOL isOlderNoMoreMsg, BOOL isNewerNoMoreMsg, BOOL isFirstLoad, NSArray<TUIMessageCellData *> *newUIMsgs);
@interface TUIMessageSearchDataProvider ()
@property(nonatomic, copy) LoadSearchMsgSucceedBlock loadSearchMsgSucceedBlock;
@property(nonatomic, copy) LoadMsgSucceedBlock loadMsgSucceedBlock;
@end
@implementation TUIMessageSearchDataProvider
- (instancetype)init {
self = [super init];
if (self) {
_isOlderNoMoreMsg = NO;
_isNewerNoMoreMsg = NO;
}
return self;
}
- (void)loadMessageWithSearchMsg:(V2TIMMessage *)searchMsg
SearchMsgSeq:(uint64_t)searchSeq
ConversationInfo:(TUIChatConversationModel *)conversation
SucceedBlock:(void (^)(BOOL isOlderNoMoreMsg, BOOL isNewerNoMoreMsg, NSArray<TUIMessageCellData *> *newMsgs))succeedBlock
FailBlock:(V2TIMFail)failBlock {
if (self.isLoadingData) {
failBlock(ERR_SUCC, @"refreshing");
return;
}
self.isLoadingData = YES;
self.isOlderNoMoreMsg = NO;
self.isNewerNoMoreMsg = NO;
self.loadSearchMsgSucceedBlock = succeedBlock;
dispatch_group_t group = dispatch_group_create();
__block NSArray *olders = @[];
__block NSArray *newers = @[];
__block BOOL isOldLoadFail = NO;
__block BOOL isNewLoadFail = NO;
__block int failCode = 0;
__block NSString *failDesc = nil;
/**
* Load the oldest pageCount messages starting from locating message
*/
{
dispatch_group_enter(group);
V2TIMMessageListGetOption *option = [[V2TIMMessageListGetOption alloc] init];
option.getType = V2TIM_GET_CLOUD_OLDER_MSG;
option.count = self.pageCount;
option.groupID = conversation.groupID;
option.userID = conversation.userID;
if (searchMsg) {
option.lastMsg = searchMsg;
} else {
option.lastMsgSeq = searchSeq;
}
[V2TIMManager.sharedInstance getHistoryMessageList:option
succ:^(NSArray<V2TIMMessage *> *msgs) {
msgs = msgs.reverseObjectEnumerator.allObjects;
olders = msgs ?: @[];
if (olders.count < self.pageCount) {
self.isOlderNoMoreMsg = YES;
}
dispatch_group_leave(group);
}
fail:^(int code, NSString *desc) {
isOldLoadFail = YES;
failCode = code;
failDesc = desc;
dispatch_group_leave(group);
}];
}
/**
* Load the latest pageCount messages starting from the locating message
*/
{
dispatch_group_enter(group);
V2TIMMessageListGetOption *option = [[V2TIMMessageListGetOption alloc] init];
option.getType = V2TIM_GET_CLOUD_NEWER_MSG;
option.count = self.pageCount;
option.groupID = conversation.groupID;
option.userID = conversation.userID;
if (searchMsg) {
option.lastMsg = searchMsg;
} else {
option.lastMsgSeq = searchSeq;
}
[V2TIMManager.sharedInstance getHistoryMessageList:option
succ:^(NSArray<V2TIMMessage *> *msgs) {
newers = msgs ?: @[];
if (newers.count < self.pageCount) {
self.isNewerNoMoreMsg = YES;
}
dispatch_group_leave(group);
}
fail:^(int code, NSString *desc) {
isNewLoadFail = YES;
failCode = code;
failDesc = desc;
dispatch_group_leave(group);
}];
}
@weakify(self);
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
@strongify(self);
self.isLoadingData = NO;
if (isOldLoadFail || isNewLoadFail) {
dispatch_async(dispatch_get_main_queue(), ^{
failBlock(failCode, failDesc);
});
}
self.isFirstLoad = NO;
NSMutableArray *results = [NSMutableArray array];
[results addObjectsFromArray:olders];
if (searchMsg) {
/**
* Pulling messages through the msg will not return the msg object itself, here you need to actively add the msg to the results list
*/
[results addObject:searchMsg];
} else {
/**
* Pulling messages through the msg seq, pulling old messages and new messages will return the msg object itself, here you need to deduplicate the msg
* object in results
*/
[results removeLastObject];
}
[results addObjectsFromArray:newers];
self.msgForOlderGet = results.firstObject;
self.msgForNewerGet = results.lastObject;
@weakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
@strongify(self);
[self.heightCache_ removeAllObjects];
[self.uiMsgs_ removeAllObjects];
NSArray *msgs = results.reverseObjectEnumerator.allObjects;
NSMutableArray *uiMsgs = [self transUIMsgFromIMMsg:msgs];
if (uiMsgs.count == 0) {
return;
}
[self getGroupMessageReceipts:msgs
uiMsgs:uiMsgs
succ:^{
[self preProcessMessage:uiMsgs];
}
fail:^{
[self preProcessMessage:uiMsgs];
}];
});
});
}
- (void)loadMessageWithIsRequestOlderMsg:(BOOL)orderType
ConversationInfo:(TUIChatConversationModel *)conversation
SucceedBlock:(void (^)(BOOL isOlderNoMoreMsg, BOOL isNewerNoMoreMsg, BOOL isFirstLoad,
NSArray<TUIMessageCellData *> *newUIMsgs))succeedBlock
FailBlock:(V2TIMFail)failBlock {
self.isLoadingData = YES;
self.loadMsgSucceedBlock = succeedBlock;
int requestCount = self.pageCount;
V2TIMMessageListGetOption *option = [[V2TIMMessageListGetOption alloc] init];
option.userID = conversation.userID;
option.groupID = conversation.groupID;
option.getType = orderType ? V2TIM_GET_CLOUD_OLDER_MSG : V2TIM_GET_CLOUD_NEWER_MSG;
option.count = requestCount;
option.lastMsg = orderType ? self.msgForOlderGet : self.msgForNewerGet;
@weakify(self);
[V2TIMManager.sharedInstance getHistoryMessageList:option
succ:^(NSArray<V2TIMMessage *> *msgs) {
@strongify(self);
if (!orderType) {
msgs = msgs.reverseObjectEnumerator.allObjects;
}
/**
* Update the lastMsg flag
* -- The current pull operation is to pull from the latest time point to the past
*/
BOOL isLastest = (self.msgForNewerGet == nil) && (self.msgForOlderGet == nil) && orderType;
if (msgs.count != 0) {
if (orderType) {
self.msgForOlderGet = msgs.lastObject;
if (self.msgForNewerGet == nil) {
self.msgForNewerGet = msgs.firstObject;
}
} else {
if (self.msgForOlderGet == nil) {
self.msgForOlderGet = msgs.lastObject;
}
self.msgForNewerGet = msgs.firstObject;
}
}
/**
* Update no data flag
*/
if (msgs.count < requestCount) {
if (orderType) {
self.isOlderNoMoreMsg = YES;
} else {
self.isNewerNoMoreMsg = YES;
}
}
if (isLastest) {
/**
* The current pull operation is to pull from the latest time point to the past
*/
self.isNewerNoMoreMsg = YES;
}
NSMutableArray<TUIMessageCellData *> *uiMsgs = [self transUIMsgFromIMMsg:msgs];
if (uiMsgs.count == 0) {
if (self.loadMsgSucceedBlock) {
self.loadMsgSucceedBlock(self.isOlderNoMoreMsg, self.isNewerNoMoreMsg, self.isFirstLoad, uiMsgs);
}
return;
}
//add media placeholder celldata
if (self.conversationModel.conversationID.length > 0) {
NSMutableArray<TUIChatMediaTask *> * tasks = [TUIChatMediaSendingManager.sharedInstance
findPlaceHolderListByConversationID:self.conversationModel.conversationID];
for (TUIChatMediaTask * task in tasks) {
if (task.placeHolderCellData) {
[uiMsgs addObject:task.placeHolderCellData];
}
}
}
[self getGroupMessageReceipts:msgs
uiMsgs:uiMsgs
succ:^{
[self preProcessMessage:uiMsgs orderType:orderType];
}
fail:^{
[self preProcessMessage:uiMsgs orderType:orderType];
}];
}
fail:^(int code, NSString *desc) {
self.isLoadingData = NO;
}];
}
- (void)getGroupMessageReceipts:(NSArray *)msgs uiMsgs:(NSArray *)uiMsgs succ:(void (^)(void))succBlock fail:(void (^)(void))failBlock {
[[V2TIMManager sharedInstance] getMessageReadReceipts:msgs
succ:^(NSArray<V2TIMMessageReceipt *> *receiptList) {
NSLog(@"getGroupMessageReceipts succeed, receiptList: %@", receiptList);
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for (V2TIMMessageReceipt *receipt in receiptList) {
[dict setObject:receipt forKey:receipt.msgID];
}
for (TUIMessageCellData *data in uiMsgs) {
V2TIMMessageReceipt *receipt = dict[data.msgID];
data.messageReceipt = receipt;
}
if (succBlock) {
succBlock();
}
}
fail:^(int code, NSString *desc) {
NSLog(@"getGroupMessageReceipts failed, code: %d, desc: %@", code, desc);
if (failBlock) {
failBlock();
}
}];
}
- (void)preProcessMessage:(NSArray *)uiMsgs {
@weakify(self);
[self preProcessMessage:uiMsgs
callback:^{
@strongify(self);
[self addUIMsgs:uiMsgs];
self.loadSearchMsgSucceedBlock(self.isOlderNoMoreMsg, self.isNewerNoMoreMsg, self.uiMsgs_);
}];
}
- (void)preProcessMessage:(NSArray *)uiMsgs orderType:(BOOL)orderType {
@weakify(self);
[self preProcessMessage:uiMsgs
callback:^{
@strongify(self);
if (orderType) {
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, uiMsgs.count)];
[self insertUIMsgs:uiMsgs atIndexes:indexSet];
} else {
[self addUIMsgs:uiMsgs];
}
if (self.loadMsgSucceedBlock) {
self.loadMsgSucceedBlock(self.isOlderNoMoreMsg, self.isNewerNoMoreMsg, self.isFirstLoad, uiMsgs);
}
self.isLoadingData = NO;
self.isFirstLoad = NO;
}];
}
- (void)removeAllSearchData {
[self.uiMsgs_ removeAllObjects];
self.isNewerNoMoreMsg = NO;
self.isOlderNoMoreMsg = NO;
self.isFirstLoad = YES;
self.msgForNewerGet = nil;
self.msgForOlderGet = nil;
self.loadSearchMsgSucceedBlock = nil;
}
- (void)findMessages:(NSArray<NSString *> *)msgIDs callback:(void (^)(BOOL success, NSString *desc, NSArray<V2TIMMessage *> *messages))callback {
[V2TIMManager.sharedInstance findMessages:msgIDs
succ:^(NSArray<V2TIMMessage *> *msgs) {
if (callback) {
callback(YES, @"", msgs);
}
}
fail:^(int code, NSString *desc) {
if (callback) {
callback(NO, desc, nil);
}
}];
}
#pragma mark - Override
- (void)onRecvNewMessage:(V2TIMMessage *)msg {
if (self.isNewerNoMoreMsg == NO) {
/**
* If the current message list has not pulled the last message, ignore the new message;
* If it is processed at this time, it will cause new messages to be added to the history list, resulting in the problem of position confusion.
*/
return;
}
if (self.dataSource.isDataSourceConsistent == NO ) {
self.isNewerNoMoreMsg = NO;
return;
}
[super onRecvNewMessage:msg];
}
@end