增加换肤功能
This commit is contained in:
91
TUIKit/TUISearch/BaseDataProvider/TUISearchDataProvider.h
Normal file
91
TUIKit/TUISearch/BaseDataProvider/TUISearchDataProvider.h
Normal 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
|
||||
458
TUIKit/TUISearch/BaseDataProvider/TUISearchDataProvider.m
Normal file
458
TUIKit/TUISearch/BaseDataProvider/TUISearchDataProvider.m
Normal 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
|
||||
@@ -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
|
||||
280
TUIKit/TUISearch/BaseDataProvider/TUISearchGroupDataProvider.m
Normal file
280
TUIKit/TUISearch/BaseDataProvider/TUISearchGroupDataProvider.m
Normal 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
|
||||
Reference in New Issue
Block a user