// Created by Tencent on 2023/07/20. // Copyright © 2023 Tencent. All rights reserved. #import "TUIMessageCellConfig.h" #import #import #import #import #import #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 CellClass; typedef NSString * MessageID; typedef NSNumber * HeightNumber; static NSMutableDictionary *gCustomMessageInfoMap = nil; @interface TUIMessageCellConfig () @property(nonatomic, weak) UITableView *tableView; @property(nonatomic, strong) NSMutableDictionary *cellClassMaps; @property(nonatomic, strong) NSMutableDictionary *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 *)cellClassMaps { if (_cellClassMaps == nil) { _cellClassMaps = [NSMutableDictionary dictionary]; } return _cellClassMaps; } - (NSMutableDictionary *)heightCacheMaps { if (_heightCacheMaps == nil) { _heightCacheMaps = [NSMutableDictionary dictionary]; } return _heightCacheMaps; } @end