TUIRepliesDetailViewController.m 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. //
  2. // TUIRepliesDetailViewController.m
  3. // TUIChat
  4. //
  5. // Created by wyl on 2022/4/27.
  6. // Copyright © 2023 Tencent. All rights reserved.
  7. //
  8. #import "TUIRepliesDetailViewController.h"
  9. #import <TIMCommon/TUIMessageCell.h>
  10. #import "TUIChatDataProvider.h"
  11. #import "TUIMessageDataProvider.h"
  12. #import "TUITextMessageCell.h"
  13. #import "TUITextMessageCellData.h"
  14. #import "TUIChatConfig.h"
  15. #import <TIMCommon/TIMDefine.h>
  16. #import <TIMCommon/TUISystemMessageCell.h>
  17. #import <TUICore/TUICore.h>
  18. #import <TUICore/TUIDarkModel.h>
  19. #import <TUICore/TUIGlobalization.h>
  20. #import <TUICore/TUIThemeManager.h>
  21. #import "TUIFaceMessageCell.h"
  22. #import "TUIFileMessageCell.h"
  23. #import "TUIFileViewController.h"
  24. #import "TUIImageMessageCell.h"
  25. #import "TUIJoinGroupMessageCell.h"
  26. #import "TUILinkCell.h"
  27. #import "TUIMediaView.h"
  28. #import "TUIMergeMessageCell.h"
  29. #import "TUIMergeMessageListController.h"
  30. #import "TUIMessageDataProvider.h"
  31. #import "TUIReferenceMessageCell.h"
  32. #import "TUIReplyMessageCell.h"
  33. #import "TUIReplyMessageCellData.h"
  34. #import "TUITextMessageCell.h"
  35. #import "TUIVideoMessageCell.h"
  36. #import "TUIVoiceMessageCell.h"
  37. #import "TUIMessageCellConfig.h"
  38. @interface TUIRepliesDetailViewController () <TUIInputControllerDelegate,
  39. UITableViewDelegate,
  40. UITableViewDataSource,
  41. TUIMessageBaseDataProviderDataSource,
  42. TUIMessageCellDelegate,
  43. TUINotificationProtocol,
  44. V2TIMAdvancedMsgListener>
  45. @property(nonatomic, strong) TUIMessageCellData *cellData;
  46. @property(nonatomic, strong) TUIMessageDataProvider *msgDataProvider;
  47. @property(nonatomic, strong) UIView *headerView;
  48. @property(nonatomic, strong) UITableView *tableView;
  49. @property(nonatomic, strong) NSArray<V2TIMMessage *> *imMsgs;
  50. @property(nonatomic, strong) NSMutableArray<TUIMessageCellData *> *uiMsgs;
  51. @property(nonatomic, assign) BOOL responseKeyboard;
  52. @property(nonatomic, strong) TUIChatConversationModel *conversationData;
  53. @property(nonatomic, strong) TUIMessageCellConfig *messageCellConfig;
  54. @property(nonatomic, strong) TUIMessageCellLayout *originCellLayout;
  55. @property TMsgDirection direction;
  56. @property(nonatomic, assign) BOOL showName;
  57. @property(nonatomic, assign) BOOL showMessageTime;
  58. @property(nonatomic) BOOL isMsgNeedReadReceipt;
  59. @end
  60. @implementation TUIRepliesDetailViewController
  61. - (instancetype)initWithCellData:(TUIMessageCellData *)data conversationData:(TUIChatConversationModel *)conversationData {
  62. self = [super init];
  63. self.cellData = data;
  64. [self setConversation:conversationData];
  65. return self;
  66. }
  67. - (void)viewDidLoad {
  68. [super viewDidLoad];
  69. [self setupViews];
  70. [self setupInputViewController];
  71. [[V2TIMManager sharedInstance] addAdvancedMsgListener:self];
  72. [TUICore registerEvent:TUICore_TUIPluginNotify
  73. subKey:TUICore_TUIPluginNotify_DidChangePluginViewSubKey
  74. object:self];
  75. }
  76. - (void)viewWillAppear:(BOOL)animated {
  77. [self updateRootMsg];
  78. [self applyData];
  79. [self updateTableViewConstraint];
  80. }
  81. - (void)viewDidAppear:(BOOL)animated {
  82. [super viewDidAppear:animated];
  83. self.responseKeyboard = YES;
  84. self.isMsgNeedReadReceipt = YES;
  85. }
  86. - (void)viewWillDisappear:(BOOL)animated {
  87. [super viewWillDisappear:animated];
  88. self.responseKeyboard = NO;
  89. [self revertRootMsg];
  90. if (self.willCloseCallback) {
  91. self.willCloseCallback();
  92. }
  93. }
  94. - (void)viewDidDisappear:(BOOL)animated {
  95. [super viewDidDisappear:animated];
  96. if (self.inputController.status == Input_Status_Input || self.inputController.status == Input_Status_Input_Keyboard) {
  97. CGPoint offset = self.tableView.contentOffset;
  98. __weak typeof(self) weakSelf = self;
  99. dispatch_async(dispatch_get_main_queue(), ^{
  100. __strong typeof(weakSelf) strongSelf = weakSelf;
  101. strongSelf.responseKeyboard = YES;
  102. [UIApplication.sharedApplication.keyWindow endEditing:YES];
  103. [strongSelf inputController:strongSelf.inputController didChangeHeight:CGRectGetMaxY(strongSelf.inputController.inputBar.frame) + Bottom_SafeHeight];
  104. [strongSelf.tableView setContentOffset:offset];
  105. });
  106. }
  107. }
  108. - (void)dealloc {
  109. NSLog(@"%s dealloc", __FUNCTION__);
  110. [TUICore unRegisterEventByObject:self];
  111. }
  112. - (void)applyData {
  113. NSArray *messageModifyReplies = self.cellData.messageModifyReplies;
  114. NSMutableArray *msgIDArray = [NSMutableArray array];
  115. if (messageModifyReplies.count > 0) {
  116. for (NSDictionary *dic in messageModifyReplies) {
  117. if (dic) {
  118. NSString *messageID = dic[@"messageID"];
  119. if (IS_NOT_EMPTY_NSSTRING(messageID)) {
  120. [msgIDArray addObject:messageID];
  121. }
  122. }
  123. }
  124. }
  125. // When the only reply is retracted, go back to the previous controller
  126. if (msgIDArray.count <= 0) {
  127. [self.navigationController popViewControllerAnimated:YES];
  128. }
  129. __weak typeof(self) weakSelf = self;
  130. [TUIChatDataProvider findMessages:msgIDArray
  131. callback:^(BOOL succ, NSString *_Nonnull error_message, NSArray *_Nonnull msgs) {
  132. __strong typeof(weakSelf) strongSelf = weakSelf;
  133. if (succ) {
  134. if (msgs.count > 0) {
  135. strongSelf.imMsgs = msgs;
  136. strongSelf.uiMsgs = [self transUIMsgFromIMMsg:msgs];
  137. dispatch_async(dispatch_get_main_queue(), ^{
  138. if (strongSelf.uiMsgs.count != 0) {
  139. [strongSelf.tableView reloadData];
  140. [strongSelf.tableView layoutIfNeeded];
  141. dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
  142. (int64_t)(0.1 * NSEC_PER_SEC)),
  143. dispatch_get_main_queue(), ^{
  144. [strongSelf scrollToBottom:NO];
  145. });
  146. }
  147. });
  148. }
  149. }
  150. }];
  151. }
  152. - (void)updateTableViewConstraint {
  153. CGFloat textViewHeight = TUIChatConfig.defaultConfig.enableMainPageInputBar? TTextView_Height:0;
  154. CGFloat height = textViewHeight + Bottom_SafeHeight;
  155. CGRect msgFrame = self.tableView.frame;
  156. msgFrame.size.height = self.view.frame.size.height - height;
  157. self.tableView.frame = msgFrame;
  158. }
  159. - (void)setupViews {
  160. self.title = TIMCommonLocalizableString(TUIKitRepliesDetailTitle);
  161. self.view.backgroundColor = TUIChatDynamicColor(@"chat_controller_bg_color", @"#FFFFFF");
  162. self.tableView.scrollsToTop = NO;
  163. self.tableView.delegate = self;
  164. self.tableView.dataSource = self;
  165. [self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
  166. self.tableView.contentInset = UIEdgeInsetsMake(5, 0, 0, 0);
  167. [self.messageCellConfig bindTableView:self.tableView];
  168. }
  169. - (void)setupInputViewController {
  170. _inputController = [[TUIInputController alloc] init];
  171. _inputController.delegate = self;
  172. _inputController.view.frame =
  173. CGRectMake(0, self.view.frame.size.height - TTextView_Height - Bottom_SafeHeight, self.view.frame.size.width, TTextView_Height + Bottom_SafeHeight);
  174. _inputController.view.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
  175. _inputController.inputBar.isFromReplyPage = YES;
  176. [self addChildViewController:_inputController];
  177. [self.view addSubview:_inputController.view];
  178. TUIFaceGroup *group = TIMConfig.defaultConfig.faceGroups[0];
  179. [_inputController.faceSegementScrollView setItems:(id) @[ group ] delegate:(id)_inputController];
  180. TUIMenuCellData *data = [[TUIMenuCellData alloc] init];
  181. data.path = group.menuPath;
  182. data.isSelected = YES;
  183. [_inputController.menuView setData:(id) @[ data ]];
  184. _inputController.view.hidden = !TUIChatConfig.defaultConfig.enableMainPageInputBar;
  185. CGFloat margin = 0;
  186. CGFloat padding = 10;
  187. _inputController.inputBar.inputTextView.frame =
  188. CGRectMake(margin, _inputController.inputBar.inputTextView.frame.origin.y,
  189. _inputController.inputBar.frame.size.width - _inputController.inputBar.faceButton.frame.size.width - margin * 2 - padding,
  190. _inputController.inputBar.inputTextView.frame.size.height);
  191. _inputController.inputBar.faceButton.frame =
  192. CGRectMake(_inputController.inputBar.frame.size.width - _inputController.inputBar.faceButton.frame.size.width - margin,
  193. _inputController.inputBar.faceButton.frame.origin.y, _inputController.inputBar.faceButton.frame.size.width,
  194. _inputController.inputBar.faceButton.frame.size.height);
  195. if (_inputController.inputBar.micButton) {
  196. _inputController.inputBar.micButton.alpha = 0;
  197. }
  198. if (_inputController.inputBar.moreButton) {
  199. _inputController.inputBar.moreButton.alpha = 0;
  200. }
  201. [_inputController.inputBar defaultLayout];
  202. }
  203. - (void)updateRootMsg {
  204. self.originCellLayout = self.cellData.cellLayout;
  205. self.direction = self.cellData.direction;
  206. self.showName = self.cellData.showName;
  207. self.showMessageTime = self.cellData.showMessageTime;
  208. TUIMessageCellData *data = self.cellData;
  209. TUIMessageCellLayout *layout = TUIMessageCellLayout.incommingMessageLayout;
  210. if ([data isKindOfClass:TUITextMessageCellData.class]) {
  211. layout = TUIMessageCellLayout.incommingTextMessageLayout;
  212. }
  213. if ([data isKindOfClass:TUIReferenceMessageCellData.class]) {
  214. layout = TUIMessageCellLayout.incommingTextMessageLayout;
  215. }
  216. if ([data isKindOfClass:TUIVoiceMessageCellData.class]) {
  217. layout = [TUIMessageCellLayout incommingVoiceMessageLayout];
  218. }
  219. self.cellData.cellLayout = layout;
  220. self.cellData.direction = MsgDirectionIncoming;
  221. self.cellData.showName = YES;
  222. self.cellData.showMessageModifyReplies = NO;
  223. self.cellData.showMessageTime = YES;
  224. }
  225. - (void)revertRootMsg {
  226. self.cellData.cellLayout = self.originCellLayout;
  227. self.cellData.direction = self.direction;
  228. self.cellData.showName = self.showName;
  229. self.cellData.showMessageModifyReplies = YES;
  230. self.cellData.showMessageTime = self.showMessageTime;
  231. }
  232. - (NSMutableArray *)transUIMsgFromIMMsg:(NSArray *)msgs {
  233. NSMutableArray *uiMsgs = [NSMutableArray array];
  234. for (NSInteger k = 0; k < msgs.count; k++) {
  235. V2TIMMessage *msg = msgs[k];
  236. TUIMessageCellData *data = [TUITextMessageCellData getCellData:msg];
  237. TUIMessageCellLayout *layout = TUIMessageCellLayout.incommingMessageLayout;
  238. if ([data isKindOfClass:TUITextMessageCellData.class]) {
  239. layout = TUIMessageCellLayout.incommingTextMessageLayout;
  240. }
  241. data.cellLayout = layout;
  242. data.direction = MsgDirectionIncoming;
  243. data.showName = YES;
  244. if (data) {
  245. data.innerMessage = msg;
  246. [uiMsgs addObject:data];
  247. }
  248. }
  249. NSArray *sortedArray = [uiMsgs sortedArrayUsingComparator:^NSComparisonResult(TUIMessageCellData *obj1, TUIMessageCellData *obj2) {
  250. if ([obj1.innerMessage.timestamp timeIntervalSince1970] == [obj2.innerMessage.timestamp timeIntervalSince1970]) {
  251. return obj1.innerMessage.seq > obj2.innerMessage.seq;
  252. } else {
  253. return [obj1.innerMessage.timestamp compare:obj2.innerMessage.timestamp];
  254. }
  255. }];
  256. uiMsgs = [NSMutableArray arrayWithArray:sortedArray];
  257. return uiMsgs;
  258. }
  259. #pragma mark - tableView
  260. - (UITableView *)tableView {
  261. if (!_tableView) {
  262. CGRect rect = self.view.bounds;
  263. _tableView = [[UITableView alloc] initWithFrame:rect style:UITableViewStylePlain];
  264. if (@available(iOS 15.0, *)) {
  265. _tableView.sectionHeaderTopPadding = 0;
  266. }
  267. [self.view addSubview:_tableView];
  268. }
  269. return _tableView;
  270. }
  271. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  272. return 2;
  273. }
  274. - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
  275. if (section == 1) {
  276. return 20;
  277. }
  278. return 0;
  279. }
  280. - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
  281. if (section == 1) {
  282. UIView *view = [[UIView alloc] init];
  283. view.backgroundColor = [UIColor clearColor];
  284. return view;
  285. }
  286. return nil;
  287. }
  288. - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
  289. if (section == 0) {
  290. return 0.5;
  291. }
  292. return 0;
  293. }
  294. - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
  295. if (section == 0) {
  296. UIView *line = [[UIView alloc] initWithFrame:CGRectMake(0, 0, Screen_Width, 0.5)];
  297. line.backgroundColor = TIMCommonDynamicColor(@"separator_color", @"#DBDBDB");
  298. return line;
  299. }
  300. return nil;
  301. }
  302. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  303. if (section == 0) {
  304. return 1;
  305. }
  306. return _uiMsgs.count;
  307. }
  308. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  309. if (indexPath.section == 0) {
  310. return [self.messageCellConfig getHeightFromMessageCellData:self.cellData];
  311. } else {
  312. if (indexPath.row < self.uiMsgs.count) {
  313. TUIMessageCellData *cellData = self.uiMsgs[indexPath.row];
  314. return [self.messageCellConfig getHeightFromMessageCellData:cellData];
  315. } else {
  316. return 0;
  317. }
  318. }
  319. }
  320. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  321. if (indexPath.section == 0) {
  322. TUIMessageCell *cell = nil;
  323. TUIMessageCellData *data = self.cellData;
  324. cell = [tableView dequeueReusableCellWithIdentifier:data.reuseId forIndexPath:indexPath];
  325. cell.delegate = self;
  326. [cell fillWithData:data];
  327. [cell notifyBottomContainerReadyOfData:nil];
  328. return cell;
  329. }
  330. TUIMessageCellData *data = _uiMsgs[indexPath.row];
  331. data.showMessageTime = YES;
  332. data.showCheckBox = NO;
  333. TUIMessageCell *cell = nil;
  334. cell = [tableView dequeueReusableCellWithIdentifier:data.reuseId forIndexPath:indexPath];
  335. [cell fillWithData:_uiMsgs[indexPath.row]];
  336. cell.delegate = self;
  337. [cell notifyBottomContainerReadyOfData:nil];
  338. if ([cell isKindOfClass:TUIBubbleMessageCell.class]) {
  339. TUIBubbleMessageCell *bubbleCell = (TUIBubbleMessageCell *)cell;
  340. if (bubbleCell.bubbleView) {
  341. bubbleCell.bubbleView.image = nil;
  342. }
  343. }
  344. return cell;
  345. }
  346. - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
  347. [self.inputController reset];
  348. }
  349. #pragma mark - TUIInputControllerDelegate
  350. - (void)inputController:(TUIInputController *)inputController didChangeHeight:(CGFloat)height {
  351. if (!self.responseKeyboard) {
  352. return;
  353. }
  354. if (self.inputController.replyData == nil) {
  355. [self onRelyMessage:self.cellData];
  356. }
  357. [UIView animateWithDuration:0.3
  358. delay:0
  359. options:UIViewAnimationOptionCurveEaseOut
  360. animations:^{
  361. CGRect msgFrame = self.tableView.frame;
  362. msgFrame.size.height = self.view.frame.size.height - height;
  363. self.tableView.frame = msgFrame;
  364. CGRect inputFrame = self.inputController.view.frame;
  365. inputFrame.origin.y = msgFrame.origin.y + msgFrame.size.height;
  366. inputFrame.size.height = height;
  367. self.inputController.view.frame = inputFrame;
  368. [self scrollToBottom:NO];
  369. }
  370. completion:nil];
  371. }
  372. - (void)inputController:(TUIInputController *)inputController didSendMessage:(V2TIMMessage *)msg {
  373. [self sendMessage:msg];
  374. }
  375. - (void)inputController:(TUIInputController *)inputController didSelectMoreCell:(TUIInputMoreCell *)cell {
  376. cell.disableDefaultSelectAction = NO;
  377. if (cell.disableDefaultSelectAction) {
  378. return;
  379. }
  380. }
  381. - (void)sendMessage:(V2TIMMessage *)message {
  382. TUIMessageCellData *cellData = nil;
  383. if (!cellData) {
  384. cellData = [TUIMessageDataProvider getCellData:message];
  385. }
  386. if (cellData) {
  387. cellData.innerMessage.needReadReceipt = self.isMsgNeedReadReceipt;
  388. [self sendUIMessage:cellData];
  389. }
  390. }
  391. - (void)sendUIMessage:(TUIMessageCellData *)cellData {
  392. @weakify(self);
  393. [self.parentPageDataProvider sendUIMsg:cellData
  394. toConversation:self.conversationData
  395. willSendBlock:^(BOOL isReSend, TUIMessageCellData *_Nonnull dateUIMsg) {
  396. @strongify(self);
  397. int delay = 1;
  398. if ([cellData isKindOfClass:[TUIImageMessageCellData class]]) {
  399. delay = 0;
  400. }
  401. @weakify(self);
  402. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  403. @strongify(self);
  404. if (cellData.status == Msg_Status_Sending) {
  405. [self changeMsg:cellData status:Msg_Status_Sending_2];
  406. }
  407. });
  408. }
  409. SuccBlock:^{
  410. @strongify(self);
  411. [self changeMsg:cellData status:Msg_Status_Succ];
  412. [self scrollToBottom:YES];
  413. }
  414. FailBlock:^(int code, NSString *desc) {
  415. @strongify(self);
  416. [TUITool makeToastError:code msg:desc];
  417. [self changeMsg:cellData status:Msg_Status_Fail];
  418. }];
  419. }
  420. - (void)scrollToBottom:(BOOL)animated {
  421. if (self.uiMsgs.count > 0) {
  422. [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.uiMsgs.count - 1 inSection:1]
  423. atScrollPosition:UITableViewScrollPositionBottom
  424. animated:animated];
  425. }
  426. }
  427. - (void)changeMsg:(TUIMessageCellData *)msg status:(TMsgStatus)status {
  428. msg.status = status;
  429. NSInteger index = [self.uiMsgs indexOfObject:msg];
  430. if ([self.tableView numberOfRowsInSection:0] > index) {
  431. TUIMessageCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];
  432. [cell fillWithData:msg];
  433. } else {
  434. NSLog(@"lack of cell");
  435. }
  436. [[NSNotificationCenter defaultCenter] postNotificationName:@"kTUINotifyMessageStatusChanged"
  437. object:nil
  438. userInfo:@{
  439. @"msg" : msg,
  440. @"status" : [NSNumber numberWithUnsignedInteger:status],
  441. @"msgSender" : self,
  442. }];
  443. }
  444. #pragma mark - Message reply
  445. - (void)onRelyMessage:(nonnull TUIMessageCellData *)data {
  446. NSString *desc = @"";
  447. desc = [self replyReferenceMessageDesc:data];
  448. TUIReplyPreviewData *replyData = [[TUIReplyPreviewData alloc] init];
  449. replyData.msgID = data.msgID;
  450. replyData.msgAbstract = desc;
  451. replyData.sender = data.senderName;
  452. replyData.type = (NSInteger)data.innerMessage.elemType;
  453. replyData.originMessage = data.innerMessage;
  454. self.inputController.replyData = replyData;
  455. }
  456. - (NSString *)replyReferenceMessageDesc:(TUIMessageCellData *)data {
  457. NSString *desc = @"";
  458. if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_FILE) {
  459. desc = data.innerMessage.fileElem.filename;
  460. } else if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_MERGER) {
  461. desc = data.innerMessage.mergerElem.title;
  462. } else if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_CUSTOM) {
  463. desc = [TUIMessageDataProvider getDisplayString:data.innerMessage];
  464. } else if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_TEXT) {
  465. desc = data.innerMessage.textElem.text;
  466. }
  467. return desc;
  468. }
  469. #pragma mark - TUIMessageCellDelegate
  470. - (void)onSelectMessage:(TUIMessageCell *)cell {
  471. if (TUIChatConfig.defaultConfig.eventConfig.chatEventListener &&
  472. [TUIChatConfig.defaultConfig.eventConfig.chatEventListener respondsToSelector:@selector(onMessageClicked:messageCellData:)]) {
  473. BOOL result = [TUIChatConfig.defaultConfig.eventConfig.chatEventListener onMessageClicked:cell messageCellData:cell.messageData];
  474. if (result) {
  475. return;
  476. }
  477. }
  478. if ([cell isKindOfClass:[TUIImageMessageCell class]]) {
  479. [self showImageMessage:(TUIImageMessageCell *)cell];
  480. }
  481. if ([cell isKindOfClass:[TUIVoiceMessageCell class]]) {
  482. [self playVoiceMessage:(TUIVoiceMessageCell *)cell];
  483. }
  484. if ([cell isKindOfClass:[TUIVideoMessageCell class]]) {
  485. [self showVideoMessage:(TUIVideoMessageCell *)cell];
  486. }
  487. if ([cell isKindOfClass:[TUIFileMessageCell class]]) {
  488. [self showFileMessage:(TUIFileMessageCell *)cell];
  489. }
  490. if ([cell isKindOfClass:[TUIMergeMessageCell class]]) {
  491. TUIMergeMessageListController *mergeVc = [[TUIMergeMessageListController alloc] init];
  492. mergeVc.mergerElem = [(TUIMergeMessageCell *)cell mergeData].mergerElem;
  493. mergeVc.delegate = self.delegate;
  494. [self.navigationController pushViewController:mergeVc animated:YES];
  495. }
  496. if ([cell isKindOfClass:[TUILinkCell class]]) {
  497. [self showLinkMessage:(TUILinkCell *)cell];
  498. }
  499. // if ([cell isKindOfClass:[TUIReplyMessageCell class]]) {
  500. // [self showReplyMessage:(TUIReplyMessageCell *)cell];
  501. // }
  502. // if ([cell isKindOfClass:[TUIReferenceMessageCell class]]) {
  503. // [self showReplyMessage:(TUIReplyMessageCell *)cell];
  504. // }
  505. if ([self.delegate respondsToSelector:@selector(messageController:onSelectMessageContent:)]) {
  506. [self.delegate messageController:nil onSelectMessageContent:cell];
  507. }
  508. }
  509. #pragma mark - V2TIMAdvancedMsgListener
  510. - (void)onRecvNewMessage:(V2TIMMessage *)msg {
  511. V2TIMMessage *imMsg = msg;
  512. if (imMsg == nil || ![imMsg isKindOfClass:V2TIMMessage.class]) {
  513. return;
  514. }
  515. if ([imMsg.msgID isEqualToString:self.cellData.msgID] ) {
  516. TUIMessageCellData *cellData = [TUIMessageDataProvider getCellData:imMsg];
  517. self.cellData.messageModifyReplies = cellData.messageModifyReplies;
  518. [self applyData];
  519. }
  520. }
  521. - (void)onRecvMessageModified:(V2TIMMessage *)msg {
  522. V2TIMMessage *imMsg = msg;
  523. if (imMsg == nil || ![imMsg isKindOfClass:V2TIMMessage.class]) {
  524. return;
  525. }
  526. if ([imMsg.msgID isEqualToString:self.cellData.msgID] ) {
  527. TUIMessageCellData *cellData = [TUIMessageDataProvider getCellData:imMsg];
  528. self.cellData.messageModifyReplies = cellData.messageModifyReplies;
  529. [self applyData];
  530. }
  531. }
  532. #pragma mark - dataProviderDataChange
  533. - (void)dataProviderDataSourceWillChange:(TUIMessageDataProvider *)dataProvider {
  534. }
  535. - (void)dataProviderDataSourceChange:(TUIMessageDataProvider *)dataProvider
  536. withType:(TUIMessageBaseDataProviderDataSourceChangeType)type
  537. atIndex:(NSUInteger)index
  538. animation:(BOOL)animation {
  539. }
  540. - (void)dataProviderDataSourceDidChange:(TUIMessageDataProvider *)dataProvider {
  541. }
  542. - (void)dataProvider:(TUIMessageBaseDataProvider *)dataProvider onRemoveHeightCache:(TUIMessageCellData *)cellData {
  543. if (cellData) {
  544. [self.messageCellConfig removeHeightCacheOfMessageCellData:cellData];
  545. }
  546. }
  547. #pragma mark - action
  548. - (void)showImageMessage:(TUIImageMessageCell *)cell {
  549. CGRect frame = [cell.thumb convertRect:cell.thumb.bounds toView:[UIApplication sharedApplication].delegate.window];
  550. TUIMediaView *mediaView = [[TUIMediaView alloc] initWithFrame:CGRectMake(0, 0, Screen_Width, Screen_Height)];
  551. [mediaView setThumb:cell.thumb frame:frame];
  552. [mediaView setCurMessage:cell.messageData.innerMessage allMessages:@[ self.cellData.innerMessage ]];
  553. [[UIApplication sharedApplication].keyWindow addSubview:mediaView];
  554. }
  555. - (void)playVoiceMessage:(TUIVoiceMessageCell *)cell {
  556. TUIVoiceMessageCellData *uiMsg = (TUIVoiceMessageCellData *)self.cellData;
  557. if (uiMsg == cell.voiceData) {
  558. [uiMsg playVoiceMessage];
  559. cell.voiceReadPoint.hidden = YES;
  560. } else {
  561. [uiMsg stopVoiceMessage];
  562. }
  563. }
  564. - (void)showVideoMessage:(TUIVideoMessageCell *)cell {
  565. CGRect frame = [cell.thumb convertRect:cell.thumb.bounds toView:[UIApplication sharedApplication].delegate.window];
  566. TUIMediaView *mediaView = [[TUIMediaView alloc] initWithFrame:CGRectMake(0, 0, Screen_Width, Screen_Height)];
  567. [mediaView setThumb:cell.thumb frame:frame];
  568. [mediaView setCurMessage:cell.messageData.innerMessage allMessages:@[ self.cellData.innerMessage ]];
  569. __weak typeof(self) weakSelf = self;
  570. mediaView.onClose = ^{
  571. [weakSelf.tableView reloadData];
  572. };
  573. [[UIApplication sharedApplication].keyWindow addSubview:mediaView];
  574. }
  575. - (void)showFileMessage:(TUIFileMessageCell *)cell {
  576. TUIFileViewController *file = [[TUIFileViewController alloc] init];
  577. file.data = [cell fileData];
  578. [self.navigationController pushViewController:file animated:YES];
  579. }
  580. - (void)showLinkMessage:(TUILinkCell *)cell {
  581. TUILinkCellData *cellData = cell.customData;
  582. if (cellData.link) {
  583. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:cellData.link]];
  584. }
  585. }
  586. - (void)setConversation:(TUIChatConversationModel *)conversationData {
  587. self.conversationData = conversationData;
  588. if (!self.msgDataProvider) {
  589. self.msgDataProvider = [[TUIMessageDataProvider alloc] initWithConversationModel:conversationData];
  590. self.msgDataProvider.dataSource = self;
  591. }
  592. [self loadMessage];
  593. }
  594. - (void)loadMessage {
  595. if (self.msgDataProvider.isLoadingData || self.msgDataProvider.isNoMoreMsg) {
  596. return;
  597. }
  598. [self.msgDataProvider
  599. loadMessageSucceedBlock:^(BOOL isFirstLoad, BOOL isNoMoreMsg, NSArray<TUIMessageCellData *> *_Nonnull newMsgs) {
  600. }
  601. FailBlock:^(int code, NSString *desc) {
  602. [TUITool makeToastError:code msg:desc];
  603. }];
  604. }
  605. #pragma mark - TUINotificationProtocol
  606. - (void)onNotifyEvent:(NSString *)key subKey:(NSString *)subKey object:(id)anObject param:(NSDictionary *)param {
  607. if ([key isEqualToString:TUICore_TUIPluginNotify] &&
  608. [subKey isEqualToString:TUICore_TUIPluginNotify_DidChangePluginViewSubKey]) {
  609. TUIMessageCellData *data = param[TUICore_TUIPluginNotify_DidChangePluginViewSubKey_Data];
  610. NSInteger section = 1;
  611. if ([data.msgID isEqualToString:self.cellData.msgID] ) {
  612. //root section
  613. section = 0;
  614. }
  615. [self.messageCellConfig removeHeightCacheOfMessageCellData:data];
  616. [self reloadAndScrollToBottomOfMessage:data.innerMessage.msgID section:section];
  617. }
  618. }
  619. - (void)reloadAndScrollToBottomOfMessage:(NSString *)messageID section:(NSInteger)section {
  620. // Dispatch the task to RunLoop to ensure that they are executed after the UITableView refresh is complete.
  621. dispatch_async(dispatch_get_main_queue(), ^{
  622. [self reloadCellOfMessage:messageID section:section];
  623. dispatch_async(dispatch_get_main_queue(), ^{
  624. [self scrollCellToBottomOfMessage:messageID section:section];
  625. });
  626. });
  627. }
  628. - (void)reloadCellOfMessage:(NSString *)messageID section:(NSInteger)section {
  629. NSIndexPath *indexPath = [self indexPathOfMessage:messageID section:section];
  630. // Disable animation when loading to avoid cell jumping.
  631. if (indexPath == nil) {
  632. return;
  633. }
  634. [UIView performWithoutAnimation:^{
  635. dispatch_async(dispatch_get_main_queue(), ^{
  636. [self.tableView reloadRowsAtIndexPaths:@[ indexPath ] withRowAnimation:UITableViewRowAnimationNone];
  637. });
  638. }];
  639. }
  640. - (void)scrollCellToBottomOfMessage:(NSString *)messageID section:(NSInteger)section {
  641. NSIndexPath *indexPath = [self indexPathOfMessage:messageID section:section];
  642. // Scroll the tableView only if the bottom of the cell is invisible.
  643. CGRect cellRect = [self.tableView rectForRowAtIndexPath:indexPath];
  644. CGRect tableViewRect = self.tableView.bounds;
  645. BOOL isBottomInvisible = cellRect.origin.y < CGRectGetMaxY(tableViewRect) && CGRectGetMaxY(cellRect) > CGRectGetMaxY(tableViewRect);
  646. if (isBottomInvisible) {
  647. [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
  648. }
  649. }
  650. - (NSIndexPath *)indexPathOfMessage:(NSString *)messageID section:(NSInteger)section {
  651. if (section == 0) {
  652. return [NSIndexPath indexPathForRow:0 inSection:section];
  653. } else {
  654. for (int i = 0; i < self.uiMsgs.count; i++) {
  655. TUIMessageCellData *data = self.uiMsgs[i];
  656. if ([data.innerMessage.msgID isEqualToString:messageID]) {
  657. return [NSIndexPath indexPathForRow:i inSection:section];
  658. }
  659. }
  660. }
  661. return nil;
  662. }
  663. - (TUIMessageCellConfig *)messageCellConfig {
  664. if (_messageCellConfig == nil) {
  665. _messageCellConfig = [[TUIMessageCellConfig alloc] init];
  666. }
  667. return _messageCellConfig;
  668. }
  669. @end