// // YOUPAIBBNIMSessionViewController.m // VQU // // Created by Elaine on 2021/10/25. // Copyright © 2021 MS. All rights reserved. // #import "YOUPAIBBNIMSessionViewController.h" #import "NIMKit.h" #import "NIMMessageCellProtocol.h" #import "NIMMessageModel.h" #import "NIMKitUtil.h" #import "NIMCustomLeftBarView.h" #import "NIMBadgeView.h" #import "UITableView+NIMScrollToBottom.h" #import "NIMMessageMaker.h" #import "UIView+NIM.h" #import "NIMSessionConfigurator.h" #import "NIMKitInfoFetchOption.h" #import "NIMKitTitleView.h" #import "NIMKitKeyboardInfo.h" @interface YOUPAIBBNIMSessionViewController () @property (nonatomic,readwrite) NIMMessage *messageForMenu; @property (nonatomic,strong) UILabel *youpaiptitleLabel; @property (nonatomic,strong) UILabel *youpaipsubTitleLabel; @property (nonatomic,strong) NSIndexPath *youpaiplastVisibleIndexPathBeforeRotation; @property (nonatomic,strong) NIMSessionConfigurator *youpaipconfigurator; @property (nonatomic,weak) id youpaipinteractor; @end @implementation YOUPAIBBNIMSessionViewController - (instancetype)initWithSession:(NIMSession *)session{ self = [super initWithNibName:nil bundle:nil]; if (self) { _session = session; } return self; } - (void)dealloc { [self removeListener]; [[NIMKit sharedKit].robotTemplateParser clean]; _tableView.delegate = nil; _tableView.dataSource = nil; } - (void)viewDidLoad { [super viewDidLoad]; //导航栏 [self youpaifsetupNav]; //消息 tableView [self youpaifsetupTableView]; //输入框 inputView [self youpaifsetupInputView]; //会话相关逻辑配置器安装 [self youpaifsetupConfigurator]; //添加监听 [self addListener]; //进入会话时,标记所有消息已读,并发送已读回执 [self markRead]; //更新已读位置 [self uiCheckReceipts:nil]; } - (void)youpaifsetupNav { [self setUpTitleView]; NIMCustomLeftBarView *leftBarView = [[NIMCustomLeftBarView alloc] init]; UIBarButtonItem *leftItem = [[UIBarButtonItem alloc] initWithCustomView:leftBarView]; if (@available(iOS 11.0, *)) { leftBarView.translatesAutoresizingMaskIntoConstraints = NO; } self.navigationItem.leftBarButtonItems = @[leftItem]; self.navigationItem.leftItemsSupplementBackButton = YES; } - (void)youpaifsetupTableView { self.view.backgroundColor = [UIColor whiteColor]; self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; self.tableView.backgroundColor = LCBkgColor; self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; self.tableView.estimatedRowHeight = 0; self.tableView.estimatedSectionHeaderHeight = 0; self.tableView.estimatedSectionFooterHeight = 0; self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; if (@available(iOS 15.0, *)) { self.tableView.sectionHeaderTopPadding = 0; } if ([self.sessionConfig respondsToSelector:@selector(sessionBackgroundImage)] && [self.sessionConfig sessionBackgroundImage]) { UIImageView *imgView = [[UIImageView alloc] initWithFrame:self.view.bounds]; imgView.image = [self.sessionConfig sessionBackgroundImage]; imgView.contentMode = UIViewContentModeScaleAspectFill; self.tableView.backgroundView = imgView; } [self.view addSubview:self.tableView]; } - (void)youpaifsetupInputView { if ([self shouldShowInputView]) { self.youpaipsessionInputView = [[YOUPAIBBNIMInputView alloc] initWithFrame:CGRectMake(0, 0, self.view.nim_width,0) config:self.sessionConfig]; self.youpaipsessionInputView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; [self.youpaipsessionInputView setSession:self.session]; [self.youpaipsessionInputView youpaifsetInputDelegate:self]; [self.youpaipsessionInputView youpaifsetInputActionDelegate:self]; [self.youpaipsessionInputView youpaifrefreshStatus:BBNIMInputStatusText]; [self.view addSubview:_youpaipsessionInputView]; } } - (void)youpaifsetupConfigurator { _youpaipconfigurator = [[NIMSessionConfigurator alloc] init]; [_youpaipconfigurator youpaifsetup:self]; BOOL youpaipneedProximityMonitor = [self needProximityMonitor]; [[NIMSDK sharedSDK].mediaManager setNeedProximityMonitor:youpaipneedProximityMonitor]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.youpaipinteractor onViewWillAppear]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self.youpaipsessionInputView endEditing:YES]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; [self.youpaipinteractor onViewDidDisappear]; } - (void)viewDidLayoutSubviews { [self changeLeftBarBadge:self.conversationManager.allUnreadCount]; [self.youpaipinteractor resetLayout]; } #pragma mark - 消息收发接口 - (void)sendMessage:(NIMMessage *)message { [self.youpaipinteractor sendMessage:message]; } #pragma mark - Touch Event - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesBegan:touches withEvent:event]; [_youpaipsessionInputView endEditing:YES]; } #pragma mark - NIMSessionConfiguratorDelegate - (void)didFetchMessageData { [self uiCheckReceipts:nil]; [self.tableView reloadData]; [self.tableView nim_scrollToBottom:NO]; } - (void)didRefreshMessageData { [self refreshSessionTitle:self.youpaifsessionTitle]; [self refreshSessionSubTitle:self.sessionSubTitle]; [self.tableView reloadData]; } - (void)didPullUpMessageData {} #pragma mark - 会话title - (NSString *)youpaifsessionTitle { NSString *title = @""; NIMSessionType type = self.session.sessionType; switch (type) { case NIMSessionTypeTeam:{ NIMTeam *team = [[[NIMSDK sharedSDK] teamManager] teamById:self.session.sessionId]; title = [NSString stringWithFormat:@"%@(%zd)",[team teamName],[team memberNumber]]; } break; case NIMSessionTypeP2P:{ title = [NIMKitUtil showNick:self.session.sessionId inSession:self.session]; } break; default: break; } return title; } - (NSString *)sessionSubTitle{return @"";}; #pragma mark - NIMChatManagerDelegate //开始发送 - (void)willSendMessage:(NIMMessage *)message { id youpaipinteractor = self.youpaipinteractor; if ([message.session isEqual:self.session]) { if ([youpaipinteractor findMessageModel:message]) { [youpaipinteractor updateMessage:message]; }else{ [youpaipinteractor addMessages:@[message]]; } } } //上传资源文件成功 - (void)uploadAttachmentSuccess:(NSString *)urlString forMessage:(NIMMessage *)message { //如果需要使用富文本推送,可以在这里进行 message apns payload 的设置 } //发送结果 - (void)sendMessage:(NIMMessage *)message didCompleteWithError:(NSError *)error { if ([message.session isEqual:_session]) { [self.youpaipinteractor updateMessage:message]; if (message.session.sessionType == NIMSessionTypeTeam) { //如果是群的话需要检查一下回执显示情况 NIMMessageReceipt *receipt = [[NIMMessageReceipt alloc] initWithMessage:message]; [self.youpaipinteractor checkReceipts:@[receipt]]; } } } //发送进度 -(void)sendMessage:(NIMMessage *)message progress:(float)progress { if ([message.session isEqual:_session]) { [self.youpaipinteractor updateMessage:message]; } } //接收消息 - (void)onRecvMessages:(NSArray *)messages { if ([self shouldAddListenerForNewMsg]) { NIMMessage *message = messages.firstObject; NIMSession *session = message.session; if (![session isEqual:self.session] || !messages.count) { return; } [self uiAddMessages:messages]; [self.youpaipinteractor markRead]; } } - (void)fetchMessageAttachment:(NIMMessage *)message progress:(float)progress { if ([message.session isEqual:_session]) { [self.youpaipinteractor updateMessage:message]; } } - (void)fetchMessageAttachment:(NIMMessage *)message didCompleteWithError:(NSError *)error { if ([message.session isEqual:_session]) { NIMMessageModel *model = [self.youpaipinteractor findMessageModel:message]; //下完缩略图之后,因为比例有变化,重新刷下宽高。 [model cleanCache]; [self.youpaipinteractor updateMessage:message]; } } - (void)onRecvMessageReceipts:(NSArray *)receipts { if ([self shouldAddListenerForNewMsg]) { NSMutableArray *handledReceipts = [[NSMutableArray alloc] init]; for (NIMMessageReceipt *receipt in receipts) { if ([receipt.session isEqual:self.session]) { [handledReceipts addObject:receipt]; } } if (handledReceipts.count) { [self uiCheckReceipts:handledReceipts]; } } } #pragma mark - NIMConversationManagerDelegate - (void)messagesDeletedInSession:(NIMSession *)session{ [self.youpaipinteractor resetMessages:nil]; [self.tableView reloadData]; } - (void)didAddRecentSession:(NIMRecentSession *)recentSession totalUnreadCount:(NSInteger)totalUnreadCount{ [self changeUnreadCount:recentSession totalUnreadCount:totalUnreadCount]; } - (void)didUpdateRecentSession:(NIMRecentSession *)recentSession totalUnreadCount:(NSInteger)totalUnreadCount{ [self changeUnreadCount:recentSession totalUnreadCount:totalUnreadCount]; } - (void)didRemoveRecentSession:(NIMRecentSession *)recentSession totalUnreadCount:(NSInteger)totalUnreadCount{ [self changeUnreadCount:recentSession totalUnreadCount:totalUnreadCount]; } - (void)changeUnreadCount:(NIMRecentSession *)recentSession totalUnreadCount:(NSInteger)totalUnreadCount{ if ([recentSession.session isEqual:self.session]) { return; } [self changeLeftBarBadge:totalUnreadCount]; } #pragma mark - NIMMediaManagerDelegate - (void)recordAudio:(NSString *)filePath didBeganWithError:(NSError *)error { if (!filePath || error) { _youpaipsessionInputView.youpaiprecording = NO; [self onRecordFailed:error]; } } - (void)recordAudio:(NSString *)filePath didCompletedWithError:(NSError *)error { if(!error) { if ([self recordFileCanBeSend:filePath]) { [self sendMessage:[NIMMessageMaker msgWithAudio:filePath]]; }else{ [self showRecordFileNotSendReason]; } } else { [self onRecordFailed:error]; } _youpaipsessionInputView.youpaiprecording = NO; } - (void)recordAudioDidCancelled { _youpaipsessionInputView.youpaiprecording = NO; } - (void)recordAudioProgress:(NSTimeInterval)currentTime { [_youpaipsessionInputView youpaifupdateAudioRecordTime:currentTime]; } - (void)recordAudioInterruptionBegin { [[NIMSDK sharedSDK].mediaManager cancelRecord]; } #pragma mark - 录音相关接口 - (void)onRecordFailed:(NSError *)error{} - (BOOL)recordFileCanBeSend:(NSString *)filepath { return YES; } - (void)showRecordFileNotSendReason{} #pragma mark - BBNIMInputDelegate - (void)didChangeInputHeight:(CGFloat)inputHeight { [self.youpaipinteractor changeLayout:inputHeight]; } #pragma mark - NIMInputActionDelegate - (BOOL)onTapMediaItem:(NIMMediaItem *)item{ SEL sel = item.selctor; BOOL youpaiphandled = sel && [self respondsToSelector:sel]; if (youpaiphandled) { NIMKit_SuppressPerformSelectorLeakWarning([self performSelector:sel withObject:item]); youpaiphandled = YES; } return youpaiphandled; } - (void)onTextChanged:(id)sender{} - (void)onSendText:(NSString *)text atUsers:(NSArray *)atUsers { NSMutableArray *users = [NSMutableArray arrayWithArray:atUsers]; if (self.session.sessionType == NIMSessionTypeP2P) { [users addObject:self.session.sessionId]; } NSString *robotsToSend = [self robotsToSend:users]; NIMMessage *message = nil; if (robotsToSend.length) { message = [NIMMessageMaker msgWithRobotQuery:text toRobot:robotsToSend]; } else { message = [NIMMessageMaker msgWithText:text]; } if (atUsers.count) { NIMMessageApnsMemberOption *apnsOption = [[NIMMessageApnsMemberOption alloc] init]; apnsOption.userIds = atUsers; apnsOption.forcePush = YES; NIMKitInfoFetchOption *option = [[NIMKitInfoFetchOption alloc] init]; option.session = self.session; NSString *me = [[NIMKit sharedKit].provider infoByUser:[NIMSDK sharedSDK].loginManager.currentAccount option:option].showName; apnsOption.apnsContent = [NSString stringWithFormat:@"%@在群里@了你",me]; message.apnsMemberOption = apnsOption; } [self sendMessage:message]; } - (NSString *)robotsToSend:(NSArray *)atUsers { for (NSString *userId in atUsers) { if ([[NIMSDK sharedSDK].robotManager isValidRobot:userId]) { return userId; } } return nil; } - (void)onSelectChartlet:(NSString *)chartletId catalog:(NSString *)catalogId{} - (void)onCancelRecording { [[NIMSDK sharedSDK].mediaManager cancelRecord]; } - (void)onStopRecording { [[NIMSDK sharedSDK].mediaManager stopRecord]; } - (void)onStartRecording { _youpaipsessionInputView.youpaiprecording = YES; NIMAudioType type = [self recordAudioType]; NSTimeInterval duration = [NIMKit sharedKit].config.recordMaxDuration; [[NIMSDK sharedSDK].mediaManager addDelegate:self]; [[NIMSDK sharedSDK].mediaManager record:type duration:duration]; } #pragma mark - NIMMessageCellDelegate - (BOOL)onTapCell:(NIMKitEvent *)event{ BOOL youpaiphandle = NO; NSString *eventName = event.eventName; if ([eventName isEqualToString:NIMKitEventNameTapAudio]) { [self.youpaipinteractor mediaAudioPressed:event.messageModel]; youpaiphandle = YES; } if ([eventName isEqualToString:NIMKitEventNameTapRobotBlock]) { NSDictionary *param = event.data; NIMMessage *message = [NIMMessageMaker msgWithRobotSelect:param[@"text"] target:param[@"target"] params:param[@"param"] toRobot:param[@"robotId"]]; [self sendMessage:message]; youpaiphandle = YES; } if ([eventName isEqualToString:NIMKitEventNameTapRobotContinueSession]) { NIMRobotObject *robotObject = (NIMRobotObject *)event.messageModel.message.messageObject; NIMRobot *robot = [[NIMSDK sharedSDK].robotManager robotInfo:robotObject.robotId]; NSString *text = [NSString stringWithFormat:@"%@%@%@",NIMInputAtStartChar,robot.nickname,NIMInputAtEndChar]; NIMInputAtItem *item = [[NIMInputAtItem alloc] init]; item.uid = robot.userId; item.name = robot.nickname; [self.youpaipsessionInputView.youpaipatCache addAtItem:item]; [self.youpaipsessionInputView.youpaiptoolBar youpaifinsertText:text]; youpaiphandle = YES; } return youpaiphandle; } - (void)onRetryMessage:(NIMMessage *)message { if (message.isReceivedMsg) { [[[NIMSDK sharedSDK] chatManager] fetchMessageAttachment:message error:nil]; }else{ [[[NIMSDK sharedSDK] chatManager] resendMessage:message error:nil]; } } - (BOOL)onLongPressCell:(NIMMessage *)message inView:(UIView *)view { BOOL youpaiphandle = NO; NSArray *items = [self menusItems:message]; if ([items count] && [self becomeFirstResponder]) { UIMenuController *controller = [UIMenuController sharedMenuController]; controller.menuItems = items; _messageForMenu = message; [controller setTargetRect:view.bounds inView:view]; [controller setMenuVisible:YES animated:YES]; youpaiphandle = YES; } return youpaiphandle; } - (BOOL)disableAudioPlayedStatusIcon:(NIMMessage *)message { BOOL youpaipdisable = NO; if ([self.sessionConfig respondsToSelector:@selector(disableAudioPlayedStatusIcon)]) { youpaipdisable = [self.sessionConfig disableAudioPlayedStatusIcon]; } return youpaipdisable; } #pragma mark - 配置项 - (id)sessionConfig { return nil; //使用默认配置 } #pragma mark - 配置项列表 //是否需要监听新消息通知 : 某些场景不需要监听新消息,如浏览服务器消息历史界面 - (BOOL)shouldAddListenerForNewMsg { BOOL youpaipshould = YES; if ([self.sessionConfig respondsToSelector:@selector(disableReceiveNewMessages)]) { youpaipshould = ![self.sessionConfig disableReceiveNewMessages]; } return youpaipshould; } //是否需要显示输入框 : 某些场景不需要显示输入框,如使用 3D touch 的场景预览会话界面内容 - (BOOL)shouldShowInputView { BOOL youpaipshould = YES; if ([self.sessionConfig respondsToSelector:@selector(disableInputView)]) { youpaipshould = ![self.sessionConfig disableInputView]; } return youpaipshould; } //当前录音格式 : NIMSDK 支持 aac 和 amr 两种格式 - (NIMAudioType)recordAudioType { NIMAudioType type = NIMAudioTypeAAC; if ([self.sessionConfig respondsToSelector:@selector(recordType)]) { type = [self.sessionConfig recordType]; } return type; } //是否需要监听感应器事件 - (BOOL)needProximityMonitor { BOOL youpaipneedProximityMonitor = YES; if ([self.sessionConfig respondsToSelector:@selector(disableProximityMonitor)]) { youpaipneedProximityMonitor = !self.sessionConfig.disableProximityMonitor; } return youpaipneedProximityMonitor; } #pragma mark - 菜单 - (NSArray *)menusItems:(NIMMessage *)message { NSMutableArray *items = [NSMutableArray array]; BOOL youpaipcopyText = NO; if (message.messageType == NIMMessageTypeText) { youpaipcopyText = YES; } if (message.messageType == NIMMessageTypeRobot) { NIMRobotObject *robotObject = (NIMRobotObject *)message.messageObject; youpaipcopyText = !robotObject.isFromRobot; } if (youpaipcopyText) { [items addObject:[[UIMenuItem alloc] initWithTitle:@"复制" action:@selector(youpaipcopyText:)]]; } [items addObject:[[UIMenuItem alloc] initWithTitle:@"删除" action:@selector(deleteMsg:)]]; return items; } - (NIMMessage *)messageForMenu { return _messageForMenu; } - (BOOL)canBecomeFirstResponder { return YES; } - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { NSArray *items = [[UIMenuController sharedMenuController] menuItems]; for (UIMenuItem *item in items) { if (action == [item action]){ return YES; } } return NO; } - (void)youpaipcopyText:(id)sender { NIMMessage *message = [self messageForMenu]; if (message.text.length) { UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; [pasteboard setString:message.text]; } } - (void)deleteMsg:(id)sender { NIMMessage *message = [self messageForMenu]; [self uiDeleteMessage:message]; [self.conversationManager deleteMessage:message]; } - (void)menuDidHide:(NSNotification *)notification { [UIMenuController sharedMenuController].menuItems = nil; } #pragma mark - 操作接口 - (void)uiAddMessages:(NSArray *)messages { [self.youpaipinteractor addMessages:messages]; } - (void)uiInsertMessages:(NSArray *)messages { [self.youpaipinteractor insertMessages:messages]; } - (NIMMessageModel *)uiDeleteMessage:(NIMMessage *)message{ NIMMessageModel *model = [self.youpaipinteractor deleteMessage:message]; if (model.shouldShowReadLabel && model.message.session.sessionType == NIMSessionTypeP2P) { [self uiCheckReceipts:nil]; } return model; } - (void)uiUpdateMessage:(NIMMessage *)message{ [self.youpaipinteractor updateMessage:message]; } - (void)uiCheckReceipts:(NSArray *)receipts { [self.youpaipinteractor checkReceipts:receipts]; } #pragma mark - NIMMeidaButton - (void)onTapMediaItemPicture:(NIMMediaItem *)item { [self.youpaipinteractor mediaPicturePressed]; } - (void)onTapMediaItemShoot:(NIMMediaItem *)item { [self.youpaipinteractor mediaShootPressed]; } - (void)onTapMediaItemLocation:(NIMMediaItem *)item { [self.youpaipinteractor mediaLocationPressed]; } #pragma mark - 旋转处理 (iOS8 or above) - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { self.youpaiplastVisibleIndexPathBeforeRotation = [self.tableView indexPathsForVisibleRows].lastObject; [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; if (self.view.window) { __weak typeof(self) wself = self; [coordinator animateAlongsideTransition:^(id context) { [[NIMSDK sharedSDK].mediaManager cancelRecord]; [wself.youpaipinteractor cleanCache]; [wself.youpaipsessionInputView youpaifreset]; [wself.tableView reloadData]; [wself.tableView scrollToRowAtIndexPath:wself.youpaiplastVisibleIndexPathBeforeRotation atScrollPosition:UITableViewScrollPositionBottom animated:NO]; } completion:nil]; } } #pragma mark - 标记已读 - (void)markRead { [self.youpaipinteractor markRead]; } #pragma mark - Private - (void)addListener { [[NIMSDK sharedSDK].chatManager addDelegate:self]; [[NIMSDK sharedSDK].conversationManager addDelegate:self]; } - (void)removeListener { [[NIMSDK sharedSDK].chatManager removeDelegate:self]; [[NIMSDK sharedSDK].conversationManager removeDelegate:self]; } - (void)changeLeftBarBadge:(NSInteger)unreadCount { NIMCustomLeftBarView *leftBarView = (NIMCustomLeftBarView *)self.navigationItem.leftBarButtonItem.customView; leftBarView.badgeView.badgeValue = @(unreadCount).stringValue; leftBarView.badgeView.hidden = !unreadCount; } - (id)conversationManager{ switch (self.session.sessionType) { case NIMSessionTypeChatroom: return nil; break; case NIMSessionTypeP2P: case NIMSessionTypeTeam: default: return [NIMSDK sharedSDK].conversationManager; } } - (void)setUpTitleView { NIMKitTitleView *titleView = (NIMKitTitleView *)self.navigationItem.titleView; if (!titleView || ![titleView isKindOfClass:[NIMKitTitleView class]]) { titleView = [[NIMKitTitleView alloc] initWithFrame:CGRectZero]; self.navigationItem.titleView = titleView; titleView.titleLabel.text = self.youpaifsessionTitle; titleView.subtitleLabel.text = self.sessionSubTitle; self.youpaiptitleLabel = titleView.titleLabel; self.youpaipsubTitleLabel = titleView.subtitleLabel; } [titleView sizeToFit]; } - (void)refreshSessionTitle:(NSString *)title { self.titleLabel.text = title; [self setUpTitleView]; } - (void)refreshSessionSubTitle:(NSString *)title { self.youpaipsubTitleLabel.text = title; [self setUpTitleView]; } @end