TUIMessageSearchDataProvider.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. //
  2. // TUIMessageSearchDataProvider.m
  3. // TXIMSDK_TUIKit_iOS
  4. //
  5. // Created by kayev on 2021/7/8.
  6. // Copyright © 2023 Tencent. All rights reserved.
  7. //
  8. #import "TUIMessageSearchDataProvider.h"
  9. #import "TUIMessageBaseDataProvider+ProtectedAPI.h"
  10. #import "TUIChatMediaSendingManager.h"
  11. typedef void (^LoadSearchMsgSucceedBlock)(BOOL isOlderNoMoreMsg, BOOL isNewerNoMoreMsg, NSArray<TUIMessageCellData *> *newMsgs);
  12. typedef void (^LoadMsgSucceedBlock)(BOOL isOlderNoMoreMsg, BOOL isNewerNoMoreMsg, BOOL isFirstLoad, NSArray<TUIMessageCellData *> *newUIMsgs);
  13. @interface TUIMessageSearchDataProvider ()
  14. @property(nonatomic, copy) LoadSearchMsgSucceedBlock loadSearchMsgSucceedBlock;
  15. @property(nonatomic, copy) LoadMsgSucceedBlock loadMsgSucceedBlock;
  16. @end
  17. @implementation TUIMessageSearchDataProvider
  18. - (instancetype)init {
  19. self = [super init];
  20. if (self) {
  21. _isOlderNoMoreMsg = NO;
  22. _isNewerNoMoreMsg = NO;
  23. }
  24. return self;
  25. }
  26. - (void)loadMessageWithSearchMsg:(V2TIMMessage *)searchMsg
  27. SearchMsgSeq:(uint64_t)searchSeq
  28. ConversationInfo:(TUIChatConversationModel *)conversation
  29. SucceedBlock:(void (^)(BOOL isOlderNoMoreMsg, BOOL isNewerNoMoreMsg, NSArray<TUIMessageCellData *> *newMsgs))succeedBlock
  30. FailBlock:(V2TIMFail)failBlock {
  31. if (self.isLoadingData) {
  32. failBlock(ERR_SUCC, @"refreshing");
  33. return;
  34. }
  35. self.isLoadingData = YES;
  36. self.isOlderNoMoreMsg = NO;
  37. self.isNewerNoMoreMsg = NO;
  38. self.loadSearchMsgSucceedBlock = succeedBlock;
  39. dispatch_group_t group = dispatch_group_create();
  40. __block NSArray *olders = @[];
  41. __block NSArray *newers = @[];
  42. __block BOOL isOldLoadFail = NO;
  43. __block BOOL isNewLoadFail = NO;
  44. __block int failCode = 0;
  45. __block NSString *failDesc = nil;
  46. /**
  47. * Load the oldest pageCount messages starting from locating message
  48. */
  49. {
  50. dispatch_group_enter(group);
  51. V2TIMMessageListGetOption *option = [[V2TIMMessageListGetOption alloc] init];
  52. option.getType = V2TIM_GET_CLOUD_OLDER_MSG;
  53. option.count = self.pageCount;
  54. option.groupID = conversation.groupID;
  55. option.userID = conversation.userID;
  56. if (searchMsg) {
  57. option.lastMsg = searchMsg;
  58. } else {
  59. option.lastMsgSeq = searchSeq;
  60. }
  61. [V2TIMManager.sharedInstance getHistoryMessageList:option
  62. succ:^(NSArray<V2TIMMessage *> *msgs) {
  63. msgs = msgs.reverseObjectEnumerator.allObjects;
  64. olders = msgs ?: @[];
  65. if (olders.count < self.pageCount) {
  66. self.isOlderNoMoreMsg = YES;
  67. }
  68. dispatch_group_leave(group);
  69. }
  70. fail:^(int code, NSString *desc) {
  71. isOldLoadFail = YES;
  72. failCode = code;
  73. failDesc = desc;
  74. dispatch_group_leave(group);
  75. }];
  76. }
  77. /**
  78. * Load the latest pageCount messages starting from the locating message
  79. */
  80. {
  81. dispatch_group_enter(group);
  82. V2TIMMessageListGetOption *option = [[V2TIMMessageListGetOption alloc] init];
  83. option.getType = V2TIM_GET_CLOUD_NEWER_MSG;
  84. option.count = self.pageCount;
  85. option.groupID = conversation.groupID;
  86. option.userID = conversation.userID;
  87. if (searchMsg) {
  88. option.lastMsg = searchMsg;
  89. } else {
  90. option.lastMsgSeq = searchSeq;
  91. }
  92. [V2TIMManager.sharedInstance getHistoryMessageList:option
  93. succ:^(NSArray<V2TIMMessage *> *msgs) {
  94. newers = msgs ?: @[];
  95. if (newers.count < self.pageCount) {
  96. self.isNewerNoMoreMsg = YES;
  97. }
  98. dispatch_group_leave(group);
  99. }
  100. fail:^(int code, NSString *desc) {
  101. isNewLoadFail = YES;
  102. failCode = code;
  103. failDesc = desc;
  104. dispatch_group_leave(group);
  105. }];
  106. }
  107. @weakify(self);
  108. dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
  109. @strongify(self);
  110. self.isLoadingData = NO;
  111. if (isOldLoadFail || isNewLoadFail) {
  112. dispatch_async(dispatch_get_main_queue(), ^{
  113. failBlock(failCode, failDesc);
  114. });
  115. }
  116. self.isFirstLoad = NO;
  117. NSMutableArray *results = [NSMutableArray array];
  118. [results addObjectsFromArray:olders];
  119. if (searchMsg) {
  120. /**
  121. * Pulling messages through the msg will not return the msg object itself, here you need to actively add the msg to the results list
  122. */
  123. [results addObject:searchMsg];
  124. } else {
  125. /**
  126. * Pulling messages through the msg seq, pulling old messages and new messages will return the msg object itself, here you need to deduplicate the msg
  127. * object in results
  128. */
  129. [results removeLastObject];
  130. }
  131. [results addObjectsFromArray:newers];
  132. self.msgForOlderGet = results.firstObject;
  133. self.msgForNewerGet = results.lastObject;
  134. @weakify(self);
  135. dispatch_async(dispatch_get_main_queue(), ^{
  136. @strongify(self);
  137. [self.heightCache_ removeAllObjects];
  138. [self.uiMsgs_ removeAllObjects];
  139. NSArray *msgs = results.reverseObjectEnumerator.allObjects;
  140. NSMutableArray *uiMsgs = [self transUIMsgFromIMMsg:msgs];
  141. if (uiMsgs.count == 0) {
  142. return;
  143. }
  144. [self getGroupMessageReceipts:msgs
  145. uiMsgs:uiMsgs
  146. succ:^{
  147. [self preProcessMessage:uiMsgs];
  148. }
  149. fail:^{
  150. [self preProcessMessage:uiMsgs];
  151. }];
  152. });
  153. });
  154. }
  155. - (void)loadMessageWithIsRequestOlderMsg:(BOOL)orderType
  156. ConversationInfo:(TUIChatConversationModel *)conversation
  157. SucceedBlock:(void (^)(BOOL isOlderNoMoreMsg, BOOL isNewerNoMoreMsg, BOOL isFirstLoad,
  158. NSArray<TUIMessageCellData *> *newUIMsgs))succeedBlock
  159. FailBlock:(V2TIMFail)failBlock {
  160. self.isLoadingData = YES;
  161. self.loadMsgSucceedBlock = succeedBlock;
  162. int requestCount = self.pageCount;
  163. V2TIMMessageListGetOption *option = [[V2TIMMessageListGetOption alloc] init];
  164. option.userID = conversation.userID;
  165. option.groupID = conversation.groupID;
  166. option.getType = orderType ? V2TIM_GET_CLOUD_OLDER_MSG : V2TIM_GET_CLOUD_NEWER_MSG;
  167. option.count = requestCount;
  168. option.lastMsg = orderType ? self.msgForOlderGet : self.msgForNewerGet;
  169. @weakify(self);
  170. [V2TIMManager.sharedInstance getHistoryMessageList:option
  171. succ:^(NSArray<V2TIMMessage *> *msgs) {
  172. @strongify(self);
  173. if (!orderType) {
  174. msgs = msgs.reverseObjectEnumerator.allObjects;
  175. }
  176. /**
  177. * Update the lastMsg flag
  178. * -- The current pull operation is to pull from the latest time point to the past
  179. */
  180. BOOL isLastest = (self.msgForNewerGet == nil) && (self.msgForOlderGet == nil) && orderType;
  181. if (msgs.count != 0) {
  182. if (orderType) {
  183. self.msgForOlderGet = msgs.lastObject;
  184. if (self.msgForNewerGet == nil) {
  185. self.msgForNewerGet = msgs.firstObject;
  186. }
  187. } else {
  188. if (self.msgForOlderGet == nil) {
  189. self.msgForOlderGet = msgs.lastObject;
  190. }
  191. self.msgForNewerGet = msgs.firstObject;
  192. }
  193. }
  194. /**
  195. * Update no data flag
  196. */
  197. if (msgs.count < requestCount) {
  198. if (orderType) {
  199. self.isOlderNoMoreMsg = YES;
  200. } else {
  201. self.isNewerNoMoreMsg = YES;
  202. }
  203. }
  204. if (isLastest) {
  205. /**
  206. * The current pull operation is to pull from the latest time point to the past
  207. */
  208. self.isNewerNoMoreMsg = YES;
  209. }
  210. NSMutableArray<TUIMessageCellData *> *uiMsgs = [self transUIMsgFromIMMsg:msgs];
  211. if (uiMsgs.count == 0) {
  212. if (self.loadMsgSucceedBlock) {
  213. self.loadMsgSucceedBlock(self.isOlderNoMoreMsg, self.isNewerNoMoreMsg, self.isFirstLoad, uiMsgs);
  214. }
  215. return;
  216. }
  217. //add media placeholder celldata
  218. if (self.conversationModel.conversationID.length > 0) {
  219. NSMutableArray<TUIChatMediaTask *> * tasks = [TUIChatMediaSendingManager.sharedInstance
  220. findPlaceHolderListByConversationID:self.conversationModel.conversationID];
  221. for (TUIChatMediaTask * task in tasks) {
  222. if (task.placeHolderCellData) {
  223. [uiMsgs addObject:task.placeHolderCellData];
  224. }
  225. }
  226. }
  227. [self getGroupMessageReceipts:msgs
  228. uiMsgs:uiMsgs
  229. succ:^{
  230. [self preProcessMessage:uiMsgs orderType:orderType];
  231. }
  232. fail:^{
  233. [self preProcessMessage:uiMsgs orderType:orderType];
  234. }];
  235. }
  236. fail:^(int code, NSString *desc) {
  237. self.isLoadingData = NO;
  238. }];
  239. }
  240. - (void)getGroupMessageReceipts:(NSArray *)msgs uiMsgs:(NSArray *)uiMsgs succ:(void (^)(void))succBlock fail:(void (^)(void))failBlock {
  241. [[V2TIMManager sharedInstance] getMessageReadReceipts:msgs
  242. succ:^(NSArray<V2TIMMessageReceipt *> *receiptList) {
  243. NSLog(@"getGroupMessageReceipts succeed, receiptList: %@", receiptList);
  244. NSMutableDictionary *dict = [NSMutableDictionary dictionary];
  245. for (V2TIMMessageReceipt *receipt in receiptList) {
  246. [dict setObject:receipt forKey:receipt.msgID];
  247. }
  248. for (TUIMessageCellData *data in uiMsgs) {
  249. V2TIMMessageReceipt *receipt = dict[data.msgID];
  250. data.messageReceipt = receipt;
  251. }
  252. if (succBlock) {
  253. succBlock();
  254. }
  255. }
  256. fail:^(int code, NSString *desc) {
  257. NSLog(@"getGroupMessageReceipts failed, code: %d, desc: %@", code, desc);
  258. if (failBlock) {
  259. failBlock();
  260. }
  261. }];
  262. }
  263. - (void)preProcessMessage:(NSArray *)uiMsgs {
  264. @weakify(self);
  265. [self preProcessMessage:uiMsgs
  266. callback:^{
  267. @strongify(self);
  268. [self addUIMsgs:uiMsgs];
  269. self.loadSearchMsgSucceedBlock(self.isOlderNoMoreMsg, self.isNewerNoMoreMsg, self.uiMsgs_);
  270. }];
  271. }
  272. - (void)preProcessMessage:(NSArray *)uiMsgs orderType:(BOOL)orderType {
  273. @weakify(self);
  274. [self preProcessMessage:uiMsgs
  275. callback:^{
  276. @strongify(self);
  277. if (orderType) {
  278. NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, uiMsgs.count)];
  279. [self insertUIMsgs:uiMsgs atIndexes:indexSet];
  280. } else {
  281. [self addUIMsgs:uiMsgs];
  282. }
  283. if (self.loadMsgSucceedBlock) {
  284. self.loadMsgSucceedBlock(self.isOlderNoMoreMsg, self.isNewerNoMoreMsg, self.isFirstLoad, uiMsgs);
  285. }
  286. self.isLoadingData = NO;
  287. self.isFirstLoad = NO;
  288. }];
  289. }
  290. - (void)removeAllSearchData {
  291. [self.uiMsgs_ removeAllObjects];
  292. self.isNewerNoMoreMsg = NO;
  293. self.isOlderNoMoreMsg = NO;
  294. self.isFirstLoad = YES;
  295. self.msgForNewerGet = nil;
  296. self.msgForOlderGet = nil;
  297. self.loadSearchMsgSucceedBlock = nil;
  298. }
  299. - (void)findMessages:(NSArray<NSString *> *)msgIDs callback:(void (^)(BOOL success, NSString *desc, NSArray<V2TIMMessage *> *messages))callback {
  300. [V2TIMManager.sharedInstance findMessages:msgIDs
  301. succ:^(NSArray<V2TIMMessage *> *msgs) {
  302. if (callback) {
  303. callback(YES, @"", msgs);
  304. }
  305. }
  306. fail:^(int code, NSString *desc) {
  307. if (callback) {
  308. callback(NO, desc, nil);
  309. }
  310. }];
  311. }
  312. #pragma mark - Override
  313. - (void)onRecvNewMessage:(V2TIMMessage *)msg {
  314. if (self.isNewerNoMoreMsg == NO) {
  315. /**
  316. * If the current message list has not pulled the last message, ignore the new message;
  317. * If it is processed at this time, it will cause new messages to be added to the history list, resulting in the problem of position confusion.
  318. */
  319. return;
  320. }
  321. if (self.dataSource.isDataSourceConsistent == NO ) {
  322. self.isNewerNoMoreMsg = NO;
  323. return;
  324. }
  325. [super onRecvNewMessage:msg];
  326. }
  327. @end