QMChatRoomViewController.m 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296
  1. //
  2. // QMChatRoomViewController.m
  3. // IMSDK-OC
  4. //
  5. // Created by HCF on 16/3/9.
  6. // Copyright © 2016年 HCF. All rights reserved.
  7. //
  8. #import "QMChatRoomViewController.h"
  9. #import "QMChatRoomInputView.h"
  10. #import "TCMessageManagerFaceView.h"
  11. #import "QMChatRoomMoreView.h"
  12. #import <QMChatSDK/QMChatSDK.h>
  13. #import <QMChatSDK/QMChatSDK-Swift.h>
  14. #import "QMRecordIndicatorView.h"
  15. #import "MJRefresh.h"
  16. #import "TZImagePickerController.h"
  17. #import "QMChatRoomShowImageController.h"
  18. #import "QMProfileManager.h"
  19. #import "QMTapGestureRecognizer.h"
  20. #import "QMChatRoomGuestBookViewController.h"
  21. #import "QMChatRoomRobotCell.h"
  22. #import "QMChatRoomTextCell.h"
  23. #import "QMChatRoomVoiceCell.h"
  24. #import "QMChatRoomImageCell.h"
  25. #import "QMChatRoomInvestigateCell.h"
  26. #import "QMChatRoomFileCell.h"
  27. #import "QMChatRoomIframeCell.h"
  28. #import "QMChatRoomCellFactory.h"
  29. #import "QMChatRoomRichTextCell.h"
  30. #import "QMChatRoomNoteCell.h"
  31. #import "QMChatRoomMp3Cell.h"
  32. #import "QMChatRoomCardCell.h"
  33. #import "QMAudioPlayer.h"
  34. #import "QMAudioRecorder.h"
  35. #import "SJVoiceTransform.h"
  36. //new
  37. #import "QMChatTileView.h"
  38. #import "QMFileManagerController.h"
  39. #import "QMAlert.h"
  40. #import "QMTextAttachment.h"
  41. #import "NSAttributedString+QMEmojiExtension.h"
  42. #import <Photos/Photos.h>
  43. #import "Reachability.h"
  44. #import "QMTextModel.h"
  45. /**
  46. 在线客服聊天界面
  47. */
  48. @interface QMChatRoomViewController ()<UITableViewDataSource, UITableViewDelegate, UITextViewDelegate, TCMessageManagerFaceViewDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate,UIAlertViewDelegate, AVAudioRecorderDelegate, QMKServiceDelegate, QMAudioRecorderDelegate> {
  49. NSMutableArray * _dataArray;
  50. NSArray * _investigateArray;
  51. int _dataNum;
  52. CGFloat _keyboardHigh;
  53. QMChatTileView *_titleView;
  54. NSTimer *breakTimer;
  55. NSTimer *breakTipTimer;
  56. NSTimer *backStatus;
  57. CGFloat _navHeight;
  58. NSString *_titleViewText;
  59. }
  60. @property (nonatomic, assign)float upProgress;
  61. @end
  62. @implementation QMChatRoomViewController
  63. #pragma mark - 生命周期
  64. // 注册通知
  65. -(instancetype)init {
  66. self = [super init];
  67. if (self) {
  68. // 建议使用willshow和willhide
  69. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
  70. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getNewReload:) name:CHATMSG_RELOAD object:nil];
  71. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(robotAction) name:ROBOT_SERVICE object:nil];
  72. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customOnline) name:CUSTOMSRV_ONLINE object:nil];
  73. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customOffline) name:CUSTOMSRV_OFFLINE object:nil];
  74. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customClaim) name:CUSTOMSRV_CLAIM object:nil];
  75. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customFinish) name:CUSTOMSRV_FINISH object:nil];
  76. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customQueue:) name:CUSTOMSRV_QUEUENUM object:nil];
  77. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customAgentMessage:) name:CUSTOMSRV_AGENT object:nil];
  78. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customInvestigate) name:CUSTOMSRV_INVESTIGATE object:nil];
  79. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customVIP) name:CUSTOMSRV_VIP object:nil];
  80. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customLeavemsg:) name:CUSTOMSRV_LEAVEMSG object:nil];
  81. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeCustomStatus) name:CUSTOMSRV_IMPORTING object:nil];
  82. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cbangeDrowMessageStatus:) name:CUSTOMSRV_DRAWMESSAGE object:nil];
  83. }
  84. return self;
  85. }
  86. // 基本配置
  87. - (void)viewWillAppear:(BOOL)animated {
  88. self.navigationController.navigationBar.barTintColor = [UIColor colorWithRed:246/255.0 green:248/255.0 blue:249/255.0 alpha:1.0];
  89. self.navigationController.navigationBar.tintColor = [UIColor colorWithRed:13/255.0 green:139/255.0 blue:249/255.0 alpha:1];
  90. [self.navigationController.navigationBar setTranslucent:NO];
  91. self.navigationController.interactivePopGestureRecognizer.delaysTouchesBegan = NO;
  92. }
  93. /**
  94. 开始会话
  95. peer: 技能组id 必传
  96. params: @{@"agent":@"8000"} 专属坐席
  97. @{@"customField":@{@"姓名":@"李三"}} 自定义字段
  98. */
  99. - (void)viewDidAppear:(BOOL)animated {
  100. if (self.isPush == NO) {
  101. __weak QMChatRoomViewController * myChatView = self;
  102. if (self.isOpenSchedule) {
  103. NSLog(@"sdk走日程管理方法");
  104. [QMConnect sdkBeginNewChatSessionSchedule: self.scheduleId processId: self.processId currentNodeId: self.currentNodeId entranceId: self.entranceId params: @{@"":@""} successBlock:^(BOOL remark) {
  105. NSLog(@"开始会话成功");
  106. dispatch_async(dispatch_get_main_queue(), ^{
  107. // 是否启动了评价功能
  108. if (remark == NO) {
  109. myChatView.addView.evaluateBtn.hidden = YES;
  110. myChatView.addView.evaluateLabel.hidden = YES;
  111. }else {
  112. myChatView.addView.evaluateBtn.hidden = NO;
  113. myChatView.addView.evaluateLabel.hidden = NO;
  114. }
  115. });
  116. } failBlock:^{
  117. NSLog(@"开始会话失败");
  118. }];
  119. }else{
  120. NSLog(@"sdk走技能组方法");
  121. [QMConnect sdkBeginNewChatSession:self.peerId params:@{@"customField":@{@"1111":@"2222",@"3333":@"4444"},@"agent":@"8129"} successBlock:^(BOOL remark) {
  122. NSLog(@"开始会话成功");
  123. dispatch_async(dispatch_get_main_queue(), ^{
  124. // 是否启动了评价功能
  125. if (remark == NO) {
  126. myChatView.addView.evaluateBtn.hidden = YES;
  127. myChatView.addView.evaluateLabel.hidden = YES;
  128. }else {
  129. myChatView.addView.evaluateBtn.hidden = NO;
  130. myChatView.addView.evaluateLabel.hidden = NO;
  131. }
  132. });
  133. } failBlock:^{
  134. NSLog(@"开始会话失败");
  135. }];
  136. }
  137. self.isPush = YES;
  138. }
  139. }
  140. - (void)viewDidLoad {
  141. [super viewDidLoad];
  142. self.view.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1];
  143. [self createUI];
  144. //判断是否是机器人(默认是关闭的)
  145. if (![QMConnect allowRobot]) {
  146. // [self insertCardInfoMessage];
  147. }
  148. /**
  149. 创建文件管理类
  150. name: 可随便填写
  151. password: 可随便填写
  152. */
  153. QMProfileManager *manger = [QMProfileManager sharedInstance];
  154. [manger loadProfile:@"name" password:@"123456"];
  155. // 网络监控
  156. Reachability *hostReach = [Reachability reachabilityForInternetConnection];
  157. hostReach.reachableBlock = ^(Reachability *reachability) {
  158. dispatch_async(dispatch_get_main_queue(), ^{
  159. // 网络连接
  160. });
  161. };
  162. hostReach.unreachableBlock = ^(Reachability *reachability) {
  163. dispatch_async(dispatch_get_main_queue(), ^{
  164. // 网络断开
  165. });
  166. };
  167. [hostReach startNotifier];
  168. [self getInvestigateData];
  169. _dataArray = [[NSMutableArray alloc]init];
  170. if (_dataNum == 0) {
  171. _dataNum = 10;
  172. }
  173. [self getData];
  174. [self.chatTableView reloadData];
  175. dispatch_async(dispatch_get_main_queue(), ^{
  176. [self scrollToEnd];
  177. });
  178. [self createNSTimer];
  179. self.heightCaches = [NSMutableDictionary dictionary];
  180. }
  181. - (void)viewWillDisappear:(BOOL)animated {
  182. if ([self.chatInputView.inputView isFirstResponder]) {
  183. [self.chatInputView.inputView resignFirstResponder];
  184. self.chatInputView.inputView.inputView = nil;
  185. }
  186. }
  187. - (void)viewDidDisappear:(BOOL)animated {
  188. NSLog(@"Appear");
  189. }
  190. // 移除通知
  191. - (void)dealloc {
  192. NSLog(@"销毁");
  193. [[NSNotificationCenter defaultCenter] removeObserver:self];
  194. }
  195. #pragma mark - 创建聊天室UI(消息列表、输入工具条、提示窗...)
  196. - (void)createUI {
  197. self.view.backgroundColor = [UIColor whiteColor];
  198. CGRect StatusRect = [[UIApplication sharedApplication] statusBarFrame];
  199. CGRect NavRect = self.navigationController.navigationBar.frame;
  200. _navHeight = StatusRect.size.height + NavRect.size.height;
  201. // 坐席信息提示
  202. _titleView = [[QMChatTileView alloc] initWithFrame: CGRectMake(0, 0, 150, 40)];
  203. _titleView.nameLabel.text = NSLocalizedString(@"title.people", nil);
  204. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.connection", nil);
  205. _titleView.intrinsicContentSize = CGSizeMake(150, 40);
  206. self.navigationItem.titleView = _titleView;
  207. // 消息列表
  208. self.chatTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight-kInputViewHeight-_navHeight) style:UITableViewStylePlain];
  209. self.chatTableView.delegate = self;
  210. self.chatTableView.dataSource = self;
  211. self.chatTableView.backgroundColor = [UIColor colorWithRed:240/255.0 green:240/255.0 blue:240/255.0 alpha:1];
  212. self.chatTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  213. self.chatTableView.estimatedRowHeight = 0;
  214. [self.view addSubview:self.chatTableView];
  215. __weak QMChatRoomViewController * myChatView = self;
  216. MJRefreshNormalHeader *mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
  217. [myChatView Refresh];
  218. }];
  219. [mj_header.lastUpdatedTimeLabel setHidden:true];
  220. self.chatTableView.mj_header = mj_header;
  221. // 输入工具条
  222. self.chatInputView = [[QMChatRoomInputView alloc] initWithFrame:CGRectMake(0, kScreenHeight-kInputViewHeight-_navHeight, kScreenWidth, kInputViewHeight)];
  223. [self.chatInputView.voiceButton addTarget:self action:@selector(voiceBtnAction:) forControlEvents:UIControlEventTouchUpInside];
  224. [self.chatInputView.faceButton addTarget:self action:@selector(faceBtnAction:) forControlEvents:UIControlEventTouchUpInside];
  225. [self.chatInputView.addButton addTarget:self action:@selector(addBtnAction:) forControlEvents:UIControlEventTouchUpInside];
  226. [self.chatInputView.RecordBtn addTarget:self action:@selector(cancelRecord:) forControlEvents:UIControlEventTouchUpOutside];
  227. [self.chatInputView.RecordBtn addTarget:self action:@selector(RecordBtnBegin:) forControlEvents:UIControlEventTouchDown];
  228. [self.chatInputView.RecordBtn addTarget:self action:@selector(RecordBtnEnd:) forControlEvents:UIControlEventTouchUpInside];
  229. [self.chatInputView.RecordBtn addTarget:self action:@selector(RecordBtnExit:) forControlEvents:UIControlEventTouchDragExit];
  230. [self.chatInputView.RecordBtn addTarget:self action:@selector(RecordBtnEnter:) forControlEvents:UIControlEventTouchDragEnter];
  231. self.chatInputView.inputView.delegate = self;
  232. [self.view addSubview:self.chatInputView];
  233. // 表情面板
  234. self.faceView = [[TCMessageManagerFaceView alloc] initWithFrame:CGRectMake(0, [UIScreen mainScreen].bounds.size.height, kScreenWidth, QM_IS_IPHONEX ? 250 : 216)];
  235. self.faceView.delegate = self;
  236. [self.faceView.sendButton addTarget:self action:@selector(sendBtnAction:) forControlEvents:UIControlEventTouchUpInside];
  237. // 扩展面板
  238. self.addView = [[QMChatRoomMoreView alloc] initWithFrame:CGRectMake(0, [UIScreen mainScreen].bounds.size.height, kScreenWidth, QM_IS_IPHONEX ? 144 : 110)];
  239. [self.addView.takePicBtn addTarget:self action:@selector(takePicBtnAction) forControlEvents:UIControlEventTouchUpInside];
  240. [self.addView.evaluateBtn addTarget:self action:@selector(evaluateBtnAction) forControlEvents:UIControlEventTouchUpInside];
  241. [self.addView.takeFileBtn addTarget:self action:@selector(takeFileBtnAction) forControlEvents:UIControlEventTouchUpInside];
  242. // 转人工
  243. self.manualButotn = [UIButton buttonWithType:UIButtonTypeSystem];
  244. self.manualButotn.frame = CGRectMake(0, 0, 60, 30);
  245. self.manualButotn.titleLabel.font = [UIFont systemFontOfSize:16];
  246. [self.manualButotn setTitle: NSLocalizedString(@"button.topeople", nil) forState:UIControlStateNormal];
  247. [self.manualButotn setTitleColor:[UIColor colorWithRed:13/255.0 green:139/255.0 blue:249/255.0 alpha:1] forState:UIControlStateNormal];
  248. [self.manualButotn addTarget:self action:@selector(customClick) forControlEvents:UIControlEventTouchUpInside];
  249. if ([QMConnect allowRobot]) {
  250. self.isRobot = true;
  251. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self.manualButotn];
  252. }else {
  253. self.isRobot = false;
  254. }
  255. // 注销
  256. self.logoutButton = [UIButton buttonWithType:UIButtonTypeSystem];
  257. self.logoutButton.frame = CGRectMake(0, 0, 50, 30);
  258. self.logoutButton.titleLabel.font = [UIFont systemFontOfSize:16];
  259. [self.logoutButton setTitle:NSLocalizedString(@"button.logout", nil) forState:UIControlStateNormal];
  260. [self.logoutButton setTitleColor:[UIColor colorWithRed:13/255.0 green:139/255.0 blue:249/255.0 alpha:1] forState:UIControlStateNormal];
  261. [self.logoutButton addTarget:self action:@selector(logoutAction) forControlEvents:UIControlEventTouchUpInside];
  262. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self.logoutButton];
  263. self.indicatorView = [[QMRecordIndicatorView alloc] init];
  264. self.indicatorView.frame = CGRectMake((kScreenWidth-150)/2, (kScreenHeight-150-_navHeight-50)/2, 150, 150);
  265. UITapGestureRecognizer * gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard)];
  266. [self.chatTableView addGestureRecognizer:gestureRecognizer];
  267. gestureRecognizer.cancelsTouchesInView = NO;
  268. }
  269. - (void)hideKeyboard {
  270. [self.chatInputView.inputView resignFirstResponder];
  271. self.chatInputView.inputView.inputView = nil;
  272. }
  273. #pragma mark - 获取数据(数据模型已存储本地)
  274. // 获取消息数据
  275. - (void)getData {
  276. _dataArray = [NSMutableArray arrayWithArray:[QMConnect getDataFromDatabase:_dataNum]];
  277. /**
  278. 获取同一个accessid(AppKey)下的全部信息 用下面此接口
  279. _dataArray = [NSMutableArray arrayWithArray:[QMConnect getAccessidAllDataFormDatabase:_dataNum]];
  280. */
  281. /**
  282. 获取同一个userId下的全部信息 用下面此接口
  283. _dataArray = [NSMutableArray arrayWithArray:[QMConnect getUserIdDataFormDatabase:_dataNum]];
  284. */
  285. }
  286. // 获取后台配置信息 、 满意度调查 、回复超时时间
  287. - (void)getInvestigateData {
  288. [QMConnect sdkGetInvestigate:^(NSArray * _Nonnull investigateArray) {
  289. dispatch_async(dispatch_get_main_queue(), ^{
  290. _investigateArray = investigateArray;
  291. });
  292. } failureBlock:^{
  293. dispatch_async(dispatch_get_main_queue(), ^{
  294. _investigateArray = [NSArray array];
  295. });
  296. }];
  297. }
  298. // 刷新TableView
  299. -(void)reloadTableView {
  300. if (_titleViewText != nil) {
  301. _titleView.stateInfoLabel.text = _titleViewText;
  302. }
  303. NSLog(@"刷新信息");
  304. [self.chatTableView reloadData];
  305. dispatch_async(dispatch_get_main_queue(), ^{
  306. [self scrollToEnd];
  307. });
  308. }
  309. /**
  310. 新消息通知、发送和接收的消息 都会走这个通知 统一刷新聊天界面
  311. */
  312. - (void)getNewReload: (NSNotification *)sender {
  313. NSLog(@"获取到新消息 %@", sender.object);
  314. [self getData];
  315. [self reloadTableView];
  316. if (backStatus.isValid) {
  317. [backStatus invalidate];
  318. }
  319. }
  320. // 滑动到底部
  321. - (void)scrollToEnd {
  322. if (_dataArray.count>0) {
  323. [_chatTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:_dataArray.count-1 inSection:0] atScrollPosition:UITableViewScrollPositionNone animated:NO];
  324. }
  325. }
  326. // 下拉刷新
  327. - (void)Refresh {
  328. if (_dataNum>_dataArray.count) {
  329. }
  330. _dataNum = _dataNum+10;
  331. [self getData];
  332. [_chatTableView reloadData];
  333. [self.chatTableView.mj_header endRefreshing];
  334. }
  335. //商品信息的卡片(默认是关闭的,需要手动打开注释)
  336. - (void)insertCardInfoMessage {
  337. [QMConnect deleteCardTypeMessage];
  338. NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
  339. [dic setObject:@"https://fs-km.7moor.com/N00000000143/km/2018-06-15/1529044921332/35522b40-7067-11e8-ab29-a3347bfc2358" forKey:@"cardImage"]; //此参数需要填写的是URL
  340. [dic setObject:@"标题" forKey:@"cardHeader"];
  341. [dic setObject:@"副标题" forKey:@"cardSubhead"];
  342. [dic setObject:@"价格" forKey:@"cardPrice"];
  343. [dic setObject:@"https://kf.7moor.com" forKey:@"cardUrl"]; //此参数需要填写的是URL
  344. [QMConnect insertCardInfoData:dic];
  345. [self getData];
  346. [self reloadTableView];
  347. }
  348. - (void)applicationWillResignActive {
  349. NSLog(@"退到后台");
  350. }
  351. - (void)applicationDidBecomeActive {
  352. NSLog(@"返回前台");
  353. }
  354. #pragma mark - TableViewDelegate TableViewDataSource
  355. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  356. return _dataArray.count;
  357. }
  358. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  359. CustomMessage * message = _dataArray[_dataArray.count-indexPath.row-1];
  360. NSString * identifier = @"";
  361. if ([message.messageType isEqualToString:@"text"]) {
  362. if ([message.isRobot isEqualToString:@"1"]) {
  363. identifier = NSStringFromClass([QMChatRoomRobotCell class]);
  364. }else {
  365. identifier = NSStringFromClass([QMChatRoomTextCell class]);
  366. }
  367. }else if ([message.messageType isEqualToString:@"voice"]) {
  368. identifier = NSStringFromClass([QMChatRoomVoiceCell class]);
  369. }else if ([message.messageType isEqualToString:@"image"]) {
  370. identifier = NSStringFromClass([QMChatRoomImageCell class]);
  371. }else if ([message.messageType isEqualToString:@"investigate"]) {
  372. identifier = NSStringFromClass([QMChatRoomInvestigateCell class]);
  373. }else if ([message.messageType isEqualToString:@"file"]) {
  374. if ([message.fileName.pathExtension.lowercaseString isEqual: @"mp3"]) {
  375. identifier = NSStringFromClass([QMChatRoomMp3Cell class]);//MP3文件以语音形式播放
  376. }else{
  377. identifier = NSStringFromClass([QMChatRoomFileCell class]);
  378. }
  379. }else if ([message.messageType isEqualToString:@"iframe"]) {
  380. identifier = NSStringFromClass([QMChatRoomIframeCell class]);
  381. }else if ([message.messageType isEqualToString:@"richText"]){
  382. identifier = NSStringFromClass([QMChatRoomRichTextCell class]);
  383. }else if ([message.messageType isEqualToString:@"withdrawMessage"]) {
  384. identifier = NSStringFromClass([QMChatRoomNoteCell class]);
  385. }else if ([message.messageType isEqualToString:@"card"]){
  386. identifier = NSStringFromClass([QMChatRoomCardCell class]);
  387. }else if ([message.messageType isEqualToString:@"cardInfo"]) {
  388. identifier = NSStringFromClass([QMChatRoomRichTextCell class]);
  389. }else {
  390. identifier = NSStringFromClass([QMChatRoomTextCell class]);
  391. }
  392. QMChatRoomBaseCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
  393. if (cell == nil) {
  394. cell = [QMChatRoomCellFactory createCellWithClassName:identifier cellModel:message indexPath:indexPath];
  395. }
  396. if (indexPath.row>0) {
  397. CustomMessage * preMessage = _dataArray[_dataArray.count-indexPath.row];
  398. UInt64 disTime = message.createdTime.longLongValue - preMessage.createdTime.longLongValue;
  399. if (disTime<3*60*1000) {
  400. cell.timeLabel.hidden = YES;
  401. }else {
  402. cell.timeLabel.hidden = NO;
  403. }
  404. }else {
  405. cell.timeLabel.hidden = NO;
  406. }
  407. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  408. // 如果需要设置头像、该方法加上一个头像路径参数
  409. [cell setData:message avater:self.avaterStr];
  410. if ([message.messageType isEqualToString:@"file"]) {
  411. [cell setProgress: self.upProgress];
  412. }
  413. if ([message.messageType isEqualToString:@"text"]) {
  414. cell.tapNetAddress = ^(NSString *address) {
  415. if ([address rangeOfString:@"http://"].location == NSNotFound && [address rangeOfString:@"https://"].location == NSNotFound) {
  416. address = [NSString stringWithFormat:@"http://%@", address];
  417. }
  418. [[UIApplication sharedApplication] openURL:[NSURL URLWithString:address]];
  419. };
  420. }
  421. // 机器人消息可以选择问题代码 答案可以选择有帮助或无帮助
  422. if ([message.messageType isEqualToString:@"text"] && [message.isRobot isEqualToString:@"1"]) {
  423. __weak QMChatRoomViewController *weakSelf = self;
  424. cell.tapSendMessage = ^(NSString *text) {
  425. [weakSelf sendText:text];
  426. };
  427. cell.didBtnAction = ^(BOOL isUseful) {
  428. if (!message.isUseful||[message.isUseful isEqualToString:@"none"]) {
  429. if ([weakSelf.heightCaches objectForKey:message._id]) {
  430. [weakSelf.heightCaches removeObjectForKey:message._id];
  431. }
  432. [weakSelf sendRobotFeedback:isUseful questionId:message.questionId messageId:message._id robotType:message.robotType robotId:message.robotId];
  433. }
  434. };
  435. }
  436. return cell;
  437. }
  438. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
  439. CustomMessage * message = _dataArray[_dataArray.count-indexPath.row-1];
  440. if ([message.messageType isEqualToString:@"withdrawMessage"]) {
  441. }else if ([message.messageType isEqualToString:@"cardInfo"]) {
  442. }else{
  443. // 取已有cell高度 时间高度计算
  444. if ([self.heightCaches objectForKey:message._id] && indexPath.row != 0) {
  445. NSNumber *tmpHeight = [self.heightCaches objectForKey:message._id];
  446. return tmpHeight.doubleValue;
  447. }
  448. }
  449. CGFloat height = 15;
  450. if (indexPath.row>0) {
  451. CustomMessage * preMessage = _dataArray[_dataArray.count-indexPath.row];
  452. UInt64 disTime = message.createdTime.longLongValue - preMessage.createdTime.longLongValue;
  453. if (disTime<3*60*1000) {
  454. height = 15;
  455. }else {
  456. height = 45;
  457. }
  458. }else {
  459. height = 45;
  460. }
  461. if ([message.messageType isEqualToString:@"text"]) {
  462. if ([message.isRobot isEqualToString:@"1"]) {
  463. CGFloat robotHeight = [QMTextModel calcRobotHeight:message.message];
  464. height += (robotHeight+5+21);
  465. }else {
  466. CGFloat textHeight = [QMTextModel calcTextHeight:message.message];
  467. height += (textHeight+5+21);
  468. }
  469. height = height>65?height:65;
  470. if ([message.isRobot isEqualToString:@"1"] && ![message.questionId isEqualToString:@""]) {
  471. if (message.isUseful) {
  472. if ([message.isUseful isEqualToString:@"none"]) {
  473. height += 30;
  474. }else {
  475. height += 60;
  476. }
  477. }else {
  478. height += 30;
  479. }
  480. }
  481. }else if ([message.messageType isEqualToString:@"voice"]) {
  482. height += 45;
  483. }else if ([message.messageType isEqualToString:@"image"]) {
  484. height += 140;
  485. }else if ([message.messageType isEqualToString:@"investigate"]) {
  486. height += 145;
  487. }else if ([message.messageType isEqualToString:@"file"]) {
  488. height += 50;
  489. }else if ([message.messageType isEqualToString:@"iframe"]) {
  490. height += message.height.intValue+30;
  491. }else if ([message.messageType isEqualToString:@"richText"]) {
  492. height += 120;
  493. }else if ([message.messageType isEqualToString:@"withdrawMessage"]) {
  494. height = 45;
  495. }else if ([message.messageType isEqualToString:@"card"]) {
  496. height += 150;
  497. }else if ([message.messageType isEqualToString:@"cardInfo"]) {
  498. height += 80;
  499. }else {
  500. height += 45;
  501. }
  502. // 存储cell高度 如果高度有变化 请提前清除对应id的缓存
  503. NSNumber *number = [NSNumber numberWithDouble:height];
  504. [self.heightCaches setObject:number forKey:message._id];
  505. return height;
  506. }
  507. #pragma mark - InputView Action
  508. //切换录音按钮
  509. - (void)voiceBtnAction:(UIButton *)button {
  510. if (self.chatInputView.RecordBtn.hidden == YES) {
  511. [self.chatInputView showRecordButton:YES];
  512. [self.chatInputView.inputView endEditing:YES];
  513. }else {
  514. [self.chatInputView showRecordButton:NO];
  515. self.chatInputView.inputView.inputView = nil;
  516. [self.chatInputView.inputView becomeFirstResponder];
  517. [self.chatInputView.inputView reloadInputViews];
  518. }
  519. }
  520. //表情按钮
  521. - (void)faceBtnAction:(UIButton *)button {
  522. if (button.tag == 1) {
  523. [self.chatInputView showEmotionView:YES];
  524. self.chatInputView.inputView.inputView = self.faceView;
  525. }else {
  526. [self.chatInputView showEmotionView:NO];
  527. self.chatInputView.inputView.inputView = nil;
  528. }
  529. [self.chatInputView.inputView becomeFirstResponder];
  530. [self.chatInputView.inputView reloadInputViews];
  531. }
  532. //扩展功能按钮
  533. - (void)addBtnAction:(UIButton *)button {
  534. if (button.tag == 3) {
  535. [self.chatInputView showMoreView:YES];
  536. self.chatInputView.inputView.inputView = self.addView;
  537. [self.chatInputView.inputView becomeFirstResponder];
  538. [self.chatInputView.inputView reloadInputViews];
  539. }else {
  540. [self.chatInputView showMoreView:NO];
  541. self.chatInputView.inputView.inputView = nil;
  542. [self.chatInputView.inputView endEditing:YES];
  543. }
  544. }
  545. #pragma mark - Record Action
  546. // 开始录音
  547. - (void)RecordBtnBegin:(UIButton *)button {
  548. NSString *fileName = [[NSUUID new] UUIDString];
  549. // 验证权限
  550. AVAuthorizationStatus authorizationStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
  551. switch (authorizationStatus) {
  552. case AVAuthorizationStatusNotDetermined:
  553. [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
  554. }];
  555. break;
  556. case AVAuthorizationStatusAuthorized:
  557. self.indicatorView.isCount = false;
  558. [self.indicatorView changeViewStatus:QMIndicatorStatusNormal];
  559. [self.view addSubview:self.indicatorView];
  560. [self changeButtonStatus:YES];
  561. [[QMAudioRecorder sharedInstance] startAudioRecord:fileName maxDuration:60.0 delegate:self];
  562. break;
  563. case AVAuthorizationStatusRestricted:
  564. NSLog(@"麦克风访问受限!");
  565. break;
  566. case AVAuthorizationStatusDenied:
  567. NSLog(@"设置允许访问麦克风");
  568. break;
  569. }
  570. }
  571. // 结束录音
  572. - (void)RecordBtnEnd:(UIButton *)button {
  573. [[QMAudioRecorder sharedInstance] stopAudioRecord];
  574. }
  575. // 取消录音
  576. - (void)cancelRecord: (UIButton *)button {
  577. [[QMAudioRecorder sharedInstance] cancelAudioRecord];
  578. [self.indicatorView removeFromSuperview];
  579. [self changeButtonStatus:NO];
  580. }
  581. - (void)RecordBtnExit: (UIButton *)button {
  582. [self.indicatorView changeViewStatus:QMIndicatorStatusCancel];
  583. }
  584. - (void)RecordBtnEnter: (UIButton *)button {
  585. [self.indicatorView changeViewStatus:QMIndicatorStatusNormal];
  586. }
  587. // 更改按钮状态
  588. - (void)changeButtonStatus:(BOOL)down {
  589. if (down == YES) {
  590. [self.chatInputView.RecordBtn setTitle:NSLocalizedString(@"button.recorder_recording", nil) forState:UIControlStateNormal];
  591. [self.chatInputView.RecordBtn setTitleColor:[UIColor colorWithRed:50/255.0f green:167/255.0f blue:255/255.0f alpha:1.0] forState:UIControlStateNormal];
  592. self.chatInputView.RecordBtn.layer.borderColor = [[UIColor colorWithRed:50/255.0f green:167/255.0f blue:255/255.0f alpha:1.0] CGColor];
  593. [self.chatInputView.RecordBtn setTintColor:[UIColor colorWithRed:50/255.0f green:167/255.0f blue:255/255.0f alpha:1.0]];
  594. }else {
  595. [self.chatInputView.RecordBtn setTitle:NSLocalizedString(@"button.recorder_normal", nil) forState:UIControlStateNormal];
  596. [self.chatInputView.RecordBtn setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
  597. self.chatInputView.RecordBtn.layer.borderColor = [[UIColor grayColor] CGColor];
  598. }
  599. }
  600. - (void)audioRecorderStart {
  601. }
  602. - (void)audioRecorderCompletion:(NSString *)fileName duration:(NSString *)duration {
  603. NSString * path = [NSString stringWithFormat:@"%@/%@/%@",NSHomeDirectory(),@"Documents",fileName];
  604. [SJVoiceTransform stransformToMp3ByUrlWithUrl:path];
  605. [self sendAudio:fileName duration:duration];
  606. if (duration.intValue >= 60) {
  607. [self.indicatorView changeViewStatus:QMIndicatorStatusLong];
  608. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  609. [self.indicatorView removeFromSuperview];
  610. [self changeButtonStatus:NO];
  611. });
  612. }else {
  613. [self.indicatorView removeFromSuperview];
  614. [self changeButtonStatus:NO];
  615. }
  616. }
  617. - (void)audioRecorderChangeInTimer:(NSTimeInterval)power total:(int)count {
  618. [self.indicatorView updateImageWithPower:power];
  619. self.indicatorView.count = count;
  620. }
  621. - (void)audioRecorderCancel {
  622. [self.indicatorView removeFromSuperview];
  623. [self changeButtonStatus:NO];
  624. }
  625. - (void)audioRecorderFail {
  626. [self.indicatorView changeViewStatus:QMIndicatorStatusShort];
  627. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  628. [self.indicatorView removeFromSuperview];
  629. [self changeButtonStatus:NO];
  630. });
  631. }
  632. #pragma mark - MoreView Action
  633. //通过摄像头获取图片
  634. - (void)photoBtnAction {
  635. if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
  636. UIImagePickerController * imagePicker = [[UIImagePickerController alloc] init];
  637. imagePicker.delegate = self;
  638. imagePicker.allowsEditing = NO;
  639. imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
  640. [self presentViewController:imagePicker animated:YES completion:nil];
  641. }
  642. }
  643. //相机代理方法
  644. - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
  645. if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
  646. UIImage * myImage = [info objectForKey:UIImagePickerControllerOriginalImage];
  647. [picker dismissViewControllerAnimated:YES completion:nil];
  648. UIImageWriteToSavedPhotosAlbum(myImage, nil, nil, nil);
  649. [self sendImage:myImage];
  650. }
  651. }
  652. //从相册获取图片
  653. - (void)takePicBtnAction {
  654. TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:5 delegate:nil];
  655. [imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isStop) {
  656. for (UIImage *image in photos) {
  657. [self sendImage:image];
  658. }
  659. }];
  660. [self presentViewController:imagePickerVc animated:YES completion:nil];
  661. }
  662. // 获取文件
  663. - (void)takeFileBtnAction {
  664. QMFileManagerController * fileViewController = [[QMFileManagerController alloc] init];
  665. [self.navigationController pushViewController:fileViewController animated:true];
  666. }
  667. // 满意度评价
  668. - (void)evaluateBtnAction {
  669. UIAlertController * investigateAlertView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"button.chat_evaluate", nil) message:nil preferredStyle:UIAlertControllerStyleAlert];
  670. for (NSDictionary * index in _investigateArray) {
  671. UIAlertAction * action = [UIAlertAction actionWithTitle:[index objectForKey:@"name"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  672. [QMConnect sdkSubmitInvestigate:[index objectForKey:@"name"] value:[index objectForKey:@"value"] successBlock:^{
  673. NSLog(@"评价成功");
  674. } failBlock:^{
  675. NSLog(@"评价失败");
  676. }];
  677. }];
  678. [investigateAlertView addAction:action];
  679. }
  680. UIAlertAction * cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"button.cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
  681. }];
  682. [investigateAlertView addAction:cancelAction];
  683. [self presentViewController:investigateAlertView animated:YES completion:nil];
  684. }
  685. #pragma mark - Send Message
  686. // 发送文本
  687. - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
  688. if ([text isEqual:@"\n"]) {
  689. if (![_chatInputView.inputView.text isEqualToString:@""]) {
  690. NSString *text = [_chatInputView.inputView.textStorage getRichString];
  691. [self sendText:text];
  692. _chatInputView.inputView.text = @"";
  693. return NO;
  694. }
  695. return NO;
  696. }
  697. return YES;
  698. }
  699. //发送表情
  700. - (void)sendBtnAction:(UIButton *)button {
  701. if (![_chatInputView.inputView.text isEqualToString:@""]) {
  702. NSString *text = [_chatInputView.inputView.textStorage getRichString];
  703. [self sendText:text];
  704. _chatInputView.inputView.text = @"";
  705. }
  706. }
  707. - (void)sendText:(NSString *)text {
  708. [QMConnect sendMsgText:text successBlock:^{
  709. NSLog(@"发送成功");
  710. dispatch_async(dispatch_get_main_queue(), ^{
  711. [self createNSTimer];
  712. });
  713. } failBlock:^{
  714. NSLog(@"发送失败");
  715. }];
  716. }
  717. // 发送图片
  718. - (void)sendImage:(UIImage *)image {
  719. [QMConnect sendMsgPic:image successBlock:^{
  720. NSLog(@"图片发送成功");
  721. dispatch_async(dispatch_get_main_queue(), ^{
  722. [self createNSTimer];
  723. });
  724. } failBlock:^{
  725. NSLog(@"图片发送失败");
  726. }];
  727. }
  728. // 发送语音
  729. - (void)sendAudio:(NSString *)fileName duration:(NSString *)duration {
  730. NSString *filePath = [NSString stringWithFormat:@"%@.mp3", fileName];
  731. [QMConnect sendMsgAudio:filePath duration:duration successBlock:^{
  732. NSLog(@"语音发送成功");
  733. dispatch_async(dispatch_get_main_queue(), ^{
  734. [self createNSTimer];
  735. });
  736. } failBlock:^(NSString * _Nonnull reason) {
  737. NSLog(@"语音发送失败");
  738. }];
  739. }
  740. // 发送文件消息
  741. - (void)sendFileMessageWithName:(NSString *)fileName AndSize:(NSString *)fileSize AndPath:(NSString *)filePath {
  742. [QMConnect sendMsgFile:fileName filePath:filePath fileSize:fileSize progressHander:^(float progress) {
  743. dispatch_async(dispatch_get_main_queue(), ^{
  744. self.upProgress = progress;
  745. [self.chatTableView reloadData];
  746. });
  747. } successBlock:^{
  748. NSLog(@"文件上传成功");
  749. dispatch_async(dispatch_get_main_queue(), ^{
  750. [self createNSTimer];
  751. self.upProgress = 0;
  752. });
  753. } failBlock:^{
  754. NSLog(@"文件上传失败");
  755. dispatch_async(dispatch_get_main_queue(), ^{
  756. self.upProgress = 0;
  757. });
  758. }];
  759. }
  760. // 失败消息重新发送
  761. - (void)resendAction:(QMTapGestureRecognizer *)gestureRecognizer {
  762. NSArray * dataArray = [[NSArray alloc] init];
  763. dataArray = [QMConnect getOneDataFromDatabase:gestureRecognizer.messageId];
  764. for (CustomMessage * custom in dataArray) {
  765. [QMConnect resendMessage:custom successBlock:^{
  766. NSLog(@"重新发送成功");
  767. dispatch_async(dispatch_get_main_queue(), ^{
  768. [self createNSTimer];
  769. });
  770. } failBlock:^{
  771. NSLog(@"重新发送失败");
  772. }];
  773. }
  774. }
  775. // 表情代理及相关处理
  776. - (void)SendTheFaceStr:(NSString *)faceStr isDelete:(BOOL)dele {
  777. if (dele) {
  778. [_chatInputView.inputView deleteBackward];
  779. }else {
  780. [self insertEmoji:faceStr];
  781. }
  782. }
  783. - (void)insertEmoji: (NSString *)code {
  784. QMTextAttachment * emojiTextAttemt = [QMTextAttachment new];
  785. NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"QMEmoticon" ofType:@"bundle"];
  786. NSString *fileName = [[NSBundle mainBundle] pathForResource:@"expressionImage" ofType:@"plist"];
  787. NSDictionary *plistDict = [NSDictionary dictionaryWithContentsOfFile:fileName];
  788. if ([plistDict objectForKey:code] != nil) {
  789. emojiTextAttemt.emojiCode = code;
  790. emojiTextAttemt.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@/%@", bundlePath, [plistDict objectForKey:code]]];
  791. emojiTextAttemt.bounds = CGRectMake(0, 0, 18, 18);
  792. NSAttributedString * attributeString = [NSAttributedString attributedStringWithAttachment:emojiTextAttemt];
  793. NSRange range = [_chatInputView.inputView selectedRange];
  794. if (range.length > 0) {
  795. [_chatInputView.inputView.textStorage deleteCharactersInRange:range];
  796. }
  797. [_chatInputView.inputView.textStorage insertAttributedString:attributeString atIndex:[_chatInputView.inputView selectedRange].location];
  798. _chatInputView.inputView.selectedRange = NSMakeRange(_chatInputView.inputView.selectedRange.location+1, 0);
  799. }
  800. [self resetTextStyle];
  801. }
  802. - (void)resetTextStyle {
  803. NSRange wholeRange = NSMakeRange(0, _chatInputView.inputView.textStorage.length);
  804. [_chatInputView.inputView.textStorage removeAttribute:NSFontAttributeName range:wholeRange];
  805. [_chatInputView.inputView.textStorage addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:18] range:wholeRange];
  806. _chatInputView.inputView.font = [UIFont systemFontOfSize:18];
  807. }
  808. // 机器人帮助评价
  809. - (void)sendRobotFeedback: (BOOL)isUseful questionId: (NSString *)questionId messageId: (NSString *)messageId robotType: (NSString *)robotType robotId: (NSString *)robotId {
  810. __weak QMChatRoomViewController *weakSelf = self;
  811. [QMConnect sdkSubmitRobotFeedback:isUseful questionId:questionId messageId:messageId robotType:robotType robotId:robotId successBlock:^{
  812. dispatch_async(dispatch_get_main_queue(), ^{
  813. [weakSelf getData];
  814. [weakSelf.chatTableView reloadData];
  815. });
  816. } failBlock:^{
  817. }];
  818. }
  819. #pragma mark - Multi Function
  820. // 注销事件
  821. - (void)logoutAction {
  822. [[QMAudioPlayer sharedInstance] stopAudioPlayer];
  823. [QMConnect logout];
  824. [self removeTimer];
  825. [self.navigationController dismissViewControllerAnimated:YES completion:nil];
  826. }
  827. // 转人工客服
  828. - (void)customClick {
  829. [QMConnect sdkConvertManual:^{
  830. NSLog(@"转人工客服成功");
  831. // [self insertCardInfoMessage];
  832. } failBlock:^{
  833. NSLog(@"转人工客服失败");
  834. dispatch_async(dispatch_get_main_queue(), ^{
  835. if (self.isOpenSchedule == false) {
  836. [self showGuestBookViewController];
  837. }
  838. });
  839. }];
  840. }
  841. #pragma mark - 留言提示
  842. - (void)showGuestBookViewController {
  843. [self.chatInputView setHidden:true];
  844. [self.manualButotn setHidden:true];
  845. self.chatTableView.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight - _navHeight);
  846. self.msg = [QMConnect leaveMessageAlert];
  847. if ([self.msg isEqualToString:@""]) {
  848. self.msg = NSLocalizedString(@"title.messageprompts", nil);
  849. }
  850. if ([QMConnect allowedLeaveMessage]) {
  851. UIAlertController *alertView = [UIAlertController alertControllerWithTitle:@"" message: self.msg preferredStyle:UIAlertControllerStyleAlert];
  852. UIAlertAction *sureAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"button.leaveMessage", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  853. QMChatRoomGuestBookViewController *guestBookViewController = [[QMChatRoomGuestBookViewController alloc] init];
  854. guestBookViewController.peerId = self.peerId;
  855. [self.navigationController pushViewController:guestBookViewController animated:YES];
  856. }];
  857. UIAlertAction *cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"button.signOut", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
  858. [self logoutAction];
  859. }];
  860. [alertView addAction:sureAction];
  861. [alertView addAction:cancel];
  862. [self presentViewController:alertView animated:YES completion:nil];
  863. }else {
  864. UIAlertController *alertView = [UIAlertController alertControllerWithTitle:@"" message: self.msg preferredStyle:UIAlertControllerStyleAlert];
  865. UIAlertAction *cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"title.iknow", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
  866. }];
  867. [alertView addAction:cancel];
  868. [self presentViewController:alertView animated:YES completion:nil];
  869. }
  870. }
  871. #pragma mark - 客服代理方法
  872. /// 当前客服状态
  873. - (void)currentAgentStatusWithStatus:(enum QMKStatus)status {
  874. switch (status) {
  875. case 0:
  876. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.now_robit", nil);
  877. self.manualButotn.hidden = NO;
  878. break;
  879. case 1:
  880. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_now", nil);
  881. self.manualButotn.hidden = YES;
  882. break;
  883. case 2:
  884. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_isline", nil);
  885. self.manualButotn.hidden = NO;
  886. break;
  887. case 3:
  888. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_now", nil);
  889. self.manualButotn.hidden = YES;
  890. break;
  891. case 4:
  892. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_isleave", nil);
  893. self.manualButotn.hidden = YES;
  894. break;
  895. default:
  896. break;
  897. }
  898. }
  899. /// 当前坐席信息
  900. - (void)currentAgentInfoWithAgent:(QMAgent * _Nonnull)agent {
  901. NSString *string = [NSString stringWithFormat:@"%@(%@)", agent.name, agent.exten];
  902. string = [string stringByReplacingOccurrencesOfString:@"\n" withString:@""];
  903. _titleView.nameLabel.text = [NSString stringWithFormat:@"%@",string];
  904. }
  905. /// 当前会话排队数
  906. - (void)currentSessionWaitNumberWithNumber:(NSInteger)number {
  907. _titleView.stateInfoLabel.text = [NSString stringWithFormat:@"%@: %ld",NSLocalizedString(@"title.line_up", nil), (long)number];
  908. self.manualButotn.hidden = YES;
  909. }
  910. /// 邀请评价
  911. - (void)inviteEvaluate {
  912. UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"" message:NSLocalizedString(@"button.chat_evaluate", nil) preferredStyle:UIAlertControllerStyleActionSheet];
  913. for (NSDictionary *index in _investigateArray) {
  914. [alertController addAction:[UIAlertAction actionWithTitle:[index objectForKey:@"name"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  915. [QMConnect sdkSubmitInvestigate:[index objectForKey:@"name"] value:[index objectForKey:@"value"] successBlock:^{
  916. NSLog(@"评价成功");
  917. } failBlock:^{
  918. NSLog(@"评价失败");
  919. }];
  920. }]];
  921. };
  922. [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"button.cancel", nil) style:UIAlertActionStyleDestructive handler:nil]];
  923. [self presentViewController:alertController animated:true completion:nil];
  924. }
  925. #pragma mark - Push Notification
  926. // 键盘通知
  927. - (void)keyboardFrameChange: (NSNotification *)notification {
  928. NSDictionary * userInfo = notification.userInfo;
  929. NSValue * value = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
  930. CGRect newFrame = [value CGRectValue];
  931. if (newFrame.origin.y == [UIScreen mainScreen].bounds.size.height) {
  932. [UIView animateWithDuration:0.3 animations:^{
  933. self.chatInputView.frame = CGRectMake(0, kScreenHeight-kInputViewHeight-_navHeight, kScreenWidth, kInputViewHeight);
  934. self.chatTableView.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight-_navHeight-kInputViewHeight);
  935. }];
  936. }else {
  937. [UIView animateWithDuration:0.3 animations:^{
  938. self.chatInputView.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height-kInputViewHeight-newFrame.size.height-_navHeight, kScreenWidth, kInputViewHeight);
  939. self.chatTableView.frame = CGRectMake(0, 0, kScreenWidth, [UIScreen mainScreen].bounds.size.height-_navHeight-kInputViewHeight-newFrame.size.height);
  940. [self scrollToEnd];
  941. }];
  942. }
  943. }
  944. // 机器人客服
  945. - (void)robotAction {
  946. NSLog(@"机器人客服");
  947. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.now_robit", nil);
  948. self.manualButotn.hidden = NO;
  949. self.isRobot = YES;
  950. }
  951. // 客服在线
  952. - (void)customOnline {
  953. NSLog(@"客服在线");
  954. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_now", nil);
  955. self.manualButotn.hidden = YES;
  956. self.isRobot = NO;
  957. [self createNSTimer];
  958. }
  959. // 客服离线
  960. - (void)customOffline {
  961. NSLog(@"客服离线");
  962. self.manualButotn.hidden = NO;
  963. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_isline", nil);
  964. [self showGuestBookViewController];
  965. }
  966. // 会话领取
  967. - (void)customClaim {
  968. NSLog(@"会话被坐席领取");
  969. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_now", nil);
  970. self.manualButotn.hidden = YES;
  971. self.isRobot = NO;
  972. }
  973. // 离线推送 (坐席在后台结束会话,返回上一界面)
  974. - (void)customFinish {
  975. NSLog(@"客服结束会话");
  976. [self.chatInputView setHidden:true];
  977. self.manualButotn.hidden = YES;
  978. self.chatTableView.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight - _navHeight);
  979. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.people_now", nil);
  980. }
  981. // 排队人数
  982. - (void)customQueue: (NSNotification *)notification {
  983. NSLog(@"排队人数 %@", notification.object);
  984. _titleView.stateInfoLabel.text = [NSString stringWithFormat:@"%@: %@",NSLocalizedString(@"title.line_up", nil), notification.object];
  985. self.manualButotn.hidden = YES;
  986. }
  987. // 满意度推送
  988. - (void)customInvestigate {
  989. NSLog(@"满意度通知");
  990. UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"" message:NSLocalizedString(@"button.chat_evaluate", nil) preferredStyle:UIAlertControllerStyleActionSheet];
  991. for (NSDictionary *index in _investigateArray) {
  992. [alertController addAction:[UIAlertAction actionWithTitle:[index objectForKey:@"name"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  993. [QMConnect sdkSubmitInvestigate:[index objectForKey:@"name"] value:[index objectForKey:@"value"] successBlock:^{
  994. NSLog(@"评价成功");
  995. } failBlock:^{
  996. NSLog(@"评价失败");
  997. }];
  998. }]];
  999. };
  1000. [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"button.cancel", nil) style:UIAlertActionStyleDestructive handler:nil]];
  1001. [self presentViewController:alertController animated:true completion:nil];
  1002. }
  1003. // 坐席信息 (坐席工号、坐席名称、坐席头像) 可能为空字符串需要判断
  1004. - (void)customAgentMessage: (NSNotification *)notification {
  1005. QMAgent *agent = notification.object;
  1006. NSString *string;
  1007. if ([agent.type isEqualToString:@"robot"]) {
  1008. // string = [NSString stringWithFormat:@"%@", agent.name];
  1009. string = @"小易";
  1010. }else if ([agent.type isEqualToString:@"activeClaim"]){
  1011. string = [NSString stringWithFormat:@"%@(%@)", agent.name, agent.exten];
  1012. [self customOnline];
  1013. }else {
  1014. string = [NSString stringWithFormat:@"%@(%@)", agent.name, agent.exten];
  1015. }
  1016. string = [string stringByReplacingOccurrencesOfString:@"\n" withString:@""];
  1017. _titleView.nameLabel.text = [NSString stringWithFormat:@"%@",string];
  1018. }
  1019. // 专属坐席不在线通知 调用接受其他坐席服务接口成功后调用 beginSession
  1020. - (void)customVIP {
  1021. [QMAlert showMessage:NSLocalizedString(@"title.schedule_notonline", nil)];
  1022. __weak QMChatRoomViewController * myChatView = self;
  1023. [QMConnect sdkAcceptOtherAgentWithPeer:self.peerId successBlock:^{
  1024. NSLog(@"成功");
  1025. [myChatView beginSession];
  1026. } failBlock:^{
  1027. NSLog(@"失败");
  1028. [QMAlert showMessage:NSLocalizedString(@"title.schedule_faile", nil)];
  1029. }];
  1030. }
  1031. #pragma mark - 日程管理的留言
  1032. - (void)customLeavemsg: (NSNotification*)notification {
  1033. NSArray *array = notification.object;
  1034. NSString *str = array[0];
  1035. [QMConnect sdkGetWebchatScheduleConfig:^(NSDictionary * _Nonnull scheduleDic) {
  1036. dispatch_async(dispatch_get_main_queue(), ^{
  1037. for (NSDictionary*dic in scheduleDic[@"leavemsgNodes"]) {
  1038. if ([str isEqualToString:dic[@"_id"]]){
  1039. self.LeaveArray = dic[@"leavemsgFields"];
  1040. QMChatRoomGuestBookViewController *guestBookViewController = [[QMChatRoomGuestBookViewController alloc] init];
  1041. guestBookViewController.peerId = array[1];
  1042. guestBookViewController.contactFields = self.LeaveArray;
  1043. guestBookViewController.leaveMsg = dic[@"contentTip"];
  1044. guestBookViewController.isScheduleLeave = true;
  1045. [self.navigationController pushViewController:guestBookViewController animated:YES];
  1046. }
  1047. }
  1048. });
  1049. } failBlock:^{
  1050. NSLog(@"日程管理进入留言失败");
  1051. }];
  1052. }
  1053. - (void)beginSession {
  1054. __weak QMChatRoomViewController * myChatView = self;
  1055. [QMConnect sdkBeginNewChatSession:self.peerId successBlock:^(BOOL remark) {
  1056. NSLog(@"开始会话成功");
  1057. dispatch_async(dispatch_get_main_queue(), ^{
  1058. // 是否启动了评价功能
  1059. if (remark == NO) {
  1060. myChatView.addView.evaluateBtn.hidden = YES;
  1061. myChatView.addView.evaluateLabel.hidden = YES;
  1062. }else {
  1063. myChatView.addView.evaluateBtn.hidden = NO;
  1064. myChatView.addView.evaluateLabel.hidden = NO;
  1065. }
  1066. });
  1067. } failBlock:^{
  1068. NSLog(@"开始会话失败");
  1069. }];
  1070. }
  1071. // 坐席正在输入
  1072. - (void)changeCustomStatus {
  1073. if (![_titleView.stateInfoLabel.text isEqual: NSLocalizedString(@"title.other_writing", nil)]) {
  1074. NSString *str = _titleView.stateInfoLabel.text;
  1075. _titleViewText = str;
  1076. }
  1077. _titleView.stateInfoLabel.text = NSLocalizedString(@"title.other_writing", nil);
  1078. backStatus = nil;
  1079. backStatus = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(backCustomStatus:) userInfo:nil repeats:NO];
  1080. [[NSRunLoop mainRunLoop] addTimer:backStatus forMode:NSRunLoopCommonModes];
  1081. }
  1082. - (void)backCustomStatus:(NSTimer *)time{
  1083. _titleView.stateInfoLabel.text = _titleViewText;
  1084. [backStatus invalidate];
  1085. }
  1086. // 撤回消息
  1087. - (void)cbangeDrowMessageStatus: (NSNotification*)notification {
  1088. NSString *messageId = notification.object;
  1089. [QMConnect changeDrawMessageStatus:messageId];
  1090. [self getData];
  1091. [self reloadTableView];
  1092. }
  1093. // 访客无响应断开计时(开启无响应断开的需要把方法中的代码放开)
  1094. - (void)createNSTimer{
  1095. NSLog(@"开启无响应定时器");
  1096. // [self removeTimer];
  1097. // self.breakDuration = [QMConnect breakSessionDuration];
  1098. // self.breakTipsDuration = [QMConnect breakSessionAlertDuration];
  1099. // if (self.breakDuration && self.breakTipsDuration && [QMConnect allowedBreakSession]) {
  1100. // breakTipTimer = [NSTimer scheduledTimerWithTimeInterval:self.breakTipsDuration * 60 target:self selector:@selector(breakTipTimerAction:) userInfo:nil repeats:NO];
  1101. // breakTimer = [NSTimer scheduledTimerWithTimeInterval:self.breakDuration * 60 target:self selector:@selector(breakTimerAction:) userInfo:nil repeats:NO];
  1102. // [[NSRunLoop mainRunLoop] addTimer:breakTimer forMode:NSRunLoopCommonModes];
  1103. // [[NSRunLoop mainRunLoop] addTimer:breakTipTimer forMode:NSRunLoopCommonModes];
  1104. // }
  1105. }
  1106. - (void)removeTimer {
  1107. if (breakTipTimer) {
  1108. [breakTipTimer invalidate];
  1109. breakTipTimer = nil;
  1110. }
  1111. if (breakTimer) {
  1112. [breakTimer invalidate];
  1113. breakTimer = nil;
  1114. }
  1115. }
  1116. - (void)breakTimerAction:(NSTimer *)time{
  1117. [self.chatInputView setHidden:true];
  1118. [self.manualButotn setHidden:true];
  1119. self.chatTableView.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight - _navHeight);
  1120. }
  1121. - (void)breakTipTimerAction:(NSTimer *)timer{
  1122. [QMAlert showMessage:[QMConnect breakSessionAlert]];
  1123. [breakTipTimer invalidate];
  1124. }
  1125. - (void)didReceiveMemoryWarning {
  1126. [super didReceiveMemoryWarning];
  1127. // Dispose of any resources that can be recreated.
  1128. }
  1129. @end