Files
TIMSDK/iOS/TUIKit/TUIChat/BaseDataProvider/TUIMessageBaseDataProvider.m
T
2023-04-25 17:03:19 +08:00

1294 lines
52 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#import <AVFoundation/AVFoundation.h>
#import "TUIMessageBaseDataProvider.h"
#import <TIMCommon/TUIMessageCell.h>
#import "TUITypingStatusCellData.h"
#import <TIMCommon/TUITagsModel.h>
#import <TIMCommon/TIMDefine.h>
#import <TUICore/TUITool.h>
#import <TUICore/TUILogin.h>
#import <TUICore/TUICore.h>
#import <TUICore/NSString+TUIUtil.h>
#import "TUIMessageProgressManager.h"
#import "TUICloudCustomDataTypeCenter.h"
#import "TUIChatConfig.h"
#import <TIMCommon/NSString+TUIEmoji.h>
/**
* 消息上方的日期时间间隔, 单位秒 , default is (5 * 60)
* Date time interval above the message in the UIMessageCell, in seconds, default is (5 * 60)
*/
#define MaxDateMessageDelay 5 * 60
@interface TUIMessageBaseDataProvider ()<V2TIMAdvancedMsgListener, TUIMessageProgressManagerDelegate>
@property (nonatomic, strong) TUIChatConversationModel *conversationModel;
@property (nonatomic, strong) NSMutableArray<TUIMessageCellData *> *uiMsgs_;
@property (nonatomic, strong) NSMutableSet<NSString *> *sentReadGroupMsgSet;
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSNumber *> *heightCache_;
@property (nonatomic, assign) BOOL isLoadingData;
@property (nonatomic, assign) BOOL isNoMoreMsg;
@property (nonatomic, assign) BOOL isFirstLoad;
@property (nonatomic, strong) V2TIMMessage *lastMsg;
@property (nonatomic, strong) V2TIMMessage *msgForDate;
@end
@implementation TUIMessageBaseDataProvider
- (instancetype)initWithConversationModel:(TUIChatConversationModel *)conversationModel {
self = [super init];
if (self) {
_conversationModel = conversationModel;
_isLoadingData = NO;
_isNoMoreMsg = NO;
_pageCount = 20;
_isFirstLoad = YES;
[self registerTUIKitNotification];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (NSMutableSet<NSString *> *)sentReadGroupMsgSet {
if (_sentReadGroupMsgSet == nil) {
_sentReadGroupMsgSet = [NSMutableSet setWithCapacity:10];
}
return _sentReadGroupMsgSet;
}
- (NSMutableArray<TUIMessageCellData *> *)uiMsgs_ {
if (_uiMsgs_ == nil) {
_uiMsgs_ = [NSMutableArray array];
}
return _uiMsgs_;
}
- (NSMutableDictionary<NSString *,NSNumber *> *)heightCache_ {
if (_heightCache_ == nil) {
_heightCache_ = [NSMutableDictionary dictionary];
}
return _heightCache_;
}
- (NSArray<TUIMessageCellData *> *)uiMsgs {
return _uiMsgs_;
}
- (NSDictionary<NSString *,NSNumber *> *)heightCache {
return _heightCache_;
}
#pragma mark - TUIKitNotification
- (void)registerTUIKitNotification {
[[V2TIMManager sharedInstance] addAdvancedMsgListener:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMessageStatusChanged:) name:TUIKitNotification_onMessageStatusChanged object:nil];
}
- (void)onMessageStatusChanged:(NSNotification *)notification
{
NSString *msgId = notification.object;
TUIMessageCellData *uiMsg = nil;
for (uiMsg in self.uiMsgs) {
if ([uiMsg.msgID isEqualToString:msgId]) {
[self.dataSource dataProviderDataSourceWillChange:self];
NSInteger index = [self.uiMsgs indexOfObject:uiMsg];
[self.dataSource dataProviderDataSourceChange:self withType:TUIMessageBaseDataProviderDataSourceChangeTypeReload atIndex:index animation:YES];
[self.dataSource dataProviderDataSourceDidChange:self];
return;
}
}
}
#pragma mark - V2TIMAdvancedMsgListener
- (void)onRecvNewMessage:(V2TIMMessage *)msg {
// immsg -> uimsg
NSMutableArray *cellDataList = [self transUIMsgFromIMMsg:@[msg]];
if (cellDataList.count == 0) {
return;
}
TUIMessageCellData *lastObj = cellDataList.lastObject;
if ([lastObj isKindOfClass:TUITypingStatusCellData.class]) {
if (![TUIChatConfig defaultConfig].enableTypingStatus) {
return;
}
TUITypingStatusCellData * stastusData = (TUITypingStatusCellData *)lastObj;
if (!NSThread.isMainThread) {
@weakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
@strongify(self);
[self dealTypingByStatusCellData:stastusData];
});
return;
}
else {
[self dealTypingByStatusCellData:stastusData];
}
return;
}
@weakify(self)
[self preProcessMessage:cellDataList callback:^{
@strongify(self)
[self.dataSource dataProviderDataSourceWillChange:self];
@autoreleasepool {
for (TUIMessageCellData *uiMsg in cellDataList) {
[self addUIMsg:uiMsg];
[self.dataSource dataProviderDataSourceChange:self
withType:TUIMessageBaseDataProviderDataSourceChangeTypeInsert
atIndex:(self.uiMsgs_.count - 1)
animation:YES];
}
}
[self.dataSource dataProviderDataSourceDidChange:self];
if ([self.dataSource respondsToSelector:@selector(dataProvider:ReceiveNewUIMsg:)]) {
/**
* 注意这里不能取 firstObject,firstObject 有可能是展示系统时间的 SystemMessageCellData
* Note that firstObject cannot be taken here, firstObject may be SystemMessageCellData that displays system time
*/
[self.dataSource dataProvider:self ReceiveNewUIMsg:cellDataList.lastObject];
}
}];
}
- (NSMutableArray *)transUIMsgFromIMMsg:(NSArray *)msgs {
NSMutableArray *uiMsgs = [NSMutableArray array];
for (NSInteger k = msgs.count - 1; k >= 0; --k) {
V2TIMMessage *msg = msgs[k];
/**
* 不是当前会话的消息,直接忽略
* Messages that are not the current session, ignore them directly
*/
if (![msg.userID isEqualToString:self.conversationModel.userID] && ![msg.groupID isEqualToString:self.conversationModel.groupID]) {
continue;
}
TUIMessageCellData *cellData = nil;
/**
* 判断是否为外部的自定义消息
* Determine whether it is a custom message outside the component
*/
if ([self.dataSource respondsToSelector:@selector(dataProvider:CustomCellDataFromNewIMMessage:)]) {
cellData = [self.dataSource dataProvider:self CustomCellDataFromNewIMMessage:msg];
}
/**
* 判断是否为组件内部消息
* Determine whether it is a component internal message
*/
if (!cellData) {
cellData = [self.class getCellData:msg];
}
if (cellData) {
TUIMessageCellData *dateMsg = [self getSystemMsgFromDate:msg.timestamp];
if (dateMsg) {
self.msgForDate = msg;
[uiMsgs addObject:dateMsg];
}
[uiMsgs addObject:cellData];
}
}
return uiMsgs;
}
/// Received message read receipts, both in group and c2c conversation.
- (void)onRecvMessageReadReceipts:(NSArray<V2TIMMessageReceipt *> *)receiptList {
if (receiptList.count == 0) {
NSLog(@"group receipt data is empty, ignore");
return;
}
if (![self.dataSource respondsToSelector:@selector(dataProvider:ReceiveReadMsgWithGroupID:msgID:readCount:unreadCount:)]) {
NSLog(@"data source can not respond to protocol, ignore");
}
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
for (V2TIMMessageReceipt *receipt in receiptList) {
[dict setObject:receipt forKey:receipt.msgID];
}
// update TUIMessageCellData readCount/unreadCount
for (TUIMessageCellData *data in self.uiMsgs) {
if ([dict.allKeys containsObject:data.innerMessage.msgID]) {
V2TIMMessageReceipt *receipt = dict[data.innerMessage.msgID];
data.messageReceipt = receipt;
if ([self.dataSource respondsToSelector:@selector(dataProvider:ReceiveReadMsgWithGroupID:msgID:readCount:unreadCount:)]){
[self.dataSource dataProvider:self ReceiveReadMsgWithGroupID:receipt.groupID msgID:receipt.msgID readCount:receipt.readCount unreadCount:receipt.unreadCount];
}
}
}
}
- (void)onRecvMessageRevoked:(NSString *)msgID {
@weakify(self)
[TUITool dispatchMainAsync:^{
@strongify(self)
TUIMessageCellData *uiMsg = nil;
for (uiMsg in self.uiMsgs) {
if ([uiMsg.msgID isEqualToString:msgID]) {
[self.dataSource dataProviderDataSourceWillChange:self];
NSUInteger index = [self.uiMsgs indexOfObject:uiMsg];
TUIMessageCellData *revokeCellData = [self.class getRevokeCellData:uiMsg.innerMessage];
[self replaceUIMsg:revokeCellData atIndex:index];
[self.dataSource dataProviderDataSourceChange:self withType:TUIMessageBaseDataProviderDataSourceChangeTypeReload atIndex:index animation:YES];
[self.dataSource dataProviderDataSourceDidChange:self];
break;
}
}
if ([self.dataSource respondsToSelector:@selector(dataProvider:ReceiveRevokeUIMsg:)]) {
[self.dataSource dataProvider:self ReceiveRevokeUIMsg:uiMsg];
}
}];
}
- (void)onRecvMessageModified:(V2TIMMessage *)msg {
V2TIMMessage *imMsg = msg;
if (imMsg == nil || ![imMsg isKindOfClass:V2TIMMessage.class]) {
return;
}
@weakify(self)
for (TUIMessageCellData *uiMsg in self.uiMsgs) {
if ([uiMsg.msgID isEqualToString:imMsg.msgID]) {
NSMutableArray *cellDataList = [self transUIMsgFromIMMsg:@[imMsg]];
for (TUIMessageCellData *cellData in cellDataList) {
if ([cellData.msgID isEqualToString:imMsg.msgID]) {
cellData.messageReceipt = uiMsg.messageReceipt;
break;
}
}
[self preProcessMessage:cellDataList callback:^{
@strongify(self)
if (cellDataList.count > 0) {
/**
* 注意这里不能取 firstObject,firstObject 有可能是展示系统时间的 SystemMessageCellData
* Note that firstObject cannot be taken here, firstObject may be SystemMessageCellData that displays system time
*/
TUIMessageCellData *cellData = cellDataList.lastObject;
NSInteger index = [self.uiMsgs indexOfObject:uiMsg];
if (index < self.uiMsgs.count) {
[self.dataSource dataProviderDataSourceWillChange:self];
[self replaceUIMsg:cellData atIndex:index];
[self.dataSource dataProviderDataSourceChange:self
withType:TUIMessageBaseDataProviderDataSourceChangeTypeReload
atIndex:index
animation:YES];
[self.dataSource dataProviderDataSourceDidChange:self];
}
}
}];
return;
}
}
}
- (void)dealTypingByStatusCellData:(TUITypingStatusCellData *)stastusData {
if (1 == stastusData.typingStatus) {
//再次收到对方输入中的通知 则重新计时
//The timer is retimed upon receipt of the notification from the other party's input
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(resetTypingStatus) object:nil];
self.conversationModel.otherSideTyping = YES;
self.conversationModel.title = [NSString stringWithFormat:@"%@...",TIMCommonLocalizableString(TUIKitTyping)];
//如果对方没有继续输入,每隔5秒结束状态
//If the other party does not continue typing, end the status every 5 seconds
[self performSelector:@selector(resetTypingStatus) withObject:nil afterDelay:5.0];
}
else {
self.conversationModel.otherSideTyping = NO;
}
}
- (void)resetTypingStatus {
self.conversationModel.otherSideTyping = NO;
}
#pragma mark - Msgs
- (void)loadMessageSucceedBlock:(void (^)(BOOL isFirstLoad, BOOL isNoMoreMsg, NSArray<TUIMessageCellData *> *newMsgs))SucceedBlock FailBlock:(V2TIMFail)FailBlock
{
if(self.isLoadingData || self.isNoMoreMsg) {
FailBlock(ERR_SUCC, @"refreshing");
return;
}
self.isLoadingData = YES;
@weakify(self)
if (self.conversationModel.userID.length > 0) {
[[V2TIMManager sharedInstance] getC2CHistoryMessageList:self.conversationModel.userID
count:self.pageCount
lastMsg:self.lastMsg
succ:^(NSArray<V2TIMMessage *> *msgs) {
@strongify(self);
if(msgs.count != 0){
self.lastMsg = msgs[msgs.count - 1];
}
[self loadMessages:msgs SucceedBlock:SucceedBlock];
}
fail:^(int code, NSString *desc) {
@strongify(self)
self.isLoadingData = NO;
if (FailBlock) {
FailBlock(code, desc);
}
}];
}
else if (self.conversationModel.groupID.length > 0) {
[[V2TIMManager sharedInstance] getGroupHistoryMessageList:self.conversationModel.groupID
count:self.pageCount
lastMsg:self.lastMsg
succ:^(NSArray<V2TIMMessage *> *msgs) {
@strongify(self);
if(msgs.count != 0) {
self.lastMsg = msgs[msgs.count - 1];
}
[self loadMessages:msgs SucceedBlock:SucceedBlock];
}
fail:^(int code, NSString *desc) {
@strongify(self)
self.isLoadingData = NO;
if (FailBlock) {
FailBlock(code, desc);
}
}];
}
}
- (void)loadMessages:(NSArray<V2TIMMessage *> *)msgs
SucceedBlock:(void (^)(BOOL isFirstLoad, BOOL isNoMoreMsg, NSArray<TUIMessageCellData *> *newMsgs))SucceedBlock
{
NSMutableArray<TUIMessageCellData *> *uiMsgs = [self transUIMsgFromIMMsg:msgs];
@weakify(self)
[self preProcessMessage:uiMsgs callback:^{
@strongify(self)
if(msgs.count < self.pageCount) {
self.isNoMoreMsg = YES;
}
if(uiMsgs.count != 0) {
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, uiMsgs.count)];
[self insertUIMsgs:uiMsgs atIndexes:indexSet];
}
self.isLoadingData = NO;
if (SucceedBlock) {
SucceedBlock(self.isFirstLoad, self.isNoMoreMsg, uiMsgs);
}
self.isFirstLoad = NO;
}];
}
- (void)preProcessMessage:(NSArray<TUIMessageCellData *> *)uiMsgs
callback:(void(^)(void))callback {
@weakify(self)
[self preProcessReactMessage:uiMsgs reactCallback:^{
@strongify(self)
[self preProcessReplyMessageV2:uiMsgs callback:callback];
}];
}
- (void)preProcessReactMessage:(NSArray<TUIMessageCellData *> *)uiMsgs reactCallback:(void(^)(void))reactCallback
{
if (uiMsgs.count == 0) {
if (reactCallback) {
reactCallback();
}
return;
}
dispatch_group_t group = dispatch_group_create();
NSArray *arrayWithoutDuplicates = [self getIDsAboutWhoUseModifyMessage:uiMsgs];
NSMutableDictionary * modifyUserMap = [NSMutableDictionary dictionaryWithCapacity:3];
dispatch_group_enter(group);
if (self.conversationModel.groupID.length>0 && arrayWithoutDuplicates.count > 0) {
[[V2TIMManager sharedInstance] getGroupMembersInfo:self.conversationModel.groupID memberList:arrayWithoutDuplicates succ:^(NSArray<V2TIMGroupMemberFullInfo *> *memberList) {
[memberList enumerateObjectsUsingBlock:^(V2TIMGroupMemberFullInfo * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
TUITagsUserModel * userModel = [[TUITagsUserModel alloc] init];
userModel.userID = obj.userID;
userModel.friendRemark = obj.friendRemark;
userModel.nameCard = obj.nameCard;
userModel.nickName = obj.nickName;
userModel.faceURL = obj.faceURL;
if (userModel && userModel.userID.length >0 ) {
[modifyUserMap setObject:userModel forKey:userModel.userID];
}
}];
dispatch_group_leave(group);
} fail:^(int code, NSString *desc) {
dispatch_group_leave(group);
}];
}
else {
[[V2TIMManager sharedInstance] getFriendsInfo:arrayWithoutDuplicates succ:^(NSArray<V2TIMFriendInfoResult *> *resultList) {
[resultList enumerateObjectsUsingBlock:^(V2TIMFriendInfoResult * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
TUITagsUserModel * userModel = [[TUITagsUserModel alloc] init];
userModel.userID = obj.friendInfo.userID;
userModel.nickName = obj.friendInfo.userFullInfo.nickName;
userModel.friendRemark = obj.friendInfo.friendRemark;
userModel.faceURL = obj.friendInfo.userFullInfo.faceURL;
if (userModel && userModel.userID.length >0 ) {
[modifyUserMap setObject:userModel forKey:userModel.userID];
}
}];
dispatch_group_leave(group);
} fail:^(int code, NSString *desc) {
dispatch_group_leave(group);
}];
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
for (TUIMessageCellData *cellData in uiMsgs) {
TUIMessageCellData *myData = (TUIMessageCellData *)cellData;
if([modifyUserMap allKeys].count > 0) {
myData.messageModifyUserInfos = modifyUserMap;
}
__weak typeof(myData) weakMyData = myData;
static TUIMessageCell * cell = nil;
if (cell == nil) {
cell = [[TUIMessageCell alloc] initWithFrame:CGRectZero];
}
if ([myData.messageModifyReacts isKindOfClass:NSDictionary.class] && [myData.messageModifyReacts allKeys].count >0 ) {
[cell prepareReactTagUI:cell.container];
[cell fillWithData:myData];
[cell layoutIfNeeded];
[cell.tagView updateView];
weakMyData.messageModifyReactsSize = cell.tagView.frame.size;
}
}
if (reactCallback) {
reactCallback();
}
});
}
- (void)preProcessReplyMessageV2:(NSArray<TUIMessageCellData *> *)uiMsgs callback:(void(^)(void))callback
{
return;
}
// Find all ids that who use React Emoji / reply
- (NSArray *)getIDsAboutWhoUseModifyMessage:(NSArray<TUIMessageCellData *> *)uiMsgs {
NSMutableArray *hasReactArray = [NSMutableArray arrayWithCapacity:3];
for (TUIMessageCellData *cellData in uiMsgs) {
TUIMessageCellData *myData = (TUIMessageCellData *)cellData;
// Emoji React
if ([myData.messageModifyReacts isKindOfClass:NSDictionary.class] && [myData.messageModifyReacts allKeys].count >0 ) {
[myData.messageModifyReacts enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
if (obj &&[obj isKindOfClass:NSArray.class] ) {
NSArray *arr = (NSArray *)obj;
if (arr.count >0) {
[hasReactArray addObjectsFromArray:obj];
}
}
}];
}
// Replies
if ([myData.messageModifyReplies isKindOfClass:NSArray.class] && myData.messageModifyReplies.count >0 ) {
[myData.messageModifyReplies enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj &&[obj isKindOfClass:NSDictionary.class] ) {
NSDictionary *dic = (NSDictionary *)obj;
if (IS_NOT_EMPTY_NSSTRING(dic[@"messageSender"])) {
[hasReactArray addObject:dic[@"messageSender"]];
}
}
}];
}
}
NSSet *set = [NSSet setWithArray:hasReactArray];
NSArray *arrayWithoutOrder = [set allObjects];
return arrayWithoutOrder;
}
- (void)sendUIMsg:(TUIMessageCellData *)uiMsg
toConversation:(TUIChatConversationModel *)conversationData
willSendBlock:(void(^)(BOOL isReSend, TUIMessageCellData *dateUIMsg))willSendBlock
SuccBlock:(nullable V2TIMSucc)succ
FailBlock:(nullable V2TIMFail)fail
{
[self preProcessMessage:@[uiMsg] callback:^{
[TUITool dispatchMainAsync:^{
V2TIMMessage *imMsg = uiMsg.innerMessage;
TUIMessageCellData *dateMsg = nil;
BOOL isReSent = NO;
if (uiMsg.status == Msg_Status_Init) {
// New message
dateMsg = [self getSystemMsgFromDate:imMsg.timestamp];
} else if (imMsg) {
// Re-sent
isReSent = YES;
dateMsg = [self getSystemMsgFromDate:[NSDate date]];
} else {
if (fail) {
fail(ERR_INVALID_PARAMETERS, @"Unknown message state");
}
return;
}
imMsg.isExcludedFromUnreadCount = [TUIConfig defaultConfig].isExcludedFromUnreadCount;
imMsg.isExcludedFromLastMessage = [TUIConfig defaultConfig].isExcludedFromLastMessage;
// Update send status
uiMsg.status = Msg_Status_Sending;
// Update sender
uiMsg.identifier = [TUILogin getUserID];
[self.dataSource dataProviderDataSourceWillChange:self];
// Handle data
if (isReSent) {
NSInteger row = [self.uiMsgs indexOfObject:uiMsg];
[self removeUImsgAtIndex:row];
[self.dataSource dataProviderDataSourceChange:self
withType:TUIMessageBaseDataProviderDataSourceChangeTypeDelete
atIndex:row
animation:YES];
}
if (dateMsg) {
[self addUIMsg:dateMsg];
[self.dataSource dataProviderDataSourceChange:self
withType:TUIMessageBaseDataProviderDataSourceChangeTypeInsert
atIndex:(self.uiMsgs.count - 1)
animation:YES];
}
[self addUIMsg:uiMsg];
[self.dataSource dataProviderDataSourceChange:self
withType:TUIMessageBaseDataProviderDataSourceChangeTypeInsert
atIndex:(self.uiMsgs.count - 1)
animation:self];
[self.dataSource dataProviderDataSourceDidChange:self];
if (willSendBlock) {
willSendBlock(isReSent, dateMsg);
}
if (dateMsg) {
self.msgForDate = imMsg;
}
uiMsg.msgID = [self.class sendMessage:imMsg
toConversation:conversationData
isSendPushInfo:YES
isOnlineUserOnly:NO
priority:V2TIM_PRIORITY_NORMAL
Progress:^(uint32_t progress) {
[TUIMessageProgressManager.shareManager appendProgress:uiMsg.msgID progress:progress];
}
SuccBlock:^{
if (succ) {
succ();
}
[TUIMessageProgressManager.shareManager notifyMessageSendingResult:uiMsg.msgID
result:TUIMessageSendingResultTypeSucc];
} FailBlock:^(int code, NSString *desc) {
if (fail) {
fail(code, desc);
}
[TUIMessageProgressManager.shareManager notifyMessageSendingResult:uiMsg.msgID
result:TUIMessageSendingResultTypeFail];
}];
uiMsg.name = [self.class getShowName:uiMsg.innerMessage];
/**
* 注意:innerMessage.faceURL 在sendMessage 内部赋值,所以需要放在最后面。 TUIMessageCell 内部监听了 avatarUrl 的变更,所以不需要再次刷新。
* Notes: innerMessage.faceURL is assigned inside sendMessage, so it needs to be last. TUIMessageCell internally monitors changes to avatarUrl, so it doesn't need to be refreshed again.
*/
uiMsg.avatarUrl = [NSURL URLWithString:[uiMsg.innerMessage faceURL]];
/**
* 发送消息需要携带【identifier】,否则再次发送消息,点击【我】的头像会导致无法进入的个人信息页面
* Sending a message needs to carry [identifier], otherwise sending the message again, clicking on the avatar of [Me] will result in an inaccessible personal information page
*/
uiMsg.identifier = [uiMsg.innerMessage sender];
}];
}];
}
- (void)revokeUIMsg:(TUIMessageCellData *)uiMsg
SuccBlock:(nullable V2TIMSucc)succ
FailBlock:(nullable V2TIMFail)fail {
V2TIMMessage *imMsg = uiMsg.innerMessage;
if (imMsg == nil) {
if (fail) {
fail(ERR_INVALID_PARAMETERS, @"cellData.innerMessage is nil");
}
return;
}
NSInteger index = [self.uiMsgs indexOfObject:uiMsg];
if (index == NSNotFound) {
if (fail) {
fail(ERR_INVALID_PARAMETERS, @"not found cellData in uiMsgs");
}
return;
}
@weakify(self);
[self.class revokeMessage:imMsg succ:^{
@strongify(self);
[self onRecvMessageRevoked:imMsg.msgID];
if (succ) {
succ();
}
} fail:fail];
}
- (void)deleteUIMsgs:(NSArray<TUIMessageCellData *> *)uiMsgs
SuccBlock:(nullable V2TIMSucc)succ
FailBlock:(nullable V2TIMFail)fail {
}
- (void)addUIMsg:(TUIMessageCellData *)cellData {
[self.uiMsgs_ addObject:cellData];
}
- (void)removeUIMsg:(TUIMessageCellData *)cellData {
if (cellData) {
[self.uiMsgs_ removeObject:cellData];
[self.heightCache_ removeObjectForKey:[self heightCacheKeyFromMsg:cellData]];
}
}
- (void)insertUIMsgs:(NSArray<TUIMessageCellData *> *)uiMsgs atIndexes:(NSIndexSet *)indexes {
[self.uiMsgs_ insertObjects:uiMsgs atIndexes:indexes];
}
- (void)addUIMsgs:(NSArray<TUIMessageCellData *> *)uiMsgs {
[self.uiMsgs_ addObjectsFromArray:uiMsgs];
}
- (void)removeUIMsgList:(NSArray<TUIMessageCellData *> *)cellDatas {
for (TUIMessageCellData *uiMsg in cellDatas) {
[self removeUIMsg:uiMsg];
}
}
- (void)removeUImsgAtIndex:(NSUInteger)index {
if (index < self.uiMsgs.count) {
TUIMessageCellData *msg = self.uiMsgs[index];
[self removeUIMsg:msg];
}
}
- (void)clearUIMsgList {
NSArray *clearArray = [NSArray arrayWithArray:self.uiMsgs];
[self removeUIMsgList:clearArray];
self.msgForDate = nil;
self.uiMsgs_ = nil;
}
- (void)replaceUIMsg:(TUIMessageCellData *)cellData atIndex:(NSUInteger)index {
if (index < self.uiMsgs.count) {
TUIMessageCellData *oldMsg = self.uiMsgs[index];
[self.heightCache_ removeObjectForKey:[self heightCacheKeyFromMsg:oldMsg]];
[self.uiMsgs_ replaceObjectAtIndex:index withObject:cellData];
} else {
[self addUIMsg:cellData];
}
}
- (BOOL)removeHeightCache:(NSUInteger)index {
if (index < self.uiMsgs.count) {
TUIMessageCellData *oldMsg = self.uiMsgs[index];
NSString *key = [self heightCacheKeyFromMsg:oldMsg];
if ([self.heightCache_ objectForKey:key]) {
[self.heightCache_ removeObjectForKey:key];
return YES;
}
}
return NO;
}
- (BOOL)removeHeightCacheOfData:(TUIMessageCellData *)data {
for (TUIMessageCellData *cellData in self.uiMsgs) {
if (data != nil && cellData == data) {
NSString *key = [self heightCacheKeyFromMsg:data];
if ([self.heightCache_ objectForKey:key]) {
[self.heightCache_ removeObjectForKey:key];
return YES;
}
}
}
return NO;
}
- (CGFloat)getCellDataHeightAtIndex:(NSUInteger)index Width:(CGFloat)width {
CGFloat height = 0;
@autoreleasepool {
if (index < self.uiMsgs_.count) {
TUIMessageCellData *msg = self.uiMsgs_[index];
NSString *key = [self heightCacheKeyFromMsg:msg];
height = [[self.heightCache_ objectForKey:key] floatValue];
if (height == 0) {
height = [msg heightOfWidth:width];
[self.heightCache_ setObject:@(height) forKey:key];
}
}
}
return height;
}
- (NSString *)heightCacheKeyFromMsg:(TUIMessageCellData *)msg {
return msg.msgID.length == 0 ? [NSString stringWithFormat:@"%p", msg] : msg.msgID;
}
- (CGFloat)getEstimatedHeightForRowAtIndex:(NSUInteger)index {
@autoreleasepool {
if (index < self.uiMsgs_.count) {
TUIMessageCellData *msg = self.uiMsgs_[index];
CGFloat height = [[self.heightCache_ objectForKey:[self heightCacheKeyFromMsg:msg]] floatValue];
if (height == 0) {
height = [msg estimatedHeight];
}
return height;
} else {
return 60;
}
}
}
- (void)sendLatestMessageReadReceipt {
[self sendMessageReadReceiptAtIndexes:@[@(self.uiMsgs.count - 1)]];
}
- (void)sendMessageReadReceiptAtIndexes:(NSArray *)indexes {
if (indexes.count == 0) {
NSLog(@"sendMessageReadReceipt, but indexes is empty, ignore");
return;
}
NSMutableArray *array = [NSMutableArray array];
for (NSNumber *i in indexes) {
if ([i intValue] < 0 || [i intValue] >= self.uiMsgs_.count) {
continue;
}
TUIMessageCellData *data = self.uiMsgs_[[i intValue]];
if (data.innerMessage.isSelf) {
continue;
}
if (data.innerMessage == nil) {
continue;
}
// Use Set to avoid sending duplicate element to SDK
if (data.msgID.length > 0) {
if ([self.sentReadGroupMsgSet containsObject:data.msgID]) {
continue;
} else {
[self.sentReadGroupMsgSet addObject:data.msgID];
}
}
// If needReadReceipt is NO, receiver won't send message read receipt
if (!data.innerMessage.needReadReceipt) {
continue;
}
[array addObject:data.innerMessage];
}
if (array.count == 0) {
return;
}
[self.class sendMessageReadReceipts:array];
}
- (NSInteger)getIndexOfMessage:(NSString *)msgID {
if (msgID.length == 0){
return -1;
}
for (int i = 0; i < self.uiMsgs.count; i++) {
TUIMessageCellData *data = self.uiMsgs[i];
if ([data.msgID isEqualToString:msgID]) {
return i;
}
}
return -1;
}
- (nullable TUIMessageCellData *)getSystemMsgFromDate:(NSDate *)date {
if (self.msgForDate == nil
|| fabs([date timeIntervalSinceDate:self.msgForDate.timestamp]) > MaxDateMessageDelay) {
TUIMessageCellData *system = [self.class getSystemMsgFromDate:date];
return system;
}
return nil;
}
@end
@implementation TUIMessageBaseDataProvider (IMSDK)
+ (NSString *)sendMessage:(V2TIMMessage *)message
toConversation:(TUIChatConversationModel *)conversationData
isSendPushInfo:(BOOL)isSendPushInfo
isOnlineUserOnly:(BOOL)isOnlineUserOnly
priority:(V2TIMMessagePriority)priority
Progress:(nullable V2TIMProgress)progress
SuccBlock:(nullable V2TIMSucc)succ
FailBlock:(nullable V2TIMFail)fail
{
NSString *userID = conversationData.userID;
NSString *groupID = conversationData.groupID;
NSAssert(userID || groupID, @"userID and groupID cannot be null at same time");
NSString *conversationID = @"";
if (IS_NOT_EMPTY_NSSTRING(userID)) {
conversationID = [NSString stringWithFormat:@"c2c_%@", userID];
}
if (IS_NOT_EMPTY_NSSTRING(groupID)) {
conversationID = [NSString stringWithFormat:@"group_%@",groupID];
}
if (IS_NOT_EMPTY_NSSTRING(conversationData.conversationID)) {
conversationID = conversationData.conversationID;
}
NSParameterAssert(message);
V2TIMOfflinePushInfo *pushInfo = nil;
if (isSendPushInfo) {
pushInfo = [[V2TIMOfflinePushInfo alloc] init];
BOOL isGroup = groupID.length > 0;
NSString *senderId = isGroup ? (groupID) : ([TUILogin getUserID]);
senderId = senderId?:@"";
NSString *nickName = isGroup ? (conversationData.title) : ([TUILogin getNickName]?:[TUILogin getUserID]);
nickName = nickName ?: @"";
NSDictionary *ext = @{
@"entity": @{
@"action": @1,
@"content": [self getDisplayString:message]?:@"",
@"sender": senderId,
@"nickname": nickName,
@"faceUrl": [TUILogin getFaceUrl]?:@"",
@"chatType": isGroup?@(V2TIM_GROUP):@(V2TIM_C2C)
}
};
NSData *data = [NSJSONSerialization dataWithJSONObject:ext options:NSJSONWritingPrettyPrinted error:nil];
pushInfo.ext = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
pushInfo.AndroidOPPOChannelID = @"tuikit";
pushInfo.AndroidSound = TUIConfig.defaultConfig.enableCustomRing ? @"private_ring" : nil;
pushInfo.AndroidHuaWeiCategory = @"IM";
pushInfo.AndroidVIVOCategory = @"IM";
}
if ([self isGroupCommunity:conversationData.groupType groupID:conversationData.groupID] ||
[self isGroupAVChatRoom:conversationData.groupType]) {
message.needReadReceipt = NO;
}
// 被隐藏的会话从通讯录入口唤起聊天页面-发送消息,需要清空被隐藏标记
// Hidden conversation evokes the chat page from the address book entry - to send a message, the hidden flag needs to be cleared
if (conversationID.length>0) {
[V2TIMManager.sharedInstance markConversation:@[conversationID] markType:@(V2TIM_CONVERSATION_MARK_TYPE_HIDE) enableMark:NO succ:nil fail:nil];
}
if (conversationData.userID.length > 0) {
//C2C
NSDictionary * cloudCustomDataDic = @{
@"needTyping":@1,
@"version":@1,
};
[message setCloudCustomData:cloudCustomDataDic forType:messageFeature];
}
return [V2TIMManager.sharedInstance sendMessage:message
receiver:userID
groupID:groupID
priority:priority
onlineUserOnly:isOnlineUserOnly
offlinePushInfo:pushInfo
progress:progress
succ:^{
succ();
}
fail:^(int code, NSString *desc) {
if (code == ERR_SDK_INTERFACE_NOT_SUPPORT) {
[TUITool postUnsupportNotificationOfService:TUIKitLocalizableString(TUIKitErrorUnsupportIntefaceMessageRead)];
}
fail(code, desc);
}];
}
+ (BOOL)isGroupCommunity:(NSString *)groupType groupID:(NSString *)groupID {
return [groupType isEqualToString:@"Community"] || [groupID startsWith:@"@TGS#_@TGS#"];
}
+ (BOOL)isGroupAVChatRoom:(NSString *)groupType {
return [groupType isEqualToString:@"AVChatRoom"];
}
+ (void)markC2CMessageAsRead:(NSString *)userID
succ:(nullable V2TIMSucc)succ
fail:(nullable V2TIMFail)fail {
[[V2TIMManager sharedInstance] markC2CMessageAsRead:userID succ:succ fail:fail];
}
+ (void)markGroupMessageAsRead:(NSString *)groupID
succ:(nullable V2TIMSucc)succ
fail:(nullable V2TIMFail)fail {
[[V2TIMManager sharedInstance] markGroupMessageAsRead:groupID succ:succ fail:fail];
}
+ (void)markConversationAsUndead:(NSArray<NSString *> *)conversationIDList enableMark:(BOOL)enableMark {
[V2TIMManager.sharedInstance markConversation:conversationIDList markType:@(V2TIM_CONVERSATION_MARK_TYPE_UNREAD) enableMark:enableMark succ:nil fail:nil];
}
+ (void)revokeMessage:(V2TIMMessage *)msg
succ:(nullable V2TIMSucc)succ
fail:(nullable V2TIMFail)fail {
[[V2TIMManager sharedInstance] revokeMessage:msg succ:succ fail:fail];
}
+ (void)deleteMessages:(NSArray<V2TIMMessage *>*)msgList
succ:(nullable V2TIMSucc)succ
fail:(nullable V2TIMFail)fail{
[[V2TIMManager sharedInstance] deleteMessages:msgList succ:succ fail:fail];
}
+ (void)modifyMessage:(V2TIMMessage *)msg
completion:(V2TIMMessageModifyCompletion)completion {
[[V2TIMManager sharedInstance] modifyMessage:msg completion:completion];
}
+ (void)sendMessageReadReceipts:(NSArray *)msgs {
[[V2TIMManager sharedInstance] sendMessageReadReceipts:msgs succ:^{
NSLog(@"sendMessageReadReceipts succeed");
} fail:^(int code, NSString *desc) {
if (code == ERR_SDK_INTERFACE_NOT_SUPPORT) {
[TUITool postUnsupportNotificationOfService:TUIKitLocalizableString(TUIKitErrorUnsupportIntefaceMessageRead)];
}
}];
}
+ (void)getReadMembersOfMessage:(V2TIMMessage *)msg
filter:(V2TIMGroupMessageReadMembersFilter)filter
nextSeq:(NSUInteger)nextSeq
completion:(void (^)(int code, NSString *desc, NSArray *members, NSUInteger nextSeq, BOOL isFinished))block {
[[V2TIMManager sharedInstance] getGroupMessageReadMemberList:msg
filter:filter
nextSeq:nextSeq
count:100
succ:^(NSMutableArray<V2TIMGroupMemberInfo *> *members, uint64_t nextSeq, BOOL isFinished) {
if (block) {
block(0, nil, members, nextSeq, isFinished);
}
} fail:^(int code, NSString *desc) {
if (block) {
block(code, desc, nil, 0, NO);
}
}];
}
+ (void)getMessageReadReceipt:(NSArray *)messages
succ:(nullable V2TIMMessageReadReceiptsSucc)succ
fail:(nullable V2TIMFail)fail {
if (messages.count == 0) {
if (fail) {
fail(-1, @"messages empty");
}
return;
}
[[V2TIMManager sharedInstance] getMessageReadReceipts:messages succ:succ fail:fail];
}
+ (TUIMessageCellData * __nullable)getCellData:(V2TIMMessage *)message {
// subclass override required
return nil;
}
+ (nullable TUIMessageCellData *)getSystemMsgFromDate:(NSDate *)date {
// subclass override required
return nil;
}
+ (TUIMessageCellData *)getRevokeCellData:(V2TIMMessage *)message {
// subclass override required
return nil;
}
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
// subclass override required
return nil;
}
+ (NSString *)getRevokeDispayString:(V2TIMMessage *)message {
NSString *str = nil;
if(message.isSelf){
str = TIMCommonLocalizableString(TUIKitMessageTipsYouRecallMessage);
}
else if(message.groupID != nil){
NSString *userString = message.nameCard;;
if(userString.length == 0){
userString = message.nickName;
}
if (userString.length == 0) {
userString = message.sender;
}
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsRecallMessageFormat), userString];
}
else if(message.userID != nil){
str = TIMCommonLocalizableString(TUIkitMessageTipsOthersRecallMessage);
}
return str;
}
+ (NSString *)getGroupTipsDisplayString:(V2TIMMessage *)message {
V2TIMGroupTipsElem *tips = message.groupTipsElem;
NSString *opUser = [self getOpUserName:tips.opMember];
NSMutableArray<NSString *> *userList = [self getUserNameList:tips.memberList];
NSString *str = nil;
switch (tips.type) {
case V2TIM_GROUP_TIPS_TYPE_JOIN:
{
if (opUser.length > 0) {
if ((userList.count == 0) ||
(userList.count == 1 && [opUser isEqualToString:userList.firstObject])) {
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsJoinGroupFormat), opUser];
} else {
NSString *users = [userList componentsJoinedByString:@"、"];
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsInviteJoinGroupFormat), opUser, users];
}
}
}
break;
case V2TIM_GROUP_TIPS_TYPE_INVITE:
{
if (userList.count > 0) {
NSString *users = [userList componentsJoinedByString:@"、"];
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsInviteJoinGroupFormat), opUser, users];
}
}
break;
case V2TIM_GROUP_TIPS_TYPE_QUIT:
{
if (opUser.length > 0) {
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsLeaveGroupFormat), opUser];
}
}
break;
case V2TIM_GROUP_TIPS_TYPE_KICKED:
{
if (userList.count > 0) {
NSString *users = [userList componentsJoinedByString:@"、"];
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsKickoffGroupFormat), opUser, users];
}
}
break;
case V2TIM_GROUP_TIPS_TYPE_SET_ADMIN:
{
if (userList.count > 0) {
NSString *users = [userList componentsJoinedByString:@"、"];
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsSettAdminFormat), users];
}
}
break;
case V2TIM_GROUP_TIPS_TYPE_CANCEL_ADMIN:
{
if (userList.count > 0) {
NSString *users = [userList componentsJoinedByString:@"、"];
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsCancelAdminFormat), users];
}
}
break;
case V2TIM_GROUP_TIPS_TYPE_GROUP_INFO_CHANGE:
{
str = [NSString stringWithFormat:@"\"%@\"", opUser];
for (V2TIMGroupChangeInfo *info in tips.groupChangeInfoList) {
switch (info.type) {
case V2TIM_GROUP_INFO_CHANGE_TYPE_NAME:
{
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIkitMessageTipsEditGroupNameFormat), str, info.value];
}
break;
case V2TIM_GROUP_INFO_CHANGE_TYPE_INTRODUCTION:
{
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsEditGroupIntroFormat), str, info.value];
}
break;
case V2TIM_GROUP_INFO_CHANGE_TYPE_NOTIFICATION:
{
if (info.value.length) {
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsEditGroupAnnounceFormat), str, info.value];
} else {
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsDeleteGroupAnnounceFormat), str];
}
}
break;
case V2TIM_GROUP_INFO_CHANGE_TYPE_FACE:
{
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsEditGroupAvatarFormat), str];
}
break;
case V2TIM_GROUP_INFO_CHANGE_TYPE_OWNER:
{
if (userList.count) {
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsEditGroupOwnerFormat), str, userList.firstObject];
} else {
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsEditGroupOwnerFormat), str, info.value];
}
}
break;
case V2TIM_GROUP_INFO_CHANGE_TYPE_SHUT_UP_ALL:
{
if (info.boolValue) {
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitSetShutupAllFormat), opUser];
} else {
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitCancelShutupAllFormat), opUser];
}
}
break;
case V2TIM_GROUP_INFO_CHANGE_TYPE_GROUP_ADD_OPT:
{
uint32_t addOpt = info.intValue;
NSString *addOptDesc = @"unknown";
if (addOpt == V2TIM_GROUP_ADD_FORBID) {
addOptDesc = TIMCommonLocalizableString(TUIKitGroupProfileJoinDisable);
} else if (addOpt == V2TIM_GROUP_ADD_AUTH) {
addOptDesc = TIMCommonLocalizableString(TUIKitGroupProfileAdminApprove);
} else if (addOpt == V2TIM_GROUP_ADD_ANY) {
addOptDesc = TIMCommonLocalizableString(TUIKitGroupProfileAutoApproval);
}
str = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsEditGroupAddOptFormat), str, addOptDesc];
}
break;
case V2TIM_GROUP_INFO_CHANGE_TYPE_GROUP_APPROVE_OPT:
{
uint32_t addOpt = info.intValue;
NSString *addOptDesc = @"unknown";
if (addOpt == V2TIM_GROUP_ADD_FORBID) {
addOptDesc = TUIKitLocalizableString(TUIKitGroupProfileJoinDisable);
} else if (addOpt == V2TIM_GROUP_ADD_AUTH) {
addOptDesc = TUIKitLocalizableString(TUIKitGroupProfileAdminApprove);
} else if (addOpt == V2TIM_GROUP_ADD_ANY) {
addOptDesc = TUIKitLocalizableString(TUIKitGroupProfileAutoApproval);
}
str = [NSString stringWithFormat:TUIKitLocalizableString(TUIKitMessageTipsEditGroupInviteOptFormat), str, addOptDesc];
}
break;
default:
break;
}
}
if (str.length > 0) {
str = [str substringToIndex:str.length - 1];
}
}
break;
case V2TIM_GROUP_TIPS_TYPE_MEMBER_INFO_CHANGE:
{
for (V2TIMGroupChangeInfo *info in tips.memberChangeInfoList) {
if ([info isKindOfClass:V2TIMGroupMemberChangeInfo.class]) {
NSString *userId = [(V2TIMGroupMemberChangeInfo *)info userID];
int32_t muteTime = [(V2TIMGroupMemberChangeInfo *)info muteTime];
NSString *myId = V2TIMManager.sharedInstance.getLoginUser;
NSString *showName = [self.class getUserName:tips with:userId];
str = [NSString stringWithFormat:@"%@ %@", [userId isEqualToString:myId] ? TIMCommonLocalizableString(You) : showName, muteTime == 0 ? TIMCommonLocalizableString(TUIKitMessageTipsUnmute): TIMCommonLocalizableString(TUIKitMessageTipsMute)];
break;
}
}
}
break;
default:
break;
}
return str;
}
+ (V2TIMMessage *)getCustomMessageWithJsonData:(NSData *)data {
return [[V2TIMManager sharedInstance] createCustomMessage:data];
}
+ (NSString *)getOpUserName:(V2TIMGroupMemberInfo *)info{
NSString *opUser;
if (info.nameCard.length > 0){
opUser = info.nameCard;
} else if (info.nickName.length > 0) {
opUser = info.nickName;
} else {
opUser = info.userID;
}
return opUser;
}
+ (NSMutableArray *)getUserNameList:(NSArray<V2TIMGroupMemberInfo *> *)infoList{
NSMutableArray<NSString *> *userNameList = [NSMutableArray array];
for (V2TIMGroupMemberInfo *info in infoList) {
if(info.nameCard.length > 0) {
[userNameList addObject:info.nameCard];
} else if (info.nickName.length > 0){
[userNameList addObject:info.nickName];
}else{
if (info.userID.length > 0) {
[userNameList addObject:info.userID];
}
}
}
return userNameList;
}
+ (NSMutableArray *)getUserIDList:(NSArray<V2TIMGroupMemberInfo *> *)infoList{
NSMutableArray<NSString *> *userIDList = [NSMutableArray array];
for (V2TIMGroupMemberInfo *info in infoList) {
if (info.userID.length > 0) {
[userIDList addObject:info.userID];
}
}
return userIDList;
}
+ (NSString *)getShowName:(V2TIMMessage *)message {
NSString *showName = message.sender;
if (message.nameCard.length > 0) {
showName = message.nameCard;
} else if (message.friendRemark.length > 0) {
showName = message.friendRemark;
} else if (message.nickName.length > 0) {
showName = message.nickName;
}
return showName;
}
+ (NSString *)getUserName:(V2TIMGroupTipsElem *)tips with:(NSString *)userId{
NSString *str = @"";
for (V2TIMGroupMemberInfo *info in tips.memberList) {
if ([info.userID isEqualToString:userId]) {
if (info.nameCard.length > 0) {
str = info.nameCard;
} else if (info.friendRemark.length > 0) {
str = info.friendRemark;
} else if (info.nickName.length > 0) {
str = info.nickName;
}
else {
str = userId;
}
break;
}
}
return str;
}
@end