Files
featherVoice/TUIKit/TUIChat/UI_Classic/Chat/TUIMessageCellConfig.m
2025-08-08 10:49:36 +08:00

252 lines
11 KiB
Objective-C

// Created by Tencent on 2023/07/20.
// Copyright © 2023 Tencent. All rights reserved.
#import "TUIMessageCellConfig.h"
#import <TUICore/TUIThemeManager.h>
#import <TIMCommon/TUIMessageCell.h>
#import <TIMCommon/TUIMessageCellData.h>
#import <TIMCommon/TUISystemMessageCell.h>
#import <TIMCommon/TUISystemMessageCellData.h>
#import "TUIMessageDataProvider.h"
#import "TUIFaceMessageCell.h"
#import "TUIFaceMessageCellData.h"
#import "TUIFileMessageCell.h"
#import "TUIFileMessageCellData.h"
#import "TUIImageMessageCell.h"
#import "TUIImageMessageCellData.h"
#import "TUIJoinGroupMessageCell.h"
#import "TUIJoinGroupMessageCellData.h"
#import "TUIMergeMessageCell.h"
#import "TUIMergeMessageCellData.h"
#import "TUIReferenceMessageCell.h"
#import "TUIReplyMessageCell.h"
#import "TUIReplyMessageCellData.h"
#import "TUITextMessageCell.h"
#import "TUITextMessageCellData.h"
#import "TUIVideoMessageCell.h"
#import "TUIVideoMessageCellData.h"
#import "TUIVoiceMessageCell.h"
#import "TUIVoiceMessageCellData.h"
#define kIsCustomMessageFromPlugin @"kIsCustomMessageFromPlugin"
typedef Class<TUIMessageCellProtocol> CellClass;
typedef NSString * MessageID;
typedef NSNumber * HeightNumber;
static NSMutableDictionary *gCustomMessageInfoMap = nil;
@interface TUIMessageCellConfig ()
@property(nonatomic, weak) UITableView *tableView;
@property(nonatomic, strong) NSMutableDictionary<TUICellDataClassName, CellClass> *cellClassMaps;
@property(nonatomic, strong) NSMutableDictionary<MessageID, HeightNumber> *heightCacheMaps;
@end
@implementation TUIMessageCellConfig (MessageCellWidth)
+ (void)setMaxTextSize:(CGSize)maxTextSz {
[TUITextMessageCell setMaxTextSize:maxTextSz];
}
@end
#pragma mark - Custom Message Register
@implementation TUIMessageCellConfig (CustomMessageRegister)
+ (NSMutableDictionary *)getCustomMessageInfoMap {
if (gCustomMessageInfoMap == nil) {
gCustomMessageInfoMap = [NSMutableDictionary dictionary];
}
return gCustomMessageInfoMap;
}
+ (void)registerBuiltInCustomMessageInfo {
[self registerCustomMessageCell:@"TUILinkCell" messageCellData:@"TUILinkCellData" forBusinessID:BussinessID_TextLink];
[self registerCustomMessageCell:@"TUIGroupCreatedCell" messageCellData:@"TUIGroupCreatedCellData" forBusinessID:BussinessID_GroupCreate];
[self registerCustomMessageCell:@"TUIEvaluationCell" messageCellData:@"TUIEvaluationCellData" forBusinessID:BussinessID_Evaluation];
[self registerCustomMessageCell:@"TUIOrderCell" messageCellData:@"TUIOrderCellData" forBusinessID:BussinessID_Order];
[self registerCustomMessageCell:@"TUIMessageCell" messageCellData:@"TUITypingStatusCellData" forBusinessID:BussinessID_Typing];
[self registerCustomMessageCell:@"TUISystemMessageCell" messageCellData:@"TUILocalTipsCellData" forBusinessID:@"local_tips"];
}
+ (void)registerExternalCustomMessageInfo {
// Insert your own custom message UI here, your businessID can not be same with built-in
//
// Example:
// [self registerCustomMessageCell:#your message cell# messageCellData:#your message cell data# forBusinessID:#your id#];
//
// ...
}
+ (void)registerCustomMessageCell:(TUICellClassName)messageCellName
messageCellData:(TUICellDataClassName)messageCellDataName
forBusinessID:(TUIBusinessID)businessID {
[self registerCustomMessageCell:messageCellName messageCellData:messageCellDataName forBusinessID:businessID isPlugin:NO];
}
+ (void)registerCustomMessageCell:(TUICellClassName)messageCellName
messageCellData:(TUICellDataClassName)messageCellDataName
forBusinessID:(TUIBusinessID)businessID
isPlugin:(BOOL)isPlugin {
NSAssert(messageCellName.length > 0, @"message cell name can not be nil");
NSAssert(messageCellDataName.length > 0, @"message cell data name can not be nil");
NSAssert(businessID.length > 0, @"businessID can not be nil");
NSAssert([[self getCustomMessageInfoMap] objectForKey:businessID] == nil, @"businessID can not be same with the exists");
NSMutableDictionary *info = [NSMutableDictionary dictionary];
info[BussinessID] = businessID;
info[TMessageCell_Name] = messageCellName;
info[TMessageCell_Data_Name] = messageCellDataName;
info[kIsCustomMessageFromPlugin] = @(isPlugin);
[[self getCustomMessageInfoMap] setObject:info forKey:businessID];
}
+ (void)enumerateCustomMessageInfo:(void(^)(NSString *messageCellName,
NSString *messageCellDataName,
NSString *businessID,
BOOL isPlugin))callback {
if (callback == nil) {
return;
}
[[self getCustomMessageInfoMap] enumerateKeysAndObjectsUsingBlock:^(TUIBusinessID key, NSDictionary *info, BOOL * _Nonnull stop) {
NSString *businessID = info[BussinessID];
NSString *messageCellName = info[TMessageCell_Name];
NSString *messageCellDataName = info[TMessageCell_Data_Name];
NSNumber *isPlugin = info[kIsCustomMessageFromPlugin];
if (businessID && messageCellName && messageCellDataName && isPlugin) {
callback(messageCellName, messageCellDataName, businessID, isPlugin);
}
}];
}
+ (nullable Class)getCustomMessageCellDataClass:(NSString *)businessID {
NSDictionary *info = [[self getCustomMessageInfoMap] objectForKey:businessID];
if (info == nil) {
return nil;
}
NSString *messageCellDataName = info[TMessageCell_Data_Name];
Class messageCellDataClass = NSClassFromString(messageCellDataName);
return messageCellDataClass;
}
+ (BOOL)isPluginCustomMessageCellData:(TUIMessageCellData *)data {
__block BOOL flag = NO;
[[self getCustomMessageInfoMap] enumerateKeysAndObjectsUsingBlock:^(TUIBusinessID key, NSDictionary *info, BOOL * _Nonnull stop) {
NSString *businessID = info[BussinessID];
NSNumber *isPlugin = info[kIsCustomMessageFromPlugin];
if (businessID && isPlugin && isPlugin.boolValue && [data.reuseId isEqualToString:businessID]) {
flag = YES;
*stop = YES;
}
}];
return flag;
}
@end
#pragma mark - Cell Message cell height
@implementation TUIMessageCellConfig (MessageCellHeight)
- (NSString *)getHeightCacheKey:(TUIMessageCellData *)msg {
return msg.msgID.length == 0 ? [NSString stringWithFormat:@"%p", msg] : msg.msgID;
}
- (CGFloat)getHeightFromMessageCellData:(TUIMessageCellData *)cellData {
static CGFloat screenWidth = 0;
if (screenWidth == 0) {
screenWidth = Screen_Width;
}
NSString *key = [self getHeightCacheKey:cellData];
CGFloat height = [[self.heightCacheMaps objectForKey:key] floatValue];
if (height == 0) {
CellClass cellClass = [self.cellClassMaps objectForKey:NSStringFromClass(cellData.class)];
if ([cellClass respondsToSelector:@selector(getHeight:withWidth:)]) {
height = [cellClass getHeight:cellData withWidth:screenWidth];
[self.heightCacheMaps setObject:@(height) forKey:key];
}
}
return height;
}
- (CGFloat)getEstimatedHeightFromMessageCellData:(TUIMessageCellData *)cellData {
NSString *key = [self getHeightCacheKey:cellData];
CGFloat height = [[self.heightCacheMaps objectForKey:key] floatValue];
return height > 0 ? height : UITableViewAutomaticDimension;;
}
- (void)removeHeightCacheOfMessageCellData:(TUIMessageCellData *)cellData {
NSString *key = [self getHeightCacheKey:cellData];
[self.heightCacheMaps removeObjectForKey:key];
}
@end
#pragma mark - TUIMessageTable
@implementation TUIMessageCellConfig
#pragma mark - Life Cycle
+ (void)load {
[self registerBuiltInCustomMessageInfo];
[self registerExternalCustomMessageInfo];
}
#pragma mark - UI
- (void)bindTableView:(UITableView *)tableView {
self.tableView = tableView;
[self bindMessageCellClass:TUITextMessageCell.class cellDataClass:TUITextMessageCellData.class reuseID:TTextMessageCell_ReuseId];
[self bindMessageCellClass:TUIVoiceMessageCell.class cellDataClass:TUIVoiceMessageCellData.class reuseID:TVoiceMessageCell_ReuseId];
[self bindMessageCellClass:TUIImageMessageCell.class cellDataClass:TUIImageMessageCellData.class reuseID:TImageMessageCell_ReuseId];
[self bindMessageCellClass:TUISystemMessageCell.class cellDataClass:TUISystemMessageCellData.class reuseID:TSystemMessageCell_ReuseId];
[self bindMessageCellClass:TUIFaceMessageCell.class cellDataClass:TUIFaceMessageCellData.class reuseID:TFaceMessageCell_ReuseId];
[self bindMessageCellClass:TUIVideoMessageCell.class cellDataClass:TUIVideoMessageCellData.class reuseID:TVideoMessageCell_ReuseId];
[self bindMessageCellClass:TUIFileMessageCell.class cellDataClass:TUIFileMessageCellData.class reuseID:TFileMessageCell_ReuseId];
[self bindMessageCellClass:TUIJoinGroupMessageCell.class cellDataClass:TUIJoinGroupMessageCellData.class reuseID:TJoinGroupMessageCell_ReuseId];
[self bindMessageCellClass:TUIMergeMessageCell.class cellDataClass:TUIMergeMessageCellData.class reuseID:TMergeMessageCell_ReuserId];
[self bindMessageCellClass:TUIReplyMessageCell.class cellDataClass:TUIReplyMessageCellData.class reuseID:TReplyMessageCell_ReuseId];
[self bindMessageCellClass:TUIReferenceMessageCell.class cellDataClass:TUIReferenceMessageCellData.class reuseID:TUIReferenceMessageCell_ReuseId];
__weak typeof(self) weakSelf = self;
[self.class enumerateCustomMessageInfo:^(NSString * _Nonnull messageCellName,
NSString * _Nonnull messageCellDataName,
NSString * _Nonnull businessID,
BOOL isPlugin) {
Class cellClass = NSClassFromString(messageCellName);
Class cellDataClass = NSClassFromString(messageCellDataName);
[weakSelf bindMessageCellClass:cellClass cellDataClass:cellDataClass reuseID:businessID];
}];
}
- (void)bindMessageCellClass:(Class)cellClass cellDataClass:(Class)cellDataClass reuseID:(NSString *)reuseID {
NSAssert(cellClass != nil, @"The UITableViewCell can not be nil");
NSAssert(cellDataClass != nil, @"The cell data class can not be nil");
NSAssert(reuseID.length > 0, @"The reuse identifier can not be nil");
[self.tableView registerClass:cellClass forCellReuseIdentifier:reuseID];
[self.cellClassMaps setObject:cellClass forKey:NSStringFromClass(cellDataClass)];
}
#pragma mark - Lazy && Read-write operate
- (NSMutableDictionary<TUICellDataClassName,CellClass> *)cellClassMaps {
if (_cellClassMaps == nil) {
_cellClassMaps = [NSMutableDictionary dictionary];
}
return _cellClassMaps;
}
- (NSMutableDictionary<MessageID,HeightNumber> *)heightCacheMaps {
if (_heightCacheMaps == nil) {
_heightCacheMaps = [NSMutableDictionary dictionary];
}
return _heightCacheMaps;
}
@end