增加换肤功能
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// TUIConversationListDataProvider.h
|
||||
// TUIConversation
|
||||
//
|
||||
// Created by harvy on 2022/7/14.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "TUIConversationListBaseDataProvider.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIConversationListDataProvider : TUIConversationListBaseDataProvider
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,179 @@
|
||||
//
|
||||
// V2TUIConversationListDataProvider.m
|
||||
// TUIConversation
|
||||
//
|
||||
// Created by harvy on 2022/7/14.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIConversationListDataProvider.h"
|
||||
#import <TIMCommon/NSString+TUIEmoji.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
#import "TUIConversationCellData.h"
|
||||
|
||||
@implementation TUIConversationListDataProvider
|
||||
- (Class)getConversationCellClass {
|
||||
return [TUIConversationCellData class];
|
||||
}
|
||||
|
||||
- (void)asnycGetLastMessageDisplay:(NSArray<TUIConversationCellData *> *)duplicateDataList addedDataList:(NSArray<TUIConversationCellData *> *)addedDataList {
|
||||
NSMutableArray *allConversationList = [NSMutableArray array];
|
||||
[allConversationList addObjectsFromArray:duplicateDataList];
|
||||
[allConversationList addObjectsFromArray:addedDataList];
|
||||
|
||||
NSMutableArray *messageList = [NSMutableArray array];
|
||||
for (TUIConversationCellData *cellData in allConversationList) {
|
||||
if (cellData.lastMessage && cellData.lastMessage.msgID) {
|
||||
[messageList addObject:cellData.lastMessage];
|
||||
}
|
||||
}
|
||||
|
||||
if (messageList.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
NSDictionary *param = @{TUICore_TUIChatService_AsyncGetDisplayStringMethod_MsgListKey : messageList};
|
||||
[TUICore callService:TUICore_TUIChatService
|
||||
method:TUICore_TUIChatService_AsyncGetDisplayStringMethod
|
||||
param:param
|
||||
resultCallback:^(NSInteger errorCode, NSString *_Nonnull errorMessage, NSDictionary *_Nonnull param) {
|
||||
if (0 != errorCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// cache
|
||||
NSMutableDictionary *dictM = [NSMutableDictionary dictionaryWithDictionary:weakSelf.lastMessageDisplayMap];
|
||||
[param enumerateKeysAndObjectsUsingBlock:^(NSString *msgID, NSString *displayString, BOOL *_Nonnull stop) {
|
||||
[dictM setObject:displayString forKey:msgID];
|
||||
}];
|
||||
weakSelf.lastMessageDisplayMap = [NSDictionary dictionaryWithDictionary:dictM];
|
||||
|
||||
// Refresh if needed
|
||||
NSMutableArray *needRefreshConvList = [NSMutableArray array];
|
||||
for (TUIConversationCellData *cellData in allConversationList) {
|
||||
if (cellData.lastMessage && cellData.lastMessage.msgID && [param.allKeys containsObject:cellData.lastMessage.msgID]) {
|
||||
cellData.subTitle = [self getLastDisplayString:cellData.innerConversation];
|
||||
cellData.foldSubTitle = [self getLastDisplayStringForFoldList:cellData.innerConversation];
|
||||
[needRefreshConvList addObject:cellData];
|
||||
}
|
||||
}
|
||||
NSMutableDictionary<NSString *, NSNumber *> *conversationMap = [NSMutableDictionary dictionary];
|
||||
for (TUIConversationCellData *item in weakSelf.conversationList) {
|
||||
if (item.conversationID) {
|
||||
[conversationMap setObject:@([weakSelf.conversationList indexOfObject:item]) forKey:item.conversationID];
|
||||
}
|
||||
}
|
||||
[weakSelf handleUpdateConversationList:needRefreshConvList positions:conversationMap];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSString *)getDisplayStringFromService:(V2TIMMessage *)msg {
|
||||
// from cache
|
||||
NSString *displayString = [self.lastMessageDisplayMap objectForKey:msg.msgID];
|
||||
if (displayString.length > 0) {
|
||||
return displayString;
|
||||
}
|
||||
|
||||
// from TUIChat
|
||||
NSDictionary *param = @{TUICore_TUIChatService_GetDisplayStringMethod_MsgKey : msg};
|
||||
return [TUICore callService:TUICore_TUIChatService method:TUICore_TUIChatService_GetDisplayStringMethod param:param];
|
||||
}
|
||||
|
||||
- (NSMutableAttributedString *)getLastDisplayString:(V2TIMConversation *)conv {
|
||||
/**
|
||||
* If has group-at message, the group-at information will be displayed first
|
||||
*/
|
||||
NSString *atStr = [self getGroupAtTipString:conv];
|
||||
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:atStr];
|
||||
NSDictionary *attributeDict = @{NSForegroundColorAttributeName : [UIColor d_systemRedColor]};
|
||||
[attributeString setAttributes:attributeDict range:NSMakeRange(0, attributeString.length)];
|
||||
BOOL hasRiskContent = conv.lastMessage.hasRiskContent;
|
||||
BOOL isRevoked = (conv.lastMessage.status == V2TIM_MSG_STATUS_LOCAL_REVOKED);
|
||||
|
||||
/**
|
||||
* If there is a draft box, the draft box information will be displayed first
|
||||
*/
|
||||
if (conv.draftText.length > 0) {
|
||||
NSAttributedString *draft = [[NSAttributedString alloc] initWithString:TIMCommonLocalizableString(TUIKitMessageTypeDraftFormat)
|
||||
attributes:@{NSForegroundColorAttributeName : RGB(250, 81, 81)}];
|
||||
[attributeString appendAttributedString:draft];
|
||||
|
||||
NSString *draftContentStr = [self getDraftContent:conv];
|
||||
draftContentStr = [draftContentStr getLocalizableStringWithFaceContent];
|
||||
NSAttributedString *draftContent = [[NSAttributedString alloc] initWithString:draftContentStr
|
||||
attributes:@{NSForegroundColorAttributeName : [UIColor d_systemGrayColor]}];
|
||||
|
||||
[attributeString appendAttributedString:draftContent];
|
||||
} else {
|
||||
/**
|
||||
* No drafts, show conversation lastMsg information
|
||||
*/
|
||||
NSString *lastMsgStr = @"";
|
||||
|
||||
/**
|
||||
* Attempt to get externally customized display information
|
||||
*/
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(getConversationDisplayString:)]) {
|
||||
lastMsgStr = [self.delegate getConversationDisplayString:conv];
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is no external customization, get the lastMsg display information through the message module
|
||||
*/
|
||||
if (lastMsgStr.length == 0 && conv.lastMessage) {
|
||||
lastMsgStr = [self getDisplayStringFromService:conv.lastMessage];
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is no lastMsg display information and no draft information, return nil directly
|
||||
*/
|
||||
if (lastMsgStr.length == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (hasRiskContent && !isRevoked) {
|
||||
[attributeString appendAttributedString:[[NSAttributedString alloc] initWithString:lastMsgStr
|
||||
attributes:@{NSForegroundColorAttributeName : RGB(233, 68, 68)}]];
|
||||
} else {
|
||||
[attributeString appendAttributedString:[[NSAttributedString alloc] initWithString:lastMsgStr]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Meeting V2TIM_RECEIVE_NOT_NOTIFY_MESSAGE ,UI
|
||||
*
|
||||
* If do-not-disturb is set, the message do-not-disturb state is displayed
|
||||
* The default state of the meeting type group is V2TIM_RECEIVE_NOT_NOTIFY_MESSAGE, and the UI does not process it.
|
||||
*/
|
||||
if ([self isConversationNotDisturb:conv] && conv.unreadCount > 0) {
|
||||
NSAttributedString *unreadString = [[NSAttributedString alloc]
|
||||
initWithString:[NSString stringWithFormat:@"[%d %@] ", conv.unreadCount, TIMCommonLocalizableString(TUIKitMessageTypeLastMsgCountFormat)]];
|
||||
[attributeString insertAttributedString:unreadString atIndex:0];
|
||||
}
|
||||
|
||||
/**
|
||||
* If the status of the lastMsg of the conversation is sending or failed, display the sending status of the message (the draft box does not need to display
|
||||
* the sending status)
|
||||
*/
|
||||
if (!conv.draftText && (V2TIM_MSG_STATUS_SENDING == conv.lastMessage.status || V2TIM_MSG_STATUS_SEND_FAIL == conv.lastMessage.status || hasRiskContent) &&
|
||||
!isRevoked) {
|
||||
UIFont *textFont = [UIFont systemFontOfSize:14];
|
||||
NSAttributedString *spaceString = [[NSAttributedString alloc] initWithString:@" " attributes:@{NSFontAttributeName : textFont}];
|
||||
NSTextAttachment *attchment = [[NSTextAttachment alloc] init];
|
||||
UIImage *image = nil;
|
||||
if (V2TIM_MSG_STATUS_SENDING == conv.lastMessage.status) {
|
||||
image = TUIConversationCommonBundleImage(@"msg_sending_for_conv");
|
||||
} else {
|
||||
image = TUIConversationCommonBundleImage(@"msg_error_for_conv");
|
||||
}
|
||||
attchment.image = image;
|
||||
attchment.bounds = CGRectMake(0, -(textFont.lineHeight - textFont.pointSize) / 2, textFont.pointSize, textFont.pointSize);
|
||||
NSAttributedString *imageString = [NSAttributedString attributedStringWithAttachment:(NSTextAttachment *)(attchment)];
|
||||
[attributeString insertAttributedString:spaceString atIndex:0];
|
||||
[attributeString insertAttributedString:imageString atIndex:0];
|
||||
}
|
||||
return attributeString;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// TUIConversationSelectModel.h
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by xiangzhang on 2021/6/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "TUIConversationSelectBaseDataProvider.h"
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIConversationSelectDataProvider : TUIConversationSelectBaseDataProvider
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// TUIConversationSelectModel.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by xiangzhang on 2021/6/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIConversationSelectDataProvider.h"
|
||||
#import "TUIConversationCellData.h"
|
||||
|
||||
@implementation TUIConversationSelectDataProvider
|
||||
|
||||
- (Class)getConversationCellClass {
|
||||
return [TUIConversationCellData class];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// TUIFoldConversationListDataProvider.h
|
||||
// TUIConversation
|
||||
//
|
||||
// Created by wyl on 2022/11/22.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIFoldConversationListBaseDataProvider.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIFoldConversationListDataProvider : TUIFoldConversationListBaseDataProvider
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// TUIFoldConversationListDataProvider.m
|
||||
// TUIConversation
|
||||
//
|
||||
// Created by wyl on 2022/11/22.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIFoldConversationListDataProvider.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
#import "TUIConversationCellData.h"
|
||||
#import <TIMCommon/NSString+TUIEmoji.h>
|
||||
|
||||
@implementation TUIFoldConversationListDataProvider
|
||||
|
||||
- (Class)getConversationCellClass {
|
||||
return [TUIConversationCellData class];
|
||||
}
|
||||
|
||||
- (NSString *)getDisplayStringFromService:(V2TIMMessage *)msg {
|
||||
NSDictionary *param = @{TUICore_TUIChatService_GetDisplayStringMethod_MsgKey : msg};
|
||||
return [TUICore callService:TUICore_TUIChatService method:TUICore_TUIChatService_GetDisplayStringMethod param:param];
|
||||
}
|
||||
|
||||
- (NSMutableAttributedString *)getLastDisplayString:(V2TIMConversation *)conv {
|
||||
/**
|
||||
* If has group-at message, the group-at information will be displayed first
|
||||
*/
|
||||
NSString *atStr = [self getGroupAtTipString:conv];
|
||||
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:atStr];
|
||||
NSDictionary *attributeDict = @{NSForegroundColorAttributeName : [UIColor d_systemRedColor]};
|
||||
[attributeString setAttributes:attributeDict range:NSMakeRange(0, attributeString.length)];
|
||||
|
||||
/**
|
||||
* If there is a draft box, the draft box information will be displayed first
|
||||
*/
|
||||
if (conv.draftText.length > 0) {
|
||||
NSAttributedString *draft = [[NSAttributedString alloc] initWithString:TIMCommonLocalizableString(TUIKitMessageTypeDraftFormat)
|
||||
attributes:@{NSForegroundColorAttributeName : RGB(250, 81, 81)}];
|
||||
[attributeString appendAttributedString:draft];
|
||||
|
||||
NSString *draftContentStr = [self getDraftContent:conv];
|
||||
|
||||
NSMutableAttributedString *draftContent = [draftContentStr getAdvancedFormatEmojiStringWithFont:[UIFont systemFontOfSize:16.0]
|
||||
textColor:TUIChatDynamicColor(@"chat_input_text_color", @"#000000")
|
||||
emojiLocations:nil];
|
||||
[attributeString appendAttributedString:draftContent];
|
||||
} else {
|
||||
/**
|
||||
* No drafts, show conversation lastMsg information
|
||||
*/
|
||||
NSString *lastMsgStr = @"";
|
||||
|
||||
/**
|
||||
* Attempt to get externally customized display information
|
||||
*/
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(getConversationDisplayString:)]) {
|
||||
lastMsgStr = [self.delegate getConversationDisplayString:conv];
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is no external customization, get the lastMsg display information through the message module
|
||||
*/
|
||||
if (lastMsgStr.length == 0 && conv.lastMessage) {
|
||||
NSDictionary *param = @{TUICore_TUIChatService_GetDisplayStringMethod_MsgKey : conv.lastMessage};
|
||||
lastMsgStr = [TUICore callService:TUICore_TUIChatService method:TUICore_TUIChatService_GetDisplayStringMethod param:param];
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is no lastMsg display information and no draft information, return nil directly
|
||||
*/
|
||||
if (lastMsgStr.length == 0) {
|
||||
return nil;
|
||||
}
|
||||
[attributeString appendAttributedString:[[NSAttributedString alloc] initWithString:lastMsgStr]];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* If do-not-disturb is set, the message do-not-disturb state is displayed
|
||||
* The default state of the meeting type group is V2TIM_RECEIVE_NOT_NOTIFY_MESSAGE, and the UI does not process it.
|
||||
*/
|
||||
if ([self isConversationNotDisturb:conv] && conv.unreadCount > 0) {
|
||||
NSAttributedString *unreadString = [[NSAttributedString alloc]
|
||||
initWithString:[NSString stringWithFormat:@"[%d %@] ", conv.unreadCount, TIMCommonLocalizableString(TUIKitMessageTypeLastMsgCountFormat)]];
|
||||
[attributeString insertAttributedString:unreadString atIndex:0];
|
||||
}
|
||||
|
||||
/**
|
||||
* If the status of the lastMsg of the conversation is sending or failed, display the sending status of the message (the draft box does not need to display
|
||||
* the sending status)
|
||||
*/
|
||||
if (!conv.draftText && (V2TIM_MSG_STATUS_SENDING == conv.lastMessage.status || V2TIM_MSG_STATUS_SEND_FAIL == conv.lastMessage.status)) {
|
||||
UIFont *textFont = [UIFont systemFontOfSize:14];
|
||||
NSAttributedString *spaceString = [[NSAttributedString alloc] initWithString:@" " attributes:@{NSFontAttributeName : textFont}];
|
||||
NSTextAttachment *attchment = [[NSTextAttachment alloc] init];
|
||||
UIImage *image = nil;
|
||||
if (V2TIM_MSG_STATUS_SENDING == conv.lastMessage.status) {
|
||||
image = TUIConversationCommonBundleImage(@"msg_sending_for_conv");
|
||||
} else {
|
||||
image = TUIConversationCommonBundleImage(@"msg_error_for_conv");
|
||||
}
|
||||
attchment.image = image;
|
||||
attchment.bounds = CGRectMake(0, -(textFont.lineHeight - textFont.pointSize) / 2, textFont.pointSize, textFont.pointSize);
|
||||
NSAttributedString *imageString = [NSAttributedString attributedStringWithAttachment:(NSTextAttachment *)(attchment)];
|
||||
[attributeString insertAttributedString:spaceString atIndex:0];
|
||||
[attributeString insertAttributedString:imageString atIndex:0];
|
||||
}
|
||||
return attributeString;
|
||||
}
|
||||
|
||||
@end
|
||||
15
TUIKit/TUIConversation/UI_Classic/Header/TUIConversation.h
Normal file
15
TUIKit/TUIConversation/UI_Classic/Header/TUIConversation.h
Normal file
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// TUIConversation.h
|
||||
// Pods
|
||||
//
|
||||
// Created by harvy on 2022/6/9.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef TUIConversation_h
|
||||
#define TUIConversation_h
|
||||
|
||||
#import "TUIConversationListController.h"
|
||||
#import "TUIConversationSelectController.h"
|
||||
|
||||
#endif /* TUIConversation_h */
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// TUIConversationObjectFactory.h
|
||||
// TUIConversation
|
||||
//
|
||||
// Created by wyl on 2023/3/29.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* TUIConversationService currently provides two services:
|
||||
* 1. Create a conversation list
|
||||
* 2. Create a conversation selector
|
||||
*
|
||||
* You can call the service through the [TUICore createObject:..] method. The different service parameters are as follows:
|
||||
* > Create a conversation list:
|
||||
* factoryName: TUICore_TUIConversationObjectFactory
|
||||
* key: TUICore_TUIConversationObjectFactory_GetConversationControllerMethod
|
||||
*
|
||||
* > Create conversation selector:
|
||||
* factoryName: TUICore_TUIConversationObjectFactory
|
||||
* key: TUICore_TUIConversationObjectFactory_ConversationSelectVC_Classic
|
||||
*
|
||||
*/
|
||||
@interface TUIConversationObjectFactory : NSObject
|
||||
+ (TUIConversationObjectFactory *)shareInstance;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// TUIConversationObjectFactory.m
|
||||
// TUIConversation
|
||||
//
|
||||
// Created by wyl on 2023/3/29.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIConversationObjectFactory.h"
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "TUIConversationListController.h"
|
||||
#import "TUIConversationSelectController.h"
|
||||
|
||||
@interface TUIConversationObjectFactory () <TUIObjectProtocol>
|
||||
@end
|
||||
|
||||
@implementation TUIConversationObjectFactory
|
||||
+ (void)load {
|
||||
[TUICore registerObjectFactory:TUICore_TUIConversationObjectFactory objectFactory:[TUIConversationObjectFactory shareInstance]];
|
||||
}
|
||||
+ (TUIConversationObjectFactory *)shareInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
static TUIConversationObjectFactory *g_sharedInstance = nil;
|
||||
dispatch_once(&onceToken, ^{
|
||||
g_sharedInstance = [[TUIConversationObjectFactory alloc] init];
|
||||
});
|
||||
return g_sharedInstance;
|
||||
}
|
||||
|
||||
#pragma mark - TUIObjectProtocol
|
||||
- (id)onCreateObject:(NSString *)method param:(nullable NSDictionary *)param {
|
||||
if ([method isEqualToString:TUICore_TUIConversationObjectFactory_GetConversationControllerMethod]) {
|
||||
return [self createConversationController];
|
||||
} else if ([method isEqualToString:TUICore_TUIConversationObjectFactory_ConversationSelectVC_Classic]) {
|
||||
return [self createConversationSelectController];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (UIViewController *)createConversationController {
|
||||
return [[TUIConversationListController alloc] init];
|
||||
}
|
||||
|
||||
- (UIViewController *)createConversationSelectController {
|
||||
return [[TUIConversationSelectController alloc] init];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIConversationService : NSObject
|
||||
|
||||
+ (TUIConversationService *)shareInstance;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
|
||||
#import "TUIConversationService.h"
|
||||
|
||||
@implementation TUIConversationService
|
||||
|
||||
static NSString *gServiceName = nil;
|
||||
|
||||
+ (void)load {
|
||||
TUIRegisterThemeResourcePath(TUIConversationThemePath, TUIThemeModuleConversation);
|
||||
}
|
||||
|
||||
+ (TUIConversationService *)shareInstance {
|
||||
static dispatch_once_t onceToken;
|
||||
static TUIConversationService *g_sharedInstance = nil;
|
||||
dispatch_once(&onceToken, ^{
|
||||
g_sharedInstance = [[TUIConversationService alloc] init];
|
||||
});
|
||||
return g_sharedInstance;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Tencent Cloud Communication Service Interface Component TUIKIT - Conversation List Component.
|
||||
*
|
||||
* This file declares the view component of the conversation list.
|
||||
* The conversation list component can display brief information of each conversation in the order of the time when new messages are sent (newer messages are
|
||||
* sorted earlier). The conversation information displayed in the conversation list includes:
|
||||
* 1. Avatar information (user avatar/group avatar)
|
||||
* 2. Conversation title (user nickname/group name)
|
||||
* 3. Conversation message overview (display the latest message content)
|
||||
* 4. The number of unread messages (if there are unread messages)
|
||||
* 5. Conversation time (receive/send time of the latest message)
|
||||
* The conversation information displayed in the conversation list is implemented by TUIConversationCell. For details, please refer to
|
||||
* TUIConversation\Cell\CellUI\TUIConversationCell.h
|
||||
*/
|
||||
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TUIConversationCellData.h"
|
||||
#import "TUIConversationListControllerListener.h"
|
||||
#import "TUIConversationTableView.h"
|
||||
|
||||
@class TUISearchBar;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
*
|
||||
* 【Module name】 message list interface component (TUIConversationListController)
|
||||
*
|
||||
* 【Function description】 It is responsible for displaying each conversation in the order in which the messages are received, and responding to the user's
|
||||
* operation, providing users with multi-session management functions. The conversation information displayed in the conversation list includes:
|
||||
* 1. Avatar information (user avatar/group avatar)
|
||||
* 2. Conversation title (user nickname/group name)
|
||||
* 3. Conversation message overview (display the latest message content)
|
||||
* 4. The number of unread messages (if there are unread messages)
|
||||
* 5. Conversation time (receive/send time of the latest message)
|
||||
*/
|
||||
@interface TUIConversationListController : UIViewController
|
||||
@property(nonatomic, strong) TUIConversationTableView *tableViewForAll;
|
||||
|
||||
@property(nonatomic, weak) id<TUIConversationListControllerListener> delegate;
|
||||
|
||||
@property(nonatomic, strong) TUIConversationListBaseDataProvider *dataProvider;
|
||||
|
||||
@property(nonatomic) BOOL isShowBanner;
|
||||
|
||||
/**
|
||||
* An identifier that identifies whether to display the conversation group, If the TUIConversationGroup component is integrated, it will be displayed by
|
||||
* default
|
||||
*/
|
||||
@property(nonatomic) BOOL isShowConversationGroup;
|
||||
|
||||
- (void)startConversation:(V2TIMConversationType)type;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,815 @@
|
||||
//
|
||||
// TUIConversationListController.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by annidyfeng on 2019/5/17.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIConversationListController.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
#import "TUIConversationCell.h"
|
||||
#import "TUIConversationListDataProvider.h"
|
||||
#import "TUIFoldListViewController.h"
|
||||
#import "TUIConversationConfig.h"
|
||||
|
||||
#define GroupBtnSpace 24
|
||||
#define GroupScrollViewHeight 30
|
||||
|
||||
@interface TUIConversationListController () <UIGestureRecognizerDelegate,
|
||||
UIPopoverPresentationControllerDelegate,
|
||||
TUIConversationTableViewDelegate,
|
||||
TUIPopViewDelegate,
|
||||
TUINotificationProtocol>
|
||||
@property(nonatomic, strong) TUINaviBarIndicatorView *titleView;
|
||||
@property(nonatomic, strong) TUIConversationListBaseDataProvider *settingDataProvider;
|
||||
@property(nonatomic, strong) UIView *tableViewContainer;
|
||||
@property(nonatomic, strong) UIView *bannerView;
|
||||
@property(nonatomic, assign) CGFloat viewHeight;
|
||||
|
||||
@property(nonatomic, assign) BOOL actualShowConversationGroup;
|
||||
@property(nonatomic, strong) UIView *groupView;
|
||||
@property(nonatomic, strong) UIScrollView *groupScrollView;
|
||||
@property(nonatomic, strong) UIView *groupAnimationView;
|
||||
@property(nonatomic, strong) UIView *groupBtnContainer;
|
||||
@property(nonatomic, strong) NSMutableArray *groupItemList;
|
||||
@property(nonatomic, strong) TUIConversationGroupItem *allGroupItem;
|
||||
@end
|
||||
|
||||
@implementation TUIConversationListController
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.isShowBanner = NO;
|
||||
self.isShowConversationGroup = NO;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onThemeChanged) name:TUIDidApplyingThemeChangedNotfication object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - NSNotification
|
||||
- (void)onThemeChanged {
|
||||
self.groupAnimationView.layer.borderColor = [TUIConversationDynamicColor(@"conversation_group_bg_color", @"#EBECF0") CGColor];
|
||||
}
|
||||
|
||||
#pragma mark - SettingDataProvider
|
||||
- (void)setDataProvider:(TUIConversationListBaseDataProvider *)dataProvider {
|
||||
self.settingDataProvider = dataProvider;
|
||||
}
|
||||
|
||||
- (TUIConversationListBaseDataProvider *)dataProvider {
|
||||
return self.settingDataProvider;
|
||||
}
|
||||
|
||||
#pragma mark - Life Cycle
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
[self setupNavigation];
|
||||
[self setupViews];
|
||||
[TUICore registerEvent:TUICore_TUIConversationGroupNotify subKey:@"" object:self];
|
||||
[TUICore registerEvent:TUICore_TUIConversationMarkNotify subKey:@"" object:self];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
[self.currentTableView reloadData];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[NSNotificationCenter.defaultCenter removeObserver:self];
|
||||
[TUICore unRegisterEventByObject:self];
|
||||
}
|
||||
|
||||
- (void)setupNavigation {
|
||||
UIButton *moreButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[moreButton setImage:TIMCommonDynamicImage(@"nav_more_img", [UIImage imageNamed:TIMCommonImagePath(@"more")]) forState:UIControlStateNormal];
|
||||
[moreButton addTarget:self action:@selector(rightBarButtonClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
moreButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
[moreButton.widthAnchor constraintEqualToConstant:24].active = YES;
|
||||
[moreButton.heightAnchor constraintEqualToConstant:24].active = YES;
|
||||
UIBarButtonItem *moreItem = [[UIBarButtonItem alloc] initWithCustomView:moreButton];
|
||||
self.navigationController.navigationItem.rightBarButtonItem = moreItem;
|
||||
|
||||
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
|
||||
self.navigationController.interactivePopGestureRecognizer.delegate = self;
|
||||
}
|
||||
|
||||
- (void)rightBarButtonClick:(UIButton *)rightBarButton {
|
||||
NSMutableArray *menus = [NSMutableArray array];
|
||||
TUIPopCellData *friend = [[TUIPopCellData alloc] init];
|
||||
|
||||
friend.image = TUIConversationDynamicImage(@"pop_icon_new_chat_img", [UIImage imageNamed:TUIConversationImagePath(@"new_chat")]);
|
||||
friend.title = TIMCommonLocalizableString(ChatsNewChatText);
|
||||
[menus addObject:friend];
|
||||
|
||||
TUIPopCellData *group = [[TUIPopCellData alloc] init];
|
||||
group.image = TUIConversationDynamicImage(@"pop_icon_new_group_img", [UIImage imageNamed:TUIConversationImagePath(@"new_groupchat")]);
|
||||
group.title = TIMCommonLocalizableString(ChatsNewGroupText);
|
||||
[menus addObject:group];
|
||||
|
||||
CGFloat height = [TUIPopCell getHeight] * menus.count + TUIPopView_Arrow_Size.height;
|
||||
CGFloat orginY = StatusBar_Height + NavBar_Height;
|
||||
CGFloat orginX = Screen_Width - 155;
|
||||
if(isRTL()){
|
||||
orginX = 10;
|
||||
}
|
||||
TUIPopView *popView = [[TUIPopView alloc] initWithFrame:CGRectMake(orginX, orginY, 145, height)];
|
||||
CGRect frameInNaviView = [self.navigationController.view convertRect:rightBarButton.frame fromView:rightBarButton.superview];
|
||||
popView.arrowPoint = CGPointMake(frameInNaviView.origin.x + frameInNaviView.size.width * 0.5, orginY);
|
||||
popView.delegate = self;
|
||||
[popView setData:menus];
|
||||
[popView showInWindow:self.view.window];
|
||||
}
|
||||
|
||||
- (void)setupViews {
|
||||
// self.view.backgroundColor = [TUIConversationConfig sharedConfig].listBackgroundColor ? : TUIConversationDynamicColor(@"conversation_bg_color", @"#FFFFFF");
|
||||
self.view.backgroundColor = [UIColor clearColor];
|
||||
self.viewHeight = self.view.mm_h;
|
||||
if (self.isShowBanner) {
|
||||
CGSize size = CGSizeMake(self.view.bounds.size.width, 60);
|
||||
self.bannerView.mm_width(size.width).mm_height(60);
|
||||
NSMutableDictionary *param = [NSMutableDictionary dictionary];
|
||||
param[TUICore_TUIConversationExtension_ConversationListBanner_BannerSize] = NSStringFromCGSize(size);
|
||||
param[TUICore_TUIConversationExtension_ConversationListBanner_ModalVC] = self;
|
||||
BOOL result = [TUICore raiseExtension:TUICore_TUIConversationExtension_ConversationListBanner_ClassicExtensionID
|
||||
parentView:self.bannerView
|
||||
param:param];
|
||||
if (!result) {
|
||||
self.bannerView.mm_height(0);
|
||||
}
|
||||
}
|
||||
|
||||
[self.view addSubview:self.tableViewContainer];
|
||||
[self.tableViewContainer addSubview:self.tableViewForAll];
|
||||
|
||||
if (self.isShowConversationGroup) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
NSArray *extensionList = [TUICore getExtensionList:TUICore_TUIConversationExtension_ConversationGroupListBanner_ClassicExtensionID param:nil];
|
||||
@weakify(self);
|
||||
[[[RACObserve(self, actualShowConversationGroup) distinctUntilChanged] skip:0] subscribeNext:^(NSNumber *showConversationGroup) {
|
||||
@strongify(self);
|
||||
if ([showConversationGroup boolValue]) {
|
||||
[self.tableViewContainer setFrame:CGRectMake(0, self.groupView.mm_maxY, self.view.mm_w, self.viewHeight - self.groupView.mm_maxY)];
|
||||
|
||||
self.groupItemList = [NSMutableArray array];
|
||||
[self addGroup:self.allGroupItem];
|
||||
|
||||
for (TUIExtensionInfo *info in extensionList) {
|
||||
TUIConversationGroupItem *groupItem = info.data[TUICore_TUIConversationExtension_ConversationGroupListBanner_GroupItemKey];
|
||||
if (groupItem) {
|
||||
[self addGroup:groupItem];
|
||||
}
|
||||
}
|
||||
[self onSelectGroup:self.allGroupItem];
|
||||
} else {
|
||||
self.tableViewContainer.frame = CGRectMake(0, self.bannerView.mm_maxY, self.view.mm_w, self.viewHeight - self.bannerView.mm_maxY);
|
||||
self.tableViewForAll.frame = self.tableViewContainer.bounds;
|
||||
}
|
||||
}];
|
||||
self.actualShowConversationGroup = (extensionList.count > 0);
|
||||
});
|
||||
} else {
|
||||
self.tableViewContainer.frame = CGRectMake(0, 0, self.view.mm_w, self.viewHeight - TabBar_Height-NavBar_Height-StatusBar_Height);
|
||||
self.tableViewForAll.frame = self.tableViewContainer.bounds;
|
||||
}
|
||||
}
|
||||
|
||||
- (TUIConversationGroupItem *)allGroupItem {
|
||||
if (!_allGroupItem) {
|
||||
_allGroupItem = [[TUIConversationGroupItem alloc] init];
|
||||
_allGroupItem.groupName = TIMCommonLocalizableString(TUIConversationGroupAll);
|
||||
}
|
||||
return _allGroupItem;
|
||||
}
|
||||
|
||||
- (UIView *)bannerView {
|
||||
if (!_bannerView) {
|
||||
_bannerView = [[UIView alloc] initWithFrame:CGRectMake(0, StatusBar_Height + NavBar_Height, 0, 0)];
|
||||
[self.view addSubview:_bannerView];
|
||||
}
|
||||
return _bannerView;
|
||||
}
|
||||
|
||||
- (TUIConversationTableView *)currentTableView {
|
||||
for (UIView *view in self.tableViewContainer.subviews) {
|
||||
if ([view isKindOfClass:[TUIConversationTableView class]]) {
|
||||
return (TUIConversationTableView *)view;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (UIView *)tableViewContainer {
|
||||
if (!_tableViewContainer) {
|
||||
_tableViewContainer = [[UIView alloc] init];
|
||||
_tableViewContainer.autoresizesSubviews = YES;
|
||||
}
|
||||
return _tableViewContainer;
|
||||
}
|
||||
|
||||
- (TUIConversationTableView *)tableViewForAll {
|
||||
if (!_tableViewForAll) {
|
||||
_tableViewForAll = [[TUIConversationTableView alloc] init];
|
||||
_tableViewForAll.backgroundColor = self.view.backgroundColor;
|
||||
_tableViewForAll.convDelegate = self;
|
||||
_tableViewForAll.tipsMsgWhenNoConversation = [NSString stringWithFormat:TIMCommonLocalizableString(TUIConversationNone), @""];
|
||||
if (self.settingDataProvider) {
|
||||
[_tableViewForAll setDataProvider:self.settingDataProvider];
|
||||
} else {
|
||||
TUIConversationListDataProvider *dataProvider = [[TUIConversationListDataProvider alloc] init];
|
||||
[_tableViewForAll setDataProvider:dataProvider];
|
||||
}
|
||||
}
|
||||
return _tableViewForAll;
|
||||
}
|
||||
|
||||
- (UIView *)groupView {
|
||||
if (!_groupView) {
|
||||
_groupView = [[UIView alloc] initWithFrame:CGRectMake(0, self.bannerView.mm_maxY, self.view.mm_w, 60)];
|
||||
[self.view addSubview:_groupView];
|
||||
|
||||
CGFloat groupExtensionBtnLeft = _groupView.mm_w - GroupScrollViewHeight - kScale375(16);
|
||||
self.groupBtnContainer = [[UIView alloc] initWithFrame:CGRectMake(groupExtensionBtnLeft, 18, GroupScrollViewHeight, GroupScrollViewHeight)];
|
||||
[_groupView addSubview:self.groupBtnContainer];
|
||||
[TUICore raiseExtension:TUICore_TUIConversationExtension_ConversationGroupManagerContainer_ClassicExtensionID
|
||||
parentView:self.groupBtnContainer
|
||||
param:@{TUICore_TUIConversationExtension_ConversationGroupManagerContainer_ParentVCKey : self}];
|
||||
|
||||
CGFloat groupScrollViewWidth = self.groupBtnContainer.mm_x - kScale375(16) - kScale375(10);
|
||||
UIView *groupScrollBackgrounView = [[UIView alloc] init];
|
||||
[_groupView addSubview:groupScrollBackgrounView];
|
||||
groupScrollBackgrounView.frame = CGRectMake(kScale375(16), 18, groupScrollViewWidth, GroupScrollViewHeight);
|
||||
self.groupScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, groupScrollViewWidth, GroupScrollViewHeight)];
|
||||
self.groupScrollView.backgroundColor = TUIConversationDynamicColor(@"conversation_group_bg_color", @"#EBECF0");
|
||||
self.groupScrollView.showsHorizontalScrollIndicator = NO;
|
||||
self.groupScrollView.showsVerticalScrollIndicator = NO;
|
||||
self.groupScrollView.bounces = NO;
|
||||
self.groupScrollView.scrollEnabled = YES;
|
||||
self.groupScrollView.layer.cornerRadius = GroupScrollViewHeight / 2.0;
|
||||
self.groupScrollView.layer.masksToBounds = YES;
|
||||
[groupScrollBackgrounView addSubview:self.groupScrollView];
|
||||
@weakify(self);
|
||||
[[[RACObserve(self.groupScrollView, contentSize) distinctUntilChanged] skip:1] subscribeNext:^(NSValue *contentSizeValue) {
|
||||
@strongify(self);
|
||||
[self.groupScrollView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
||||
make.leading.mas_equalTo(groupScrollBackgrounView.mas_leading);
|
||||
make.height.mas_equalTo(GroupScrollViewHeight);
|
||||
make.width.mas_equalTo(MIN(groupScrollViewWidth, [contentSizeValue CGSizeValue].width));
|
||||
make.centerY.mas_equalTo(groupScrollBackgrounView);
|
||||
}];
|
||||
}];
|
||||
|
||||
self.groupAnimationView = [[UIView alloc] init];
|
||||
self.groupAnimationView.backgroundColor = TUIConversationDynamicColor(@"conversation_group_animate_view_color", @"#FFFFFF");
|
||||
self.groupAnimationView.layer.cornerRadius = GroupScrollViewHeight / 2.0;
|
||||
self.groupAnimationView.layer.masksToBounds = YES;
|
||||
self.groupAnimationView.layer.borderWidth = 1;
|
||||
self.groupAnimationView.layer.borderColor = [TUIConversationDynamicColor(@"conversation_group_bg_color", @"#EBECF0") CGColor];
|
||||
[self.groupScrollView addSubview:self.groupAnimationView];
|
||||
if (isRTL()) {
|
||||
[groupScrollBackgrounView resetFrameToFitRTL];
|
||||
[self.groupBtnContainer resetFrameToFitRTL];
|
||||
self.groupScrollView.transform = CGAffineTransformMakeRotation(M_PI);
|
||||
NSArray *subViews = self.groupScrollView.subviews;
|
||||
for (UIView *subView in subViews) {
|
||||
subView.transform = CGAffineTransformMakeRotation(M_PI);
|
||||
}
|
||||
}
|
||||
}
|
||||
return _groupView;
|
||||
}
|
||||
|
||||
#pragma mark Conversation Group Manager
|
||||
- (void)createGroupBtn:(TUIConversationGroupItem *)groupItem positionX:(CGFloat)positionX {
|
||||
UIButton *groupBtn = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[groupBtn setBackgroundColor:[UIColor clearColor]];
|
||||
[groupBtn setAttributedTitle:[self getGroupBtnAttributedString:groupItem] forState:UIControlStateNormal];
|
||||
[groupBtn setTitleColor:TUIConversationDynamicColor(@"conversation_group_btn_unselect_color", @"#666666") forState:UIControlStateNormal];
|
||||
[groupBtn.titleLabel setFont:[UIFont systemFontOfSize:16]];
|
||||
[groupBtn addTarget:self action:@selector(onGroupBtnClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[groupBtn sizeToFit];
|
||||
groupBtn.mm_x = positionX;
|
||||
groupBtn.mm_w = groupBtn.mm_w + GroupBtnSpace;
|
||||
groupBtn.mm_h = GroupScrollViewHeight;
|
||||
groupItem.groupBtn = groupBtn;
|
||||
if (isRTL()) {
|
||||
groupBtn.transform = CGAffineTransformMakeRotation(M_PI);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)updateGroupBtn:(TUIConversationGroupItem *)groupItem {
|
||||
[groupItem.groupBtn setAttributedTitle:[self getGroupBtnAttributedString:groupItem] forState:UIControlStateNormal];
|
||||
if(isRTL()) {
|
||||
groupItem.groupBtn.mm_w = groupItem.groupBtn.mm_w ;
|
||||
groupItem.groupBtn.mm_h = GroupScrollViewHeight;
|
||||
}
|
||||
else {
|
||||
[groupItem.groupBtn sizeToFit];
|
||||
groupItem.groupBtn.mm_w = groupItem.groupBtn.mm_w + GroupBtnSpace;
|
||||
groupItem.groupBtn.mm_h = GroupScrollViewHeight;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onGroupBtnClick:(UIButton *)btn {
|
||||
for (TUIConversationGroupItem *groupItem in self.groupItemList) {
|
||||
if ([groupItem.groupBtn isEqual:btn]) {
|
||||
[self onSelectGroup:groupItem];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadGroupList:(NSArray<TUIConversationGroupItem *> *)groupItemList {
|
||||
NSString *currentSelectGroup = @"";
|
||||
for (TUIConversationGroupItem *groupItem in self.groupItemList) {
|
||||
if (groupItem.groupBtn.isSelected) {
|
||||
currentSelectGroup = groupItem.groupName;
|
||||
}
|
||||
[groupItem.groupBtn removeFromSuperview];
|
||||
}
|
||||
[self.groupItemList removeAllObjects];
|
||||
[self.groupScrollView setContentSize:CGSizeZero];
|
||||
|
||||
[self addGroup:self.allGroupItem];
|
||||
for (TUIConversationGroupItem *groupItem in groupItemList) {
|
||||
[self addGroup:groupItem];
|
||||
if ([groupItem.groupName isEqualToString:currentSelectGroup]) {
|
||||
groupItem.groupBtn.selected = YES;
|
||||
self.groupAnimationView.frame = groupItem.groupBtn.frame;
|
||||
}
|
||||
}
|
||||
if (isRTL()) {
|
||||
NSArray *subViews = self.groupScrollView.subviews;
|
||||
for (UIView *subView in subViews) {
|
||||
subView.transform = CGAffineTransformMakeRotation(M_PI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addGroup:(TUIConversationGroupItem *)addGroup {
|
||||
[self createGroupBtn:addGroup positionX:self.groupScrollView.contentSize.width];
|
||||
[self.groupItemList addObject:addGroup];
|
||||
[self.groupScrollView addSubview:addGroup.groupBtn];
|
||||
[self.groupScrollView setContentSize:CGSizeMake(addGroup.groupBtn.mm_maxX, GroupScrollViewHeight)];
|
||||
}
|
||||
|
||||
- (void)insertGroup:(TUIConversationGroupItem *)insertGroup atIndex:(NSInteger)index {
|
||||
if (index < self.groupItemList.count) {
|
||||
for (int i = 0; i < self.groupItemList.count; ++i) {
|
||||
TUIConversationGroupItem *groupItem = self.groupItemList[i];
|
||||
if (i == index) {
|
||||
[self createGroupBtn:insertGroup positionX:groupItem.groupBtn.mm_x];
|
||||
[self.groupScrollView addSubview:insertGroup.groupBtn];
|
||||
}
|
||||
if (i >= index) {
|
||||
groupItem.groupBtn.mm_x += insertGroup.groupBtn.mm_w;
|
||||
if (groupItem.groupBtn.isSelected) {
|
||||
self.groupAnimationView.frame = groupItem.groupBtn.frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
[self.groupItemList insertObject:insertGroup atIndex:index];
|
||||
[self.groupScrollView setContentSize:CGSizeMake(self.groupScrollView.contentSize.width + insertGroup.groupBtn.mm_w, GroupScrollViewHeight)];
|
||||
} else {
|
||||
[self addGroup:insertGroup];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateGroup:(TUIConversationGroupItem *)updateGroup {
|
||||
CGFloat offsetX = 0;
|
||||
for (int i = 0; i < self.groupItemList.count; ++i) {
|
||||
TUIConversationGroupItem *groupItem = self.groupItemList[i];
|
||||
if (offsetX != 0) {
|
||||
groupItem.groupBtn.mm_x += offsetX;
|
||||
}
|
||||
if ([groupItem.groupName isEqualToString:updateGroup.groupName]) {
|
||||
groupItem.unreadCount = updateGroup.unreadCount;
|
||||
CGFloat oldBtnWidth = groupItem.groupBtn.mm_w;
|
||||
[self updateGroupBtn:groupItem];
|
||||
CGFloat newBtnWidth = groupItem.groupBtn.mm_w;
|
||||
offsetX = newBtnWidth - oldBtnWidth;
|
||||
}
|
||||
if (groupItem.groupBtn.isSelected) {
|
||||
self.groupAnimationView.frame = groupItem.groupBtn.frame;
|
||||
}
|
||||
}
|
||||
[self.groupScrollView setContentSize:CGSizeMake(self.groupScrollView.contentSize.width + offsetX, GroupScrollViewHeight)];
|
||||
}
|
||||
|
||||
- (void)renameGroup:(NSString *)oldName newName:(NSString *)newName {
|
||||
CGFloat offsetX = 0;
|
||||
for (int i = 0; i < self.groupItemList.count; ++i) {
|
||||
TUIConversationGroupItem *groupItem = self.groupItemList[i];
|
||||
if (offsetX != 0) {
|
||||
groupItem.groupBtn.mm_x += offsetX;
|
||||
}
|
||||
if ([groupItem.groupName isEqualToString:oldName]) {
|
||||
groupItem.groupName = newName;
|
||||
CGFloat oldBtnWidth = groupItem.groupBtn.mm_w;
|
||||
[self updateGroupBtn:groupItem];
|
||||
CGFloat newBtnWidth = groupItem.groupBtn.mm_w;
|
||||
offsetX = newBtnWidth - oldBtnWidth;
|
||||
}
|
||||
if (groupItem.groupBtn.isSelected) {
|
||||
self.groupAnimationView.frame = groupItem.groupBtn.frame;
|
||||
}
|
||||
}
|
||||
[self.groupScrollView setContentSize:CGSizeMake(self.groupScrollView.contentSize.width + offsetX, GroupScrollViewHeight)];
|
||||
}
|
||||
|
||||
- (void)deleteGroup:(TUIConversationGroupItem *)deleteGroup {
|
||||
CGFloat offsetX = 0;
|
||||
NSUInteger removeIndex = 0;
|
||||
BOOL isSelectedGroup = NO;
|
||||
for (int i = 0; i < self.groupItemList.count; ++i) {
|
||||
TUIConversationGroupItem *groupItem = self.groupItemList[i];
|
||||
if (offsetX != 0) {
|
||||
groupItem.groupBtn.mm_x += offsetX;
|
||||
}
|
||||
if ([groupItem.groupName isEqualToString:deleteGroup.groupName]) {
|
||||
[groupItem.groupBtn removeFromSuperview];
|
||||
offsetX = -groupItem.groupBtn.mm_w;
|
||||
removeIndex = i;
|
||||
isSelectedGroup = groupItem.groupBtn.isSelected;
|
||||
}
|
||||
if (groupItem.groupBtn.isSelected) {
|
||||
self.groupAnimationView.frame = groupItem.groupBtn.frame;
|
||||
}
|
||||
}
|
||||
[self.groupItemList removeObjectAtIndex:removeIndex];
|
||||
[self.groupScrollView setContentSize:CGSizeMake(self.groupScrollView.contentSize.width + offsetX, GroupScrollViewHeight)];
|
||||
if (isSelectedGroup) {
|
||||
[self onSelectGroup:self.groupItemList.firstObject];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onSelectGroup:(TUIConversationGroupItem *)selectGroupItem {
|
||||
for (int i = 0; i < self.groupItemList.count; ++i) {
|
||||
TUIConversationGroupItem *groupItem = self.groupItemList[i];
|
||||
if ([groupItem.groupName isEqualToString:selectGroupItem.groupName]) {
|
||||
groupItem.groupBtn.selected = YES;
|
||||
|
||||
[UIView animateWithDuration:0.1
|
||||
animations:^{
|
||||
self.groupAnimationView.frame = groupItem.groupBtn.frame;
|
||||
}];
|
||||
for (UIView *view in self.tableViewContainer.subviews) {
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
if ([groupItem.groupName isEqualToString:TIMCommonLocalizableString(TUIConversationGroupAll)]) {
|
||||
self.tableViewForAll.frame = self.tableViewContainer.bounds;
|
||||
[self.tableViewContainer addSubview:self.tableViewForAll];
|
||||
} else {
|
||||
[TUICore raiseExtension:TUICore_TUIConversationExtension_ConversationListContainer_ClassicExtensionID
|
||||
parentView:self.tableViewContainer
|
||||
param:@{TUICore_TUIConversationExtension_ConversationListContainer_GroupNameKey : groupItem.groupName}];
|
||||
self.currentTableView.convDelegate = self;
|
||||
}
|
||||
} else {
|
||||
groupItem.groupBtn.selected = NO;
|
||||
}
|
||||
[self updateGroupBtn:groupItem];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSMutableAttributedString *)getGroupBtnAttributedString:(TUIConversationGroupItem *)groupItem {
|
||||
NSMutableString *content = [NSMutableString stringWithString:@""];
|
||||
NSMutableString *contentName = [NSMutableString stringWithString: groupItem.groupName];
|
||||
NSMutableString *contentNum = [NSMutableString stringWithString:@""];
|
||||
NSMutableAttributedString *attributeString = nil;
|
||||
NSInteger unreadCount = groupItem.unreadCount;
|
||||
if (unreadCount > 0) {
|
||||
[contentNum appendString:(unreadCount > 99 ? @"99+" : [@(unreadCount) stringValue])];
|
||||
}
|
||||
if (isRTL()){
|
||||
[content appendString:@"\u200E"];
|
||||
[content appendString:contentNum];
|
||||
[content appendString:@" "];
|
||||
[content appendString:@"\u202B"];
|
||||
[content appendString:contentName];
|
||||
attributeString = [[NSMutableAttributedString alloc] initWithString:content];
|
||||
}
|
||||
else {
|
||||
[content appendString:contentName];
|
||||
[content appendString:@" "];
|
||||
[content appendString:contentNum];
|
||||
attributeString = [[NSMutableAttributedString alloc] initWithString:content];
|
||||
}
|
||||
|
||||
[attributeString setAttributes:@{
|
||||
NSForegroundColorAttributeName : TUIConversationDynamicColor(@"conversation_group_btn_select_color", @"#147AFF"),
|
||||
NSFontAttributeName : [UIFont systemFontOfSize:12],
|
||||
NSBaselineOffsetAttributeName : @(1)
|
||||
}
|
||||
range:[content rangeOfString:contentNum]];
|
||||
if (groupItem.groupBtn.isSelected) {
|
||||
[attributeString setAttributes:@{
|
||||
NSFontAttributeName : [UIFont systemFontOfSize:16],
|
||||
NSForegroundColorAttributeName : TUIConversationDynamicColor(@"conversation_group_btn_select_color", @"#147AFF")
|
||||
}
|
||||
range:[content rangeOfString:contentName]];
|
||||
} else {
|
||||
[attributeString setAttributes:@{
|
||||
NSFontAttributeName : [UIFont systemFontOfSize:16],
|
||||
NSForegroundColorAttributeName : TUIConversationDynamicColor(@"conversation_group_btn_unselect_color", @"#666666")
|
||||
}
|
||||
range:[content rangeOfString:contentName]];
|
||||
}
|
||||
return attributeString;
|
||||
}
|
||||
|
||||
#pragma mark TUINotificationProtocol
|
||||
- (void)onNotifyEvent:(NSString *)key subKey:(NSString *)subKey object:(nullable id)anObject param:(nullable NSDictionary *)param {
|
||||
if ([key isEqualToString:TUICore_TUIConversationGroupNotify] || [key isEqualToString:TUICore_TUIConversationMarkNotify]) {
|
||||
if (!self.actualShowConversationGroup) {
|
||||
self.actualShowConversationGroup = YES;
|
||||
}
|
||||
}
|
||||
if ([key isEqualToString:TUICore_TUIConversationGroupNotify]) {
|
||||
if ([param objectForKey:TUICore_TUIConversationGroupNotify_GroupListReloadKey]) {
|
||||
NSArray *groupItemList = [param objectForKey:TUICore_TUIConversationGroupNotify_GroupListReloadKey];
|
||||
if (groupItemList) {
|
||||
[self reloadGroupList:groupItemList];
|
||||
}
|
||||
} else if ([param objectForKey:TUICore_TUIConversationGroupNotify_GroupAddKey]) {
|
||||
TUIConversationGroupItem *groupItem = [param objectForKey:TUICore_TUIConversationGroupNotify_GroupAddKey];
|
||||
if (groupItem) {
|
||||
[self addGroup:groupItem];
|
||||
}
|
||||
} else if ([param objectForKey:TUICore_TUIConversationGroupNotify_GroupUpdateKey]) {
|
||||
TUIConversationGroupItem *groupItem = [param objectForKey:TUICore_TUIConversationGroupNotify_GroupUpdateKey];
|
||||
if (groupItem) {
|
||||
[self updateGroup:groupItem];
|
||||
}
|
||||
} else if ([param objectForKey:TUICore_TUIConversationGroupNotify_GroupRenameKey]) {
|
||||
NSDictionary *renameItem = [param objectForKey:TUICore_TUIConversationGroupNotify_GroupRenameKey];
|
||||
if (renameItem) {
|
||||
[self renameGroup:renameItem.allKeys.firstObject newName:renameItem.allValues.firstObject];
|
||||
}
|
||||
} else if ([param objectForKey:TUICore_TUIConversationGroupNotify_GroupDeleteKey]) {
|
||||
TUIConversationGroupItem *groupItem = [param objectForKey:TUICore_TUIConversationGroupNotify_GroupDeleteKey];
|
||||
if (groupItem) {
|
||||
[self deleteGroup:groupItem];
|
||||
}
|
||||
}
|
||||
} else if ([key isEqualToString:TUICore_TUIConversationMarkNotify]) {
|
||||
if ([param objectForKey:TUICore_TUIConversationGroupNotify_MarkAddKey]) {
|
||||
TUIConversationGroupItem *groupItem = [param objectForKey:TUICore_TUIConversationGroupNotify_MarkAddKey];
|
||||
if (groupItem) {
|
||||
[self insertGroup:groupItem atIndex:groupItem.groupIndex];
|
||||
}
|
||||
} else if ([param objectForKey:TUICore_TUIConversationGroupNotify_MarkUpdateKey]) {
|
||||
TUIConversationGroupItem *groupItem = [param objectForKey:TUICore_TUIConversationGroupNotify_MarkUpdateKey];
|
||||
if (groupItem) {
|
||||
[self updateGroup:groupItem];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma TUIConversationTableViewDelegate
|
||||
- (void)tableViewDidScroll:(CGFloat)offsetY {
|
||||
if (!self.bannerView || self.bannerView.hidden || !self.isShowBanner) {
|
||||
return;
|
||||
}
|
||||
UIEdgeInsets safeAreaInsets = UIEdgeInsetsZero;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
safeAreaInsets = self.currentTableView.adjustedContentInset;
|
||||
}
|
||||
CGFloat contentSizeHeight = self.currentTableView.contentSize.height + safeAreaInsets.top + safeAreaInsets.bottom;
|
||||
if (contentSizeHeight > self.currentTableView.mm_h && self.currentTableView.contentOffset.y + self.currentTableView.mm_h > contentSizeHeight) {
|
||||
return;
|
||||
}
|
||||
if (offsetY > self.bannerView.mm_h) {
|
||||
offsetY = self.bannerView.mm_h;
|
||||
}
|
||||
if (offsetY < 0) {
|
||||
offsetY = 0;
|
||||
}
|
||||
self.bannerView.mm_top(StatusBar_Height + NavBar_Height - offsetY);
|
||||
if (self.actualShowConversationGroup) {
|
||||
self.groupView.mm_top(self.bannerView.mm_maxY);
|
||||
self.tableViewContainer.mm_top(self.groupView.mm_maxY).mm_height(self.viewHeight - self.groupView.mm_maxY);
|
||||
} else {
|
||||
self.tableViewContainer.mm_top(self.bannerView.mm_maxY).mm_height(self.viewHeight - self.bannerView.mm_maxY);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tableViewDidSelectCell:(TUIConversationCellData *)data {
|
||||
if (data.isLocalConversationFoldList) {
|
||||
[TUIConversationListDataProvider cacheConversationFoldListSettings_FoldItemIsUnread:NO];
|
||||
|
||||
TUIFoldListViewController *foldVC = [[TUIFoldListViewController alloc] init];
|
||||
[self.navigationController pushViewController:foldVC animated:YES];
|
||||
|
||||
@weakify(self);
|
||||
foldVC.dismissCallback = ^(NSMutableAttributedString *_Nonnull foldStr, NSArray *_Nonnull sortArr, NSArray *_Nonnull needRemoveFromCacheMapArray) {
|
||||
@strongify(self);
|
||||
data.foldSubTitle = foldStr;
|
||||
data.subTitle = data.foldSubTitle;
|
||||
data.isMarkAsUnread = NO;
|
||||
|
||||
if (sortArr.count <= 0) {
|
||||
data.orderKey = 0;
|
||||
if ([self.dataProvider.conversationList containsObject:data]) {
|
||||
[self.dataProvider hideConversation:data];
|
||||
}
|
||||
}
|
||||
|
||||
for (NSString *removeId in needRemoveFromCacheMapArray) {
|
||||
if ([self.dataProvider.markFoldMap objectForKey:removeId]) {
|
||||
[self.dataProvider.markFoldMap removeObjectForKey:removeId];
|
||||
}
|
||||
}
|
||||
|
||||
[TUIConversationListDataProvider cacheConversationFoldListSettings_FoldItemIsUnread:NO];
|
||||
[self.currentTableView reloadData];
|
||||
};
|
||||
return;
|
||||
}
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(conversationListController:didSelectConversation:)]) {
|
||||
[self.delegate conversationListController:self didSelectConversation:data];
|
||||
} else {
|
||||
NSDictionary *param = @{
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_Title : data.title ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_UserID : data.userID ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_GroupID : data.groupID ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_AvatarImage : data.avatarImage ?: [UIImage new],
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_AvatarUrl : data.faceUrl ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_ConversationID : data.conversationID ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_AtTipsStr : data.atTipsStr ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_AtMsgSeqs : data.atMsgSeqs ?: @[],
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_Draft : data.draftText ?: @""
|
||||
};
|
||||
[self.navigationController pushViewController:TUICore_TUIChatObjectFactory_ChatViewController_Classic param:param forResult:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tableViewDidShowAlert:(UIAlertController *)ac {
|
||||
[self presentViewController:ac animated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma TUIPopViewDelegate
|
||||
- (void)popView:(TUIPopView *)popView didSelectRowAtIndex:(NSInteger)index {
|
||||
if (0 == index) {
|
||||
[self startConversation:V2TIM_C2C];
|
||||
} else {
|
||||
[self startConversation:V2TIM_GROUP];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)startConversation:(V2TIMConversationType)type {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
void (^selectContactCompletion)(NSArray<TUICommonContactSelectCellData *> *) = ^(NSArray<TUICommonContactSelectCellData *> *array) {
|
||||
if (V2TIM_C2C == type) {
|
||||
NSDictionary *param = @{
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_Title : array.firstObject.title ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_UserID : array.firstObject.identifier ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_AvatarImage : array.firstObject.avatarImage ?: [UIImage new],
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_AvatarUrl : array.firstObject.avatarUrl.absoluteString ?: @""
|
||||
};
|
||||
[weakSelf.navigationController pushViewController:TUICore_TUIChatObjectFactory_ChatViewController_Classic param:param forResult:nil];
|
||||
|
||||
NSMutableArray *tempArray = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
|
||||
[tempArray removeObjectAtIndex:tempArray.count - 2];
|
||||
weakSelf.navigationController.viewControllers = tempArray;
|
||||
} else {
|
||||
NSString *loginUser = [[V2TIMManager sharedInstance] getLoginUser];
|
||||
[[V2TIMManager sharedInstance]
|
||||
getUsersInfo:@[ loginUser ]
|
||||
succ:^(NSArray<V2TIMUserFullInfo *> *infoList) {
|
||||
NSString *showName = loginUser;
|
||||
if (infoList.firstObject.nickName.length > 0) {
|
||||
showName = infoList.firstObject.nickName;
|
||||
}
|
||||
NSMutableString *groupName = [NSMutableString stringWithString:showName];
|
||||
for (TUICommonContactSelectCellData *item in array) {
|
||||
[groupName appendFormat:@"、%@", item.title];
|
||||
}
|
||||
|
||||
if ([groupName length] > 10) {
|
||||
groupName = [groupName substringToIndex:10].mutableCopy;
|
||||
}
|
||||
void (^createGroupCompletion)(BOOL, V2TIMGroupInfo *) = ^(BOOL isSuccess, V2TIMGroupInfo *_Nonnull info) {
|
||||
NSDictionary *param = @{
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_Title : info.groupName ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_GroupID : info.groupID ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_AvatarUrl : info.faceURL ?: @""
|
||||
};
|
||||
[self.navigationController pushViewController:TUICore_TUIChatObjectFactory_ChatViewController_Classic param:param forResult:nil];
|
||||
|
||||
NSMutableArray *tempArray = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
|
||||
for (UIViewController *vc in self.navigationController.viewControllers) {
|
||||
if ([vc isKindOfClass:NSClassFromString(@"TUIGroupCreateController")] ||
|
||||
[vc isKindOfClass:NSClassFromString(@"TUIContactSelectController")]) {
|
||||
[tempArray removeObject:vc];
|
||||
}
|
||||
}
|
||||
|
||||
weakSelf.navigationController.viewControllers = tempArray;
|
||||
};
|
||||
|
||||
NSDictionary *param = @{
|
||||
TUICore_TUIContactObjectFactory_GetGroupCreateControllerMethod_TitleKey : array.firstObject.title ?: @"",
|
||||
TUICore_TUIContactObjectFactory_GetGroupCreateControllerMethod_GroupNameKey : groupName ?: @"",
|
||||
TUICore_TUIContactObjectFactory_GetGroupCreateControllerMethod_GroupTypeKey : GroupType_Work,
|
||||
TUICore_TUIContactObjectFactory_GetGroupCreateControllerMethod_CompletionKey : createGroupCompletion,
|
||||
TUICore_TUIContactObjectFactory_GetGroupCreateControllerMethod_ContactListKey : array ?: @[]
|
||||
};
|
||||
|
||||
UIViewController *groupVC = (UIViewController *)[TUICore createObject:TUICore_TUIContactObjectFactory
|
||||
key:TUICore_TUIContactObjectFactory_GetGroupCreateControllerMethod
|
||||
param:param];
|
||||
[weakSelf.navigationController pushViewController:(UIViewController *)groupVC animated:YES];
|
||||
}
|
||||
fail:nil];
|
||||
}
|
||||
};
|
||||
NSDictionary *param = @{
|
||||
TUICore_TUIContactObjectFactory_GetContactSelectControllerMethod_TitleKey : TIMCommonLocalizableString(ChatsSelectContact),
|
||||
TUICore_TUIContactObjectFactory_GetContactSelectControllerMethod_MaxSelectCount : @(type == V2TIM_C2C ? 1 : INT_MAX),
|
||||
TUICore_TUIContactObjectFactory_GetContactSelectControllerMethod_CompletionKey : selectContactCompletion
|
||||
};
|
||||
UIViewController *vc = [TUICore createObject:TUICore_TUIContactObjectFactory
|
||||
key:TUICore_TUIContactObjectFactory_GetContactSelectControllerMethod
|
||||
param:param];
|
||||
[self.navigationController pushViewController:vc animated:YES];
|
||||
}
|
||||
|
||||
#pragma mark TUIConversationListDataProviderDelegate
|
||||
- (NSString *)getConversationDisplayString:(V2TIMConversation *)conversation {
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(getConversationDisplayString:)]) {
|
||||
return [self.delegate getConversationDisplayString:conversation];
|
||||
}
|
||||
V2TIMMessage *msg = conversation.lastMessage;
|
||||
if (msg.customElem == nil || msg.customElem.data == nil) {
|
||||
return nil;
|
||||
}
|
||||
NSDictionary *param = [TUITool jsonData2Dictionary:msg.customElem.data];
|
||||
if (param != nil && [param isKindOfClass:[NSDictionary class]]) {
|
||||
NSString *businessID = param[@"businessID"];
|
||||
if (![businessID isKindOfClass:[NSString class]]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// whether custom jump message
|
||||
if ([businessID isEqualToString:BussinessID_TextLink] || ([(NSString *)param[@"text"] length] > 0 && [(NSString *)param[@"link"] length] > 0)) {
|
||||
NSString *desc = param[@"text"];
|
||||
if (msg.status == V2TIM_MSG_STATUS_LOCAL_REVOKED) {
|
||||
V2TIMUserFullInfo *info = msg.revokerInfo;
|
||||
NSString * revokeReason = msg.revokeReason;
|
||||
BOOL hasRiskContent = msg.hasRiskContent;
|
||||
if (hasRiskContent) {
|
||||
desc = TIMCommonLocalizableString(TUIKitMessageTipsRecallRiskContent);
|
||||
}
|
||||
else if (info) {
|
||||
NSString *userName = info.nickName;
|
||||
desc = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsRecallMessageFormat), userName];
|
||||
}
|
||||
else if (msg.isSelf) {
|
||||
desc = TIMCommonLocalizableString(TUIKitMessageTipsYouRecallMessage);
|
||||
} else if (msg.userID.length > 0) {
|
||||
desc = TIMCommonLocalizableString(TUIKitMessageTipsOthersRecallMessage);
|
||||
} else if (msg.groupID.length > 0) {
|
||||
/**
|
||||
* For the name display of group messages, the group business card is displayed first, the nickname has the second priority, and the user ID
|
||||
* has the lowest priority.
|
||||
*/
|
||||
NSString *userName = msg.nameCard;
|
||||
if (userName.length == 0) {
|
||||
userName = msg.nickName ?: msg.sender;
|
||||
}
|
||||
desc = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsRecallMessageFormat), userName];
|
||||
}
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
|
||||
return UIModalPresentationNone;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface IUConversationView : UIView
|
||||
@property(nonatomic, strong) UIView *view;
|
||||
@end
|
||||
|
||||
@implementation IUConversationView
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)];
|
||||
[self addSubview:self.view];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TUIConversationSelectController.h
|
||||
// Pods
|
||||
//
|
||||
// Created by harvy on 2020/12/8.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
@class TUIConversationCellData;
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIConversationSelectController : UIViewController
|
||||
|
||||
+ (instancetype)showIn:(UIViewController *)presentVC;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,450 @@
|
||||
//
|
||||
// TUIConversationSelectController.m
|
||||
// Pods
|
||||
//
|
||||
// Created by harvy on 2020/12/8.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIConversationSelectController.h"
|
||||
#import <TIMCommon/TIMCommonModel.h>
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/NSDictionary+TUISafe.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
#import "TUIConversationCell.h"
|
||||
#import "TUIConversationCellData.h"
|
||||
#import "TUIConversationSelectDataProvider.h"
|
||||
|
||||
typedef void (^TUIConversationSelectCompletHandler)(BOOL);
|
||||
|
||||
@interface TUIConversationSelectController () <UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
@property(nonatomic, strong) UITableView *tableView;
|
||||
@property(nonatomic, strong) TUIContactListPicker *pickerView;
|
||||
@property(nonatomic, strong) TUICommonTableViewCell *headerView;
|
||||
|
||||
@property(nonatomic, assign) BOOL enableMuliple;
|
||||
@property(nonatomic, strong) NSMutableArray<TUIConversationCellData *> *currentSelectedList;
|
||||
|
||||
@property(nonatomic, strong) TUIConversationSelectDataProvider *dataProvider;
|
||||
|
||||
@property(nonatomic, weak) UIViewController *showContactSelectVC;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TUIConversationSelectController
|
||||
|
||||
static NSString *const Id = @"con";
|
||||
|
||||
#pragma mark - Life
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
[self setupViews];
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews {
|
||||
[super viewWillLayoutSubviews];
|
||||
[self updateLayout];
|
||||
}
|
||||
|
||||
- (TUIConversationSelectDataProvider *)dataProvider {
|
||||
if (!_dataProvider) {
|
||||
_dataProvider = [[TUIConversationSelectDataProvider alloc] init];
|
||||
[_dataProvider loadConversations];
|
||||
}
|
||||
return _dataProvider;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
NSLog(@"%s dealloc", __FUNCTION__);
|
||||
}
|
||||
|
||||
#pragma mark - API
|
||||
+ (instancetype)showIn:(UIViewController *)presentVC {
|
||||
TUIConversationSelectController *vc = [[TUIConversationSelectController alloc] init];
|
||||
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
|
||||
nav.modalPresentationStyle = UIModalPresentationFullScreen;
|
||||
UIViewController *pVc = presentVC;
|
||||
if (pVc == nil) {
|
||||
pVc = UIApplication.sharedApplication.keyWindow.rootViewController;
|
||||
}
|
||||
[pVc presentViewController:nav animated:YES completion:nil];
|
||||
return vc;
|
||||
}
|
||||
|
||||
#pragma mark - UI
|
||||
- (void)setupViews {
|
||||
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:TIMCommonLocalizableString(Cancel)
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(doCancel)];
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:TIMCommonLocalizableString(Multiple)
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(doMultiple)];
|
||||
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
_headerView = [[TUICommonTableViewCell alloc] init];
|
||||
_headerView.textLabel.text = TIMCommonLocalizableString(TUIKitRelayTargetCreateNewChat);
|
||||
_headerView.textLabel.font = [UIFont systemFontOfSize:15.0];
|
||||
_headerView.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
|
||||
[_headerView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onCreateSessionOrSelectContact)]];
|
||||
|
||||
_tableView = [[UITableView alloc] init];
|
||||
_tableView.delegate = self;
|
||||
_tableView.dataSource = self;
|
||||
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
_tableView.tableHeaderView = self.headerView;
|
||||
[_tableView registerClass:TUIConversationCell.class forCellReuseIdentifier:Id];
|
||||
[self.view addSubview:_tableView];
|
||||
|
||||
_pickerView = [[TUIContactListPicker alloc] init];
|
||||
|
||||
[_pickerView setBackgroundColor:[UIColor groupTableViewBackgroundColor]];
|
||||
[_pickerView setHidden:YES];
|
||||
[_pickerView.accessoryBtn addTarget:self action:@selector(doPickerDone) forControlEvents:UIControlEventTouchUpInside];
|
||||
__weak typeof(self) weakSelf = self;
|
||||
_pickerView.onCancel = ^(TUICommonContactSelectCellData *data) {
|
||||
TUIConversationCellData *tmp = nil;
|
||||
for (TUIConversationCellData *convCellData in weakSelf.currentSelectedList) {
|
||||
if ([convCellData.conversationID isEqualToString:data.identifier]) {
|
||||
tmp = convCellData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tmp == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
tmp.selected = NO;
|
||||
[weakSelf.currentSelectedList removeObject:tmp];
|
||||
[weakSelf updatePickerView];
|
||||
[weakSelf.tableView reloadData];
|
||||
};
|
||||
[self.view addSubview:_pickerView];
|
||||
|
||||
@weakify(self);
|
||||
[RACObserve(self.dataProvider, dataList) subscribeNext:^(id _Nullable x) {
|
||||
@strongify(self);
|
||||
[self.tableView reloadData];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateLayout {
|
||||
[self.pickerView setHidden:!self.enableMuliple];
|
||||
self.headerView.frame = CGRectMake(0, 0, self.view.bounds.size.width, 55);
|
||||
_headerView.textLabel.text =
|
||||
self.enableMuliple ? TIMCommonLocalizableString(TUIKitRelayTargetSelectFromContacts) : TIMCommonLocalizableString(TUIKitRelayTargetCreateNewChat);
|
||||
|
||||
if (!self.enableMuliple) {
|
||||
self.tableView.frame = self.view.bounds;
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat pH = 55;
|
||||
CGFloat pMargin = 0;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
pMargin = self.view.safeAreaInsets.bottom;
|
||||
}
|
||||
[self.pickerView setFrame:CGRectMake(0, self.view.bounds.size.height - pH - pMargin, self.view.bounds.size.width, pH + pMargin)];
|
||||
self.tableView.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height - pH - pMargin);
|
||||
}
|
||||
|
||||
- (void)updatePickerView {
|
||||
NSMutableArray *arrayM = [NSMutableArray array];
|
||||
for (TUIConversationCellData *convCellData in self.currentSelectedList) {
|
||||
TUICommonContactSelectCellData *data = [[TUICommonContactSelectCellData alloc] init];
|
||||
data.avatarUrl = [NSURL URLWithString:convCellData.faceUrl];
|
||||
data.avatarImage = convCellData.avatarImage;
|
||||
data.title = convCellData.title;
|
||||
data.identifier = convCellData.conversationID;
|
||||
[arrayM addObject:data];
|
||||
}
|
||||
self.pickerView.selectArray = [NSArray arrayWithArray:arrayM];
|
||||
}
|
||||
|
||||
#pragma mark - Action
|
||||
- (void)doCancel {
|
||||
if (self.enableMuliple) {
|
||||
self.enableMuliple = NO;
|
||||
|
||||
for (TUIConversationCellData *cellData in self.dataProvider.dataList) {
|
||||
cellData.selected = NO;
|
||||
}
|
||||
|
||||
[self.currentSelectedList removeAllObjects];
|
||||
self.pickerView.selectArray = @[];
|
||||
[self updatePickerView];
|
||||
[self updateLayout];
|
||||
[self.tableView reloadData];
|
||||
} else {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)doMultiple {
|
||||
self.enableMuliple = YES;
|
||||
[self updateLayout];
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)onCreateSessionOrSelectContact {
|
||||
NSMutableArray *ids = NSMutableArray.new;
|
||||
for (TUIConversationCellData *cd in self.currentSelectedList) {
|
||||
if (![cd.userID isEqualToString:[[V2TIMManager sharedInstance] getLoginUser]]) {
|
||||
if (cd.userID.length > 0) {
|
||||
[ids addObject:cd.userID];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@weakify(self);
|
||||
void (^selectContactCompletion)(NSArray<TUICommonContactSelectCellData *> *) = ^(NSArray<TUICommonContactSelectCellData *> *array) {
|
||||
@strongify(self);
|
||||
[self dealSelectBlock:array];
|
||||
};
|
||||
|
||||
UIViewController *vc = [TUICore createObject:TUICore_TUIContactObjectFactory
|
||||
key:TUICore_TUIContactObjectFactory_GetContactSelectControllerMethod
|
||||
param:@{
|
||||
TUICore_TUIContactObjectFactory_GetContactSelectControllerMethod_DisableIdsKey : ids,
|
||||
TUICore_TUIContactObjectFactory_GetContactSelectControllerMethod_CompletionKey : selectContactCompletion,
|
||||
}];
|
||||
|
||||
[self.navigationController pushViewController:(UIViewController *)vc animated:YES];
|
||||
self.showContactSelectVC = vc;
|
||||
}
|
||||
|
||||
- (void)dealSelectBlock:(NSArray<TUICommonContactSelectCellData *> *)array {
|
||||
NSArray<TUICommonContactSelectCellData *> *selectArray = array;
|
||||
if (![selectArray.firstObject isKindOfClass:TUICommonContactSelectCellData.class]) {
|
||||
NSAssert(NO, @"Error value type");
|
||||
}
|
||||
if (self.enableMuliple) {
|
||||
/**
|
||||
* Multiple selection: Select from address book -> Create conversation for each contact -> Every contact will be displayed in pickerView
|
||||
*/
|
||||
for (TUICommonContactSelectCellData *contact in selectArray) {
|
||||
if ([self existInSelectedArray:contact.identifier]) {
|
||||
continue;
|
||||
}
|
||||
TUIConversationCellData *conv = [self findItemInDataListArray:contact.identifier];
|
||||
if (!conv) {
|
||||
conv = [[TUIConversationCellData alloc] init];
|
||||
conv.conversationID = contact.identifier;
|
||||
conv.userID = contact.identifier;
|
||||
conv.groupID = @"";
|
||||
conv.avatarImage = contact.avatarImage;
|
||||
conv.faceUrl = contact.avatarUrl.absoluteString;
|
||||
} else {
|
||||
conv.selected = !conv.selected;
|
||||
}
|
||||
|
||||
[self.currentSelectedList addObject:conv];
|
||||
}
|
||||
[self updatePickerView];
|
||||
[self.tableView reloadData];
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
} else {
|
||||
/**
|
||||
* Single Choice: Create a new chat (or a group chat if there are multiple people) -> Create a group chat for the selected contact -> Forward directly
|
||||
*/
|
||||
if (selectArray.count <= 1) {
|
||||
TUICommonContactSelectCellData *contact = selectArray.firstObject;
|
||||
if (contact) {
|
||||
TUIConversationCellData *conv = [[TUIConversationCellData alloc] init];
|
||||
conv.conversationID = contact.identifier;
|
||||
conv.userID = contact.identifier;
|
||||
conv.groupID = @"";
|
||||
conv.avatarImage = contact.avatarImage;
|
||||
conv.faceUrl = contact.avatarUrl.absoluteString;
|
||||
self.currentSelectedList = [NSMutableArray arrayWithArray:@[ conv ]];
|
||||
[self tryFinishSelected:^(BOOL finished) {
|
||||
if (finished) {
|
||||
[self notifyFinishSelecting];
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}];
|
||||
}
|
||||
return;
|
||||
}
|
||||
[self tryFinishSelected:^(BOOL finished) {
|
||||
if (finished) {
|
||||
[self createGroupWithContacts:selectArray
|
||||
completion:^(BOOL success) {
|
||||
if (success) {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
- (BOOL)existInSelectedArray:(NSString *)identifier {
|
||||
for (TUIConversationCellData *cellData in self.currentSelectedList) {
|
||||
if (cellData.userID.length && [cellData.userID isEqualToString:identifier]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (TUIConversationCellData *)findItemInDataListArray:(NSString *)identifier {
|
||||
for (TUIConversationCellData *cellData in self.dataProvider.dataList) {
|
||||
if (cellData.userID.length && [cellData.userID isEqualToString:identifier]) {
|
||||
return cellData;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)doPickerDone {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self tryFinishSelected:^(BOOL finished) {
|
||||
if (finished) {
|
||||
[self notifyFinishSelecting];
|
||||
[weakSelf dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
// confirm whether to forward or not
|
||||
- (void)tryFinishSelected:(TUIConversationSelectCompletHandler)handler {
|
||||
UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:TIMCommonLocalizableString(TUIKitRelayConfirmForward)
|
||||
message:nil
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertVc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Cancel)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
if (handler) {
|
||||
handler(NO);
|
||||
}
|
||||
}]];
|
||||
[alertVc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Confirm)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
if (handler) {
|
||||
handler(YES);
|
||||
}
|
||||
}]];
|
||||
[self presentViewController:alertVc animated:YES completion:nil];
|
||||
}
|
||||
|
||||
// notify others that the user has finished selecting conversations
|
||||
- (void)notifyFinishSelecting {
|
||||
if (self.navigateValueCallback) {
|
||||
NSMutableArray *temMArr = [NSMutableArray arrayWithCapacity:self.currentSelectedList.count];
|
||||
for (TUIConversationCellData *cellData in self.currentSelectedList) {
|
||||
[temMArr addObject:@{
|
||||
TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_ConversationID : cellData.conversationID ?: @"",
|
||||
TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_Title : cellData.title ?: @"",
|
||||
TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_UserID : cellData.userID ?: @"",
|
||||
TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_GroupID : cellData.groupID ?: @"",
|
||||
}];
|
||||
}
|
||||
self.navigateValueCallback(@{TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList : temMArr});
|
||||
}
|
||||
}
|
||||
|
||||
// create a new group to receive the forwarding messages
|
||||
- (void)createGroupWithContacts:(NSArray *)contacts completion:(void (^)(BOOL success))completion {
|
||||
@weakify(self);
|
||||
void (^createGroupCompletion)(BOOL, NSString *, NSString *) = ^(BOOL success, NSString *groupID, NSString *groupName) {
|
||||
@strongify(self);
|
||||
if (!success) {
|
||||
[TUITool makeToast:TIMCommonLocalizableString(TUIKitRelayTargetCrateGroupError)];
|
||||
if (completion) {
|
||||
completion(NO);
|
||||
}
|
||||
return;
|
||||
}
|
||||
TUIConversationCellData *cellData = [[TUIConversationCellData alloc] init];
|
||||
cellData.groupID = groupID;
|
||||
cellData.title = groupName;
|
||||
self.currentSelectedList = [NSMutableArray arrayWithArray:@[ cellData ]];
|
||||
[self notifyFinishSelecting];
|
||||
if (completion) {
|
||||
completion(YES);
|
||||
}
|
||||
};
|
||||
NSDictionary *param = @{
|
||||
TUICore_TUIContactService_CreateGroupMethod_GroupTypeKey : GroupType_Meeting,
|
||||
TUICore_TUIContactService_CreateGroupMethod_OptionKey : @(V2TIM_GROUP_ADD_ANY),
|
||||
TUICore_TUIContactService_CreateGroupMethod_ContactsKey : contacts,
|
||||
TUICore_TUIContactService_CreateGroupMethod_CompletionKey : createGroupCompletion
|
||||
};
|
||||
[TUICore callService:TUICore_TUIContactService method:TUICore_TUIContactService_CreateGroupMethod param:param];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate, UITableViewDataSource
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return self.dataProvider.dataList.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
TUIConversationCell *cell = [tableView dequeueReusableCellWithIdentifier:Id forIndexPath:indexPath];
|
||||
if (indexPath.row < 0 || indexPath.row >= self.dataProvider.dataList.count) {
|
||||
return cell;
|
||||
}
|
||||
TUIConversationCellData *cellData = self.dataProvider.dataList[indexPath.row];
|
||||
cellData.showCheckBox = self.enableMuliple;
|
||||
[cell fillWithData:cellData];
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:NO];
|
||||
TUIConversationCellData *cellData = self.dataProvider.dataList[indexPath.row];
|
||||
cellData.selected = !cellData.selected;
|
||||
if (!self.enableMuliple) {
|
||||
self.currentSelectedList = [NSMutableArray arrayWithArray:@[ cellData ]];
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self tryFinishSelected:^(BOOL finished) {
|
||||
if (finished) {
|
||||
[self notifyFinishSelecting];
|
||||
[weakSelf dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
if ([self.currentSelectedList containsObject:cellData]) {
|
||||
[self.currentSelectedList removeObject:cellData];
|
||||
} else {
|
||||
[self.currentSelectedList addObject:cellData];
|
||||
}
|
||||
|
||||
[self updatePickerView];
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return 56.0;
|
||||
}
|
||||
|
||||
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
|
||||
UIView *titleView = [[UIView alloc] init];
|
||||
titleView.backgroundColor = [UIColor groupTableViewBackgroundColor];
|
||||
titleView.bounds = CGRectMake(0, 0, self.tableView.bounds.size.width, 30);
|
||||
UILabel *label = [[UILabel alloc] init];
|
||||
label.text = TIMCommonLocalizableString(TUIKitRelayRecentMessages);
|
||||
label.font = [UIFont systemFontOfSize:12.0];
|
||||
label.textColor = [UIColor darkGrayColor];
|
||||
label.textAlignment = NSTextAlignmentLeft;
|
||||
[titleView addSubview:label];
|
||||
label.frame = CGRectMake(10, 0, self.tableView.bounds.size.width - 10, 30);
|
||||
return titleView;
|
||||
}
|
||||
|
||||
#pragma mark - Lazy
|
||||
|
||||
- (NSMutableArray<TUIConversationCellData *> *)currentSelectedList {
|
||||
if (_currentSelectedList == nil) {
|
||||
_currentSelectedList = [NSMutableArray array];
|
||||
}
|
||||
return _currentSelectedList;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// TUIConversationTableView.h
|
||||
// TUIConversation
|
||||
//
|
||||
// Created by xiangzhang on 2023/3/17.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TUIConversationCell.h"
|
||||
#import "TUIConversationListControllerListener.h"
|
||||
#import "TUIConversationListDataProvider.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString *gConversationCell_ReuseId = @"TConversationCell";
|
||||
|
||||
typedef void (^TUIConversationMarkUnreadCountChanged)(NSInteger markUnreadCount, NSInteger markHideUnreadCount);
|
||||
|
||||
@protocol TUIConversationTableViewDelegate <NSObject>
|
||||
@optional
|
||||
- (void)tableViewDidScroll:(CGFloat)offsetY;
|
||||
- (void)tableViewDidSelectCell:(TUIConversationCellData *)data;
|
||||
- (void)tableViewDidShowAlert:(UIAlertController *)ac;
|
||||
@end
|
||||
|
||||
@interface TUIConversationTableView : UITableView
|
||||
@property(nonatomic, weak) id<TUIConversationTableViewDelegate> convDelegate;
|
||||
@property(nonatomic, strong) TUIConversationListBaseDataProvider *dataProvider;
|
||||
@property(nonatomic, copy) TUIConversationMarkUnreadCountChanged unreadCountChanged;
|
||||
@property(nonatomic, strong) NSString *tipsMsgWhenNoConversation;
|
||||
@property(nonatomic, assign) BOOL disableMoreActionExtension;
|
||||
|
||||
- (void)tableViewDidSelectCell:(TUIConversationCellData *)data;
|
||||
- (void)tableViewFillCell:(TUIConversationCell *)cell withData:(TUIConversationCellData *)data;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
623
TUIKit/TUIConversation/UI_Classic/UI/TUIConversationTableView.m
Normal file
623
TUIKit/TUIConversation/UI_Classic/UI/TUIConversationTableView.m
Normal file
@@ -0,0 +1,623 @@
|
||||
//
|
||||
// TUIConversationTableView.m
|
||||
// TUIConversation
|
||||
//
|
||||
// Created by xiangzhang on 2023/3/17.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIConversationTableView.h"
|
||||
#import <TIMCommon/TUISecondConfirm.h>
|
||||
#import <TUICore/TUICore.h>
|
||||
#import "TUIConversationCell.h"
|
||||
#import "TUIFoldListViewController.h"
|
||||
#import "TUIConversationConfig.h"
|
||||
|
||||
@interface TUIConversationTableView () <UITableViewDelegate, UITableViewDataSource, TUIConversationListDataProviderDelegate>
|
||||
@property(nonatomic, strong) UIImageView *tipsView;
|
||||
@property(nonatomic, strong) UILabel *tipsLabel;
|
||||
@property (nonatomic, assign) BOOL hideMarkReadAction;
|
||||
@property (nonatomic, assign) BOOL hideDeleteAction;
|
||||
@property (nonatomic, assign) BOOL hideHideAction;
|
||||
@property (nonatomic, strong) NSArray *customizedItems;
|
||||
@end
|
||||
|
||||
@implementation TUIConversationTableView {
|
||||
TUIConversationListBaseDataProvider *_dataProvider;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self setTableView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
[self setTableView];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame {
|
||||
[super setFrame:frame];
|
||||
[self setTipsViewFrame];
|
||||
}
|
||||
|
||||
- (void)setTableView {
|
||||
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
// self.backgroundColor = TUIConversationDynamicColor(@"conversation_bg_color", @"#FFFFFF");
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.tableFooterView = [[UIView alloc] init];
|
||||
self.contentInset = UIEdgeInsetsMake(0, 0, 8, 0);
|
||||
[self registerClass:[TUIConversationCell class] forCellReuseIdentifier:gConversationCell_ReuseId];
|
||||
self.estimatedRowHeight = TConversationCell_Height;
|
||||
self.rowHeight = TConversationCell_Height;
|
||||
self.delaysContentTouches = NO;
|
||||
[self setSeparatorColor:TIMCommonDynamicColor(@"separator_color", @"#DBDBDB")];
|
||||
self.delegate = self;
|
||||
self.dataSource = self;
|
||||
[self addSubview:self.tipsView];
|
||||
[self addSubview:self.tipsLabel];
|
||||
self.disableMoreActionExtension = NO;
|
||||
|
||||
[self setTipsViewFrame];
|
||||
|
||||
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(onFriendInfoChanged:) name:@"FriendInfoChangedNotification" object:nil];
|
||||
}
|
||||
|
||||
- (void)onFriendInfoChanged:(NSNotification *)notice {
|
||||
V2TIMFriendInfo *friendInfo = notice.object;
|
||||
if (friendInfo == nil) {
|
||||
return;
|
||||
}
|
||||
for (TUIConversationCellData *cellData in self.dataProvider.conversationList) {
|
||||
if ([cellData.userID isEqualToString:friendInfo.userID]) {
|
||||
NSString *title = friendInfo.friendRemark;
|
||||
if (title.length == 0) {
|
||||
title = friendInfo.userFullInfo.nickName;
|
||||
}
|
||||
if (title.length == 0) {
|
||||
title = friendInfo.userID;
|
||||
}
|
||||
cellData.title = title;
|
||||
[self reloadData];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImageView *)tipsView {
|
||||
if (!_tipsView) {
|
||||
_tipsView = [[UIImageView alloc] init];
|
||||
_tipsView.image = TUIConversationDynamicImage(@"no_conversation_img", [UIImage imageNamed:TUIConversationImagePath(@"no_conversation")]);
|
||||
_tipsView.hidden = YES;
|
||||
}
|
||||
return _tipsView;
|
||||
}
|
||||
|
||||
- (UILabel *)tipsLabel {
|
||||
if (!_tipsLabel) {
|
||||
_tipsLabel = [[UILabel alloc] init];
|
||||
_tipsLabel.textColor = TIMCommonDynamicColor(@"nodata_tips_color", @"#999999");
|
||||
_tipsLabel.font = [UIFont systemFontOfSize:14.0];
|
||||
_tipsLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_tipsLabel.hidden = YES;
|
||||
}
|
||||
return _tipsLabel;
|
||||
}
|
||||
|
||||
- (void)setTipsViewFrame {
|
||||
self.tipsView.mm_width(128).mm_height(109).mm__centerX(self.mm_centerX).mm__centerY(self.mm_centerY - 60);
|
||||
self.tipsLabel.mm_width(300).mm_height(20).mm__centerX(self.mm_centerX).mm_top(self.tipsView.mm_maxY + 18);
|
||||
}
|
||||
|
||||
- (void)updateTipsViewStatus {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
if (0 == self.dataProvider.conversationList.count) {
|
||||
self.tipsView.hidden = NO;
|
||||
self.tipsLabel.hidden = NO;
|
||||
self.tipsLabel.text = self.tipsMsgWhenNoConversation;
|
||||
} else {
|
||||
self.tipsView.hidden = YES;
|
||||
self.tipsLabel.hidden = YES;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setDataProvider:(TUIConversationListBaseDataProvider *)dataProvider {
|
||||
_dataProvider = dataProvider;
|
||||
if (_dataProvider) {
|
||||
_dataProvider.delegate = self;
|
||||
[_dataProvider loadNexPageConversations];
|
||||
}
|
||||
}
|
||||
|
||||
- (TUIConversationListBaseDataProvider *)dataProvider {
|
||||
return _dataProvider;
|
||||
;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[NSNotificationCenter.defaultCenter removeObserver:self];
|
||||
}
|
||||
|
||||
#pragma mark TUIConversationListDataProviderDelegate
|
||||
- (void)insertConversationsAtIndexPaths:(NSArray *)indexPaths {
|
||||
if (!NSThread.isMainThread) {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[weakSelf insertConversationsAtIndexPaths:indexPaths];
|
||||
});
|
||||
return;
|
||||
}
|
||||
[UIView performWithoutAnimation:^{
|
||||
[self insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)reloadConversationsAtIndexPaths:(NSArray *)indexPaths {
|
||||
if (!NSThread.isMainThread) {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[weakSelf reloadConversationsAtIndexPaths:indexPaths];
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (self.isEditing) {
|
||||
self.editing = NO;
|
||||
}
|
||||
[UIView performWithoutAnimation:^{
|
||||
[self reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)deleteConversationAtIndexPaths:(NSArray *)indexPaths {
|
||||
if (!NSThread.isMainThread) {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[weakSelf deleteConversationAtIndexPaths:indexPaths];
|
||||
});
|
||||
return;
|
||||
}
|
||||
[self deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
|
||||
}
|
||||
|
||||
- (void)reloadAllConversations {
|
||||
if (!NSThread.isMainThread) {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[weakSelf reloadAllConversations];
|
||||
});
|
||||
return;
|
||||
}
|
||||
[self reloadData];
|
||||
}
|
||||
|
||||
- (void)updateMarkUnreadCount:(NSInteger)markUnreadCount markHideUnreadCount:(NSInteger)markHideUnreadCount {
|
||||
if (self.unreadCountChanged) {
|
||||
self.unreadCountChanged(markUnreadCount, markHideUnreadCount);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)parseActionHiddenTagAndCustomizedItems:(TUIConversationCellData *)cellData {
|
||||
id<TUIConversationConfigDataSource> dataSource = [TUIConversationConfig sharedConfig].moreMenuDataSource;
|
||||
NSArray *customizedItems = @[];
|
||||
if (dataSource && [dataSource respondsToSelector:@selector(conversationShouldHideItemsInMoreMenu:)]) {
|
||||
NSInteger flag = [dataSource conversationShouldHideItemsInMoreMenu:cellData];
|
||||
self.hideDeleteAction = flag & TUIConversationItemInMoreMenu_Delete;
|
||||
self.hideMarkReadAction = flag & TUIConversationItemInMoreMenu_MarkRead;
|
||||
self.hideHideAction = flag & TUIConversationItemInMoreMenu_Hide;
|
||||
}
|
||||
if (dataSource && [dataSource respondsToSelector:@selector(conversationShouldAddNewItemsToMoreMenu:)]) {
|
||||
NSArray *items = [dataSource conversationShouldAddNewItemsToMoreMenu:cellData];
|
||||
if ([items isKindOfClass:NSArray.class] && items.count > 0) {
|
||||
self.customizedItems = items;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Table view data source
|
||||
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
|
||||
[self.dataProvider loadNexPageConversations];
|
||||
}
|
||||
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
CGFloat offsetY = scrollView.contentOffset.y;
|
||||
if (self.convDelegate && [self.convDelegate respondsToSelector:@selector(tableViewDidScroll:)]) {
|
||||
[self.convDelegate tableViewDidScroll:offsetY];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
[self updateTipsViewStatus];
|
||||
return self.dataProvider.conversationList.count;
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
TUIConversationCellData *cellData = self.dataProvider.conversationList[indexPath.row];
|
||||
NSMutableArray *rowActions = [NSMutableArray array];
|
||||
[self parseActionHiddenTagAndCustomizedItems:cellData];
|
||||
|
||||
@weakify(self);
|
||||
if (cellData.isLocalConversationFoldList) {
|
||||
UITableViewRowAction *markHideAction =
|
||||
[UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault
|
||||
title:TIMCommonLocalizableString(MarkHide)
|
||||
handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull indexPath) {
|
||||
@strongify(self);
|
||||
[self.dataProvider markConversationHide:cellData];
|
||||
if (cellData.isLocalConversationFoldList) {
|
||||
[TUIConversationListDataProvider cacheConversationFoldListSettings_HideFoldItem:YES];
|
||||
}
|
||||
}];
|
||||
markHideAction.backgroundColor = RGB(242, 147, 64);
|
||||
if (!self.hideHideAction) {
|
||||
[rowActions addObject:markHideAction];
|
||||
}
|
||||
return rowActions;
|
||||
}
|
||||
// UITableViewRowAction *deleteAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive
|
||||
// title:TIMCommonLocalizableString(Delete)
|
||||
// handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull indexPath) {
|
||||
// @strongify(self);
|
||||
// TUISecondConfirmBtnInfo *cancelBtnInfo = [[TUISecondConfirmBtnInfo alloc] init];
|
||||
// cancelBtnInfo.tile = TIMCommonLocalizableString(Cancel);
|
||||
// cancelBtnInfo.click = ^{
|
||||
// self.editing = NO;
|
||||
// };
|
||||
// TUISecondConfirmBtnInfo *confirmBtnInfo = [[TUISecondConfirmBtnInfo alloc] init];
|
||||
// confirmBtnInfo.tile = TIMCommonLocalizableString(Delete);
|
||||
// confirmBtnInfo.click = ^{
|
||||
// [self.dataProvider removeConversation:cellData];
|
||||
// self.editing = NO;
|
||||
// };
|
||||
// [TUISecondConfirm show:TIMCommonLocalizableString(TUIKitConversationTipsDelete)
|
||||
// cancelBtnInfo:cancelBtnInfo
|
||||
// confirmBtnInfo:confirmBtnInfo];
|
||||
// }];
|
||||
// deleteAction.backgroundColor = RGB(255, 88, 88);
|
||||
|
||||
|
||||
// Delete action
|
||||
UITableViewRowAction *deleteAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive
|
||||
title:TIMCommonLocalizableString(Delete)
|
||||
handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull indexPath) {
|
||||
@strongify(self);
|
||||
TUISecondConfirmBtnInfo *cancelBtnInfo = [[TUISecondConfirmBtnInfo alloc] init];
|
||||
cancelBtnInfo.tile = TIMCommonLocalizableString(Cancel);
|
||||
cancelBtnInfo.click = ^{
|
||||
self.editing = NO;
|
||||
};
|
||||
TUISecondConfirmBtnInfo *confirmBtnInfo = [[TUISecondConfirmBtnInfo alloc] init];
|
||||
confirmBtnInfo.tile = TIMCommonLocalizableString(Delete);
|
||||
confirmBtnInfo.click = ^{
|
||||
[self.dataProvider removeConversation:cellData];
|
||||
self.editing = NO;
|
||||
};
|
||||
[TUISecondConfirm show:TIMCommonLocalizableString(TUIKitConversationTipsDelete)
|
||||
cancelBtnInfo:cancelBtnInfo
|
||||
confirmBtnInfo:confirmBtnInfo];
|
||||
}];
|
||||
deleteAction.backgroundColor = RGB(255, 88, 88);
|
||||
if (!self.hideDeleteAction) {
|
||||
[rowActions addObject:deleteAction];
|
||||
}
|
||||
NSString*topTitle = cellData.isOnTop?TIMCommonLocalizableString(CancelStickonTop):TIMCommonLocalizableString(StickyonTop);
|
||||
UITableViewRowAction *onTopAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive
|
||||
title:topTitle
|
||||
handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull indexPath) {
|
||||
@strongify(self);
|
||||
TUIConversationPin *pin = [TUIConversationPin sharedInstance];
|
||||
if (!cellData.isOnTop){
|
||||
[pin addTopConversation:cellData.conversationID callback:^(BOOL success, NSString * _Nullable errorMessage) {
|
||||
NSLog(@"置顶成功");
|
||||
}];
|
||||
}else{
|
||||
[pin removeTopConversation:cellData.conversationID callback:^(BOOL success, NSString * _Nullable errorMessage) {
|
||||
NSLog(@"取消置顶成功");
|
||||
}];
|
||||
}
|
||||
|
||||
}];
|
||||
onTopAction.backgroundColor = RGB(54,101,249);
|
||||
[rowActions addObject:onTopAction];
|
||||
|
||||
|
||||
// MarkAsRead action
|
||||
// UITableViewRowAction *markAsReadAction =
|
||||
// [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault
|
||||
// title:(cellData.isMarkAsUnread || cellData.unreadCount > 0) ? TIMCommonLocalizableString(MarkAsRead)
|
||||
// : TIMCommonLocalizableString(MarkAsUnRead)
|
||||
// handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull indexPath) {
|
||||
// @strongify(self);
|
||||
// if (cellData.isMarkAsUnread || cellData.unreadCount > 0) {
|
||||
// [self.dataProvider markConversationAsRead:cellData];
|
||||
// if (cellData.isLocalConversationFoldList) {
|
||||
// [TUIConversationListDataProvider cacheConversationFoldListSettings_FoldItemIsUnread:NO];
|
||||
// }
|
||||
// } else {
|
||||
// [self.dataProvider markConversationAsUnRead:cellData];
|
||||
// if (cellData.isLocalConversationFoldList) {
|
||||
// [TUIConversationListDataProvider cacheConversationFoldListSettings_FoldItemIsUnread:YES];
|
||||
// }
|
||||
// }
|
||||
// }];
|
||||
// markAsReadAction.backgroundColor = RGB(20, 122, 255);
|
||||
// if (!self.hideMarkReadAction) {
|
||||
// [rowActions addObject:markAsReadAction];
|
||||
// }
|
||||
//
|
||||
// // More action
|
||||
// NSArray *moreExtensionList =
|
||||
// [TUICore getExtensionList:TUICore_TUIConversationExtension_ConversationCellMoreAction_ClassicExtensionID
|
||||
// param:@{
|
||||
// TUICore_TUIConversationExtension_ConversationCellAction_ConversationIDKey : cellData.conversationID,
|
||||
// TUICore_TUIConversationExtension_ConversationCellAction_MarkListKey : cellData.conversationMarkList ?: @[],
|
||||
// TUICore_TUIConversationExtension_ConversationCellAction_GroupListKey : cellData.conversationGroupList ?: @[]
|
||||
// }];
|
||||
// if (self.disableMoreActionExtension || 0 == moreExtensionList.count) {
|
||||
// UITableViewRowAction *markAsHideAction =
|
||||
// [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive
|
||||
// title:TIMCommonLocalizableString(MarkHide)
|
||||
// handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull indexPath) {
|
||||
// @strongify(self);
|
||||
// [self.dataProvider markConversationHide:cellData];
|
||||
// if (cellData.isLocalConversationFoldList) {
|
||||
// [TUIConversationListDataProvider cacheConversationFoldListSettings_HideFoldItem:YES];
|
||||
// }
|
||||
// }];
|
||||
// markAsHideAction.backgroundColor = RGB(242, 147, 64);
|
||||
// if (!self.hideHideAction) {
|
||||
// [rowActions addObject:markAsHideAction];
|
||||
// }
|
||||
// } else {
|
||||
// UITableViewRowAction *moreAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive
|
||||
// title:TIMCommonLocalizableString(More)
|
||||
// handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull indexPath) {
|
||||
// @strongify(self);
|
||||
// self.editing = NO;
|
||||
// [self showMoreAction:cellData extensionList:moreExtensionList];
|
||||
// }];
|
||||
// moreAction.backgroundColor = RGB(242, 147, 64);
|
||||
// [rowActions addObject:moreAction];
|
||||
// }
|
||||
return rowActions;
|
||||
}
|
||||
|
||||
// available ios 11 +
|
||||
- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView
|
||||
trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath API_AVAILABLE(ios(11.0)) {
|
||||
TUIConversationCellData *cellData = self.dataProvider.conversationList[indexPath.row];
|
||||
|
||||
[self parseActionHiddenTagAndCustomizedItems:cellData];
|
||||
|
||||
// config Actions
|
||||
@weakify(self);
|
||||
if (cellData.isLocalConversationFoldList && !self.hideHideAction) {
|
||||
UIContextualAction *markHideAction = [UIContextualAction
|
||||
contextualActionWithStyle:UIContextualActionStyleNormal
|
||||
title:TIMCommonLocalizableString(MarkHide)
|
||||
handler:^(UIContextualAction *_Nonnull action, __kindof UIView *_Nonnull sourceView, void (^_Nonnull completionHandler)(BOOL)) {
|
||||
@strongify(self);
|
||||
[self.dataProvider markConversationHide:cellData];
|
||||
if (cellData.isLocalConversationFoldList) {
|
||||
[TUIConversationListDataProvider cacheConversationFoldListSettings_HideFoldItem:YES];
|
||||
}
|
||||
}];
|
||||
markHideAction.backgroundColor = RGB(242, 147, 64);
|
||||
UISwipeActionsConfiguration *configuration = [UISwipeActionsConfiguration configurationWithActions:@[ markHideAction ]];
|
||||
configuration.performsFirstActionWithFullSwipe = NO;
|
||||
return configuration;
|
||||
}
|
||||
|
||||
NSMutableArray *arrayM = [NSMutableArray array];
|
||||
|
||||
// Delete action
|
||||
UIContextualAction *deleteAction = [UIContextualAction
|
||||
contextualActionWithStyle:UIContextualActionStyleNormal
|
||||
title:TIMCommonLocalizableString(Delete)
|
||||
handler:^(UIContextualAction *_Nonnull action, __kindof UIView *_Nonnull sourceView, void (^_Nonnull completionHandler)(BOOL)) {
|
||||
@strongify(self);
|
||||
TUISecondConfirmBtnInfo *cancelBtnInfo = [[TUISecondConfirmBtnInfo alloc] init];
|
||||
cancelBtnInfo.tile = TIMCommonLocalizableString(Cancel);
|
||||
cancelBtnInfo.click = ^{
|
||||
self.editing = NO;
|
||||
};
|
||||
TUISecondConfirmBtnInfo *confirmBtnInfo = [[TUISecondConfirmBtnInfo alloc] init];
|
||||
confirmBtnInfo.tile = TIMCommonLocalizableString(Delete);
|
||||
confirmBtnInfo.click = ^{
|
||||
[self.dataProvider removeConversation:cellData];
|
||||
self.editing = NO;
|
||||
};
|
||||
[TUISecondConfirm show:TIMCommonLocalizableString(TUIKitConversationTipsDelete)
|
||||
cancelBtnInfo:cancelBtnInfo
|
||||
confirmBtnInfo:confirmBtnInfo];
|
||||
}];
|
||||
deleteAction.backgroundColor = RGB(242, 77, 76);
|
||||
if (!self.hideDeleteAction) {
|
||||
[arrayM addObject:deleteAction];
|
||||
}
|
||||
|
||||
UIContextualAction *markAsReadAction = [UIContextualAction
|
||||
contextualActionWithStyle:UIContextualActionStyleNormal
|
||||
title:(cellData.isOnTop) ? TIMCommonLocalizableString(CancelStickonTop)
|
||||
: TIMCommonLocalizableString(StickyonTop)
|
||||
handler:^(UIContextualAction *_Nonnull action, __kindof UIView *_Nonnull sourceView, void (^_Nonnull completionHandler)(BOOL)) {
|
||||
@strongify(self);
|
||||
TUIConversationPin *pin = [TUIConversationPin sharedInstance];
|
||||
if (!cellData.isOnTop){
|
||||
[pin addTopConversation:cellData.conversationID callback:^(BOOL success, NSString * _Nullable errorMessage) {
|
||||
NSLog(@"置顶成功");
|
||||
}];
|
||||
}else{
|
||||
[pin removeTopConversation:cellData.conversationID callback:^(BOOL success, NSString * _Nullable errorMessage) {
|
||||
NSLog(@"取消置顶成功");
|
||||
}];
|
||||
}
|
||||
}];
|
||||
markAsReadAction.backgroundColor = RGB(20, 122, 255);
|
||||
[arrayM addObject:markAsReadAction];
|
||||
|
||||
|
||||
// More action
|
||||
// NSArray *moreExtensionList =
|
||||
// [TUICore getExtensionList:TUICore_TUIConversationExtension_ConversationCellMoreAction_ClassicExtensionID
|
||||
// param:@{
|
||||
// TUICore_TUIConversationExtension_ConversationCellAction_ConversationIDKey : cellData.conversationID,
|
||||
// TUICore_TUIConversationExtension_ConversationCellAction_MarkListKey : cellData.conversationMarkList ?: @[],
|
||||
// TUICore_TUIConversationExtension_ConversationCellAction_GroupListKey : cellData.conversationGroupList ?: @[],
|
||||
// }];
|
||||
// if (self.disableMoreActionExtension || 0 == moreExtensionList.count) {
|
||||
// UIContextualAction *markAsHideAction = [UIContextualAction
|
||||
// contextualActionWithStyle:UIContextualActionStyleNormal
|
||||
// title:TIMCommonLocalizableString(MarkHide)
|
||||
// handler:^(UIContextualAction *_Nonnull action, __kindof UIView *_Nonnull sourceView, void (^_Nonnull completionHandler)(BOOL)) {
|
||||
// @strongify(self);
|
||||
// [self.dataProvider markConversationHide:cellData];
|
||||
// if (cellData.isLocalConversationFoldList) {
|
||||
// [TUIConversationListDataProvider cacheConversationFoldListSettings_HideFoldItem:YES];
|
||||
// }
|
||||
// }];
|
||||
// markAsHideAction.backgroundColor = RGB(242, 147, 64);
|
||||
// if (!self.hideHideAction) {
|
||||
// [arrayM addObject:markAsHideAction];
|
||||
// }
|
||||
// } else {
|
||||
// UIContextualAction *moreAction = [UIContextualAction
|
||||
// contextualActionWithStyle:UIContextualActionStyleNormal
|
||||
// title:TIMCommonLocalizableString(More)
|
||||
// handler:^(UIContextualAction *_Nonnull action, __kindof UIView *_Nonnull sourceView, void (^_Nonnull completionHandler)(BOOL)) {
|
||||
// @strongify(self);
|
||||
// self.editing = NO;
|
||||
// [self showMoreAction:cellData extensionList:moreExtensionList];
|
||||
// }];
|
||||
// moreAction.backgroundColor = RGB(242, 147, 64);
|
||||
// [arrayM addObject:moreAction];
|
||||
// }
|
||||
|
||||
UISwipeActionsConfiguration *configuration = [UISwipeActionsConfiguration configurationWithActions:[NSArray arrayWithArray:arrayM]];
|
||||
configuration.performsFirstActionWithFullSwipe = NO;
|
||||
return configuration;
|
||||
}
|
||||
|
||||
// MARK: action
|
||||
- (void)showMoreAction:(TUIConversationCellData *)cellData extensionList:(NSArray *)extensionList {
|
||||
UIAlertController *ac = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
__weak typeof(self) weakSelf = self;
|
||||
if (self.customizedItems.count > 0) {
|
||||
for (UIAlertAction *action in self.customizedItems) {
|
||||
[ac tuitheme_addAction:action];
|
||||
}
|
||||
}
|
||||
if (!self.hideHideAction) {
|
||||
[ac tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(MarkHide)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
[strongSelf.dataProvider markConversationHide:cellData];
|
||||
if (cellData.isLocalConversationFoldList) {
|
||||
[TUIConversationListDataProvider cacheConversationFoldListSettings_HideFoldItem:YES];
|
||||
}
|
||||
}]];
|
||||
}
|
||||
[self addCustomAction:ac cellData:cellData];
|
||||
[ac tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Cancel) style:UIAlertActionStyleCancel handler:nil]];
|
||||
if (self.convDelegate && [self.convDelegate respondsToSelector:@selector(tableViewDidShowAlert:)]) {
|
||||
[self.convDelegate tableViewDidShowAlert:ac];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addCustomAction:(UIAlertController *)ac cellData:(TUIConversationCellData *)cellData {
|
||||
NSArray *extensionList =
|
||||
[TUICore getExtensionList:TUICore_TUIConversationExtension_ConversationCellMoreAction_ClassicExtensionID
|
||||
param:@{
|
||||
TUICore_TUIConversationExtension_ConversationCellAction_ConversationIDKey : cellData.conversationID,
|
||||
TUICore_TUIConversationExtension_ConversationCellAction_MarkListKey : cellData.conversationMarkList ?: @[],
|
||||
TUICore_TUIConversationExtension_ConversationCellAction_GroupListKey : cellData.conversationGroupList ?: @[]
|
||||
}];
|
||||
for (TUIExtensionInfo *info in extensionList) {
|
||||
UIAlertAction *action = [UIAlertAction actionWithTitle:info.text
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
info.onClicked(@{});
|
||||
}];
|
||||
[ac tuitheme_addAction:action];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
TUIConversationCell *cell = [tableView dequeueReusableCellWithIdentifier:gConversationCell_ReuseId forIndexPath:indexPath];
|
||||
if (cell && indexPath.row < self.dataProvider.conversationList.count) {
|
||||
TUIConversationCellData *data = [self.dataProvider.conversationList objectAtIndex:indexPath.row];
|
||||
[self tableViewFillCell:cell withData:data];
|
||||
|
||||
NSArray *extensionList =
|
||||
[TUICore getExtensionList:TUICore_TUIConversationExtension_ConversationCellUpperRightCorner_ClassicExtensionID
|
||||
param:@{
|
||||
TUICore_TUIConversationExtension_ConversationCellUpperRightCorner_GroupListKey : data.conversationGroupList ?: @[],
|
||||
TUICore_TUIConversationExtension_ConversationCellUpperRightCorner_MarkListKey : data.conversationMarkList ?: @[],
|
||||
}];
|
||||
if (extensionList.count > 0) {
|
||||
TUIExtensionInfo *info = extensionList.firstObject;
|
||||
if (info.text.length > 0) {
|
||||
cell.timeLabel.text = info.text;
|
||||
} else if (info.icon) {
|
||||
NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
|
||||
textAttachment.image = info.icon;
|
||||
NSAttributedString *imageStr = [NSAttributedString attributedStringWithAttachment:textAttachment];
|
||||
cell.timeLabel.attributedText = imageStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)tableViewFillCell:(TUIConversationCell *)cell withData:(TUIConversationCellData *)data {
|
||||
[cell fillWithData:data];
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
TUIConversationCellData *data = [self.dataProvider.conversationList objectAtIndex:indexPath.row];
|
||||
[self tableViewDidSelectCell:data];
|
||||
}
|
||||
|
||||
- (void)tableViewDidSelectCell:(TUIConversationCellData *)data {
|
||||
if (self.convDelegate && [self.convDelegate respondsToSelector:@selector(tableViewDidSelectCell:)]) {
|
||||
[self.convDelegate tableViewDidSelectCell:data];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
// Turn on or off the length of the last line of dividers by controlling this switch
|
||||
BOOL needLastLineFromZeroToMax = NO;
|
||||
if ([cell respondsToSelector:@selector(setSeparatorInset:)]) {
|
||||
[cell setSeparatorInset:UIEdgeInsetsMake(0, 16, 0, 16)];
|
||||
if (needLastLineFromZeroToMax && indexPath.row == (self.dataProvider.conversationList.count - 1)) {
|
||||
[cell setSeparatorInset:UIEdgeInsetsZero];
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent the cell from inheriting the Table View's margin settings
|
||||
if (needLastLineFromZeroToMax && [cell respondsToSelector:@selector(setPreservesSuperviewLayoutMargins:)]) {
|
||||
[cell setPreservesSuperviewLayoutMargins:NO];
|
||||
}
|
||||
|
||||
// Explictly set your cell's layout margins
|
||||
if (needLastLineFromZeroToMax && [cell respondsToSelector:@selector(setLayoutMargins:)]) {
|
||||
[cell setLayoutMargins:UIEdgeInsetsZero];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TUIFoldListViewController.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by wyl on 2022/7/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIFoldListViewController : UIViewController
|
||||
|
||||
@property(nonatomic, copy) void (^dismissCallback)(NSMutableAttributedString *foldStr, NSArray *sortArr, NSArray *needRemoveFromCacheMapArray);
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
153
TUIKit/TUIConversation/UI_Classic/UI/TUIFoldListViewController.m
Normal file
153
TUIKit/TUIConversation/UI_Classic/UI/TUIFoldListViewController.m
Normal file
@@ -0,0 +1,153 @@
|
||||
//
|
||||
// TUIFoldListViewController.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by wyl on 2022/7/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIFoldListViewController.h"
|
||||
#import <TUICore/TUICore.h>
|
||||
#import "TUIConversationCellData.h"
|
||||
#import "TUIConversationListController.h"
|
||||
#import "TUIFoldConversationListDataProvider.h"
|
||||
|
||||
@interface TUIFoldListViewController () <TUINavigationControllerDelegate, TUIConversationListControllerListener>
|
||||
@property(nonatomic, strong) TUINaviBarIndicatorView *titleView;
|
||||
@property(nonatomic, copy) NSString *mainTitle;
|
||||
@property(nonatomic, strong) TUIConversationListController *conv;
|
||||
@end
|
||||
|
||||
@implementation TUIFoldListViewController
|
||||
|
||||
- (void)dealloc {
|
||||
}
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.conv = [[TUIConversationListController alloc] init];
|
||||
self.conv.isShowConversationGroup = NO;
|
||||
self.conv.isShowBanner = NO;
|
||||
self.conv.dataProvider = [[TUIFoldConversationListDataProvider alloc] init];
|
||||
self.conv.dataProvider.delegate = (id)self.conv;
|
||||
self.conv.tableViewForAll.tipsMsgWhenNoConversation = TIMCommonLocalizableString(TUIKitContactNoGroupChats);
|
||||
self.conv.tableViewForAll.disableMoreActionExtension = YES;
|
||||
self.conv.delegate = self;
|
||||
[self addChildViewController:self.conv];
|
||||
[self.view addSubview:self.conv.view];
|
||||
|
||||
[self setupNavigator];
|
||||
}
|
||||
|
||||
- (void)setTitle:(NSString *)title {
|
||||
self.mainTitle = title;
|
||||
}
|
||||
|
||||
- (void)setupNavigator {
|
||||
TUINavigationController *naviController = (TUINavigationController *)self.navigationController;
|
||||
naviController.uiNaviDelegate = self;
|
||||
_titleView = [[TUINaviBarIndicatorView alloc] init];
|
||||
self.navigationItem.titleView = _titleView;
|
||||
[self.titleView setTitle:TIMCommonLocalizableString(TUIKitConversationMarkFoldGroups)];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
TUINavigationController *naviController = (TUINavigationController *)self.navigationController;
|
||||
naviController.uiNaviDelegate = self;
|
||||
}
|
||||
|
||||
#pragma mark - TUINavigationControllerDelegate
|
||||
- (void)navigationControllerDidClickLeftButton:(TUINavigationController *)controller {
|
||||
[self excuteDismissCallback];
|
||||
}
|
||||
|
||||
- (void)navigationControllerDidSideSlideReturn:(TUINavigationController *)controller fromViewController:(UIViewController *)fromViewController {
|
||||
[self excuteDismissCallback];
|
||||
}
|
||||
|
||||
- (void)excuteDismissCallback {
|
||||
if (self.dismissCallback) {
|
||||
NSMutableAttributedString *foldSubTitle = [[NSMutableAttributedString alloc] initWithString:@""];
|
||||
TUIFoldConversationListDataProvider *foldProvider = (TUIFoldConversationListDataProvider *)self.conv.dataProvider;
|
||||
NSArray *needRemoveFromCacheMapArray = foldProvider.needRemoveConversationList;
|
||||
if (self.conv.dataProvider.conversationList.count > 0) {
|
||||
NSMutableArray *sortArray = [NSMutableArray arrayWithArray:self.conv.dataProvider.conversationList];
|
||||
[self sortDataList:sortArray];
|
||||
TUIConversationCellData *lastItem = sortArray[0];
|
||||
if (lastItem && [lastItem isKindOfClass:TUIConversationCellData.class]) {
|
||||
foldSubTitle = lastItem.foldSubTitle;
|
||||
}
|
||||
self.dismissCallback(foldSubTitle, sortArray, needRemoveFromCacheMapArray);
|
||||
} else {
|
||||
self.dismissCallback(foldSubTitle, @[], needRemoveFromCacheMapArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sortDataList:(NSMutableArray<TUIConversationCellData *> *)dataList {
|
||||
[dataList sortUsingComparator:^NSComparisonResult(TUIConversationCellData *obj1, TUIConversationCellData *obj2) {
|
||||
return obj1.orderKey < obj2.orderKey;
|
||||
}];
|
||||
}
|
||||
#pragma mark TUIConversationListControllerListener
|
||||
|
||||
- (NSString *)getConversationDisplayString:(V2TIMConversation *)conversation {
|
||||
V2TIMMessage *msg = conversation.lastMessage;
|
||||
if (msg.customElem == nil || msg.customElem.data == nil) {
|
||||
return nil;
|
||||
}
|
||||
NSDictionary *param = [TUITool jsonData2Dictionary:msg.customElem.data];
|
||||
if (param != nil && [param isKindOfClass:[NSDictionary class]]) {
|
||||
NSString *businessID = param[@"businessID"];
|
||||
if (![businessID isKindOfClass:[NSString class]]) {
|
||||
return nil;
|
||||
}
|
||||
// Determine whether it is a custom jump message
|
||||
if ([businessID isEqualToString:BussinessID_TextLink] || ([(NSString *)param[@"text"] length] > 0 && [(NSString *)param[@"link"] length] > 0)) {
|
||||
NSString *desc = param[@"text"];
|
||||
if (msg.status == V2TIM_MSG_STATUS_LOCAL_REVOKED) {
|
||||
V2TIMUserFullInfo *info = msg.revokerInfo;
|
||||
NSString * revokeReason = msg.revokeReason;
|
||||
BOOL hasRiskContent = msg.hasRiskContent;
|
||||
if (hasRiskContent) {
|
||||
desc = TIMCommonLocalizableString(TUIKitMessageTipsRecallRiskContent);
|
||||
}
|
||||
else if (info) {
|
||||
NSString *userName = info.nickName;
|
||||
desc = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsRecallMessageFormat), userName];
|
||||
}
|
||||
else if (msg.isSelf) {
|
||||
desc = TIMCommonLocalizableString(TUIKitMessageTipsYouRecallMessage);
|
||||
} else if (msg.userID.length > 0) {
|
||||
desc = TIMCommonLocalizableString(TUIKitMessageTipsOthersRecallMessage);
|
||||
} else if (msg.groupID.length > 0) {
|
||||
// For the name display of group messages, the group business card is displayed first, the nickname is the second priority, and the user ID is the lowest priority.
|
||||
NSString *userName = msg.nameCard;
|
||||
if (userName.length == 0) {
|
||||
userName = msg.nickName ?: msg.sender;
|
||||
}
|
||||
desc = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitMessageTipsRecallMessageFormat), userName];
|
||||
}
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)conversationListController:(TUIConversationListController *)conversationController didSelectConversation:(TUIConversationCellData *)conversation {
|
||||
NSDictionary *param = @{
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_ConversationID : conversation.conversationID ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_UserID : conversation.userID ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_GroupID : conversation.groupID ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_Title : conversation.title ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_AvatarUrl : conversation.faceUrl ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_AvatarImage : conversation.avatarImage ?: [UIImage new],
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_Draft : conversation.draftText ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_AtTipsStr : conversation.atTipsStr ?: @"",
|
||||
TUICore_TUIChatObjectFactory_ChatViewController_AtMsgSeqs : conversation.atMsgSeqs ?: @[]
|
||||
};
|
||||
[self.navigationController pushViewController:TUICore_TUIChatObjectFactory_ChatViewController_Classic param:param forResult:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user