TUIMessageCellConfig.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. // Created by Tencent on 2023/07/20.
  2. // Copyright © 2023 Tencent. All rights reserved.
  3. #import "TUIMessageCellConfig.h"
  4. #import <TUICore/TUIThemeManager.h>
  5. #import <TIMCommon/TUIMessageCell.h>
  6. #import <TIMCommon/TUIMessageCellData.h>
  7. #import <TIMCommon/TUISystemMessageCell.h>
  8. #import <TIMCommon/TUISystemMessageCellData.h>
  9. #import "TUIMessageDataProvider.h"
  10. #import "TUIFaceMessageCell.h"
  11. #import "TUIFaceMessageCellData.h"
  12. #import "TUIFileMessageCell.h"
  13. #import "TUIFileMessageCellData.h"
  14. #import "TUIImageMessageCell.h"
  15. #import "TUIImageMessageCellData.h"
  16. #import "TUIJoinGroupMessageCell.h"
  17. #import "TUIJoinGroupMessageCellData.h"
  18. #import "TUIMergeMessageCell.h"
  19. #import "TUIMergeMessageCellData.h"
  20. #import "TUIReferenceMessageCell.h"
  21. #import "TUIReplyMessageCell.h"
  22. #import "TUIReplyMessageCellData.h"
  23. #import "TUITextMessageCell.h"
  24. #import "TUITextMessageCellData.h"
  25. #import "TUIVideoMessageCell.h"
  26. #import "TUIVideoMessageCellData.h"
  27. #import "TUIVoiceMessageCell.h"
  28. #import "TUIVoiceMessageCellData.h"
  29. #define kIsCustomMessageFromPlugin @"kIsCustomMessageFromPlugin"
  30. typedef Class<TUIMessageCellProtocol> CellClass;
  31. typedef NSString * MessageID;
  32. typedef NSNumber * HeightNumber;
  33. static NSMutableDictionary *gCustomMessageInfoMap = nil;
  34. @interface TUIMessageCellConfig ()
  35. @property(nonatomic, weak) UITableView *tableView;
  36. @property(nonatomic, strong) NSMutableDictionary<TUICellDataClassName, CellClass> *cellClassMaps;
  37. @property(nonatomic, strong) NSMutableDictionary<MessageID, HeightNumber> *heightCacheMaps;
  38. @end
  39. @implementation TUIMessageCellConfig (MessageCellWidth)
  40. + (void)setMaxTextSize:(CGSize)maxTextSz {
  41. [TUITextMessageCell setMaxTextSize:maxTextSz];
  42. }
  43. @end
  44. #pragma mark - Custom Message Register
  45. @implementation TUIMessageCellConfig (CustomMessageRegister)
  46. + (NSMutableDictionary *)getCustomMessageInfoMap {
  47. if (gCustomMessageInfoMap == nil) {
  48. gCustomMessageInfoMap = [NSMutableDictionary dictionary];
  49. }
  50. return gCustomMessageInfoMap;
  51. }
  52. + (void)registerBuiltInCustomMessageInfo {
  53. [self registerCustomMessageCell:@"TUILinkCell" messageCellData:@"TUILinkCellData" forBusinessID:BussinessID_TextLink];
  54. [self registerCustomMessageCell:@"TUIGroupCreatedCell" messageCellData:@"TUIGroupCreatedCellData" forBusinessID:BussinessID_GroupCreate];
  55. [self registerCustomMessageCell:@"TUIEvaluationCell" messageCellData:@"TUIEvaluationCellData" forBusinessID:BussinessID_Evaluation];
  56. [self registerCustomMessageCell:@"TUIOrderCell" messageCellData:@"TUIOrderCellData" forBusinessID:BussinessID_Order];
  57. [self registerCustomMessageCell:@"TUIMessageCell" messageCellData:@"TUITypingStatusCellData" forBusinessID:BussinessID_Typing];
  58. [self registerCustomMessageCell:@"TUISystemMessageCell" messageCellData:@"TUILocalTipsCellData" forBusinessID:@"local_tips"];
  59. }
  60. + (void)registerExternalCustomMessageInfo {
  61. // Insert your own custom message UI here, your businessID can not be same with built-in
  62. //
  63. // Example:
  64. // [self registerCustomMessageCell:#your message cell# messageCellData:#your message cell data# forBusinessID:#your id#];
  65. //
  66. // ...
  67. [self registerCustomMessageCell:@"MOSystemNormalCell" messageCellData:@"MOSystemNormalCellData" forBusinessID:@"MOSystemNormalCell"];
  68. [self registerCustomMessageCell:@"MOUserLinkCell" messageCellData:@"MOUserLinkCellData" forBusinessID:@"MOUserLinkCell"];
  69. [self registerCustomMessageCell:@"MOUserNotificationCell" messageCellData:@"MOUserNotificationCellData" forBusinessID:@"MOUserNotificationCell"];
  70. }
  71. + (void)registerCustomMessageCell:(TUICellClassName)messageCellName
  72. messageCellData:(TUICellDataClassName)messageCellDataName
  73. forBusinessID:(TUIBusinessID)businessID {
  74. [self registerCustomMessageCell:messageCellName messageCellData:messageCellDataName forBusinessID:businessID isPlugin:NO];
  75. }
  76. + (void)registerCustomMessageCell:(TUICellClassName)messageCellName
  77. messageCellData:(TUICellDataClassName)messageCellDataName
  78. forBusinessID:(TUIBusinessID)businessID
  79. isPlugin:(BOOL)isPlugin {
  80. NSAssert(messageCellName.length > 0, @"message cell name can not be nil");
  81. NSAssert(messageCellDataName.length > 0, @"message cell data name can not be nil");
  82. NSAssert(businessID.length > 0, @"businessID can not be nil");
  83. NSAssert([[self getCustomMessageInfoMap] objectForKey:businessID] == nil, @"businessID can not be same with the exists");
  84. NSMutableDictionary *info = [NSMutableDictionary dictionary];
  85. info[BussinessID] = businessID;
  86. info[TMessageCell_Name] = messageCellName;
  87. info[TMessageCell_Data_Name] = messageCellDataName;
  88. info[kIsCustomMessageFromPlugin] = @(isPlugin);
  89. [[self getCustomMessageInfoMap] setObject:info forKey:businessID];
  90. }
  91. + (void)enumerateCustomMessageInfo:(void(^)(NSString *messageCellName,
  92. NSString *messageCellDataName,
  93. NSString *businessID,
  94. BOOL isPlugin))callback {
  95. if (callback == nil) {
  96. return;
  97. }
  98. [[self getCustomMessageInfoMap] enumerateKeysAndObjectsUsingBlock:^(TUIBusinessID key, NSDictionary *info, BOOL * _Nonnull stop) {
  99. NSString *businessID = info[BussinessID];
  100. NSString *messageCellName = info[TMessageCell_Name];
  101. NSString *messageCellDataName = info[TMessageCell_Data_Name];
  102. NSNumber *isPlugin = info[kIsCustomMessageFromPlugin];
  103. if (businessID && messageCellName && messageCellDataName && isPlugin) {
  104. callback(messageCellName, messageCellDataName, businessID, isPlugin);
  105. }
  106. }];
  107. }
  108. + (nullable Class)getCustomMessageCellDataClass:(NSString *)businessID {
  109. NSDictionary *info = [[self getCustomMessageInfoMap] objectForKey:businessID];
  110. if (info == nil) {
  111. return nil;
  112. }
  113. NSString *messageCellDataName = info[TMessageCell_Data_Name];
  114. Class messageCellDataClass = NSClassFromString(messageCellDataName);
  115. return messageCellDataClass;
  116. }
  117. + (BOOL)isPluginCustomMessageCellData:(TUIMessageCellData *)data {
  118. __block BOOL flag = NO;
  119. [[self getCustomMessageInfoMap] enumerateKeysAndObjectsUsingBlock:^(TUIBusinessID key, NSDictionary *info, BOOL * _Nonnull stop) {
  120. NSString *businessID = info[BussinessID];
  121. NSNumber *isPlugin = info[kIsCustomMessageFromPlugin];
  122. if (businessID && isPlugin && isPlugin.boolValue && [data.reuseId isEqualToString:businessID]) {
  123. flag = YES;
  124. *stop = YES;
  125. }
  126. }];
  127. return flag;
  128. }
  129. @end
  130. #pragma mark - Cell Message cell height
  131. @implementation TUIMessageCellConfig (MessageCellHeight)
  132. - (NSString *)getHeightCacheKey:(TUIMessageCellData *)msg {
  133. return msg.msgID.length == 0 ? [NSString stringWithFormat:@"%p", msg] : msg.msgID;
  134. }
  135. - (CGFloat)getHeightFromMessageCellData:(TUIMessageCellData *)cellData {
  136. static CGFloat screenWidth = 0;
  137. if (screenWidth == 0) {
  138. screenWidth = Screen_Width;
  139. }
  140. NSString *key = [self getHeightCacheKey:cellData];
  141. CGFloat height = [[self.heightCacheMaps objectForKey:key] floatValue];
  142. if (height == 0) {
  143. CellClass cellClass = [self.cellClassMaps objectForKey:NSStringFromClass(cellData.class)];
  144. if ([cellClass respondsToSelector:@selector(getHeight:withWidth:)]) {
  145. height = [cellClass getHeight:cellData withWidth:screenWidth];
  146. [self.heightCacheMaps setObject:@(height) forKey:key];
  147. }
  148. }
  149. return height;
  150. }
  151. - (CGFloat)getEstimatedHeightFromMessageCellData:(TUIMessageCellData *)cellData {
  152. NSString *key = [self getHeightCacheKey:cellData];
  153. CGFloat height = [[self.heightCacheMaps objectForKey:key] floatValue];
  154. return height > 0 ? height : UITableViewAutomaticDimension;;
  155. }
  156. - (void)removeHeightCacheOfMessageCellData:(TUIMessageCellData *)cellData {
  157. NSString *key = [self getHeightCacheKey:cellData];
  158. [self.heightCacheMaps removeObjectForKey:key];
  159. }
  160. @end
  161. #pragma mark - TUIMessageTable
  162. @implementation TUIMessageCellConfig
  163. #pragma mark - Life Cycle
  164. + (void)load {
  165. [self registerBuiltInCustomMessageInfo];
  166. [self registerExternalCustomMessageInfo];
  167. }
  168. #pragma mark - UI
  169. - (void)bindTableView:(UITableView *)tableView {
  170. self.tableView = tableView;
  171. [self bindMessageCellClass:TUITextMessageCell.class cellDataClass:TUITextMessageCellData.class reuseID:TTextMessageCell_ReuseId];
  172. [self bindMessageCellClass:TUIVoiceMessageCell.class cellDataClass:TUIVoiceMessageCellData.class reuseID:TVoiceMessageCell_ReuseId];
  173. [self bindMessageCellClass:TUIImageMessageCell.class cellDataClass:TUIImageMessageCellData.class reuseID:TImageMessageCell_ReuseId];
  174. [self bindMessageCellClass:TUISystemMessageCell.class cellDataClass:TUISystemMessageCellData.class reuseID:TSystemMessageCell_ReuseId];
  175. [self bindMessageCellClass:TUIFaceMessageCell.class cellDataClass:TUIFaceMessageCellData.class reuseID:TFaceMessageCell_ReuseId];
  176. [self bindMessageCellClass:TUIVideoMessageCell.class cellDataClass:TUIVideoMessageCellData.class reuseID:TVideoMessageCell_ReuseId];
  177. [self bindMessageCellClass:TUIFileMessageCell.class cellDataClass:TUIFileMessageCellData.class reuseID:TFileMessageCell_ReuseId];
  178. [self bindMessageCellClass:TUIJoinGroupMessageCell.class cellDataClass:TUIJoinGroupMessageCellData.class reuseID:TJoinGroupMessageCell_ReuseId];
  179. [self bindMessageCellClass:TUIMergeMessageCell.class cellDataClass:TUIMergeMessageCellData.class reuseID:TMergeMessageCell_ReuserId];
  180. [self bindMessageCellClass:TUIReplyMessageCell.class cellDataClass:TUIReplyMessageCellData.class reuseID:TReplyMessageCell_ReuseId];
  181. [self bindMessageCellClass:TUIReferenceMessageCell.class cellDataClass:TUIReferenceMessageCellData.class reuseID:TUIReferenceMessageCell_ReuseId];
  182. __weak typeof(self) weakSelf = self;
  183. [self.class enumerateCustomMessageInfo:^(NSString * _Nonnull messageCellName,
  184. NSString * _Nonnull messageCellDataName,
  185. NSString * _Nonnull businessID,
  186. BOOL isPlugin) {
  187. Class cellClass = NSClassFromString(messageCellName);
  188. Class cellDataClass = NSClassFromString(messageCellDataName);
  189. [weakSelf bindMessageCellClass:cellClass cellDataClass:cellDataClass reuseID:businessID];
  190. }];
  191. }
  192. - (void)bindMessageCellClass:(Class)cellClass cellDataClass:(Class)cellDataClass reuseID:(NSString *)reuseID {
  193. NSAssert(cellClass != nil, @"The UITableViewCell can not be nil");
  194. NSAssert(cellDataClass != nil, @"The cell data class can not be nil");
  195. NSAssert(reuseID.length > 0, @"The reuse identifier can not be nil");
  196. [self.tableView registerClass:cellClass forCellReuseIdentifier:reuseID];
  197. [self.cellClassMaps setObject:cellClass forKey:NSStringFromClass(cellDataClass)];
  198. }
  199. #pragma mark - Lazy && Read-write operate
  200. - (NSMutableDictionary<TUICellDataClassName,CellClass> *)cellClassMaps {
  201. if (_cellClassMaps == nil) {
  202. _cellClassMaps = [NSMutableDictionary dictionary];
  203. }
  204. return _cellClassMaps;
  205. }
  206. - (NSMutableDictionary<MessageID,HeightNumber> *)heightCacheMaps {
  207. if (_heightCacheMaps == nil) {
  208. _heightCacheMaps = [NSMutableDictionary dictionary];
  209. }
  210. return _heightCacheMaps;
  211. }
  212. @end