TUIBaseChatViewController.m 70 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577
  1. //
  2. // TUIBaseChatViewController.m
  3. // UIKit
  4. //
  5. // Created by annidyfeng on 2019/5/21.
  6. // Copyright © 2023 Tencent. All rights reserved.
  7. //
  8. #import "TUIBaseChatViewController.h"
  9. #import <AVFoundation/AVFoundation.h>
  10. #import <AssetsLibrary/AssetsLibrary.h>
  11. #import <MobileCoreServices/MobileCoreServices.h>
  12. #import <Photos/Photos.h>
  13. #import <TIMCommon/NSString+TUIEmoji.h>
  14. #import <TIMCommon/TIMDefine.h>
  15. #import <TUICore/NSDictionary+TUISafe.h>
  16. #import <TUICore/TUICore.h>
  17. #import <TUICore/TUILogin.h>
  18. #import <TUICore/TUIThemeManager.h>
  19. #import <TUICore/TUITool.h>
  20. #import <TUICore/NSString+TUIUtil.h>
  21. #import "ReactiveObjC/ReactiveObjC.h"
  22. #import "TUIAIDenoiseSignatureManager.h"
  23. #import "TUIBaseMessageController.h"
  24. #import "TUICameraViewController.h"
  25. #import "TUIChatConfig.h"
  26. #import "TUIChatDataProvider.h"
  27. #import "TUIChatMediaDataProvider.h"
  28. #import "TUIChatModifyMessageHelper.h"
  29. #import "TUICloudCustomDataTypeCenter.h"
  30. #import "TUIFileMessageCellData.h"
  31. #import "TUIImageMessageCellData.h"
  32. #import "TUIJoinGroupMessageCell.h"
  33. #import "TUIMessageController.h"
  34. #import "TUIMessageDataProvider.h"
  35. #import "TUIMessageMultiChooseView.h"
  36. #import "TUIMessageReadViewController.h"
  37. #import "TUIReplyMessageCellData.h"
  38. #import "TUITextMessageCellData.h"
  39. #import "TUIVideoMessageCellData.h"
  40. #import "TUIVoiceMessageCellData.h"
  41. #import "TUIChatShortcutMenuView.h"
  42. static UIView *gCustomTopView;
  43. static UIView *gTopExentsionView;
  44. static UIView *gGroupPinTopView;
  45. static CGRect gCustomTopViewRect;
  46. @interface TUIBaseChatViewController () <TUIBaseMessageControllerDelegate,
  47. TUIInputControllerDelegate,
  48. UIImagePickerControllerDelegate,
  49. UIDocumentPickerDelegate,
  50. UINavigationControllerDelegate,
  51. TUIMessageMultiChooseViewDelegate,
  52. TUIChatBaseDataProviderDelegate,
  53. TUINotificationProtocol,
  54. TUIJoinGroupMessageCellDelegate,
  55. V2TIMConversationListener,
  56. TUINavigationControllerDelegate,
  57. TUIChatMediaDataListener,
  58. TUIBaseMessageControllerDelegate_Two,
  59. TIMInputViewMoreActionProtocol>
  60. @property(nonatomic, strong) TUINaviBarIndicatorView *titleView;
  61. @property(nonatomic, strong) TUIMessageMultiChooseView *multiChooseView;
  62. @property(nonatomic, assign) BOOL responseKeyboard;
  63. @property(nonatomic, assign) BOOL isPageAppears;
  64. @property(nonatomic, strong) TUIChatDataProvider *dataProvider;
  65. @property(nonatomic, assign) BOOL firstAppear;
  66. @property(nonatomic, copy) NSString *mainTitle;
  67. @property(nonatomic, strong) UIImageView *backgroudView;
  68. @property(nonatomic, strong) TUIChatMediaDataProvider *mediaProvider;
  69. @end
  70. @implementation TUIBaseChatViewController
  71. #pragma mark - Life Cycle
  72. - (instancetype)init {
  73. self = [super init];
  74. if (self) {
  75. [TUIBaseChatViewController createCachePath];
  76. [[TUIAIDenoiseSignatureManager sharedInstance] updateSignature];
  77. [[NSNotificationCenter defaultCenter] addObserver:self
  78. selector:@selector(reloadTopViewsAndMessagePage)
  79. name:TUICore_TUIChatExtension_ChatViewTopArea_ChangedNotification
  80. object:nil];
  81. [TUIChatMediaSendingManager.sharedInstance addCurrentVC:self];
  82. }
  83. return self;
  84. }
  85. - (void)setTitle:(NSString *)title {
  86. self.mainTitle = title;
  87. }
  88. - (void)viewDidLoad {
  89. [super viewDidLoad];
  90. [self setupTopViews];
  91. // data provider
  92. self.dataProvider = [[TUIChatDataProvider alloc] init];
  93. self.dataProvider.delegate = self;
  94. // setupUI
  95. self.firstAppear = YES;
  96. self.view.backgroundColor = TIMCommonDynamicColor(@"controller_bg_color", @"#FFFFFF");
  97. self.edgesForExtendedLayout = UIRectEdgeNone;
  98. [self configBackgroundView];
  99. [self setupNavigator];
  100. [self setupMessageController];
  101. [self setupInputMoreMenu];
  102. [self setupInputController];
  103. [self setupShortcutView];
  104. // reset then setup bottom container and its margin
  105. NSDictionary *userInfo = @{TUIKitNotification_onMessageVCBottomMarginChanged_Margin: @(0)};
  106. [[NSNotificationCenter defaultCenter] postNotificationName:TUIKitNotification_onMessageVCBottomMarginChanged object:nil userInfo:userInfo];
  107. [self setupBottomContainerView];
  108. // Notify
  109. [self configNotify];
  110. }
  111. - (void)viewWillAppear:(BOOL)animated {
  112. [super viewWillAppear:animated];
  113. [self configTopViewsViewWillAppear];
  114. }
  115. - (void)configTopViewsViewWillAppear {
  116. if (gCustomTopView.superview != self.view) {
  117. if (CGRectEqualToRect(gCustomTopView.frame, CGRectZero)) {
  118. gCustomTopView.frame = CGRectMake(0, CGRectGetMaxY(gTopExentsionView.frame), gCustomTopViewRect.size.width, gCustomTopViewRect.size.height);
  119. }
  120. [self.view addSubview:gCustomTopView];
  121. }
  122. if (gTopExentsionView.superview != self.view) {
  123. [self.view addSubview:gTopExentsionView];
  124. }
  125. if (gGroupPinTopView.superview != self.view && self.conversationData.groupID.length > 0){
  126. [self.view addSubview:gGroupPinTopView];
  127. }
  128. [self reloadTopViewsAndMessagePage];
  129. }
  130. - (void)setupTopViews {
  131. if (gTopExentsionView) {
  132. [gTopExentsionView removeFromSuperview];
  133. }
  134. else {
  135. gTopExentsionView = [[UIView alloc] init];
  136. gTopExentsionView.clipsToBounds = YES;
  137. }
  138. if (gGroupPinTopView) {
  139. [gGroupPinTopView removeFromSuperview];
  140. }
  141. else {
  142. gGroupPinTopView = [[UIView alloc] init];
  143. gGroupPinTopView.clipsToBounds = YES;
  144. }
  145. if (gTopExentsionView) {
  146. [self setupTopExentsionView];
  147. }
  148. if (gCustomTopView) {
  149. [self setupCustomTopView];
  150. gCustomTopView.frame = CGRectMake(0, CGRectGetMaxY(gTopExentsionView.frame), gCustomTopViewRect.size.width, gCustomTopViewRect.size.height);
  151. }
  152. if (gGroupPinTopView && self.conversationData.groupID.length > 0) {
  153. [self setupGroupPinTopView];
  154. gGroupPinTopView.frame = CGRectMake(0, CGRectGetMaxY(gCustomTopView.frame), gGroupPinTopView.frame.size.width, gGroupPinTopView.frame.size.height);;
  155. }
  156. }
  157. - (void)reloadTopViewsAndMessagePage {
  158. gCustomTopView.frame = CGRectMake(0, CGRectGetMaxY(gTopExentsionView.frame), gCustomTopView.frame.size.width, gCustomTopView.frame.size.height);
  159. if (gGroupPinTopView) {
  160. gGroupPinTopView.frame = CGRectMake(0, CGRectGetMaxY(gCustomTopView.frame), gGroupPinTopView.frame.size.width, gGroupPinTopView.frame.size.height);;
  161. }
  162. CGFloat topMarginByCustomView = [self topMarginByCustomView];
  163. if (_messageController.view.mm_y != topMarginByCustomView) {
  164. CGFloat textViewHeight = TUIChatConfig.defaultConfig.enableMainPageInputBar? TTextView_Height:0;
  165. _messageController.view.frame = CGRectMake(0, topMarginByCustomView, self.view.mm_w,
  166. self.view.mm_h - textViewHeight - Bottom_SafeHeight - topMarginByCustomView);
  167. [self.messageController scrollToBottom:YES];
  168. }
  169. }
  170. - (void)dealloc {
  171. [TUICore unRegisterEventByObject:self];
  172. }
  173. - (void)appWillResignActive:(NSNotification *)notification {
  174. [self saveDraft];
  175. }
  176. - (void)willMoveToParentViewController:(UIViewController *)parent {
  177. if (parent == nil) {
  178. [self saveDraft];
  179. }
  180. }
  181. - (void)viewDidLayoutSubviews {
  182. [self layoutBottomContanerView];
  183. }
  184. - (void)viewDidAppear:(BOOL)animated {
  185. [super viewDidAppear:animated];
  186. self.responseKeyboard = YES;
  187. self.isPageAppears = YES;
  188. if (self.firstAppear) {
  189. [self loadDraft];
  190. self.firstAppear = NO;
  191. }
  192. if (self.needScrollToBottom) {
  193. [self.messageController scrollToBottom:YES];
  194. self.needScrollToBottom = NO;
  195. }
  196. }
  197. - (void)viewWillDisappear:(BOOL)animated {
  198. [super viewWillDisappear:animated];
  199. self.responseKeyboard = NO;
  200. self.isPageAppears = NO;
  201. [self openMultiChooseBoard:NO];
  202. [self.messageController enableMultiSelectedMode:NO];
  203. }
  204. - (void)viewDidDisappear:(BOOL)animated {
  205. [super viewDidDisappear:animated];
  206. }
  207. - (UIInterfaceOrientationMask)supportedInterfaceOrientations{
  208. if (_conversationData.isLimitedPortraitOrientation) {
  209. return UIInterfaceOrientationMaskPortrait;
  210. } else {
  211. return UIInterfaceOrientationMaskAllButUpsideDown;
  212. }
  213. }
  214. - (void)setupNavigator {
  215. TUINavigationController *naviController = (TUINavigationController *)self.navigationController;
  216. if ([naviController isKindOfClass:TUINavigationController.class]) {
  217. naviController.uiNaviDelegate = self;
  218. UIImage *backimg = TIMCommonDynamicImage(@"nav_back_img", [UIImage imageNamed:TIMCommonImagePath(@"nav_back")]);
  219. backimg = [backimg rtl_imageFlippedForRightToLeftLayoutDirection];
  220. naviController.navigationItemBackArrowImage = backimg;
  221. }
  222. _titleView = [[TUINaviBarIndicatorView alloc] init];
  223. self.navigationItem.titleView = _titleView;
  224. self.navigationItem.title = @"";
  225. __weak typeof(self) weakSelf = self;
  226. [[RACObserve(_conversationData, title) distinctUntilChanged] subscribeNext:^(NSString *title) {
  227. [weakSelf.titleView setTitle:title];
  228. }];
  229. [[RACObserve(_conversationData, otherSideTyping) distinctUntilChanged] subscribeNext:^(id otherSideTyping) {
  230. BOOL otherSideTypingFlag = [otherSideTyping boolValue];
  231. if (!otherSideTypingFlag) {
  232. [weakSelf checkTitle:YES];
  233. weakSelf.theTypingBlock ? weakSelf.theTypingBlock(NO) : nil;
  234. }
  235. else {
  236. NSString *typingText = [NSString stringWithFormat:@"%@...", TIMCommonLocalizableString(TUIKitTyping)];
  237. [weakSelf.titleView setTitle:typingText];
  238. weakSelf.theTypingBlock ? weakSelf.theTypingBlock(YES) : nil;
  239. }
  240. }];
  241. [self checkTitle:NO];
  242. [TUIChatDataProvider
  243. getTotalUnreadMessageCountWithSuccBlock:^(UInt64 totalCount) {
  244. [weakSelf onChangeUnReadCount:totalCount];
  245. }
  246. fail:nil];
  247. _unRead = [[TUIUnReadView alloc] init];
  248. CGSize itemSize = CGSizeMake(25, 25);
  249. NSMutableArray *rightBarButtonList = [NSMutableArray array];
  250. NSMutableDictionary *param = [NSMutableDictionary dictionary];
  251. if (self.conversationData.userID.length > 0) {
  252. param[TUICore_TUIChatExtension_NavigationMoreItem_UserID] = self.conversationData.userID;
  253. } else if (self.conversationData.groupID.length > 0) {
  254. param[TUICore_TUIChatExtension_NavigationMoreItem_GroupID] = self.conversationData.groupID;
  255. }
  256. param[TUICore_TUIChatExtension_NavigationMoreItem_ItemSize] = NSStringFromCGSize(itemSize);
  257. param[TUICore_TUIChatExtension_NavigationMoreItem_FilterVideoCall] = @(!TUIChatConfig.defaultConfig.enableVideoCall);
  258. param[TUICore_TUIChatExtension_NavigationMoreItem_FilterAudioCall] = @(!TUIChatConfig.defaultConfig.enableAudioCall);
  259. NSArray<TUIExtensionInfo *> *extensionList = [TUICore getExtensionList:TUICore_TUIChatExtension_NavigationMoreItem_ClassicExtensionID param:param];
  260. TUIExtensionInfo *maxWeightInfo = [TUIExtensionInfo new];
  261. maxWeightInfo.weight = INT_MIN;
  262. for (TUIExtensionInfo *info in extensionList) {
  263. if (maxWeightInfo.weight < info.weight) {
  264. maxWeightInfo = info;
  265. }
  266. }
  267. if (maxWeightInfo == nil) {
  268. return;
  269. }
  270. if (maxWeightInfo.icon && maxWeightInfo.onClicked) {
  271. UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, itemSize.width, itemSize.height)];
  272. [button.widthAnchor constraintEqualToConstant:itemSize.width].active = YES;
  273. [button.heightAnchor constraintEqualToConstant:itemSize.height].active = YES;
  274. button.tui_extValueObj = maxWeightInfo;
  275. [button addTarget:self action:@selector(rightBarButtonClick:) forControlEvents:UIControlEventTouchUpInside];
  276. [button setImage:maxWeightInfo.icon forState:UIControlStateNormal];
  277. UIBarButtonItem *rightItem = [[UIBarButtonItem alloc] initWithCustomView:button];
  278. [rightBarButtonList addObject:rightItem];
  279. }
  280. if (rightBarButtonList.count > 0) {
  281. self.navigationItem.rightBarButtonItems = rightBarButtonList.reverseObjectEnumerator.allObjects;
  282. }
  283. }
  284. - (void)setupMessageController {
  285. TUIMessageController *vc = [[TUIMessageController alloc] init];
  286. vc.hightlightKeyword = self.highlightKeyword;
  287. vc.locateMessage = self.locateMessage;
  288. vc.isMsgNeedReadReceipt = self.conversationData.msgNeedReadReceipt && [TUIChatConfig defaultConfig].msgNeedReadReceipt;
  289. _messageController = vc;
  290. _messageController.delegate = self;
  291. _messageController.delegate_Two = self;
  292. [_messageController setConversation:self.conversationData];
  293. CGFloat textViewHeight = TUIChatConfig.defaultConfig.enableMainPageInputBar? TTextView_Height:0;
  294. _messageController.view.frame = CGRectMake(0, [self topMarginByCustomView], self.view.frame.size.width,
  295. self.view.frame.size.height - textViewHeight - Bottom_SafeHeight - [self topMarginByCustomView]);
  296. [self addChildViewController:_messageController];
  297. [self.view addSubview:_messageController.view];
  298. [_messageController didMoveToParentViewController:self];
  299. }
  300. - (void)setupTopExentsionView {
  301. if (gTopExentsionView.superview != self.view) {
  302. [self.view addSubview:gTopExentsionView];
  303. }
  304. gTopExentsionView.frame = CGRectMake(0, 0, self.view.frame.size.width, 0);
  305. NSMutableDictionary *param = [NSMutableDictionary dictionary];
  306. if (self.conversationData.userID.length > 0) {
  307. param[TUICore_TUIChatExtension_ChatViewTopArea_ChatID] = self.conversationData.userID;
  308. param[TUICore_TUIChatExtension_ChatViewTopArea_IsGroup] = @"0";
  309. } else if (self.conversationData.groupID.length > 0) {
  310. param[TUICore_TUIChatExtension_ChatViewTopArea_IsGroup] = @"1";
  311. param[TUICore_TUIChatExtension_ChatViewTopArea_ChatID] = self.conversationData.groupID;
  312. }
  313. [TUICore raiseExtension:TUICore_TUIChatExtension_ChatViewTopArea_ClassicExtensionID parentView:gTopExentsionView param:param];
  314. }
  315. - (void)setupGroupPinTopView {
  316. if (gGroupPinTopView.superview != self.view) {
  317. [self.view addSubview:gGroupPinTopView];
  318. }
  319. gGroupPinTopView.backgroundColor = [UIColor clearColor];
  320. gGroupPinTopView.frame = CGRectMake(0, 0, self.view.frame.size.width, 0);
  321. }
  322. - (void)setupCustomTopView {
  323. if (gCustomTopView.superview != self.view) {
  324. [self.view addSubview:gCustomTopView];
  325. }
  326. }
  327. - (void)setupBottomContainerView {
  328. [self.view addSubview:self.bottomContainerView];
  329. NSArray *shortcutMenuItems = self.conversationData.shortcutMenuItems;
  330. CGFloat viewHeight = self.conversationData.shortcutViewHeight;
  331. if (shortcutMenuItems.count > 0) {
  332. TUIChatShortcutMenuView *view = [[TUIChatShortcutMenuView alloc] initWithDataSource:shortcutMenuItems];
  333. view.viewHeight = viewHeight;
  334. view.itemHorizontalSpacing = 0.0;
  335. if (self.conversationData.shortcutViewBackgroundColor != nil) {
  336. view.backgroundColor = self.conversationData.shortcutViewBackgroundColor;
  337. }
  338. [self.bottomContainerView addSubview:view];
  339. [view updateFrame];
  340. } else {
  341. [self notifyBttomContainerReady];
  342. }
  343. }
  344. - (void)layoutBottomContanerView {
  345. if (self.bottomContainerView.mm_y == self.messageController.view.mm_maxY) {
  346. return;
  347. }
  348. if (self.conversationData.shortcutMenuItems.count > 0) {
  349. CGFloat height = self.conversationData.shortcutViewHeight > 0 ? self.conversationData.shortcutViewHeight : 46;
  350. self.messageController.view.mm_h = self.messageController.view.mm_h - height;
  351. self.bottomContainerView.frame = CGRectMake(0, self.messageController.view.mm_maxY,
  352. self.messageController.view.mm_w, height);
  353. }
  354. }
  355. - (void)setupInputController {
  356. _inputController = [[TUIInputController alloc] init];
  357. _inputController.delegate = self;
  358. @weakify(self);
  359. [RACObserve(self, moreMenus) subscribeNext:^(NSArray *x) {
  360. @strongify(self);
  361. [self.inputController.moreView setData:x];
  362. }];
  363. _inputController.view.frame =
  364. CGRectMake(0, self.view.frame.size.height - TTextView_Height - Bottom_SafeHeight, self.view.frame.size.width, TTextView_Height + Bottom_SafeHeight);
  365. _inputController.view.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
  366. [self addChildViewController:_inputController];
  367. [self.view addSubview:_inputController.view];
  368. _inputController.view.hidden = !TUIChatConfig.defaultConfig.enableMainPageInputBar;
  369. self.moreMenus = [self.dataProvider getMoreMenuCellDataArray:self.conversationData.groupID
  370. userID:self.conversationData.userID
  371. conversationModel:self.conversationData
  372. actionController:self];
  373. }
  374. - (void)setupShortcutView {
  375. id<TUIChatShortcutViewDataSource> dataSource = [TUIChatConfig defaultConfig].shortcutViewDataSource;
  376. if (dataSource && [dataSource respondsToSelector:@selector(itemsInShortcutViewOfModel:)]) {
  377. NSArray *items = [dataSource itemsInShortcutViewOfModel:self.conversationData];
  378. if (items.count > 0) {
  379. self.conversationData.shortcutMenuItems = items;
  380. if (dataSource && [dataSource respondsToSelector:@selector(shortcutViewBackgroundColorOfModel:)]) {
  381. UIColor *backgroundColor = [dataSource shortcutViewBackgroundColorOfModel:self.conversationData];
  382. self.conversationData.shortcutViewBackgroundColor = backgroundColor;
  383. }
  384. if (dataSource && [dataSource respondsToSelector:@selector(shortcutViewHeightOfModel:)]) {
  385. CGFloat height = [dataSource shortcutViewHeightOfModel:self.conversationData];
  386. self.conversationData.shortcutViewHeight = height;
  387. }
  388. }
  389. }
  390. }
  391. - (void)setupInputMoreMenu {
  392. id<TUIChatInputBarConfigDataSource> dataSource = [TUIChatConfig defaultConfig].inputBarDataSource;
  393. if (dataSource && [dataSource respondsToSelector:@selector(inputBarShouldHideItemsInMoreMenuOfModel:)]) {
  394. TUIChatInputBarMoreMenuItem tag = [dataSource inputBarShouldHideItemsInMoreMenuOfModel:self.conversationData];
  395. self.conversationData.enableFile = !(tag & TUIChatInputBarMoreMenuItem_File);
  396. self.conversationData.enablePoll = !(tag & TUIChatInputBarMoreMenuItem_Poll);
  397. self.conversationData.enableRoom = !(tag & TUIChatInputBarMoreMenuItem_Room);
  398. self.conversationData.enableAlbum = !(tag & TUIChatInputBarMoreMenuItem_Album);
  399. self.conversationData.enableAudioCall = !(tag & TUIChatInputBarMoreMenuItem_AudioCall);
  400. self.conversationData.enableVideoCall = !(tag & TUIChatInputBarMoreMenuItem_VideoCall);
  401. self.conversationData.enableGroupNote = !(tag & TUIChatInputBarMoreMenuItem_GroupNote);
  402. self.conversationData.enableTakePhoto = !(tag & TUIChatInputBarMoreMenuItem_TakePhoto);
  403. self.conversationData.enableRecordVideo = !(tag & TUIChatInputBarMoreMenuItem_RecordVideo);
  404. self.conversationData.enableWelcomeCustomMessage = !(tag & TUIChatInputBarMoreMenuItem_CustomMessage);
  405. }
  406. if (dataSource && [dataSource respondsToSelector:@selector(inputBarShouldAddNewItemsToMoreMenuOfModel:)]) {
  407. NSArray *items = [dataSource inputBarShouldAddNewItemsToMoreMenuOfModel:self.conversationData];
  408. if ([items isKindOfClass:NSArray.class]) {
  409. self.conversationData.customizedNewItemsInMoreMenu = items;
  410. }
  411. }
  412. }
  413. - (void)configBackgroundView {
  414. self.backgroudView = [[UIImageView alloc] init];
  415. self.backgroudView.backgroundColor =
  416. TUIChatConfig.defaultConfig.backgroudColor ? TUIChatConfig.defaultConfig.backgroudColor : TUIChatDynamicColor(@"chat_controller_bg_color", @"#FFFFFF");
  417. NSString *conversationID = [self getConversationID];
  418. NSString *imgUrl = [self getBackgroundImageUrlByConversationID:conversationID];
  419. if (TUIChatConfig.defaultConfig.backgroudImage) {
  420. self.backgroudView.backgroundColor = UIColor.clearColor;
  421. self.backgroudView.image = TUIChatConfig.defaultConfig.backgroudImage;
  422. } else if (IS_NOT_EMPTY_NSSTRING(imgUrl)) {
  423. [self.backgroudView sd_setImageWithURL:[NSURL URLWithString:imgUrl] placeholderImage:nil];
  424. }
  425. CGFloat textViewHeight = TUIChatConfig.defaultConfig.enableMainPageInputBar? TTextView_Height:0;
  426. self.backgroudView.frame =
  427. CGRectMake(0, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height - textViewHeight - Bottom_SafeHeight);
  428. [self.view insertSubview:self.backgroudView atIndex:0];
  429. }
  430. - (void)configNotify {
  431. [[V2TIMManager sharedInstance] addConversationListener:self];
  432. [TUICore registerEvent:TUICore_TUIConversationNotify subKey:TUICore_TUIConversationNotify_ClearConversationUIHistorySubKey object:self];
  433. [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(onFriendInfoChanged:) name:@"FriendInfoChangedNotification" object:nil];
  434. [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(appWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil];
  435. [TUICore registerEvent:TUICore_TUIContactNotify subKey:TUICore_TUIContactNotify_UpdateConversationBackgroundImageSubKey object:self];
  436. }
  437. #pragma mark - Extension
  438. - (void)notifyBttomContainerReady {
  439. [TUICore registerEvent:TUICore_TUIPluginNotify
  440. subKey:TUICore_TUIPluginNotify_PluginViewDidAddToSuperview
  441. object:self];
  442. [TUICore raiseExtension:TUICore_TUIChatExtension_ChatVCBottomContainer_ClassicExtensionID
  443. parentView:self.bottomContainerView
  444. param:@{TUICore_TUIChatExtension_ChatVCBottomContainer_UserID: self.conversationData.userID ? : @"",
  445. TUICore_TUIChatExtension_ChatVCBottomContainer_VC: self}];
  446. }
  447. - (UIView *)bottomContainerView {
  448. if (!_bottomContainerView) {
  449. _bottomContainerView = [[UIView alloc] init];
  450. }
  451. return _bottomContainerView;
  452. }
  453. #pragma mark - Public Methods
  454. - (void)sendMessage:(V2TIMMessage *)message {
  455. [self.messageController sendMessage:message];
  456. }
  457. - (void)sendMessage:(V2TIMMessage *)message placeHolderCellData:(TUIMessageCellData *)placeHolderCellData {
  458. [self.messageController sendMessage:message placeHolderCellData:placeHolderCellData];
  459. }
  460. - (void)saveDraft {
  461. NSString *content = [self.inputController.inputBar.inputTextView.textStorage tui_getPlainString];
  462. TUIReplyPreviewData *previewData = nil;
  463. if (self.inputController.referenceData) {
  464. previewData = self.inputController.referenceData;
  465. } else if (self.inputController.replyData) {
  466. previewData = self.inputController.replyData;
  467. }
  468. if (previewData) {
  469. NSDictionary *dict = @{
  470. @"content" : content ?: @"",
  471. @"messageReply" : @{
  472. @"messageID" : previewData.msgID ?: @"",
  473. @"messageAbstract" : [previewData.msgAbstract ?: @"" getInternationalStringWithfaceContent],
  474. @"messageSender" : previewData.sender ?: @"",
  475. @"messageType" : @(previewData.type),
  476. @"messageTime" :
  477. @(previewData.originMessage.timestamp ? [previewData.originMessage.timestamp timeIntervalSince1970] : 0), // Compatible for web
  478. @"messageSequence" : @(previewData.originMessage.seq), // Compatible for web
  479. @"version" : @(kDraftMessageReplyVersion),
  480. },
  481. };
  482. NSMutableDictionary *mudic = [NSMutableDictionary dictionaryWithDictionary:dict];
  483. if (IS_NOT_EMPTY_NSSTRING(previewData.messageRootID)) {
  484. [mudic setObject:previewData.messageRootID forKey:@"messageRootID"];
  485. }
  486. NSError *error = nil;
  487. NSData *data = [NSJSONSerialization dataWithJSONObject:mudic options:0 error:&error];
  488. if (error == nil) {
  489. content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  490. }
  491. }
  492. [TUIChatDataProvider saveDraftWithConversationID:self.conversationData.conversationID Text:content];
  493. }
  494. - (void)loadDraft {
  495. NSString *draft = self.conversationData.draftText;
  496. if (draft.length == 0) {
  497. return;
  498. }
  499. NSError *error = nil;
  500. NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:[draft dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&error];
  501. if (error || jsonDict == nil) {
  502. NSMutableAttributedString *formatEmojiString = [draft getAdvancedFormatEmojiStringWithFont:kTUIInputNoramlFont
  503. textColor:kTUIInputNormalTextColor
  504. emojiLocations:nil];
  505. [self.inputController.inputBar addDraftToInputBar:formatEmojiString];
  506. return;
  507. }
  508. /**
  509. *
  510. * Display draft
  511. */
  512. NSString *draftContent = [jsonDict.allKeys containsObject:@"content"] ? jsonDict[@"content"] : @"";
  513. NSMutableAttributedString *formatEmojiString = [draftContent getAdvancedFormatEmojiStringWithFont:kTUIInputNoramlFont
  514. textColor:kTUIInputNormalTextColor
  515. emojiLocations:nil];
  516. [self.inputController.inputBar addDraftToInputBar:formatEmojiString];
  517. NSString *messageRootID = [jsonDict.allKeys containsObject:@"messageRootID"] ? jsonDict[@"messageRootID"] : @"";
  518. /**
  519. *
  520. * Display message reply preview bar
  521. */
  522. if ([jsonDict isKindOfClass:NSDictionary.class] && [jsonDict.allKeys containsObject:@"messageReply"]) {
  523. NSDictionary *reply = jsonDict[@"messageReply"];
  524. if ([reply isKindOfClass:NSDictionary.class] && [reply.allKeys containsObject:@"messageID"] && [reply.allKeys containsObject:@"messageAbstract"] &&
  525. [reply.allKeys containsObject:@"messageSender"] && [reply.allKeys containsObject:@"messageType"] && [reply.allKeys containsObject:@"version"]) {
  526. NSInteger version = [reply[@"version"] integerValue];
  527. if (version <= kDraftMessageReplyVersion) {
  528. if (IS_NOT_EMPTY_NSSTRING(messageRootID)) {
  529. TUIReplyPreviewData *replyData = [[TUIReplyPreviewData alloc] init];
  530. replyData.msgID = reply[@"messageID"];
  531. replyData.msgAbstract = reply[@"messageAbstract"];
  532. replyData.sender = reply[@"messageSender"];
  533. replyData.type = [reply[@"messageType"] integerValue];
  534. replyData.messageRootID = messageRootID;
  535. [self.inputController showReplyPreview:replyData];
  536. } else {
  537. TUIReferencePreviewData *replyData = [[TUIReferencePreviewData alloc] init];
  538. replyData.msgID = reply[@"messageID"];
  539. replyData.msgAbstract = reply[@"messageAbstract"];
  540. replyData.sender = reply[@"messageSender"];
  541. replyData.type = [reply[@"messageType"] integerValue];
  542. [self.inputController showReferencePreview:replyData];
  543. }
  544. }
  545. }
  546. }
  547. }
  548. + (void)setCustomTopView:(UIView *)view {
  549. gCustomTopView = view;
  550. gCustomTopViewRect = view.frame;
  551. gCustomTopView.clipsToBounds = YES;
  552. }
  553. + (UIView *)customTopView {
  554. return gCustomTopView;
  555. }
  556. + (UIView *)groupPinTopView {
  557. return gGroupPinTopView;
  558. }
  559. + (UIView *)topAreaBottomView {
  560. if (gGroupPinTopView) {
  561. return gGroupPinTopView;
  562. }
  563. if (gCustomTopView) {
  564. return gCustomTopView;
  565. }
  566. if (gTopExentsionView) {
  567. return gTopExentsionView;
  568. }
  569. return nil;
  570. }
  571. #pragma mark - Getters & Setters
  572. - (void)setConversationData:(TUIChatConversationModel *)conversationData {
  573. _conversationData = conversationData;
  574. // conversationData
  575. NSDictionary *param = @{TUICore_TUIChatExtension_GetChatConversationModelParams_UserID: self.conversationData.userID ? : @""};
  576. NSArray<TUIExtensionInfo *> *extensionList = [TUICore getExtensionList:TUICore_TUIChatExtension_GetChatConversationModelParams param:param];
  577. TUIExtensionInfo *extention = extensionList.firstObject;
  578. if (extention) {
  579. _conversationData.msgNeedReadReceipt = [extention.data[TUICore_TUIChatExtension_GetChatConversationModelParams_MsgNeedReadReceipt] boolValue];
  580. _conversationData.enableVideoCall = [extention.data[TUICore_TUIChatExtension_GetChatConversationModelParams_EnableVideoCall] boolValue];
  581. _conversationData.enableAudioCall = [extention.data[TUICore_TUIChatExtension_GetChatConversationModelParams_EnableAudioCall] boolValue];
  582. _conversationData.enableWelcomeCustomMessage =
  583. [extention.data[TUICore_TUIChatExtension_GetChatConversationModelParams_EnableWelcomeCustomMessage] boolValue];
  584. }
  585. }
  586. - (CGFloat)topMarginByCustomView {
  587. CGFloat gCutomTopViewH = gCustomTopView && gCustomTopView.superview ? gCustomTopView.mm_h : 0 ;
  588. CGFloat gTopExtsionH = gTopExentsionView && gTopExentsionView.superview ? gTopExentsionView.mm_h : 0;
  589. CGFloat gGroupPinTopViewH = gGroupPinTopView && gGroupPinTopView.superview ? gGroupPinTopView.mm_h : 0;
  590. CGFloat height = gCutomTopViewH + gTopExtsionH + gGroupPinTopViewH;
  591. return height;
  592. }
  593. - (void)setIsBlackUser:(BOOL)isBlackUser{
  594. _isBlackUser = isBlackUser;
  595. if(isBlackUser){
  596. if(self.direction == 2){
  597. self.inputController.inputBar.inputTextView.placeHolderColor = [UIColor grayColor];
  598. self.inputController.inputBar.inputTextView.placeHolder = NSLocalizedString(@"mimo_chat_black_my_do_tip", nil);
  599. }
  600. else{
  601. self.inputController.inputBar.inputTextView.placeHolderColor = [UIColor grayColor];
  602. self.inputController.inputBar.inputTextView.placeHolder = NSLocalizedString(@"mimo_chat_black_she_do_tip", nil);
  603. }
  604. }
  605. }
  606. #pragma mark - Event Response
  607. - (void)onChangeUnReadCount:(UInt64)totalCount {
  608. /**
  609. * The reason for the asynchrony here: The current chat page receives messages continuously and frequently, it may not be marked as read, and unread changes
  610. * will also be received at this time. In theory, the unreads at this time will not include the current session.
  611. */
  612. __weak typeof(self) weakSelf = self;
  613. dispatch_async(dispatch_get_main_queue(), ^{
  614. [weakSelf.unRead setNum:totalCount];
  615. });
  616. }
  617. - (void)checkTitle:(BOOL)force {
  618. if (force || self.conversationData.title.length == 0) {
  619. if (self.conversationData.userID.length > 0) {
  620. self.conversationData.title = self.conversationData.userID;
  621. @weakify(self);
  622. [TUIChatDataProvider getFriendInfoWithUserId:self.conversationData.userID
  623. SuccBlock:^(V2TIMFriendInfoResult *_Nonnull friendInfoResult) {
  624. @strongify(self);
  625. if (friendInfoResult.relation & V2TIM_FRIEND_RELATION_TYPE_IN_MY_FRIEND_LIST &&
  626. friendInfoResult.friendInfo.friendRemark.length > 0) {
  627. self.conversationData.title = friendInfoResult.friendInfo.friendRemark;
  628. } else {
  629. [TUIChatDataProvider getUserInfoWithUserId:self.conversationData.userID
  630. SuccBlock:^(V2TIMUserFullInfo *_Nonnull userInfo) {
  631. if (userInfo.nickName.length > 0) {
  632. self.conversationData.title = userInfo.nickName;
  633. }
  634. }
  635. failBlock:nil];
  636. }
  637. }
  638. failBlock:nil];
  639. } else if (self.conversationData.groupID.length > 0) {
  640. [TUIChatDataProvider getGroupInfoWithGroupID:self.conversationData.groupID
  641. SuccBlock:^(V2TIMGroupInfoResult *_Nonnull groupResult) {
  642. if (groupResult.info.groupName.length > 0 &&
  643. self.conversationData.enableRoom) {
  644. self.conversationData.title = groupResult.info.groupName;
  645. }
  646. if ([groupResult.info.groupType isEqualToString:@"Room"] ) {
  647. self.navigationItem.rightBarButtonItems = nil;
  648. }
  649. }
  650. failBlock:nil];
  651. }
  652. }
  653. }
  654. - (void)leftBarButtonClick {
  655. [self.navigationController popViewControllerAnimated:YES];
  656. }
  657. - (void)rightBarButtonClick:(UIButton *)button {
  658. [self.inputController reset];
  659. TUIExtensionInfo *info = button.tui_extValueObj;
  660. if (info == nil || ![info isKindOfClass:TUIExtensionInfo.class] || info.onClicked == nil) {
  661. return;
  662. }
  663. NSMutableDictionary *param = [NSMutableDictionary dictionary];
  664. if (self.conversationData.userID.length > 0) {
  665. param[TUICore_TUIChatExtension_NavigationMoreItem_UserID] = self.conversationData.userID;
  666. } else if (self.conversationData.groupID.length > 0) {
  667. param[TUICore_TUIChatExtension_NavigationMoreItem_GroupID] = self.conversationData.groupID;
  668. }
  669. if (self.navigationController) {
  670. param[TUICore_TUIChatExtension_NavigationMoreItem_PushVC] = self.navigationController;
  671. }
  672. info.onClicked(param);
  673. }
  674. - (void)getUserOrFriendProfileVCWithUserID:(NSString *)userID succBlock:(void (^)(UIViewController *vc))succ failBlock:(nullable V2TIMFail)fail {
  675. NSDictionary *param = @{
  676. TUICore_TUIContactObjectFactory_GetUserOrFriendProfileVCMethod_UserIDKey: userID ? : @"",
  677. TUICore_TUIContactObjectFactory_GetUserOrFriendProfileVCMethod_SuccKey: succ ? : ^(UIViewController *vc){},
  678. TUICore_TUIContactObjectFactory_GetUserOrFriendProfileVCMethod_FailKey: fail ? : ^(int code, NSString * desc){}
  679. };
  680. [TUICore createObject:TUICore_TUIContactObjectFactory key:TUICore_TUIContactObjectFactory_GetUserOrFriendProfileVCMethod param:param];
  681. }
  682. #pragma mark - TUICore notify
  683. - (void)onNotifyEvent:(NSString *)key subKey:(NSString *)subKey object:(id)anObject param:(NSDictionary *)param {
  684. if ([key isEqualToString:TUICore_TUIConversationNotify] && [subKey isEqualToString:TUICore_TUIConversationNotify_ClearConversationUIHistorySubKey]) {
  685. [self.messageController clearUImsg];
  686. } else if ([key isEqualToString:TUICore_TUIContactNotify] && [subKey isEqualToString:TUICore_TUIContactNotify_UpdateConversationBackgroundImageSubKey]) {
  687. NSString *conversationID = param[TUICore_TUIContactNotify_UpdateConversationBackgroundImageSubKey_ConversationID];
  688. if (IS_NOT_EMPTY_NSSTRING(conversationID)) {
  689. [self updateBackgroundImageUrlByConversationID:conversationID];
  690. }
  691. } else if ([key isEqualToString:TUICore_TUIPluginNotify] && [subKey isEqualToString:TUICore_TUIPluginNotify_PluginViewDidAddToSuperview]) {
  692. float height = [param[TUICore_TUIPluginNotify_PluginViewDidAddToSuperviewSubKey_PluginViewHeight] floatValue];
  693. self.messageController.view.frame = CGRectMake(0, [self topMarginByCustomView],
  694. self.view.frame.size.width, self.messageController.view.mm_h - height);
  695. [self.messageController.view setNeedsLayout];
  696. [self.messageController.view layoutIfNeeded];
  697. dispatch_async(dispatch_get_main_queue(), ^{
  698. self.bottomContainerView.frame = CGRectMake(0, self.messageController.view.mm_maxY,
  699. self.messageController.view.mm_w, height);
  700. });
  701. NSDictionary *userInfo = @{TUIKitNotification_onMessageVCBottomMarginChanged_Margin: @(height)};
  702. [[NSNotificationCenter defaultCenter] postNotificationName:TUIKitNotification_onMessageVCBottomMarginChanged object:nil userInfo:userInfo];
  703. }
  704. }
  705. - (void)updateBackgroundImageUrlByConversationID:(NSString *)conversationID {
  706. if ([[self getConversationID] isEqualToString:conversationID]) {
  707. self.backgroudView.backgroundColor = UIColor.clearColor;
  708. NSString *imgUrl = [self getBackgroundImageUrlByConversationID:conversationID];
  709. if (IS_NOT_EMPTY_NSSTRING(imgUrl)) {
  710. [self.backgroudView sd_setImageWithURL:[NSURL URLWithString:imgUrl] placeholderImage:nil];
  711. } else {
  712. self.backgroudView.image = nil;
  713. }
  714. }
  715. }
  716. - (NSString *)getBackgroundImageUrlByConversationID:(NSString *)targerConversationID {
  717. if (targerConversationID.length == 0) {
  718. return nil;
  719. }
  720. NSDictionary *dict = [NSUserDefaults.standardUserDefaults objectForKey:@"conversation_backgroundImage_map"];
  721. if (dict == nil) {
  722. dict = @{};
  723. }
  724. NSString *conversationID_UserID = [NSString stringWithFormat:@"%@_%@", targerConversationID, [TUILogin getUserID]];
  725. if (![dict isKindOfClass:NSDictionary.class] || ![dict.allKeys containsObject:conversationID_UserID]) {
  726. return nil;
  727. }
  728. return [dict objectForKey:conversationID_UserID];
  729. }
  730. - (NSString *)getConversationID {
  731. NSString *conversationID = @"";
  732. if (self.conversationData.conversationID.length > 0) {
  733. conversationID = self.conversationData.conversationID;
  734. } else if (self.conversationData.userID.length > 0) {
  735. conversationID = [NSString stringWithFormat:@"c2c_%@", self.conversationData.userID];
  736. } else if (self.conversationData.groupID.length > 0) {
  737. conversationID = [NSString stringWithFormat:@"group_%@", self.conversationData.groupID];
  738. }
  739. return conversationID;
  740. }
  741. #pragma mark - TUIInputControllerDelegate
  742. - (void)inputController:(TUIInputController *)inputController didChangeHeight:(CGFloat)height {
  743. if (!self.responseKeyboard) {
  744. return;
  745. }
  746. [UIView animateWithDuration:0.3
  747. delay:0
  748. options:UIViewAnimationOptionCurveEaseOut
  749. animations:^{
  750. CGRect msgFrame = self.messageController.view.frame;
  751. CGFloat calHeight = self.view.frame.size.height - height - [self topMarginByCustomView] - self.bottomContainerView.mm_h;
  752. msgFrame.size.height = MAX(0, calHeight);
  753. self.messageController.view.frame = msgFrame;
  754. if (self.bottomContainerView.mm_h > 0) {
  755. CGRect containerFrame = self.bottomContainerView.frame;
  756. containerFrame.origin.y = msgFrame.origin.y + msgFrame.size.height;
  757. self.bottomContainerView.frame = containerFrame;
  758. CGRect inputFrame = self.inputController.view.frame;
  759. inputFrame.origin.y = self.bottomContainerView.mm_maxY;
  760. inputFrame.size.height = height;
  761. self.inputController.view.frame = inputFrame;
  762. } else {
  763. CGRect inputFrame = self.inputController.view.frame;
  764. inputFrame.origin.y = msgFrame.origin.y + msgFrame.size.height;
  765. inputFrame.size.height = height;
  766. self.inputController.view.frame = inputFrame;
  767. }
  768. [self.messageController scrollToBottom:NO];
  769. }
  770. completion:nil];
  771. }
  772. - (void)inputController:(TUIInputController *)inputController didSendMessage:(V2TIMMessage *)msg {
  773. if(self.canSend){
  774. [self.messageController sendMessage:msg];
  775. }
  776. }
  777. - (void)inputControllerDidInputAt:(TUIInputController *)inputController {
  778. /**
  779. * Handle to GroupChatVC
  780. */
  781. }
  782. - (void)inputController:(TUIInputController *)inputController didDeleteAt:(NSString *)atText {
  783. /**
  784. * Handle to GroupChatVC
  785. */
  786. }
  787. - (void)inputControllerBeginTyping:(TUIInputController *)inputController {
  788. // for C2CChatVC
  789. }
  790. - (void)inputControllerEndTyping:(TUIInputController *)inputController {
  791. // for C2CChatVC
  792. }
  793. - (BOOL)currentUserIsSuperOwnerInGroup {
  794. //for GroupChatVC
  795. return NO;
  796. }
  797. - (void)inputController:(TUIInputController *)inputController didSelectMoreCell:(TUIInputMoreCell *)cell {
  798. cell.disableDefaultSelectAction = NO;
  799. if (cell.disableDefaultSelectAction) {
  800. return;
  801. }
  802. TUIInputMoreCellData *data = cell.data;
  803. if (data == nil || data.onClicked == nil) {
  804. return;
  805. }
  806. NSMutableDictionary *param = [NSMutableDictionary dictionary];
  807. if (self.conversationData.userID.length > 0) {
  808. param[TUICore_TUIChatExtension_InputViewMoreItem_UserID] = self.conversationData.userID;
  809. } else if (self.conversationData.groupID.length > 0) {
  810. param[TUICore_TUIChatExtension_InputViewMoreItem_GroupID] = self.conversationData.groupID;
  811. }
  812. if (self.navigationController) {
  813. param[TUICore_TUIChatExtension_InputViewMoreItem_PushVC] = self.navigationController;
  814. param[TUICore_TUIChatExtension_InputViewMoreItem_VC] = self;
  815. }
  816. data.onClicked(param);
  817. }
  818. - (void)inputControllerDidClickMore:(TUIInputController *)inputController {
  819. self.moreMenus = [self.dataProvider getMoreMenuCellDataArray:self.conversationData.groupID
  820. userID:self.conversationData.userID
  821. conversationModel:self.conversationData
  822. actionController:self];
  823. }
  824. #pragma mark - TUIBaseMessageControllerDelegate
  825. - (void)didTapInMessageController:(TUIBaseMessageController *)controller {
  826. [self.inputController reset];
  827. }
  828. - (BOOL)messageController:(TUIBaseMessageController *)controller willShowMenuInCell:(TUIMessageCell *)cell {
  829. if ([self.inputController.inputBar.inputTextView isFirstResponder]) {
  830. self.inputController.inputBar.inputTextView.overrideNextResponder = cell;
  831. return YES;
  832. }
  833. return NO;
  834. }
  835. - (V2TIMMessage *)messageController_Two:(TUIBaseMessageController *)controller onNewMessage:(V2TIMMessage *)message{
  836. if(self.delegate_Two && [self.delegate_Two respondsToSelector:@selector(baseChatViewController_Two:onNewMessage:)]){
  837. V2TIMMessage *newMsg = [self.delegate_Two baseChatViewController_Two:controller onNewMessage:message];
  838. if(newMsg){
  839. return newMsg;
  840. }
  841. }
  842. return nil;
  843. }
  844. - (TUIMessageCell *)messageController:(TUIBaseMessageController *)controller onShowMessageData:(TUIMessageCellData *)data {
  845. return nil;
  846. }
  847. - (void)messageController:(TUIBaseMessageController *)controller willDisplayCell:(TUIMessageCell *)cell withData:(TUIMessageCellData *)cellData {
  848. if ([cell isKindOfClass:[TUIJoinGroupMessageCell class]]) {
  849. TUIJoinGroupMessageCell *joinCell = (TUIJoinGroupMessageCell *)cell;
  850. joinCell.joinGroupDelegate = self;
  851. }
  852. }
  853. - (void)messageController:(TUIBaseMessageController *)controller onSelectMessageAvatar:(TUIMessageCell *)cell {
  854. NSString *userID = nil;
  855. if (cell.messageData.innerMessage.groupID.length > 0) {
  856. userID = cell.messageData.innerMessage.sender;
  857. } else {
  858. if (cell.messageData.isUseMsgReceiverAvatar) {
  859. if (cell.messageData.innerMessage.isSelf) {
  860. userID = cell.messageData.innerMessage.userID;
  861. } else {
  862. userID = V2TIMManager.sharedInstance.getLoginUser;
  863. }
  864. } else {
  865. userID = cell.messageData.innerMessage.sender;
  866. }
  867. }
  868. if (userID == nil) {
  869. return;
  870. }
  871. // Get extensions first
  872. NSMutableDictionary *param = [NSMutableDictionary dictionary];
  873. if (self.conversationData.userID.length > 0) {
  874. param[TUICore_TUIChatExtension_ClickAvatar_UserID] = self.conversationData.userID;
  875. } else if (self.conversationData.groupID.length > 0) {
  876. param[TUICore_TUIChatExtension_ClickAvatar_GroupID] = self.conversationData.groupID;
  877. }
  878. if (self.navigationController) {
  879. param[TUICore_TUIChatExtension_ClickAvatar_PushVC] = self.navigationController;
  880. }
  881. NSArray<TUIExtensionInfo *> *extensionList = [TUICore getExtensionList:TUICore_TUIChatExtension_ClickAvatar_ClassicExtensionID param:param];
  882. if (extensionList.count > 0) {
  883. TUIExtensionInfo *maxWeightInfo = [TUIExtensionInfo new];
  884. maxWeightInfo.weight = INT_MIN;
  885. for (TUIExtensionInfo *info in extensionList) {
  886. if (maxWeightInfo.weight < info.weight) {
  887. maxWeightInfo = info;
  888. }
  889. }
  890. if (maxWeightInfo == nil) {
  891. return;
  892. }
  893. if (maxWeightInfo.onClicked) {
  894. maxWeightInfo.onClicked(param);
  895. }
  896. } else {
  897. [self getUserOrFriendProfileVCWithUserID:userID
  898. succBlock:^(UIViewController *vc) {
  899. [self.navigationController pushViewController:vc animated:YES];
  900. }
  901. failBlock:nil];
  902. }
  903. [self.inputController reset];
  904. }
  905. - (void)messageController:(TUIBaseMessageController *)controller onSelectMessageContent:(TUIMessageCell *)cell {
  906. cell.disableDefaultSelectAction = NO;
  907. if (cell.disableDefaultSelectAction) {
  908. return;
  909. }
  910. }
  911. - (void)messageController:(TUIBaseMessageController *)controller onSelectMessageMenu:(NSInteger)menuType withData:(TUIMessageCellData *)data {
  912. [self onSelectMessageMenu:menuType withData:data];
  913. }
  914. - (void)didHideMenuInMessageController:(TUIBaseMessageController *)controller {
  915. self.inputController.inputBar.inputTextView.overrideNextResponder = nil;
  916. }
  917. - (void)messageController:(TUIBaseMessageController *)controller onReEditMessage:(TUIMessageCellData *)data {
  918. V2TIMMessage *message = data.innerMessage;
  919. if (message.elemType == V2TIM_ELEM_TYPE_TEXT) {
  920. NSString *text = message.textElem.text;
  921. self.inputController.inputBar.inputTextView.text = text;
  922. [self.inputController.inputBar.inputTextView becomeFirstResponder];
  923. }
  924. }
  925. - (CGFloat)getTopMarginByCustomView {
  926. return [self topMarginByCustomView];
  927. }
  928. #pragma mark - TUIChatBaseDataProviderDelegate
  929. - (NSString *)dataProvider:(TUIChatDataProvider *)dataProvider mergeForwardTitleWithMyName:(NSString *)name {
  930. return [self forwardTitleWithMyName:name];
  931. }
  932. - (NSString *)dataProvider:(TUIChatDataProvider *)dataProvider mergeForwardMsgAbstactForMessage:(V2TIMMessage *)message {
  933. return @"";
  934. }
  935. - (void)dataProvider:(TUIChatBaseDataProvider *)dataProvider sendMessage:(V2TIMMessage *)message {
  936. [self.messageController sendMessage:message];
  937. }
  938. - (void)onSelectPhotoMoreCellData {
  939. [self.mediaProvider selectPhoto];
  940. }
  941. - (void)onTakePictureMoreCellData {
  942. [self.mediaProvider takePicture];
  943. }
  944. - (void)onTakeVideoMoreCellData {
  945. [self.mediaProvider takeVideo];
  946. }
  947. - (void)onMultimediaRecordMoreCellData {
  948. [self.mediaProvider multimediaRecord];
  949. }
  950. - (void)onSelectFileMoreCellData {
  951. [self.mediaProvider selectFile];
  952. }
  953. #pragma mark - TUINavigationControllerDelegate
  954. - (void)navigationControllerDidClickLeftButton:(TUINavigationController *)controller {
  955. if (controller.currentShowVC == self) {
  956. [self.messageController readReport];
  957. }
  958. }
  959. - (void)navigationControllerDidSideSlideReturn:(TUINavigationController *)controller fromViewController:(UIViewController *)fromViewController {
  960. if ([fromViewController isEqual:self]) {
  961. [self.messageController readReport];
  962. }
  963. }
  964. #pragma mark - : &
  965. - (void)onSelectMessageMenu:(NSInteger)menuType withData:(TUIMessageCellData *)data {
  966. if (menuType == 0) {
  967. [self openMultiChooseBoard:YES];
  968. } else if (menuType == 1) {
  969. if (data == nil) {
  970. return;
  971. }
  972. NSMutableArray *uiMsgs = [NSMutableArray arrayWithArray:@[ data ]];
  973. [self prepareForwardMessages:uiMsgs];
  974. }
  975. }
  976. - (void)openMultiChooseBoard:(BOOL)open {
  977. [self.view endEditing:YES];
  978. if (_multiChooseView) {
  979. [_multiChooseView removeFromSuperview];
  980. }
  981. if (open) {
  982. _multiChooseView = [[TUIMessageMultiChooseView alloc] init];
  983. _multiChooseView.frame = UIScreen.mainScreen.bounds;
  984. _multiChooseView.delegate = self;
  985. _multiChooseView.titleLabel.text = self.conversationData.title;
  986. if (@available(iOS 12.0, *)) {
  987. if (@available(iOS 13.0, *)) {
  988. // > ios 12
  989. [UIApplication.sharedApplication.keyWindow addSubview:_multiChooseView];
  990. } else {
  991. // ios = 12
  992. UIView *view = self.navigationController.view;
  993. if (view == nil) {
  994. view = self.view;
  995. }
  996. [view addSubview:_multiChooseView];
  997. }
  998. } else {
  999. // < ios 12
  1000. [UIApplication.sharedApplication.keyWindow addSubview:_multiChooseView];
  1001. }
  1002. } else {
  1003. [self.messageController enableMultiSelectedMode:NO];
  1004. }
  1005. }
  1006. - (void)messageMultiChooseViewOnCancelClicked:(TUIMessageMultiChooseView *)multiChooseView {
  1007. [self openMultiChooseBoard:NO];
  1008. [self.messageController enableMultiSelectedMode:NO];
  1009. }
  1010. - (void)messageMultiChooseViewOnRelayClicked:(TUIMessageMultiChooseView *)multiChooseView {
  1011. NSArray *uiMsgs = [self.messageController multiSelectedResult:TUIMultiResultOptionAll];
  1012. [self prepareForwardMessages:uiMsgs];
  1013. }
  1014. - (void)messageMultiChooseViewOnDeleteClicked:(TUIMessageMultiChooseView *)multiChooseView {
  1015. NSArray *uiMsgs = [self.messageController multiSelectedResult:TUIMultiResultOptionAll];
  1016. if (uiMsgs.count == 0) {
  1017. [TUITool makeToast:TIMCommonLocalizableString(TUIKitRelayNoMessageTips)];
  1018. return;
  1019. }
  1020. [self.messageController deleteMessages:uiMsgs];
  1021. [self openMultiChooseBoard:NO];
  1022. [self.messageController enableMultiSelectedMode:NO];
  1023. }
  1024. - (void)prepareForwardMessages:(NSArray<TUIMessageCellData *> *)uiMsgs {
  1025. if (uiMsgs.count == 0) {
  1026. [TUITool makeToast:TIMCommonLocalizableString(TUIKitRelayNoMessageTips)];
  1027. return;
  1028. }
  1029. BOOL hasSendFailedMsg = NO;
  1030. BOOL canForwardMsg = YES;
  1031. for (TUIMessageCellData *data in uiMsgs) {
  1032. if (data.status != Msg_Status_Succ) {
  1033. hasSendFailedMsg = YES;
  1034. }
  1035. canForwardMsg &= [data canForward];
  1036. if (hasSendFailedMsg && !canForwardMsg) {
  1037. break;
  1038. }
  1039. }
  1040. if (hasSendFailedMsg) {
  1041. UIAlertController *vc = [UIAlertController alertControllerWithTitle:TIMCommonLocalizableString(TUIKitRelayUnsupportForward)
  1042. message:nil
  1043. preferredStyle:UIAlertControllerStyleAlert];
  1044. [vc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Confirm)
  1045. style:UIAlertActionStyleDefault
  1046. handler:^(UIAlertAction *_Nonnull action){
  1047. }]];
  1048. [self presentViewController:vc animated:YES completion:nil];
  1049. return;
  1050. }
  1051. if (!canForwardMsg) {
  1052. UIAlertController *vc = [UIAlertController alertControllerWithTitle:TIMCommonLocalizableString(TUIKitRelayPluginNotAllowed)
  1053. message:nil
  1054. preferredStyle:UIAlertControllerStyleAlert];
  1055. [vc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Confirm)
  1056. style:UIAlertActionStyleDefault
  1057. handler:^(UIAlertAction *_Nonnull action){
  1058. }]];
  1059. [self presentViewController:vc animated:YES completion:nil];
  1060. return;
  1061. }
  1062. __weak typeof(self) weakSelf = self;
  1063. UIAlertController *tipsVc = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
  1064. // Forward one-by-one
  1065. [tipsVc
  1066. tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(TUIKitRelayOneByOneForward)
  1067. style:UIAlertActionStyleDefault
  1068. handler:^(UIAlertAction *_Nonnull action) {
  1069. if (uiMsgs.count <= 30) {
  1070. [weakSelf selectTarget:NO toForwardMessage:uiMsgs orForwardText:nil];
  1071. return;
  1072. }
  1073. UIAlertController *vc =
  1074. [UIAlertController alertControllerWithTitle:TIMCommonLocalizableString(TUIKitRelayOneByOnyOverLimit)
  1075. message:nil
  1076. preferredStyle:UIAlertControllerStyleAlert];
  1077. [vc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Cancel)
  1078. style:UIAlertActionStyleDefault
  1079. handler:nil]];
  1080. [vc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(TUIKitRelayCombineForwad)
  1081. style:UIAlertActionStyleDefault
  1082. handler:^(UIAlertAction *_Nonnull action) {
  1083. [weakSelf selectTarget:YES
  1084. toForwardMessage:uiMsgs
  1085. orForwardText:nil];
  1086. }]];
  1087. [weakSelf presentViewController:vc animated:YES completion:nil];
  1088. }]];
  1089. // Merge-forward
  1090. [tipsVc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(TUIKitRelayCombineForwad)
  1091. style:UIAlertActionStyleDefault
  1092. handler:^(UIAlertAction *_Nonnull action) {
  1093. [weakSelf selectTarget:YES toForwardMessage:uiMsgs orForwardText:nil];
  1094. }]];
  1095. [tipsVc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Cancel) style:UIAlertActionStyleDefault handler:nil]];
  1096. [self presentViewController:tipsVc animated:YES completion:nil];
  1097. }
  1098. - (void)selectTarget:(BOOL)mergeForward toForwardMessage:(NSArray<TUIMessageCellData *> *)uiMsgs orForwardText:(NSString *)forwardText {
  1099. __weak typeof(self) weakSelf = self;
  1100. UINavigationController *nav = [[UINavigationController alloc] init];
  1101. nav.modalPresentationStyle = UIModalPresentationFullScreen;
  1102. [self presentViewController:TUICore_TUIConversationObjectFactory_ConversationSelectVC_Classic
  1103. param:nil
  1104. embbedIn:nav
  1105. forResult:^(NSDictionary *_Nonnull param) {
  1106. NSArray<NSDictionary *> *selectList = param[TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList];
  1107. NSMutableArray<TUIChatConversationModel *> *targetList = [NSMutableArray arrayWithCapacity:selectList.count];
  1108. for (NSDictionary *selectItem in selectList) {
  1109. TUIChatConversationModel *model = [[TUIChatConversationModel alloc] init];
  1110. model.title = selectItem[TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_Title];
  1111. model.userID = selectItem[TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_UserID];
  1112. model.groupID = selectItem[TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_GroupID];
  1113. model.conversationID = selectItem[TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_ConversationID];
  1114. [targetList addObject:model];
  1115. }
  1116. if (uiMsgs.count > 0) {
  1117. [weakSelf forwardMessages:uiMsgs toTargets:targetList merge:mergeForward];
  1118. } else if (forwardText.length > 0) {
  1119. [weakSelf forwardText:forwardText toConverations:targetList];
  1120. }
  1121. }];
  1122. }
  1123. - (void)forwardMessages:(NSArray<TUIMessageCellData *> *)uiMsgs toTargets:(NSArray<TUIChatConversationModel *> *)targets merge:(BOOL)merge {
  1124. if (uiMsgs.count == 0 || targets.count == 0) {
  1125. return;
  1126. }
  1127. @weakify(self);
  1128. [self.dataProvider getForwardMessageWithCellDatas:uiMsgs
  1129. toTargets:targets
  1130. Merge:merge
  1131. ResultBlock:^(TUIChatConversationModel *_Nonnull targetConversation, NSArray<V2TIMMessage *> *_Nonnull msgs) {
  1132. @strongify(self);
  1133. TUIChatConversationModel *convCellData = targetConversation;
  1134. NSTimeInterval timeInterval = convCellData.groupID.length ? 0.09 : 0.05;
  1135. /**
  1136. *
  1137. * Forward to currernt chat vc
  1138. */
  1139. if ([convCellData.conversationID isEqualToString:self.conversationData.conversationID]) {
  1140. dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  1141. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  1142. dispatch_async(queue, ^{
  1143. for (V2TIMMessage *imMsg in msgs) {
  1144. dispatch_async(dispatch_get_main_queue(), ^{
  1145. [self.messageController sendMessage:imMsg];
  1146. dispatch_semaphore_signal(semaphore);
  1147. });
  1148. dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  1149. [NSThread sleepForTimeInterval:timeInterval];
  1150. }
  1151. });
  1152. return;
  1153. }
  1154. /**
  1155. *
  1156. * Forward to other chat user
  1157. */
  1158. TUISendMessageAppendParams *appendParams = [[TUISendMessageAppendParams alloc] init];
  1159. appendParams.isSendPushInfo = YES;
  1160. appendParams.isOnlineUserOnly = NO;
  1161. appendParams.priority = V2TIM_PRIORITY_NORMAL;
  1162. for (V2TIMMessage *message in msgs) {
  1163. message.needReadReceipt = self.conversationData.msgNeedReadReceipt && [TUIChatConfig defaultConfig].msgNeedReadReceipt;
  1164. [TUIMessageDataProvider sendMessage:message
  1165. toConversation:convCellData
  1166. appendParams:appendParams
  1167. Progress:nil
  1168. SuccBlock:^{
  1169. /**
  1170. * Messages sent to other chats need to broadcast the message sending status, which is convenient to refresh the message status after
  1171. * entering the corresponding chat
  1172. */
  1173. [NSNotificationCenter.defaultCenter postNotificationName:TUIKitNotification_onMessageStatusChanged object:message];
  1174. }
  1175. FailBlock:^(int code, NSString *desc) {
  1176. [NSNotificationCenter.defaultCenter postNotificationName:TUIKitNotification_onMessageStatusChanged object:message];
  1177. }];
  1178. /**
  1179. * The delay here is to ensure the order of the receiving end as much as possible when forwarding in batches one by one
  1180. */
  1181. [NSThread sleepForTimeInterval:timeInterval];
  1182. }
  1183. }
  1184. fail:^(int code, NSString *desc) {
  1185. NSLog(@"%@", desc);
  1186. NSAssert(NO, desc);
  1187. }];
  1188. }
  1189. - (NSString *)forwardTitleWithMyName:(NSString *)nameStr {
  1190. return @"";
  1191. }
  1192. #pragma mark - Message reply
  1193. - (void)messageController:(TUIBaseMessageController *)controller onRelyMessage:(nonnull TUIMessageCellData *)data {
  1194. @weakify(self);
  1195. [self.inputController exitReplyAndReference:^{
  1196. @strongify(self);
  1197. NSString *desc = @"";
  1198. desc = [self replyReferenceMessageDesc:data];
  1199. TUIReplyPreviewData *replyData = [[TUIReplyPreviewData alloc] init];
  1200. replyData.msgID = data.msgID;
  1201. replyData.msgAbstract = desc;
  1202. replyData.sender = data.senderName;
  1203. replyData.type = (NSInteger)data.innerMessage.elemType;
  1204. replyData.originMessage = data.innerMessage;
  1205. NSMutableDictionary *cloudResultDic = [[NSMutableDictionary alloc] initWithCapacity:5];
  1206. if (replyData.originMessage.cloudCustomData) {
  1207. NSDictionary *originDic = [TUITool jsonData2Dictionary:replyData.originMessage.cloudCustomData];
  1208. if (originDic && [originDic isKindOfClass:[NSDictionary class]]) {
  1209. [cloudResultDic addEntriesFromDictionary:originDic];
  1210. }
  1211. }
  1212. NSString *messageParentReply = cloudResultDic[@"messageReply"];
  1213. NSString *messageRootID = [messageParentReply valueForKey:@"messageRootID"];
  1214. if (!IS_NOT_EMPTY_NSSTRING(messageRootID)) {
  1215. /**
  1216. * If the original message does not have messageRootID, you need to make the msgID of the current original message as the root
  1217. */
  1218. if (IS_NOT_EMPTY_NSSTRING(replyData.originMessage.msgID)) {
  1219. messageRootID = replyData.originMessage.msgID;
  1220. }
  1221. }
  1222. replyData.messageRootID = messageRootID;
  1223. [self.inputController showReplyPreview:replyData];
  1224. }];
  1225. }
  1226. - (NSString *)replyReferenceMessageDesc:(TUIMessageCellData *)data {
  1227. NSString *desc = @"";
  1228. if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_FILE) {
  1229. desc = data.innerMessage.fileElem.filename;
  1230. } else if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_MERGER) {
  1231. desc = data.innerMessage.mergerElem.title;
  1232. } else if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_CUSTOM) {
  1233. desc = [TUIMessageDataProvider getDisplayString:data.innerMessage];
  1234. } else if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_TEXT) {
  1235. desc = data.innerMessage.textElem.text;
  1236. }
  1237. return desc;
  1238. }
  1239. #pragma mark - Message quote
  1240. - (void)messageController:(TUIBaseMessageController *)controller onReferenceMessage:(TUIMessageCellData *)data {
  1241. @weakify(self);
  1242. [self.inputController exitReplyAndReference:^{
  1243. @strongify(self);
  1244. NSString *desc = @"";
  1245. desc = [self replyReferenceMessageDesc:data];
  1246. TUIReferencePreviewData *referenceData = [[TUIReferencePreviewData alloc] init];
  1247. referenceData.msgID = data.msgID;
  1248. referenceData.msgAbstract = desc;
  1249. referenceData.sender = data.senderName;
  1250. referenceData.type = (NSInteger)data.innerMessage.elemType;
  1251. referenceData.originMessage = data.innerMessage;
  1252. [self.inputController showReferencePreview:referenceData];
  1253. }];
  1254. }
  1255. #pragma mark - Message translation forward
  1256. - (void)messageController:(TUIBaseMessageController *)controller onForwardText:(NSString *)text {
  1257. if (text.length == 0) {
  1258. return;
  1259. }
  1260. [self selectTarget:NO toForwardMessage:nil orForwardText:text];
  1261. }
  1262. - (void)forwardText:(NSString *)text toConverations:(NSArray<TUIChatConversationModel *> *)conversations {
  1263. TUISendMessageAppendParams *appendParams = [[TUISendMessageAppendParams alloc] init];
  1264. appendParams.isSendPushInfo = YES;
  1265. appendParams.isOnlineUserOnly = NO;
  1266. appendParams.priority = V2TIM_PRIORITY_NORMAL;
  1267. for (TUIChatConversationModel *conversation in conversations) {
  1268. V2TIMMessage *message = [[V2TIMManager sharedInstance] createTextMessage:text];
  1269. dispatch_async(dispatch_get_main_queue(), ^{
  1270. if ([conversation.conversationID isEqualToString:self.conversationData.conversationID]) {
  1271. // Send forward text to myself
  1272. [self.messageController sendMessage:message];
  1273. } else {
  1274. // Send to other conversation
  1275. message.needReadReceipt = self.conversationData.msgNeedReadReceipt && [TUIChatConfig defaultConfig].msgNeedReadReceipt;
  1276. [TUIMessageDataProvider sendMessage:message
  1277. toConversation:conversation
  1278. appendParams:appendParams
  1279. Progress:nil
  1280. SuccBlock:^{
  1281. [NSNotificationCenter.defaultCenter postNotificationName:TUIKitNotification_onMessageStatusChanged object:message];
  1282. }
  1283. FailBlock:^(int code, NSString *desc) {
  1284. [NSNotificationCenter.defaultCenter postNotificationName:TUIKitNotification_onMessageStatusChanged object:message];
  1285. }];
  1286. }
  1287. });
  1288. }
  1289. }
  1290. #pragma mark - Private Methods
  1291. + (void)createCachePath {
  1292. NSFileManager *fileManager = [NSFileManager defaultManager];
  1293. if (![fileManager fileExistsAtPath:TUIKit_Image_Path]) {
  1294. [fileManager createDirectoryAtPath:TUIKit_Image_Path withIntermediateDirectories:YES attributes:nil error:nil];
  1295. }
  1296. if (![fileManager fileExistsAtPath:TUIKit_Video_Path]) {
  1297. [fileManager createDirectoryAtPath:TUIKit_Video_Path withIntermediateDirectories:YES attributes:nil error:nil];
  1298. }
  1299. if (![fileManager fileExistsAtPath:TUIKit_Voice_Path]) {
  1300. [fileManager createDirectoryAtPath:TUIKit_Voice_Path withIntermediateDirectories:YES attributes:nil error:nil];
  1301. }
  1302. if (![fileManager fileExistsAtPath:TUIKit_File_Path]) {
  1303. [fileManager createDirectoryAtPath:TUIKit_File_Path withIntermediateDirectories:YES attributes:nil error:nil];
  1304. }
  1305. if (![fileManager fileExistsAtPath:TUIKit_DB_Path]) {
  1306. [fileManager createDirectoryAtPath:TUIKit_DB_Path withIntermediateDirectories:YES attributes:nil error:nil];
  1307. }
  1308. }
  1309. #pragma mark - TUIJoinGroupMessageCellDelegate
  1310. - (void)didTapOnRestNameLabel:(TUIJoinGroupMessageCell *)cell withIndex:(NSInteger)index {
  1311. NSString *userId = cell.joinData.userIDList[index];
  1312. [self getUserOrFriendProfileVCWithUserID:userId
  1313. succBlock:^(UIViewController *vc) {
  1314. [self.navigationController pushViewController:vc animated:YES];
  1315. }
  1316. failBlock:^(int code, NSString *desc) {
  1317. [TUITool makeToastError:code msg:desc];
  1318. }];
  1319. }
  1320. #pragma mark - V2TIMConversationListener
  1321. - (void)onConversationChanged:(NSArray<V2TIMConversation *> *)conversationList {
  1322. for (V2TIMConversation *conv in conversationList) {
  1323. if ([conv.conversationID isEqualToString:self.conversationData.conversationID]) {
  1324. if (!self.conversationData.otherSideTyping) {
  1325. self.conversationData.title = conv.showName;
  1326. }
  1327. break;
  1328. }
  1329. }
  1330. }
  1331. #pragma mark - FriendInfoChangedNotification
  1332. - (void)onFriendInfoChanged:(NSNotification *)notice {
  1333. [self checkTitle:YES];
  1334. }
  1335. #pragma mark - Media Provider
  1336. - (void)sendPlaceHolderUIMessage:(TUIMessageCellData *)cellData {
  1337. [self.messageController sendPlaceHolderUIMessage:cellData];
  1338. }
  1339. - (TUIChatMediaDataProvider *)mediaProvider {
  1340. if (_mediaProvider == nil) {
  1341. _mediaProvider = [[TUIChatMediaDataProvider alloc] init];
  1342. _mediaProvider.listener = self;
  1343. _mediaProvider.presentViewController = self;
  1344. _mediaProvider.conversationID = _conversationData.conversationID;
  1345. }
  1346. return _mediaProvider;
  1347. }
  1348. - (void)onProvideImage:(NSString *)imageUrl {
  1349. V2TIMMessage *message = [V2TIMManager.sharedInstance createImageMessage:imageUrl];
  1350. [self sendMessage:message];
  1351. }
  1352. - (void)onProvideImageError:(NSString *)errorMessage {
  1353. [TUITool makeToast:errorMessage];
  1354. }
  1355. - (void)onProvidePlaceholderVideoSnapshot:(NSString *)snapshotUrl
  1356. SnapImage:(UIImage *)image
  1357. Completion:(void (^__nullable)(BOOL finished, TUIMessageCellData *placeHolderCellData))completion {
  1358. TUIMessageCellData *videoCellData = [TUIVideoMessageCellData placeholderCellDataWithSnapshotUrl:snapshotUrl thubImage:image];
  1359. [self.messageController sendPlaceHolderUIMessage:videoCellData];
  1360. if (completion) {
  1361. completion(YES,videoCellData);
  1362. }
  1363. }
  1364. - (void)onProvideVideo:(NSString *)videoUrl
  1365. snapshot:(NSString *)snapshotUrl
  1366. duration:(NSInteger)duration
  1367. placeHolderCellData:(TUIMessageCellData *)placeHolderCellData {
  1368. V2TIMMessage *message = [V2TIMManager.sharedInstance createVideoMessage:videoUrl
  1369. type:videoUrl.pathExtension
  1370. duration:(int)duration
  1371. snapshotPath:snapshotUrl];
  1372. [self sendMessage:message placeHolderCellData:placeHolderCellData];
  1373. }
  1374. - (void)onProvideVideoError:(NSString *)errorMessage {
  1375. [TUITool makeToast:errorMessage];
  1376. }
  1377. - (void)onProvideFile:(NSString *)fileUrl filename:(NSString *)filename fileSize:(NSInteger)fileSize {
  1378. V2TIMMessage *message = [V2TIMManager.sharedInstance createFileMessage:fileUrl fileName:filename];
  1379. [self sendMessage:message];
  1380. }
  1381. - (void)onProvideFileError:(NSString *)errorMessage {
  1382. [TUITool makeToast:errorMessage];
  1383. }
  1384. - (NSString *)currentConversationID {
  1385. return self.conversationData.conversationID;
  1386. }
  1387. - (BOOL)isPageAppears {
  1388. return self.responseKeyboard;
  1389. }
  1390. @end