增加换肤功能

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,91 @@
//
// TUISearchDataProvider.h
// Pods
//
// Created by harvy on 2020/12/28.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <TIMCommon/TIMDefine.h>
@class TUISearchResultCellModel;
NS_ASSUME_NONNULL_BEGIN
#ifndef __TUISearchDataProvider_H__
#define __TUISearchDataProvider_H__
#define kSearchChatHistoryConversationId @"Id"
#define kSearchChatHistoryConverationInfo @"conversation"
#define kSearchChatHistoryConversationMsgs @"msgs"
///////////////////////////////////////////////////////////////////// Configuration /////////////////////////////////////////////////////////////////
/**
* The default maximum number of each module, if it is equal to or exceeds, it will display "View More***"
*/
#define kMaxNumOfPerModule 3
/**
* The enumeration name represents the searched module, and the enumeration value represents the order between modules
*/
typedef NS_ENUM(NSInteger, TUISearchResultModule) {
TUISearchResultModuleAll = 1 << 0,
TUISearchResultModuleContact = 1 << 1,
TUISearchResultModuleGroup = 1 << 2,
TUISearchResultModuleChatHistory = 1 << 3,
};
typedef NSString *TUISearchParamKey;
FOUNDATION_EXTERN TUISearchParamKey TUISearchChatHistoryParamKeyConversationId;
FOUNDATION_EXTERN TUISearchParamKey TUISearchChatHistoryParamKeyCount;
FOUNDATION_EXPORT TUISearchParamKey TUISearchChatHistoryParamKeyPage;
FOUNDATION_EXTERN NSUInteger TUISearchDefaultPageSize;
static inline NSString *titleForModule(TUISearchResultModule module, BOOL isHeader) {
NSString *headerTitle = @"";
NSString *footerTitle = @"";
switch (module) {
case TUISearchResultModuleContact:
headerTitle = TIMCommonLocalizableString(TUIKitSearchItemHeaderTitleContact);
footerTitle = TIMCommonLocalizableString(TUIKitSearchItemFooterTitleContact);
break;
case TUISearchResultModuleGroup:
headerTitle = TIMCommonLocalizableString(TUIKitSearchItemHeaderTitleGroup);
footerTitle = TIMCommonLocalizableString(TUIKitSearchItemFooterTitleGroup);
break;
case TUISearchResultModuleChatHistory:
headerTitle = TIMCommonLocalizableString(TUIkitSearchItemHeaderTitleChatHistory);
footerTitle = TIMCommonLocalizableString(TUIKitSearchItemFooterTitleChatHistory);
break;
default:
break;
}
return isHeader ? headerTitle : footerTitle;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endif
@protocol TUISearchResultDelegate <NSObject>
- (void)onSearchResults:(NSDictionary<NSNumber *, NSArray<TUISearchResultCellModel *> *> *)results forModules:(TUISearchResultModule)modules;
- (void)onSearchError:(NSString *)errMsg;
@end
@interface TUISearchDataProvider : NSObject
@property(nonatomic, weak) id<TUISearchResultDelegate> delegate;
@property(nonatomic, strong, readonly) NSMutableDictionary<NSNumber *, NSArray<TUISearchResultCellModel *> *> *resultSet;
- (void)searchForKeyword:(NSString *)keyword forModules:(TUISearchResultModule)modules param:(NSDictionary<TUISearchParamKey, id> *__nullable)param;
+ (NSAttributedString *)attributeStringWithText:(NSString *__nullable)text key:(NSString *__nullable)key;
+ (NSString *)matchedTextForMessage:(V2TIMMessage *)msg withKey:(NSString *)key;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,458 @@
//
// TUISearchDataProvider.m
// Pods
//
// Created by harvy on 2020/12/28.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUISearchDataProvider.h"
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUIThemeManager.h>
#import "TUISearchGroupDataProvider.h"
#import "TUISearchResultCellModel.h"
#import <TUICore/NSString+TUIUtil.h>
TUISearchParamKey TUISearchChatHistoryParamKeyConversationId = @"TUISearchChatHistoryParamKeyConversationId";
TUISearchParamKey TUISearchChatHistoryParamKeyCount = @"TUISearchChatHistoryParamKeyCount";
TUISearchParamKey TUISearchChatHistoryParamKeyPage = @"TUISearchChatHistoryParamKeyPage";
NSUInteger TUISearchDefaultPageSize = 20;
typedef void (^TUISearchResultCallback)(BOOL succ, NSString *__nullable errMsg, NSArray<TUISearchResultCellModel *> *__nullable results);
@interface TUISearchDataProvider ()
@property(nonatomic, strong) NSMutableDictionary<NSNumber *, NSArray<TUISearchResultCellModel *> *> *resultSet;
@end
@implementation TUISearchDataProvider
- (void)searchForKeyword:(NSString *)keyword forModules:(TUISearchResultModule)modules param:(NSDictionary<TUISearchParamKey, id> *__nullable)param {
__weak typeof(self) weakSelf = self;
if (![NSThread isMainThread]) {
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf searchForKeyword:keyword forModules:modules param:param];
});
return;
}
if (keyword.length == 0) {
[self.resultSet removeAllObjects];
if ([self.delegate respondsToSelector:@selector(onSearchResults:forModules:)]) {
[self.delegate onSearchResults:self.resultSet forModules:modules];
}
return;
}
dispatch_group_t group = dispatch_group_create();
BOOL request = NO;
// Contact
if ((modules == TUISearchResultModuleAll) || (modules & TUISearchResultModuleContact)) {
request = YES;
dispatch_group_enter(group);
[self searchContacts:keyword
callback:^(BOOL succ, NSString *errMsg, NSArray *results) {
if (succ && results) {
dispatch_async(dispatch_get_main_queue(), ^{
if (results.count) {
[weakSelf.resultSet setObject:results forKey:@(TUISearchResultModuleContact)];
} else {
[weakSelf.resultSet removeObjectForKey:@(TUISearchResultModuleContact)];
}
dispatch_group_leave(group);
});
return;
}
dispatch_group_leave(group);
}];
}
// Group
if ((modules == TUISearchResultModuleAll) || (modules & TUISearchResultModuleGroup)) {
request = YES;
dispatch_group_enter(group);
[self searchGroups:keyword
callback:^(BOOL succ, NSString *errMsg, NSArray *results) {
if (succ && results) {
dispatch_async(dispatch_get_main_queue(), ^{
if (results.count) {
[weakSelf.resultSet setObject:results forKey:@(TUISearchResultModuleGroup)];
} else {
[weakSelf.resultSet removeObjectForKey:@(TUISearchResultModuleGroup)];
}
dispatch_group_leave(group);
});
return;
}
dispatch_group_leave(group);
}];
}
// Chat history
if ((modules == TUISearchResultModuleAll) || (modules & TUISearchResultModuleChatHistory)) {
request = YES;
dispatch_group_enter(group);
[self searchChatHistory:keyword
param:param
callback:^(BOOL succ, NSString *_Nullable errMsg, NSArray<TUISearchResultCellModel *> *_Nullable results) {
if (succ && results) {
dispatch_async(dispatch_get_main_queue(), ^{
if (results.count) {
[weakSelf.resultSet setObject:results forKey:@(TUISearchResultModuleChatHistory)];
} else {
[weakSelf.resultSet removeObjectForKey:@(TUISearchResultModuleChatHistory)];
}
});
}
dispatch_group_leave(group);
}];
}
if (!request) {
if ([self.delegate respondsToSelector:@selector(onSearchError:)]) {
[self.delegate onSearchError:@"search module not exists"];
}
return;
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
if ([self.delegate respondsToSelector:@selector(onSearchResults:forModules:)]) {
[self.delegate onSearchResults:self.resultSet forModules:modules];
}
});
}
- (void)searchContacts:(NSString *)keyword callback:(TUISearchResultCallback)callback {
if (keyword == nil || callback == nil) {
if (callback) {
callback(NO, @"invalid parameters, keyword is null", nil);
}
return;
}
V2TIMFriendSearchParam *param = [[V2TIMFriendSearchParam alloc] init];
param.keywordList = @[ keyword ];
param.isSearchUserID = YES;
param.isSearchNickName = YES;
param.isSearchRemark = YES;
[V2TIMManager.sharedInstance searchFriends:param
succ:^(NSArray<V2TIMFriendInfoResult *> *infoList) {
NSMutableArray *arrayM = [NSMutableArray array];
for (V2TIMFriendInfoResult *result in infoList) {
TUISearchResultCellModel *cellModel = [[TUISearchResultCellModel alloc] init];
NSString *title = result.friendInfo.friendRemark;
if (title.length == 0) {
title = result.friendInfo.userFullInfo.nickName;
}
if (title.length == 0) {
title = result.friendInfo.userID;
}
NSString *why = @"";
if ([result.friendInfo.friendRemark.lowercaseString containsString:keyword.lowercaseString]) {
why = result.friendInfo.friendRemark;
} else if ([result.friendInfo.userFullInfo.nickName.lowercaseString containsString:keyword.lowercaseString]) {
why = result.friendInfo.userFullInfo.nickName;
} else if ([result.friendInfo.userID.lowercaseString containsString:keyword.lowercaseString]) {
why = result.friendInfo.userID;
}
if (why.length) {
if ([why isEqualToString:title]) {
why = nil;
} else {
why = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitSearchResultMatchFormat), why];
}
}
cellModel.titleAttributeString = [TUISearchDataProvider attributeStringWithText:title key:keyword];
cellModel.detailsAttributeString = [TUISearchDataProvider attributeStringWithText:why key:keyword];
cellModel.groupID = nil;
cellModel.avatarUrl = result.friendInfo.userFullInfo.faceURL;
cellModel.avatarImage = DefaultAvatarImage;
cellModel.context = result.friendInfo;
[arrayM addObject:cellModel];
}
callback(YES, nil, arrayM);
}
fail:^(int code, NSString *desc) {
callback(NO, desc, nil);
}];
}
- (void)searchGroups:(NSString *)keyword callback:(TUISearchResultCallback)callback {
if (keyword == nil || callback == nil) {
if (callback) {
callback(NO, @"invalid parameters, keyword is null", nil);
}
return;
}
TUISearchGroupParam *param = [[TUISearchGroupParam alloc] init];
param.keywordList = @[ keyword ];
param.isSearchGroupID = YES;
param.isSearchGroupName = YES;
param.isSearchGroupMember = YES;
param.isSearchMemberRemark = YES;
param.isSearchMemberUserID = YES;
param.isSearchMemberNickName = YES;
param.isSearchMemberNameCard = YES;
[TUISearchGroupDataProvider searchGroups:param
succ:^(NSArray<TUISearchGroupResult *> *_Nonnull resultSet) {
NSMutableArray *arrayM = [NSMutableArray array];
for (TUISearchGroupResult *result in resultSet) {
TUISearchResultCellModel *cellModel = [[TUISearchResultCellModel alloc] init];
NSString *title = result.groupInfo.groupName;
if (title.length == 0) {
title = result.groupInfo.groupID;
}
cellModel.titleAttributeString = [TUISearchDataProvider attributeStringWithText:title key:keyword];
cellModel.detailsAttributeString = nil;
cellModel.groupID = result.groupInfo.groupID;
cellModel.groupType = result.groupInfo.groupType;
cellModel.avatarImage = DefaultGroupAvatarImageByGroupType(result.groupInfo.groupType);
cellModel.avatarUrl = result.groupInfo.faceURL;
cellModel.context = result.groupInfo;
[arrayM addObject:cellModel];
if (result.matchField == TUISearchGroupMatchFieldGroupID) {
NSString *text = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitSearchResultMatchGroupIDFormat), result.matchValue];
cellModel.detailsAttributeString = [TUISearchDataProvider attributeStringWithText:text key:keyword];
} else if (result.matchField == TUISearchGroupMatchFieldMember && result.matchMembers.count) {
NSString *text = TIMCommonLocalizableString(TUIKitSearchResultMatchGroupMember);
for (int i = 0; i < result.matchMembers.count; i++) {
TUISearchGroupMemberMatchResult *memberResult = result.matchMembers[i];
text = [text stringByAppendingString:memberResult.memberMatchValue];
if (i < result.matchMembers.count - 1) {
text = [text stringByAppendingString:@"、"];
}
}
cellModel.detailsAttributeString = [TUISearchDataProvider attributeStringWithText:text key:keyword];
}
}
callback(YES, nil, arrayM);
}
fail:^(NSInteger code, NSString *_Nonnull desc) {
callback(NO, desc, nil);
}];
}
- (void)searchChatHistory:(NSString *)keyword param:(NSDictionary<TUISearchParamKey, id> *__nullable)paramters callback:(TUISearchResultCallback)callback {
if (keyword == nil || callback == nil) {
if (callback) {
callback(NO, @"invalid parameters, keyword is null", nil);
}
return;
}
NSUInteger pageSize = TUISearchDefaultPageSize;
NSUInteger pageIndex = 0;
NSString *conversationID = nil;
NSArray *allKeys = paramters.allKeys;
BOOL displayWithConveration = YES;
if ([allKeys containsObject:TUISearchChatHistoryParamKeyCount]) {
pageSize = [paramters[TUISearchChatHistoryParamKeyCount] integerValue];
}
if ([allKeys containsObject:TUISearchChatHistoryParamKeyPage]) {
pageIndex = [paramters[TUISearchChatHistoryParamKeyPage] integerValue];
}
if ([allKeys containsObject:TUISearchChatHistoryParamKeyConversationId]) {
conversationID = paramters[TUISearchChatHistoryParamKeyConversationId];
displayWithConveration = NO;
}
V2TIMMessageSearchParam *param = [[V2TIMMessageSearchParam alloc] init];
param.keywordList = @[ keyword ];
param.messageTypeList = nil;
param.conversationID = conversationID;
param.searchTimePosition = 0;
param.searchTimePeriod = 0;
param.pageIndex = pageIndex;
param.pageSize = pageSize;
[V2TIMManager.sharedInstance searchLocalMessages:param
succ:^(V2TIMMessageSearchResult *searchResult) {
if (searchResult.totalCount == 0) {
if (callback) {
callback(YES, nil, @[]);
}
return;
}
NSMutableArray *conversationIds = [NSMutableArray array];
NSMutableDictionary *conversationInfoMap = [NSMutableDictionary dictionary];
NSMutableDictionary *conversationMessageMap = [NSMutableDictionary dictionary];
NSMutableDictionary *conversationCountMap = [NSMutableDictionary dictionary];
NSArray<V2TIMMessageSearchResultItem *> *messageSearchResultItems = searchResult.messageSearchResultItems;
for (V2TIMMessageSearchResultItem *searchItem in messageSearchResultItems) {
NSString *conversationID = searchItem.conversationID;
NSUInteger messageCount = searchItem.messageCount;
NSArray<V2TIMMessage *> *messageList = searchItem.messageList ?: @[];
if (conversationID.length == 0) {
continue;
}
[conversationIds addObject:conversationID];
conversationMessageMap[conversationID] = messageList;
conversationCountMap[conversationID] = @(messageCount);
}
if (conversationIds.count == 0) {
if (callback) {
callback(YES, nil, @[]);
}
return;
}
[V2TIMManager.sharedInstance getConversationList:conversationIds
succ:^(NSArray<V2TIMConversation *> *list) {
for (V2TIMConversation *conversation in list) {
if (conversation.conversationID) {
conversationInfoMap[conversation.conversationID] = conversation;
}
}
NSMutableArray *arrayM = [NSMutableArray array];
for (NSString *conversationId in conversationIds) {
if (![conversationInfoMap.allKeys containsObject:conversationId]) {
continue;
}
V2TIMConversation *conv = conversationInfoMap[conversationId];
NSArray *messageList = conversationMessageMap[conversationId];
NSUInteger count = [conversationCountMap[conversationId] integerValue];
if (displayWithConveration) {
TUISearchResultCellModel *cellModel = [[TUISearchResultCellModel alloc] init];
NSString *desc = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitSearchResultDisplayChatHistoryCountFormat), count];
if (messageList.count == 1) {
V2TIMMessage *firstMessage = messageList.firstObject;
desc = [TUISearchDataProvider matchedTextForMessage:(V2TIMMessage *)firstMessage withKey:keyword];
}
cellModel.title = conv.showName;
cellModel.detailsAttributeString = [TUISearchDataProvider attributeStringWithText:desc key:messageList.count == 1 ? keyword : nil];
cellModel.groupID = conv.groupID;
cellModel.avatarImage = conv.type == V2TIM_GROUP ? DefaultGroupAvatarImageByGroupType(conv.groupType) : DefaultAvatarImage;
cellModel.groupType = conv.groupType;
cellModel.avatarUrl = conv.faceUrl;
cellModel.context = @{
kSearchChatHistoryConversationId : conversationId,
kSearchChatHistoryConverationInfo : conv,
kSearchChatHistoryConversationMsgs : messageList
};
[arrayM addObject:cellModel];
} else {
for (V2TIMMessage *message in messageList) {
TUISearchResultCellModel *cellModel = [[TUISearchResultCellModel alloc] init];
cellModel.title = message.nickName ?: message.sender;
NSString *desc = [TUISearchDataProvider matchedTextForMessage:message withKey:keyword];
cellModel.detailsAttributeString = [TUISearchDataProvider attributeStringWithText:desc key:keyword];
cellModel.groupID = conv.groupID;
cellModel.groupType = conv.groupType;
cellModel.avatarUrl = message.faceURL;
cellModel.avatarImage = conv.type == V2TIM_GROUP ? DefaultGroupAvatarImageByGroupType(conv.groupType) : DefaultAvatarImage;
cellModel.context = message;
[arrayM addObject:cellModel];
}
}
}
TUISearchResultCellModel *lastCellModel = [arrayM lastObject];
lastCellModel.hideSeparatorLine = YES;
if (callback) {
callback(YES, nil, arrayM);
}
}
fail:^(int code, NSString *desc) {
if (callback) {
callback(NO, desc, nil);
}
}];
}
fail:^(int code, NSString *desc) {
if (callback) {
callback(NO, desc, nil);
}
if (code == ERR_SDK_INTERFACE_NOT_SUPPORT) {
[TUITool postUnsupportNotificationOfService:TIMCommonLocalizableString(TUIKitErrorUnsupportIntefaceSearch)];
}
}];
}
+ (NSAttributedString *)attributeStringWithText:(NSString *)text key:(NSString *)key {
if (text.length == 0) {
return nil;
}
if (key == nil || key.length == 0 || ![text.lowercaseString tui_containsString:key.lowercaseString]) {
NSAttributedString *attributeString = [[NSAttributedString alloc] initWithString:text
attributes:@{NSForegroundColorAttributeName : [UIColor darkGrayColor]}];
return attributeString;
}
NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:text
attributes:@{NSForegroundColorAttributeName : [UIColor darkGrayColor]}];
NSUInteger loc = 0;
NSUInteger len = text.length;
while (len > 0) {
NSRange range = [text.lowercaseString rangeOfString:key.lowercaseString options:NSCaseInsensitiveSearch range:NSMakeRange(loc, len)];
if (range.length) {
[attr addAttribute:NSForegroundColorAttributeName value:TIMCommonDynamicColor(@"primary_theme_color", @"#147AFF") range:range];
loc = range.location + 1;
len = text.length - loc;
} else {
len = 0;
loc = 0;
}
}
return [[NSAttributedString alloc] initWithAttributedString:attr];
}
+ (NSString *)matchedTextForMessage:(V2TIMMessage *)msg withKey:(NSString *)key {
if (key.length == 0) {
return @"";
}
if ((msg.elemType == V2TIM_ELEM_TYPE_TEXT) && [msg.textElem.text tui_containsString:key]) {
return msg.textElem.text;
} else if ((msg.elemType == V2TIM_ELEM_TYPE_IMAGE) && [msg.imageElem.path tui_containsString:key]) {
return msg.imageElem.path;
} else if ((msg.elemType == V2TIM_ELEM_TYPE_SOUND) && [msg.soundElem.path tui_containsString:key]) {
return msg.soundElem.path;
} else if (msg.elemType == V2TIM_ELEM_TYPE_VIDEO) {
if ([msg.videoElem.videoPath tui_containsString:key]) {
return msg.videoElem.videoPath;
} else if ([msg.videoElem.snapshotPath tui_containsString:key]) {
return msg.videoElem.snapshotPath;
} else {
return @"";
}
} else if ((msg.elemType == V2TIM_ELEM_TYPE_FILE) && [msg.fileElem.path tui_containsString:key]) {
return msg.fileElem.path;
} else if (msg.elemType == V2TIM_ELEM_TYPE_MERGER) {
NSArray *abM = msg.mergerElem.abstractList;
NSString *abs = @"";
for (NSString *ab in abM) {
abs = [abs stringByAppendingString:ab];
abs = [abs stringByAppendingString:@","];
}
if ([msg.mergerElem.title tui_containsString:key]) {
return msg.mergerElem.title;
} else if ([abs tui_containsString:key]) {
return abs;
} else {
return @"";
}
}
return @"";
}
#pragma mark - Lazy
- (NSMutableDictionary *)resultSet {
if (_resultSet == nil) {
_resultSet = [NSMutableDictionary dictionary];
}
return _resultSet;
}
@end

View File

@@ -0,0 +1,74 @@
//
// TUISearchGroupDataProvider.h
// Pods
//
// Created by harvy on 2021/3/30.
// Copyright © 2023 Tencent. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <TIMCommon/TIMDefine.h>
@class TUISearchGroupResult;
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, TUISearchGroupMatchField) {
TUISearchGroupMatchFieldGroupID = 0x1 << 1,
TUISearchGroupMatchFieldGroupName = 0x1 << 2,
TUISearchGroupMatchFieldMember = 0x1 << 3,
};
typedef NS_ENUM(NSInteger, TUISearchGroupMemberMatchField) {
TUISearchGroupMemberMatchFieldUserID = 0x1 << 1,
TUISearchGroupMemberMatchFieldNickName = 0x1 << 2,
TUISearchGroupMemberMatchFieldRemark = 0x1 << 3,
TUISearchGroupMemberMatchFieldNameCard = 0x1 << 4,
};
typedef void (^TUISearchGroupResultListSucc)(NSArray<TUISearchGroupResult *> *resultSet);
typedef void (^TUISearchGroupResultListFail)(NSInteger code, NSString *desc);
#pragma mark - Paramter
@interface TUISearchGroupParam : NSObject
@property(nonatomic, copy) NSArray<NSString *> *keywordList;
@property(nonatomic, assign) BOOL isSearchGroupID;
@property(nonatomic, assign) BOOL isSearchGroupName;
@property(nonatomic, assign) BOOL isSearchGroupMember;
@property(nonatomic, assign) BOOL isSearchMemberUserID;
@property(nonatomic, assign) BOOL isSearchMemberNickName;
@property(nonatomic, assign) BOOL isSearchMemberRemark;
@property(nonatomic, assign) BOOL isSearchMemberNameCard;
@end
#pragma mark - Group member match result
@interface TUISearchGroupMemberMatchResult : NSObject
@property(nonatomic, strong, readonly) V2TIMGroupMemberFullInfo *memberInfo;
@property(nonatomic, assign, readonly) TUISearchGroupMemberMatchField memberMatchField;
@property(nonatomic, copy, readonly) NSString *memberMatchValue;
@end
#pragma mark - Group match result
@interface TUISearchGroupResult : NSObject
@property(nonatomic, strong, readonly) V2TIMGroupInfo *groupInfo;
@property(nonatomic, assign, readonly) TUISearchGroupMatchField matchField;
@property(nonatomic, copy, readonly) NSString *__nullable matchValue;
@property(nonatomic, strong, readonly) NSArray<TUISearchGroupMemberMatchResult *> *__nullable matchMembers;
@end
#pragma mark - Group Search
@interface TUISearchGroupDataProvider : NSObject
+ (void)searchGroups:(TUISearchGroupParam *)searchParam succ:(TUISearchGroupResultListSucc __nullable)succ fail:(TUISearchGroupResultListFail __nullable)fail;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,280 @@
//
// TUISearchGroupDataProvider.m
// Pods
//
// Created by harvy on 2021/3/30.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUISearchGroupDataProvider.h"
#import "NSString+TUIUtil.h"
static BOOL match(NSArray *keywords, NSString *text) {
BOOL isMatch = NO;
for (NSString *keyword in keywords) {
if ([text.lowercaseString tui_containsString:keyword]) {
isMatch |= YES;
break;
}
}
return isMatch;
}
@interface TUISearchGroupResult ()
@property(nonatomic, copy) NSString *groupId;
@property(nonatomic, strong) NSArray<V2TIMGroupMemberFullInfo *> *memberInfos;
@property(nonatomic, strong) V2TIMGroupInfo *groupInfo;
@property(nonatomic, assign) TUISearchGroupMatchField matchField;
@property(nonatomic, copy) NSString *matchValue;
@property(nonatomic, strong) NSArray<TUISearchGroupMemberMatchResult *> *matchMembers;
@end
@implementation TUISearchGroupResult
@end
@interface TUISearchGroupMemberMatchResult ()
@property(nonatomic, strong) V2TIMGroupMemberFullInfo *memberInfo;
@property(nonatomic, assign) TUISearchGroupMemberMatchField memberMatchField;
@property(nonatomic, copy) NSString *memberMatchValue;
@end
@implementation TUISearchGroupMemberMatchResult
@end
@implementation TUISearchGroupParam
@end
@implementation TUISearchGroupDataProvider
+ (void)searchGroups:(TUISearchGroupParam *)searchParam succ:(TUISearchGroupResultListSucc __nullable)succ fail:(TUISearchGroupResultListFail __nullable)fail {
if (searchParam == nil) {
if (fail) {
fail(-1, @"Invalid paramters, searchParam is null");
}
return;
}
if (searchParam.keywordList == nil || searchParam.keywordList.count == 0 || searchParam.keywordList.count > 5) {
if (fail) {
fail(-1, @"Invalid paramters, keyword count is zero or beyond the limit of five");
}
return;
}
NSMutableArray *keywords = [NSMutableArray array];
for (NSString *keyword in searchParam.keywordList) {
[keywords addObject:keyword.lowercaseString];
}
__block NSArray *groupsOne = nil;
__block NSArray *groupsTwo = nil;
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self doSearchGroups:searchParam
keywords:keywords
succ:^(NSArray<TUISearchGroupResult *> *_Nonnull resultSet) {
groupsOne = resultSet;
dispatch_group_leave(group);
}
fail:^(NSInteger code, NSString *_Nonnull desc) {
dispatch_group_leave(group);
}];
BOOL isSearchMember = searchParam.isSearchGroupMember;
if (isSearchMember) {
dispatch_group_enter(group);
[self doSearchMembers:searchParam
keywords:keywords
succ:^(NSArray<TUISearchGroupResult *> *_Nonnull resultSet) {
groupsTwo = resultSet;
dispatch_group_leave(group);
}
fail:^(NSInteger code, NSString *_Nonnull desc) {
dispatch_group_leave(group);
}];
}
__weak typeof(self) weakSelf = self;
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSArray *resultSet = [weakSelf mergeGroupSets:groupsOne withOthers:groupsTwo];
dispatch_async(dispatch_get_main_queue(), ^{
if (succ) {
succ(resultSet);
}
});
});
}
+ (void)doSearchGroups:(TUISearchGroupParam *)searchParam
keywords:(NSArray<NSString *> *)keywords
succ:(TUISearchGroupResultListSucc)succ
fail:(TUISearchGroupResultListFail)fail {
V2TIMGroupSearchParam *groupParam = [[V2TIMGroupSearchParam alloc] init];
groupParam.keywordList = keywords;
groupParam.isSearchGroupID = searchParam.isSearchGroupID;
groupParam.isSearchGroupName = searchParam.isSearchGroupName;
[V2TIMManager.sharedInstance searchGroups:groupParam
succ:^(NSArray<V2TIMGroupInfo *> *groupList) {
NSMutableArray *arrayM = [NSMutableArray array];
for (V2TIMGroupInfo *groupInfo in groupList) {
TUISearchGroupResult *result = [[TUISearchGroupResult alloc] init];
result.groupId = groupInfo.groupID;
result.groupInfo = groupInfo;
result.matchMembers = nil;
if (match(keywords, groupInfo.groupName)) {
result.matchField = TUISearchGroupMatchFieldGroupName;
result.matchValue = groupInfo.groupName;
[arrayM addObject:result];
continue;
}
if (match(keywords, groupInfo.groupID)) {
result.matchField = TUISearchGroupMatchFieldGroupID;
result.matchValue = groupInfo.groupID;
[arrayM addObject:result];
continue;
}
}
if (succ) {
succ(arrayM);
}
}
fail:^(int code, NSString *desc) {
if (fail) {
fail(code, desc);
}
}];
}
+ (void)doSearchMembers:(TUISearchGroupParam *)searchParam
keywords:(NSArray<NSString *> *)keywords
succ:(TUISearchGroupResultListSucc)succ
fail:(TUISearchGroupResultListFail)fail {
V2TIMGroupMemberSearchParam *memberParam = [[V2TIMGroupMemberSearchParam alloc] init];
memberParam.keywordList = keywords;
memberParam.groupIDList = nil;
memberParam.isSearchMemberUserID = searchParam.isSearchMemberUserID;
memberParam.isSearchMemberNickName = searchParam.isSearchMemberNickName;
memberParam.isSearchMemberNameCard = searchParam.isSearchMemberNameCard;
memberParam.isSearchMemberRemark = searchParam.isSearchMemberRemark;
[V2TIMManager.sharedInstance searchGroupMembers:memberParam
succ:^(NSDictionary<NSString *, NSArray<V2TIMGroupMemberFullInfo *> *> *memberList) {
NSMutableArray<TUISearchGroupResult *> *resultSet = [NSMutableArray array];
NSMutableArray *groupIds = [NSMutableArray array];
[memberList enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull groupId, NSArray<V2TIMGroupMemberFullInfo *> *_Nonnull obj, BOOL *_Nonnull stop) {
[groupIds addObject:groupId];
TUISearchGroupResult *result = [[TUISearchGroupResult alloc] init];
result.groupId = groupId;
result.matchField = TUISearchGroupMatchFieldMember;
result.matchValue = nil;
result.memberInfos = obj;
[resultSet addObject:result];
}];
NSMutableDictionary *groupInfoMap = [NSMutableDictionary dictionary];
dispatch_group_t group = dispatch_group_create();
{
dispatch_group_enter(group);
[V2TIMManager.sharedInstance getGroupsInfo:groupIds
succ:^(NSArray<V2TIMGroupInfoResult *> *groupResultList) {
for (V2TIMGroupInfoResult *groupInfoResult in groupResultList) {
if (groupInfoResult.resultCode == 0) {
groupInfoMap[groupInfoResult.info.groupID] = groupInfoResult.info;
}
}
dispatch_group_leave(group);
}
fail:^(int code, NSString *desc) {
dispatch_group_leave(group);
}];
}
{
dispatch_group_enter(group);
for (TUISearchGroupResult *result in resultSet) {
NSArray *members = result.memberInfos;
NSMutableArray *arrayM = [NSMutableArray array];
for (V2TIMGroupMemberFullInfo *memberInfo in members) {
TUISearchGroupMemberMatchResult *memberMatchResult = [[TUISearchGroupMemberMatchResult alloc] init];
if (match(keywords, memberInfo.nameCard)) {
memberMatchResult.memberMatchField = TUISearchGroupMemberMatchFieldNameCard;
memberMatchResult.memberMatchValue = memberInfo.nameCard;
[arrayM addObject:memberMatchResult];
continue;
}
if (match(keywords, memberInfo.friendRemark)) {
memberMatchResult.memberMatchField = TUISearchGroupMemberMatchFieldRemark;
memberMatchResult.memberMatchValue = memberInfo.friendRemark;
[arrayM addObject:memberMatchResult];
continue;
}
if (match(keywords, memberInfo.nickName)) {
memberMatchResult.memberMatchField = TUISearchGroupMemberMatchFieldNickName;
memberMatchResult.memberMatchValue = memberInfo.nickName;
[arrayM addObject:memberMatchResult];
continue;
}
if (match(keywords, memberInfo.userID)) {
memberMatchResult.memberMatchField = TUISearchGroupMemberMatchFieldUserID;
memberMatchResult.memberMatchValue = memberInfo.userID;
[arrayM addObject:memberMatchResult];
continue;
}
}
result.matchMembers = arrayM;
}
dispatch_group_leave(group);
}
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSMutableArray *arrayM = [NSMutableArray array];
NSArray *validGroupIds = groupInfoMap.allKeys;
for (TUISearchGroupResult *result in resultSet) {
if ([validGroupIds containsObject:result.groupId]) {
result.groupInfo = groupInfoMap[result.groupId];
[arrayM addObject:result];
}
}
if (succ) {
succ(arrayM);
}
});
}
fail:^(int code, NSString *desc) {
if (fail) {
fail(code, desc);
}
}];
}
+ (NSArray *)mergeGroupSets:(NSArray *)groupsOne withOthers:(NSArray *)groupsTwo {
NSMutableArray *arrayM = [NSMutableArray array];
NSMutableDictionary *map = [NSMutableDictionary dictionary];
for (TUISearchGroupResult *result in groupsOne) {
[arrayM addObject:result];
map[result.groupId] = @(1);
}
for (TUISearchGroupResult *result in groupsTwo) {
if ([map objectForKey:result.groupId]) {
continue;
}
[arrayM addObject:result];
}
[arrayM sortUsingComparator:^NSComparisonResult(TUISearchGroupResult *obj1, TUISearchGroupResult *obj2) {
return obj1.groupInfo.lastMessageTime > obj2.groupInfo.lastMessageTime ? NSOrderedDescending : NSOrderedAscending;
}];
return arrayM;
}
@end