TUIConversationSelectController.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. //
  2. // TUIConversationSelectController.m
  3. // Pods
  4. //
  5. // Created by harvy on 2020/12/8.
  6. // Copyright © 2023 Tencent. All rights reserved.
  7. //
  8. #import "TUIConversationSelectController.h"
  9. #import <TIMCommon/TIMCommonModel.h>
  10. #import <TIMCommon/TIMDefine.h>
  11. #import <TUICore/NSDictionary+TUISafe.h>
  12. #import <TUICore/TUICore.h>
  13. #import "TUIConversationCell.h"
  14. #import "TUIConversationCellData.h"
  15. #import "TUIConversationSelectDataProvider.h"
  16. typedef void (^TUIConversationSelectCompletHandler)(BOOL);
  17. @interface TUIConversationSelectController () <UITableViewDelegate, UITableViewDataSource>
  18. @property(nonatomic, strong) UITableView *tableView;
  19. @property(nonatomic, strong) TUIContactListPicker *pickerView;
  20. @property(nonatomic, strong) TUICommonTableViewCell *headerView;
  21. @property(nonatomic, assign) BOOL enableMuliple;
  22. @property(nonatomic, strong) NSMutableArray<TUIConversationCellData *> *currentSelectedList;
  23. @property(nonatomic, strong) TUIConversationSelectDataProvider *dataProvider;
  24. @property(nonatomic, weak) UIViewController *showContactSelectVC;
  25. @end
  26. @implementation TUIConversationSelectController
  27. static NSString *const Id = @"con";
  28. #pragma mark - Life
  29. - (void)viewDidLoad {
  30. [super viewDidLoad];
  31. [self setupViews];
  32. }
  33. - (void)viewWillLayoutSubviews {
  34. [super viewWillLayoutSubviews];
  35. [self updateLayout];
  36. }
  37. - (TUIConversationSelectDataProvider *)dataProvider {
  38. if (!_dataProvider) {
  39. _dataProvider = [[TUIConversationSelectDataProvider alloc] init];
  40. [_dataProvider loadConversations];
  41. }
  42. return _dataProvider;
  43. }
  44. - (void)dealloc {
  45. NSLog(@"%s dealloc", __FUNCTION__);
  46. }
  47. #pragma mark - API
  48. + (instancetype)showIn:(UIViewController *)presentVC {
  49. TUIConversationSelectController *vc = [[TUIConversationSelectController alloc] init];
  50. UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
  51. nav.modalPresentationStyle = UIModalPresentationFullScreen;
  52. UIViewController *pVc = presentVC;
  53. if (pVc == nil) {
  54. pVc = UIApplication.sharedApplication.keyWindow.rootViewController;
  55. }
  56. [pVc presentViewController:nav animated:YES completion:nil];
  57. return vc;
  58. }
  59. #pragma mark - UI
  60. - (void)setupViews {
  61. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:TIMCommonLocalizableString(Cancel)
  62. style:UIBarButtonItemStylePlain
  63. target:self
  64. action:@selector(doCancel)];
  65. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:TIMCommonLocalizableString(Multiple)
  66. style:UIBarButtonItemStylePlain
  67. target:self
  68. action:@selector(doMultiple)];
  69. self.view.backgroundColor = [UIColor whiteColor];
  70. _headerView = [[TUICommonTableViewCell alloc] init];
  71. _headerView.textLabel.text = TIMCommonLocalizableString(TUIKitRelayTargetCreateNewChat);
  72. _headerView.textLabel.font = [UIFont systemFontOfSize:15.0];
  73. _headerView.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  74. [_headerView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onCreateSessionOrSelectContact)]];
  75. _tableView = [[UITableView alloc] init];
  76. _tableView.delegate = self;
  77. _tableView.dataSource = self;
  78. _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  79. _tableView.tableHeaderView = self.headerView;
  80. [_tableView registerClass:TUIConversationCell.class forCellReuseIdentifier:Id];
  81. [self.view addSubview:_tableView];
  82. _pickerView = [[TUIContactListPicker alloc] init];
  83. [_pickerView setBackgroundColor:[UIColor groupTableViewBackgroundColor]];
  84. [_pickerView setHidden:YES];
  85. [_pickerView.accessoryBtn addTarget:self action:@selector(doPickerDone) forControlEvents:UIControlEventTouchUpInside];
  86. __weak typeof(self) weakSelf = self;
  87. _pickerView.onCancel = ^(TUICommonContactSelectCellData *data) {
  88. TUIConversationCellData *tmp = nil;
  89. for (TUIConversationCellData *convCellData in weakSelf.currentSelectedList) {
  90. if ([convCellData.conversationID isEqualToString:data.identifier]) {
  91. tmp = convCellData;
  92. break;
  93. }
  94. }
  95. if (tmp == nil) {
  96. return;
  97. }
  98. tmp.selected = NO;
  99. [weakSelf.currentSelectedList removeObject:tmp];
  100. [weakSelf updatePickerView];
  101. [weakSelf.tableView reloadData];
  102. };
  103. [self.view addSubview:_pickerView];
  104. @weakify(self);
  105. [RACObserve(self.dataProvider, dataList) subscribeNext:^(id _Nullable x) {
  106. @strongify(self);
  107. [self.tableView reloadData];
  108. }];
  109. }
  110. - (void)updateLayout {
  111. [self.pickerView setHidden:!self.enableMuliple];
  112. self.headerView.frame = CGRectMake(0, 0, self.view.bounds.size.width, 55);
  113. _headerView.textLabel.text =
  114. self.enableMuliple ? TIMCommonLocalizableString(TUIKitRelayTargetSelectFromContacts) : TIMCommonLocalizableString(TUIKitRelayTargetCreateNewChat);
  115. if (!self.enableMuliple) {
  116. self.tableView.frame = self.view.bounds;
  117. return;
  118. }
  119. CGFloat pH = 55;
  120. CGFloat pMargin = 0;
  121. if (@available(iOS 11.0, *)) {
  122. pMargin = self.view.safeAreaInsets.bottom;
  123. }
  124. [self.pickerView setFrame:CGRectMake(0, self.view.bounds.size.height - pH - pMargin, self.view.bounds.size.width, pH + pMargin)];
  125. self.tableView.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height - pH - pMargin);
  126. }
  127. - (void)updatePickerView {
  128. NSMutableArray *arrayM = [NSMutableArray array];
  129. for (TUIConversationCellData *convCellData in self.currentSelectedList) {
  130. TUICommonContactSelectCellData *data = [[TUICommonContactSelectCellData alloc] init];
  131. data.avatarUrl = [NSURL URLWithString:convCellData.faceUrl];
  132. data.avatarImage = convCellData.avatarImage;
  133. data.title = convCellData.title;
  134. data.identifier = convCellData.conversationID;
  135. [arrayM addObject:data];
  136. }
  137. self.pickerView.selectArray = [NSArray arrayWithArray:arrayM];
  138. }
  139. #pragma mark - Action
  140. - (void)doCancel {
  141. if (self.enableMuliple) {
  142. self.enableMuliple = NO;
  143. for (TUIConversationCellData *cellData in self.dataProvider.dataList) {
  144. cellData.selected = NO;
  145. }
  146. [self.currentSelectedList removeAllObjects];
  147. self.pickerView.selectArray = @[];
  148. [self updatePickerView];
  149. [self updateLayout];
  150. [self.tableView reloadData];
  151. } else {
  152. [self dismissViewControllerAnimated:YES completion:nil];
  153. }
  154. }
  155. - (void)doMultiple {
  156. self.enableMuliple = YES;
  157. [self updateLayout];
  158. [self.tableView reloadData];
  159. }
  160. - (void)onCreateSessionOrSelectContact {
  161. NSMutableArray *ids = NSMutableArray.new;
  162. for (TUIConversationCellData *cd in self.currentSelectedList) {
  163. if (![cd.userID isEqualToString:[[V2TIMManager sharedInstance] getLoginUser]]) {
  164. if (cd.userID.length > 0) {
  165. [ids addObject:cd.userID];
  166. }
  167. }
  168. }
  169. @weakify(self);
  170. void (^selectContactCompletion)(NSArray<TUICommonContactSelectCellData *> *) = ^(NSArray<TUICommonContactSelectCellData *> *array) {
  171. @strongify(self);
  172. [self dealSelectBlock:array];
  173. };
  174. UIViewController *vc = [TUICore createObject:TUICore_TUIContactObjectFactory
  175. key:TUICore_TUIContactObjectFactory_GetContactSelectControllerMethod
  176. param:@{
  177. TUICore_TUIContactObjectFactory_GetContactSelectControllerMethod_DisableIdsKey : ids,
  178. TUICore_TUIContactObjectFactory_GetContactSelectControllerMethod_CompletionKey : selectContactCompletion,
  179. }];
  180. [self.navigationController pushViewController:(UIViewController *)vc animated:YES];
  181. self.showContactSelectVC = vc;
  182. }
  183. - (void)dealSelectBlock:(NSArray<TUICommonContactSelectCellData *> *)array {
  184. NSArray<TUICommonContactSelectCellData *> *selectArray = array;
  185. if (![selectArray.firstObject isKindOfClass:TUICommonContactSelectCellData.class]) {
  186. NSAssert(NO, @"Error value type");
  187. }
  188. if (self.enableMuliple) {
  189. /**
  190. * Multiple selection: Select from address book -> Create conversation for each contact -> Every contact will be displayed in pickerView
  191. */
  192. for (TUICommonContactSelectCellData *contact in selectArray) {
  193. if ([self existInSelectedArray:contact.identifier]) {
  194. continue;
  195. }
  196. TUIConversationCellData *conv = [self findItemInDataListArray:contact.identifier];
  197. if (!conv) {
  198. conv = [[TUIConversationCellData alloc] init];
  199. conv.conversationID = contact.identifier;
  200. conv.userID = contact.identifier;
  201. conv.groupID = @"";
  202. conv.avatarImage = contact.avatarImage;
  203. conv.faceUrl = contact.avatarUrl.absoluteString;
  204. } else {
  205. conv.selected = !conv.selected;
  206. }
  207. [self.currentSelectedList addObject:conv];
  208. }
  209. [self updatePickerView];
  210. [self.tableView reloadData];
  211. [self.navigationController popViewControllerAnimated:YES];
  212. } else {
  213. /**
  214. * Single Choice: Create a new chat (or a group chat if there are multiple people) -> Create a group chat for the selected contact -> Forward directly
  215. */
  216. if (selectArray.count <= 1) {
  217. TUICommonContactSelectCellData *contact = selectArray.firstObject;
  218. if (contact) {
  219. TUIConversationCellData *conv = [[TUIConversationCellData alloc] init];
  220. conv.conversationID = contact.identifier;
  221. conv.userID = contact.identifier;
  222. conv.groupID = @"";
  223. conv.avatarImage = contact.avatarImage;
  224. conv.faceUrl = contact.avatarUrl.absoluteString;
  225. self.currentSelectedList = [NSMutableArray arrayWithArray:@[ conv ]];
  226. [self tryFinishSelected:^(BOOL finished) {
  227. if (finished) {
  228. [self notifyFinishSelecting];
  229. [self dismissViewControllerAnimated:YES completion:nil];
  230. }
  231. }];
  232. }
  233. return;
  234. }
  235. [self tryFinishSelected:^(BOOL finished) {
  236. if (finished) {
  237. [self createGroupWithContacts:selectArray
  238. completion:^(BOOL success) {
  239. if (success) {
  240. [self dismissViewControllerAnimated:YES completion:nil];
  241. }
  242. }];
  243. }
  244. }];
  245. }
  246. }
  247. - (BOOL)existInSelectedArray:(NSString *)identifier {
  248. for (TUIConversationCellData *cellData in self.currentSelectedList) {
  249. if (cellData.userID.length && [cellData.userID isEqualToString:identifier]) {
  250. return YES;
  251. }
  252. }
  253. return NO;
  254. }
  255. - (TUIConversationCellData *)findItemInDataListArray:(NSString *)identifier {
  256. for (TUIConversationCellData *cellData in self.dataProvider.dataList) {
  257. if (cellData.userID.length && [cellData.userID isEqualToString:identifier]) {
  258. return cellData;
  259. }
  260. }
  261. return nil;
  262. }
  263. - (void)doPickerDone {
  264. __weak typeof(self) weakSelf = self;
  265. [self tryFinishSelected:^(BOOL finished) {
  266. if (finished) {
  267. [self notifyFinishSelecting];
  268. [weakSelf dismissViewControllerAnimated:YES completion:nil];
  269. }
  270. }];
  271. }
  272. // confirm whether to forward or not
  273. - (void)tryFinishSelected:(TUIConversationSelectCompletHandler)handler {
  274. UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:TIMCommonLocalizableString(TUIKitRelayConfirmForward)
  275. message:nil
  276. preferredStyle:UIAlertControllerStyleAlert];
  277. [alertVc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Cancel)
  278. style:UIAlertActionStyleDefault
  279. handler:^(UIAlertAction *_Nonnull action) {
  280. if (handler) {
  281. handler(NO);
  282. }
  283. }]];
  284. [alertVc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Confirm)
  285. style:UIAlertActionStyleDefault
  286. handler:^(UIAlertAction *_Nonnull action) {
  287. if (handler) {
  288. handler(YES);
  289. }
  290. }]];
  291. [self presentViewController:alertVc animated:YES completion:nil];
  292. }
  293. // notify others that the user has finished selecting conversations
  294. - (void)notifyFinishSelecting {
  295. if (self.navigateValueCallback) {
  296. NSMutableArray *temMArr = [NSMutableArray arrayWithCapacity:self.currentSelectedList.count];
  297. for (TUIConversationCellData *cellData in self.currentSelectedList) {
  298. [temMArr addObject:@{
  299. TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_ConversationID : cellData.conversationID ?: @"",
  300. TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_Title : cellData.title ?: @"",
  301. TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_UserID : cellData.userID ?: @"",
  302. TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_GroupID : cellData.groupID ?: @"",
  303. }];
  304. }
  305. self.navigateValueCallback(@{TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList : temMArr});
  306. }
  307. }
  308. // create a new group to receive the forwarding messages
  309. - (void)createGroupWithContacts:(NSArray *)contacts completion:(void (^)(BOOL success))completion {
  310. @weakify(self);
  311. void (^createGroupCompletion)(BOOL, NSString *, NSString *) = ^(BOOL success, NSString *groupID, NSString *groupName) {
  312. @strongify(self);
  313. if (!success) {
  314. [TUITool makeToast:TIMCommonLocalizableString(TUIKitRelayTargetCrateGroupError)];
  315. if (completion) {
  316. completion(NO);
  317. }
  318. return;
  319. }
  320. TUIConversationCellData *cellData = [[TUIConversationCellData alloc] init];
  321. cellData.groupID = groupID;
  322. cellData.title = groupName;
  323. self.currentSelectedList = [NSMutableArray arrayWithArray:@[ cellData ]];
  324. [self notifyFinishSelecting];
  325. if (completion) {
  326. completion(YES);
  327. }
  328. };
  329. NSDictionary *param = @{
  330. TUICore_TUIContactService_CreateGroupMethod_GroupTypeKey : GroupType_Meeting,
  331. TUICore_TUIContactService_CreateGroupMethod_OptionKey : @(V2TIM_GROUP_ADD_ANY),
  332. TUICore_TUIContactService_CreateGroupMethod_ContactsKey : contacts,
  333. TUICore_TUIContactService_CreateGroupMethod_CompletionKey : createGroupCompletion
  334. };
  335. [TUICore callService:TUICore_TUIContactService method:TUICore_TUIContactService_CreateGroupMethod param:param];
  336. }
  337. #pragma mark - UITableViewDelegate, UITableViewDataSource
  338. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  339. return self.dataProvider.dataList.count;
  340. }
  341. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  342. TUIConversationCell *cell = [tableView dequeueReusableCellWithIdentifier:Id forIndexPath:indexPath];
  343. if (indexPath.row < 0 || indexPath.row >= self.dataProvider.dataList.count) {
  344. return cell;
  345. }
  346. TUIConversationCellData *cellData = self.dataProvider.dataList[indexPath.row];
  347. cellData.showCheckBox = self.enableMuliple;
  348. [cell fillWithData:cellData];
  349. return cell;
  350. }
  351. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  352. [tableView deselectRowAtIndexPath:indexPath animated:NO];
  353. TUIConversationCellData *cellData = self.dataProvider.dataList[indexPath.row];
  354. cellData.selected = !cellData.selected;
  355. if (!self.enableMuliple) {
  356. self.currentSelectedList = [NSMutableArray arrayWithArray:@[ cellData ]];
  357. __weak typeof(self) weakSelf = self;
  358. [self tryFinishSelected:^(BOOL finished) {
  359. if (finished) {
  360. [self notifyFinishSelecting];
  361. [weakSelf dismissViewControllerAnimated:YES completion:nil];
  362. }
  363. }];
  364. return;
  365. }
  366. if ([self.currentSelectedList containsObject:cellData]) {
  367. [self.currentSelectedList removeObject:cellData];
  368. } else {
  369. [self.currentSelectedList addObject:cellData];
  370. }
  371. [self updatePickerView];
  372. [self.tableView reloadData];
  373. }
  374. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  375. return 56.0;
  376. }
  377. - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
  378. UIView *titleView = [[UIView alloc] init];
  379. titleView.backgroundColor = [UIColor groupTableViewBackgroundColor];
  380. titleView.bounds = CGRectMake(0, 0, self.tableView.bounds.size.width, 30);
  381. UILabel *label = [[UILabel alloc] init];
  382. label.text = TIMCommonLocalizableString(TUIKitRelayRecentMessages);
  383. label.font = [UIFont systemFontOfSize:12.0];
  384. label.textColor = [UIColor darkGrayColor];
  385. label.textAlignment = NSTextAlignmentLeft;
  386. [titleView addSubview:label];
  387. label.frame = CGRectMake(10, 0, self.tableView.bounds.size.width - 10, 30);
  388. return titleView;
  389. }
  390. #pragma mark - Lazy
  391. - (NSMutableArray<TUIConversationCellData *> *)currentSelectedList {
  392. if (_currentSelectedList == nil) {
  393. _currentSelectedList = [NSMutableArray array];
  394. }
  395. return _currentSelectedList;
  396. }
  397. @end