TUIMessageController.m 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. // Created by Tencent on 2023/06/09.
  2. // Copyright © 2023 Tencent. All rights reserved.
  3. #import "TUIMessageController.h"
  4. #import <TIMCommon/TIMDefine.h>
  5. #import <TUICore/TUIGlobalization.h>
  6. #import <TUICore/TUIThemeManager.h>
  7. #import <TUICore/UIView+TUILayout.h>
  8. #import "TUIBaseMessageController+ProtectedAPI.h"
  9. #import "TUIChatConfig.h"
  10. #import "TUIChatModifyMessageHelper.h"
  11. #import "TUIChatSmallTongueView.h"
  12. #import "TUIMessageSearchDataProvider.h"
  13. #import "TUIReferenceMessageCell.h"
  14. #import "TUIReplyMessageCell.h"
  15. #import "TUIReplyMessageCellData.h"
  16. #import "TUITextMessageCell.h"
  17. #define MSG_GET_COUNT 20
  18. @interface TUIMessageController () <TUIChatSmallTongueViewDelegate,TUIMessageBaseDataProviderDataSource_Two>
  19. @property(nonatomic, strong) UIActivityIndicatorView *bottomIndicatorView;
  20. @property(nonatomic, assign) uint64_t locateGroupMessageSeq;
  21. @property(nonatomic, strong) TUIChatSmallTongueView *tongueView;
  22. @property(nonatomic, strong) NSMutableArray *receiveMsgs;
  23. @property(nonatomic, weak) UIImageView *backgroudView;
  24. @end
  25. @implementation TUIMessageController
  26. #pragma mark - Life Cycle
  27. - (void)viewDidLoad {
  28. [super viewDidLoad];
  29. self.bottomIndicatorView =
  30. [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.frame.size.width, TMessageController_Header_Height)];
  31. self.bottomIndicatorView.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
  32. self.tableView.tableFooterView = self.bottomIndicatorView;
  33. self.tableView.backgroundColor = UIColor.clearColor;
  34. [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(keyboardWillShow) name:UIKeyboardWillShowNotification object:nil];
  35. [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(onBottomMarginChanged:)
  36. name:TUIKitNotification_onMessageVCBottomMarginChanged object:nil];
  37. self.receiveMsgs = [NSMutableArray array];
  38. dispatch_async(dispatch_get_main_queue(), ^{
  39. if (self.conversationData.atMsgSeqs.count > 0) {
  40. TUIChatSmallTongue *tongue = [[TUIChatSmallTongue alloc] init];
  41. tongue.type = TUIChatSmallTongueType_SomeoneAt;
  42. tongue.parentView = self.view.superview;
  43. tongue.atTipsStr = self.conversationData.atTipsStr;
  44. tongue.atMsgSeqs = [self.conversationData.atMsgSeqs copy];
  45. [TUIChatSmallTongueManager showTongue:tongue delegate:self];
  46. }
  47. });
  48. }
  49. - (void)dealloc {
  50. [TUIChatSmallTongueManager removeTongue];
  51. [NSNotificationCenter.defaultCenter removeObserver:self];
  52. NSLog(@"%s dealloc", __FUNCTION__);
  53. }
  54. - (void)viewWillAppear:(BOOL)animated {
  55. [super viewWillAppear:animated];
  56. [TUIChatSmallTongueManager hideTongue:NO];
  57. }
  58. - (void)viewWillDisappear:(BOOL)animated {
  59. [super viewWillDisappear:animated];
  60. [TUIChatSmallTongueManager hideTongue:YES];
  61. }
  62. #pragma mark - Notification
  63. - (void)keyboardWillShow {
  64. if (![self messageSearchDataProvider].isNewerNoMoreMsg) {
  65. [[self messageSearchDataProvider] removeAllSearchData];
  66. [self.tableView reloadData];
  67. [self loadMessages:YES];
  68. }
  69. }
  70. - (void)onBottomMarginChanged:(NSNotification *)notification {
  71. NSDictionary *userInfo = notification.userInfo;
  72. if ([userInfo.allKeys containsObject:TUIKitNotification_onMessageVCBottomMarginChanged_Margin] &&
  73. [userInfo[TUIKitNotification_onMessageVCBottomMarginChanged_Margin] isKindOfClass:NSNumber.class]) {
  74. float margin = [userInfo[TUIKitNotification_onMessageVCBottomMarginChanged_Margin] floatValue];
  75. [TUIChatSmallTongueManager adaptTongueBottomMargin:margin];
  76. }
  77. }
  78. #pragma mark - Overrider
  79. - (void)willShowMediaMessage:(TUIMessageCell *)cell {
  80. [TUIChatSmallTongueManager hideTongue:YES];
  81. }
  82. - (void)didCloseMediaMessage:(TUIMessageCell *)cell {
  83. [TUIChatSmallTongueManager hideTongue:NO];
  84. }
  85. #pragma mark - UIScrollViewDelegate
  86. - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
  87. [super scrollViewDidScroll:scrollView];
  88. if (scrollView.contentOffset.y <= TMessageController_Header_Height
  89. && (scrollView.isTracking || scrollView.isDecelerating)
  90. && ![self messageSearchDataProvider].isOlderNoMoreMsg
  91. && !self.indicatorView.isAnimating) {
  92. // Display pull-to-refresh icon
  93. [self.indicatorView startAnimating];
  94. } else if ([self isScrollToBottomIndicatorViewY:scrollView]) {
  95. if ((scrollView.isTracking || scrollView.isDecelerating)
  96. && ![self messageSearchDataProvider].isNewerNoMoreMsg
  97. && !self.bottomIndicatorView.isAnimating) {
  98. // Display pull-to-refresh icon
  99. [self.bottomIndicatorView startAnimating];
  100. }
  101. /**
  102. * Remove the "back to the latest position", "xxx new message" bottom-banner-tips
  103. */
  104. if (self.isInVC) {
  105. [TUIChatSmallTongueManager removeTongue:TUIChatSmallTongueType_ScrollToBoom];
  106. [TUIChatSmallTongueManager removeTongue:TUIChatSmallTongueType_ReceiveNewMsg];
  107. }
  108. } else if (self.isInVC && 0 == self.receiveMsgs.count
  109. && self.tableView.contentSize.height - self.tableView.contentOffset.y >= Screen_Height * 2.0) {
  110. CGPoint point = [scrollView.panGestureRecognizer translationInView:scrollView];
  111. /**
  112. * When swiping, add a "back to last position" bottom-banner-tips
  113. */
  114. if (point.y > 0) {
  115. TUIChatSmallTongue *tongue = [[TUIChatSmallTongue alloc] init];
  116. tongue.type = TUIChatSmallTongueType_ScrollToBoom;
  117. tongue.parentView = self.view.superview;
  118. [TUIChatSmallTongueManager showTongue:tongue delegate:self];
  119. }
  120. } else if (self.isInVC && self.tableView.contentSize.height - self.tableView.contentOffset.y >= 20) {
  121. /**
  122. * Remove the "someone @ me" bottom-banner-tips
  123. */
  124. [TUIChatSmallTongueManager removeTongue:TUIChatSmallTongueType_SomeoneAt];
  125. } else {
  126. if (self.indicatorView.isAnimating) {
  127. [self.indicatorView stopAnimating];
  128. }
  129. if (self.bottomIndicatorView.isAnimating) {
  130. [self.bottomIndicatorView stopAnimating];
  131. }
  132. }
  133. }
  134. - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
  135. [super scrollViewDidEndDecelerating:scrollView];
  136. if (scrollView.contentOffset.y <= TMessageController_Header_Height && ![self messageSearchDataProvider].isOlderNoMoreMsg) {
  137. /**
  138. * Pull old news
  139. */
  140. [self loadMessages:YES];
  141. } else if ([self isScrollToBottomIndicatorViewY:scrollView] && ![self messageSearchDataProvider].isNewerNoMoreMsg) {
  142. /**
  143. * Load latese message
  144. */
  145. [self loadMessages:NO];
  146. }
  147. }
  148. - (BOOL)isScrollToBottomIndicatorViewY:(UIScrollView *)scrollView {
  149. /**
  150. * +2 pixels when scrolling to critical point
  151. */
  152. return (scrollView.contentOffset.y + self.tableView.mm_h + 2) > (scrollView.contentSize.height - self.indicatorView.mm_h);
  153. }
  154. #pragma mark - Getters & Setters
  155. - (void)setConversation:(TUIChatConversationModel *)conversationData {
  156. self.conversationData = conversationData;
  157. self.messageDataProvider = [[TUIMessageSearchDataProvider alloc] initWithConversationModel:self.conversationData];
  158. self.messageDataProvider.dataSource = self;
  159. self.messageDataProvider.dataSource_Two = self;
  160. if (self.locateMessage) {
  161. [self loadAndScrollToLocateMessages:NO isHighlight:YES];
  162. } else {
  163. [[self messageSearchDataProvider] removeAllSearchData];
  164. [self loadMessages:YES];
  165. }
  166. [self loadGroupInfo];
  167. }
  168. #pragma mark - Private Methods
  169. - (TUIMessageSearchDataProvider *)messageSearchDataProvider {
  170. return (TUIMessageSearchDataProvider *)self.messageDataProvider;
  171. }
  172. - (void)loadAndScrollToLocateMessages:(BOOL)scrollToBoom isHighlight:(BOOL)isHighlight{
  173. if (!self.locateMessage && self.locateGroupMessageSeq == 0) {
  174. return;
  175. }
  176. @weakify(self);
  177. [[self messageSearchDataProvider]
  178. loadMessageWithSearchMsg:self.locateMessage
  179. SearchMsgSeq:self.locateGroupMessageSeq
  180. ConversationInfo:self.conversationData
  181. SucceedBlock:^(BOOL isOlderNoMoreMsg, BOOL isNewerNoMoreMsg, NSArray<TUIMessageCellData *> *_Nonnull newMsgs) {
  182. @strongify(self);
  183. [self.indicatorView stopAnimating];
  184. [self.bottomIndicatorView stopAnimating];
  185. self.indicatorView.mm_h = 0;
  186. self.bottomIndicatorView.mm_h = 0;
  187. [self.tableView reloadData];
  188. [self.tableView layoutIfNeeded];
  189. dispatch_async(dispatch_get_main_queue(), ^{
  190. [self scrollToLocateMessage:scrollToBoom];
  191. if (isHighlight) {
  192. [self highlightKeyword];
  193. }
  194. });
  195. }
  196. FailBlock:^(int code, NSString *desc){}];
  197. }
  198. - (void)scrollToLocateMessage:(BOOL)scrollToBoom {
  199. /**
  200. * First find the coordinate offset of locateMsg
  201. */
  202. CGFloat offsetY = 0;
  203. NSInteger index = 0;
  204. for (TUIMessageCellData *uiMsg in [self messageSearchDataProvider].uiMsgs) {
  205. if ([self isLocateMessage:uiMsg]) {
  206. break;
  207. }
  208. offsetY += [self getHeightFromMessageCellData:uiMsg];
  209. index++;
  210. }
  211. /**
  212. *
  213. * The locateMsg not found
  214. */
  215. if (index == [self messageSearchDataProvider].uiMsgs.count) {
  216. return;
  217. }
  218. /**
  219. * tableview
  220. * Offset half the height of the tableview
  221. */
  222. offsetY -= self.tableView.frame.size.height / 2.0;
  223. if (offsetY <= TMessageController_Header_Height) {
  224. offsetY = TMessageController_Header_Height + 0.1;
  225. }
  226. if (offsetY > TMessageController_Header_Height) {
  227. [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]
  228. atScrollPosition:UITableViewScrollPositionMiddle
  229. animated:YES];
  230. }
  231. }
  232. - (void)highlightKeyword {
  233. TUIMessageCellData *cellData = nil;
  234. for (TUIMessageCellData *tmp in [self messageSearchDataProvider].uiMsgs) {
  235. if ([self isLocateMessage:tmp]) {
  236. cellData = tmp;
  237. break;
  238. }
  239. }
  240. if (cellData == nil || cellData.innerMessage.elemType == V2TIM_ELEM_TYPE_GROUP_TIPS) {
  241. return;
  242. }
  243. @weakify(self);
  244. dispatch_async(dispatch_get_main_queue(), ^{
  245. @strongify(self);
  246. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[[self messageDataProvider].uiMsgs indexOfObject:cellData] inSection:0];
  247. cellData.highlightKeyword = self.hightlightKeyword.length ? self.hightlightKeyword : @"hightlight";
  248. TUIMessageCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
  249. [cell fillWithData:cellData];
  250. @weakify(self);
  251. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  252. @strongify(self);
  253. NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[[self messageDataProvider].uiMsgs indexOfObject:cellData] inSection:0];
  254. cellData.highlightKeyword = nil;
  255. TUIMessageCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
  256. [cell fillWithData:cellData];
  257. });
  258. });
  259. }
  260. - (BOOL)isLocateMessage:(TUIMessageCellData *)uiMsg {
  261. if (self.locateMessage) {
  262. if ([uiMsg.innerMessage.msgID isEqualToString:self.locateMessage.msgID]) {
  263. return YES;
  264. }
  265. } else {
  266. if (self.conversationData.groupID.length > 0 && uiMsg.innerMessage && uiMsg.innerMessage.seq == self.locateGroupMessageSeq) {
  267. return YES;
  268. }
  269. }
  270. return NO;
  271. }
  272. - (void)loadMessages:(BOOL)order {
  273. if ([self messageSearchDataProvider].isLoadingData) {
  274. return;
  275. }
  276. if (order && [self messageSearchDataProvider].isOlderNoMoreMsg) {
  277. [self.indicatorView stopAnimating];
  278. return;
  279. }
  280. if (!order && [self messageSearchDataProvider].isNewerNoMoreMsg) {
  281. [self.bottomIndicatorView stopAnimating];
  282. return;
  283. }
  284. @weakify(self);
  285. [[self messageSearchDataProvider]
  286. loadMessageWithIsRequestOlderMsg:order
  287. ConversationInfo:self.conversationData
  288. SucceedBlock:^(BOOL isOlderNoMoreMsg, BOOL isNewerNoMoreMsg, BOOL isFirstLoad, NSArray<TUIMessageCellData *> *_Nonnull newUIMsgs) {
  289. @strongify(self);
  290. [self.indicatorView stopAnimating];
  291. [self.bottomIndicatorView stopAnimating];
  292. if (isOlderNoMoreMsg) {
  293. self.indicatorView.mm_h = 0;
  294. } else {
  295. self.indicatorView.mm_h = TMessageController_Header_Height;
  296. }
  297. if (isNewerNoMoreMsg) {
  298. self.bottomIndicatorView.mm_h = 0;
  299. } else {
  300. self.bottomIndicatorView.mm_h = TMessageController_Header_Height;
  301. }
  302. [self.tableView reloadData];
  303. [self.tableView layoutIfNeeded];
  304. [newUIMsgs enumerateObjectsWithOptions:NSEnumerationReverse
  305. usingBlock:^(TUIMessageCellData *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
  306. if (obj.direction == MsgDirectionIncoming) {
  307. self.C2CIncomingLastMsg = obj.innerMessage;
  308. *stop = YES;
  309. }
  310. }];
  311. if (isFirstLoad) {
  312. [self scrollToBottom:NO];
  313. } else {
  314. if (order) {
  315. NSInteger index = 0;
  316. if (newUIMsgs.count > 0) {
  317. index = newUIMsgs.count - 1;
  318. }
  319. if (self.messageDataProvider.uiMsgs.count > 0) {
  320. [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]
  321. atScrollPosition:UITableViewScrollPositionTop
  322. animated:NO];
  323. }
  324. }
  325. }
  326. }
  327. FailBlock:^(int code, NSString *desc){
  328. }];
  329. }
  330. - (void)showReplyMessage:(TUIReplyMessageCell *)cell {
  331. NSString *originMsgID = @"";
  332. NSString *msgAbstract = @"";
  333. if ([cell isKindOfClass:TUIReplyMessageCell.class]) {
  334. TUIReplyMessageCell *acell = (TUIReplyMessageCell *)cell;
  335. TUIReplyMessageCellData *cellData = acell.replyData;
  336. originMsgID = cellData.messageRootID;
  337. msgAbstract = cellData.msgAbstract;
  338. } else if ([cell isKindOfClass:TUIReferenceMessageCell.class]) {
  339. TUIReferenceMessageCell *acell = (TUIReferenceMessageCell *)cell;
  340. TUIReferenceMessageCellData *cellData = acell.referenceData;
  341. originMsgID = cellData.originMsgID;
  342. msgAbstract = cellData.msgAbstract;
  343. }
  344. @weakify(self);
  345. [(TUIMessageSearchDataProvider *)self.messageDataProvider
  346. findMessages:@[ originMsgID ?: @"" ]
  347. callback:^(BOOL success, NSString *_Nonnull desc, NSArray<V2TIMMessage *> *_Nonnull msgs) {
  348. @strongify(self);
  349. if (!success) {
  350. [TUITool makeToast:TIMCommonLocalizableString(TUIKitReplyMessageNotFoundOriginMessage)];
  351. return;
  352. }
  353. V2TIMMessage *message = msgs.firstObject;
  354. if (message == nil) {
  355. [TUITool makeToast:TIMCommonLocalizableString(TUIKitReplyMessageNotFoundOriginMessage)];
  356. return;
  357. }
  358. if (message.status == V2TIM_MSG_STATUS_HAS_DELETED || message.status == V2TIM_MSG_STATUS_LOCAL_REVOKED) {
  359. [TUITool makeToast:TIMCommonLocalizableString(TUIKitReplyMessageNotFoundOriginMessage)];
  360. return;
  361. }
  362. BOOL hasRiskContent = message.hasRiskContent;
  363. if ([cell isKindOfClass:TUIReplyMessageCell.class]) {
  364. if (hasRiskContent) {
  365. return;
  366. }
  367. [self jumpDetailPageByMessage:message];
  368. } else if ([cell isKindOfClass:TUIReferenceMessageCell.class]) {
  369. [self locateAssignMessage:message matchKeyWord:msgAbstract];
  370. }
  371. }];
  372. }
  373. - (void)jumpDetailPageByMessage:(V2TIMMessage *)message {
  374. NSMutableArray *uiMsgs = [self.messageDataProvider transUIMsgFromIMMsg:@[ message ]];
  375. if (uiMsgs.count == 0) {
  376. return;
  377. }
  378. [self.messageDataProvider preProcessMessage:uiMsgs
  379. callback:^{
  380. for (TUIMessageCellData *cellData in uiMsgs) {
  381. if ([cellData.innerMessage.msgID isEqual:message.msgID]) {
  382. [self onJumpToRepliesDetailPage:cellData];
  383. return;
  384. }
  385. }
  386. }];
  387. }
  388. - (void)locateAssignMessage:(V2TIMMessage *)message matchKeyWord:(NSString *)keyword {
  389. if (message == nil) {
  390. return;
  391. }
  392. self.locateMessage = message;
  393. self.hightlightKeyword = keyword;
  394. BOOL memoryExist = NO;
  395. for (TUIMessageCellData *cellData in self.messageDataProvider.uiMsgs) {
  396. if ([cellData.innerMessage.msgID isEqual:message.msgID]) {
  397. memoryExist = YES;
  398. break;
  399. }
  400. }
  401. if (memoryExist) {
  402. [self scrollToLocateMessage:NO];
  403. [self highlightKeyword];
  404. return;
  405. }
  406. TUIMessageSearchDataProvider *provider = (TUIMessageSearchDataProvider *)self.messageDataProvider;
  407. provider.isNewerNoMoreMsg = NO;
  408. provider.isOlderNoMoreMsg = NO;
  409. [self loadAndScrollToLocateMessages:NO isHighlight:YES];
  410. }
  411. - (void)findMessages:(NSArray<NSString *> *)msgIDs callback:(void (^)(BOOL success, NSString *desc, NSArray<V2TIMMessage *> *messages))callback {
  412. TUIMessageSearchDataProvider *provider = (TUIMessageSearchDataProvider *)self.messageDataProvider;
  413. if (provider) {
  414. [provider findMessages:msgIDs callback:callback];
  415. }
  416. }
  417. #pragma mark - TUIMessageBaseDataProviderDataSource
  418. - (void)dataProvider:(TUIMessageDataProvider *)dataProvider ReceiveNewUIMsg:(TUIMessageCellData *)uiMsg {
  419. [super dataProvider:dataProvider ReceiveNewUIMsg:uiMsg];
  420. /**
  421. * When viewing historical messages, if you scroll more than two screens, after receiving a new message, add a "xxx new message" bottom-banner-tips
  422. */
  423. if (self.tableView.contentSize.height - self.tableView.contentOffset.y >= Screen_Height * 2.0) {
  424. [self.receiveMsgs addObject:uiMsg];
  425. TUIChatSmallTongue *tongue = [[TUIChatSmallTongue alloc] init];
  426. tongue.type = TUIChatSmallTongueType_ReceiveNewMsg;
  427. tongue.parentView = self.view.superview;
  428. tongue.unreadMsgCount = self.receiveMsgs.count;
  429. [TUIChatSmallTongueManager showTongue:tongue delegate:self];
  430. }
  431. if (self.isInVC) {
  432. self.C2CIncomingLastMsg = uiMsg.innerMessage;
  433. }
  434. }
  435. - (void)dataProvider:(TUIMessageDataProvider *)dataProvider ReceiveRevokeUIMsg:(TUIMessageCellData *)uiMsg {
  436. /**
  437. * Recalled messages need to be removed from "xxx new messages" bottom-banner-tips
  438. */
  439. [super dataProvider:dataProvider ReceiveRevokeUIMsg:uiMsg];
  440. if ([self.receiveMsgs containsObject:uiMsg]) {
  441. [self.receiveMsgs removeObject:uiMsg];
  442. TUIChatSmallTongue *tongue = [[TUIChatSmallTongue alloc] init];
  443. tongue.type = TUIChatSmallTongueType_ReceiveNewMsg;
  444. tongue.parentView = self.view.superview;
  445. tongue.unreadMsgCount = self.receiveMsgs.count;
  446. if (tongue.unreadMsgCount != 0) {
  447. [TUIChatSmallTongueManager showTongue:tongue delegate:self];
  448. } else {
  449. [TUIChatSmallTongueManager removeTongue:TUIChatSmallTongueType_ReceiveNewMsg];
  450. }
  451. }
  452. /*
  453. * When the retracted message is a "reply" type of message, go to the root message to delete the currently retracted message.
  454. */
  455. if ([uiMsg isKindOfClass:TUIReplyMessageCellData.class]) {
  456. TUIReplyMessageCellData *cellData = (TUIReplyMessageCellData *)uiMsg;
  457. NSString *messageRootID = @"";
  458. NSString *revokeMsgID = @"";
  459. messageRootID = cellData.messageRootID;
  460. revokeMsgID = cellData.msgID;
  461. [(TUIMessageSearchDataProvider *)self.messageDataProvider
  462. findMessages:@[ messageRootID ?: @"" ]
  463. callback:^(BOOL success, NSString *_Nonnull desc, NSArray<V2TIMMessage *> *_Nonnull msgs) {
  464. if (success) {
  465. V2TIMMessage *message = msgs.firstObject;
  466. [[TUIChatModifyMessageHelper defaultHelper] modifyMessage:message revokeMsgID:revokeMsgID];
  467. }
  468. }];
  469. }
  470. /*
  471. The message whose reference is withdrawn should not expose the original message content, so it is necessary to traverse all reference messages and reply messages and replace the original message content with "TIMCommonLocalizableString(TUIKitRepliesOriginMessageRevoke)"
  472. */
  473. for (TUIMessageCellData * cellData in self.messageDataProvider.uiMsgs) {
  474. if ([cellData isKindOfClass:TUIReplyMessageCellData.class]) {
  475. TUIReplyMessageCellData *replyMessageData = (TUIReplyMessageCellData *)cellData;
  476. if ([replyMessageData.originMessage.msgID isEqualToString:uiMsg.msgID]) {
  477. [self.messageDataProvider processQuoteMessage:@[replyMessageData]];
  478. }
  479. }
  480. }
  481. }
  482. #pragma mark - TUIChatSmallTongueViewDelegate
  483. - (void)onChatSmallTongueClick:(TUIChatSmallTongue *)tongue {
  484. switch (tongue.type) {
  485. case TUIChatSmallTongueType_ScrollToBoom: {
  486. @weakify(self)
  487. [self.messageDataProvider getLastMessage:YES succ:^(V2TIMMessage * _Nonnull message) {
  488. @strongify(self)
  489. if (!message) return;
  490. self.locateMessage = message;
  491. for (TUIMessageCellData *cellData in self.messageDataProvider.uiMsgs) {
  492. if ([self isLocateMessage:cellData]) {
  493. [self scrollToLocateMessage:YES];
  494. return;
  495. }
  496. }
  497. [self loadAndScrollToLocateMessages:YES isHighlight:NO];
  498. } fail:^(int code, NSString *desc) {
  499. NSLog(@"getLastMessage failed");
  500. }];
  501. } break;
  502. case TUIChatSmallTongueType_ReceiveNewMsg: {
  503. [TUIChatSmallTongueManager removeTongue:TUIChatSmallTongueType_ReceiveNewMsg];
  504. TUIMessageCellData *cellData = self.receiveMsgs.firstObject;
  505. if (cellData) {
  506. self.locateMessage = cellData.innerMessage;
  507. [self scrollToLocateMessage:YES];
  508. [self highlightKeyword];
  509. }
  510. [self.receiveMsgs removeAllObjects];
  511. } break;
  512. case TUIChatSmallTongueType_SomeoneAt: {
  513. [TUIChatSmallTongueManager removeTongue:TUIChatSmallTongueType_SomeoneAt];
  514. [self.conversationData.atMsgSeqs removeAllObjects];
  515. self.locateGroupMessageSeq = [tongue.atMsgSeqs.firstObject integerValue];
  516. for (TUIMessageCellData *cellData in self.messageDataProvider.uiMsgs) {
  517. if ([self isLocateMessage:cellData]) {
  518. [self scrollToLocateMessage:YES];
  519. [self highlightKeyword];
  520. return;
  521. }
  522. }
  523. [self loadAndScrollToLocateMessages:YES isHighlight:YES];
  524. } break;
  525. default:
  526. break;
  527. }
  528. }
  529. @end