// // SessionViewController.m // xplan-ios // // Created by zu on 2021/11/28. // #import "SessionViewController.h" #import #import #import #import ///Tool #import "XCCurrentVCStackManager.h" #import "NIMMessageMaker.h" #import "XPMacro.h" #import "UITableView+NIMScrollToBottom.h" #import "ThemeColor.h" #import "NSArray+Safe.h" ///Model #import "RoomInfoModel.h" #import "UserInfoModel.h" #import "MessageMenuModel.h" #import "SessionRiskCache.h" ///View #import "XPSessionMessageHeadView.h" #import "XPMineUserInfoViewController.h" #import "XPRoomViewController.h" #import "RoomHostDelegate.h" #import "SessionToolbarView.h" #import "MessageCell.h" #import "SessionChatLimitView.h" #import "XPSendGiftView.h" #import "SessionNavView.h" #import "SessionInfoViewController.h" #import "SessionRiskView.h" ///P #import "MessagePresenter.h" #import "MessageProtocol.h" @interface SessionViewController () @property (nonatomic, strong) NIMSession * session; @property (nonatomic, strong) NSMutableArray * messages; ///导航栏 @property (nonatomic,strong) SessionNavView *sessionNavView; @property (nonatomic, strong) UITableView * sessionTableView; ///跟随进房卡片 @property (nonatomic, strong) XPSessionMessageHeadView *followInRoomView; @property (nonatomic, strong) UserInfoModel *userInfo; ///输入框 @property (nonatomic,strong) SessionToolbarView *toolbarView; ///最后的一条消息 @property (nonatomic,strong) NIMMessage *lastMessage; ///私聊风险提示 @property (nonatomic,strong) SessionRiskView *riskAlertView; @end @implementation SessionViewController - (BOOL)isHiddenNavBar { return YES; } - (void)dealloc { [[NIMSDK sharedSDK].chatManager removeDelegate:self]; [[NIMSDK sharedSDK].conversationManager removeDelegate:self]; [[NIMSDK sharedSDK].mediaManager removeDelegate:self]; [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (MessagePresenter *)createPresenter { return [[MessagePresenter alloc] init]; } - (instancetype)initWithSession:(NIMSession *)session { self = [super init]; if (self) { _session = session; } return self; } - (void)viewDidLoad { [super viewDidLoad]; [self initViews]; [self initLayout]; [IQKeyboardManager sharedManager].enable = NO; [IQKeyboardManager sharedManager].enableAutoToolbar = NO; [self initHeaderAndFooterRrfresh]; [[NIMSDK sharedSDK].chatManager addDelegate:self]; [[NIMSDK sharedSDK].conversationManager addDelegate:self]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.presenter getFansLike:self.session.sessionId]; self.sessionNavView.userId = self.session.sessionId; } - (void)initHeaderAndFooterRrfresh { MJRefreshNormalHeader *header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(headerRefresh)]; header.stateLabel.font = [UIFont systemFontOfSize:10.0]; header.lastUpdatedTimeLabel.font = [UIFont systemFontOfSize:10.0]; header.stateLabel.textColor = [ThemeColor secondTextColor]; header.lastUpdatedTimeLabel.textColor = [ThemeColor secondTextColor]; self.sessionTableView.mj_header = header; [self headerRefresh]; [self initData]; } - (void)headerRefresh { if (self.messages.count > 0) { [[[NIMSDK sharedSDK] conversationManager] messagesInSession:self.session message:self.messages.firstObject limit:20 completion:^(NSError * _Nullable error, NSArray * _Nullable messages) { [self.sessionTableView.mj_header endRefreshing]; if (error == nil && messages.count > 0) { for (int i = ((int)messages.count -1); i > 0; i--) { NIMMessage * message = [messages safeObjectAtIndex1:i]; [self.messages insertObject:message atIndex:0]; } } [self.sessionTableView reloadData]; }]; } else { [[[NIMSDK sharedSDK] conversationManager] messagesInSession:self.session message:nil limit:20 completion:^(NSError * _Nullable error, NSArray * _Nullable messages) { [self.sessionTableView.mj_header endRefreshing]; if (error == nil) { [self.messages addObjectsFromArray:messages]; } [self.sessionTableView reloadData]; [self.sessionTableView nim_scrollToBottom:NO]; }]; } } - (UINavigationController *)getKeyWindowNav { if ([XCCurrentVCStackManager shareManager].getCurrentVC) { return [XCCurrentVCStackManager shareManager].getCurrentVC.navigationController; } return self.navigationController; } #pragma mark - Private Method - (void)initViews { self.sessionNavView.isInRoom = self.openType == SessionListOpenTypeRoom; [self.view addSubview:self.sessionNavView]; [self.view addSubview:self.sessionTableView]; [self.view addSubview:self.toolbarView]; if (self.openType == SessionListOpenTypeDefault) { [self.view addSubview:self.followInRoomView]; } } - (void)initLayout { [self.sessionNavView mas_makeConstraints:^(MASConstraintMaker *make) { make.height.mas_equalTo(self.openType == SessionListOpenTypeRoom ? 44 : kNavigationHeight); make.left.top.right.mas_equalTo(self.view); }]; [self.sessionTableView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.mas_equalTo(self.view); make.top.mas_equalTo(self.sessionNavView.mas_bottom); make.bottom.mas_equalTo(self.toolbarView.mas_top); }]; [self.toolbarView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.mas_equalTo(self.view); make.bottom.mas_equalTo(self.view); }]; if (self.openType == SessionListOpenTypeDefault) { [self.followInRoomView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.mas_equalTo(self.view); make.top.mas_equalTo(self.sessionNavView.mas_bottom); make.height.mas_equalTo(80); }]; } } - (void)viewDidLayoutSubviews{ if (self.openType == SessionListOpenTypeRoom) { self.view.frame = CGRectMake(0, 0, KScreenWidth, kHalfScreenHeight); } } - (void)initData { NIMUser * user = [[NIMSDK sharedSDK].userManager userInfo:self.session.sessionId]; if (user) { self.sessionNavView.title = user.userInfo.nickName; } else { self.sessionNavView.title = @"加载中……"; } [[NIMSDK sharedSDK].conversationManager markAllMessagesReadInSession:self.session]; [self.presenter getChatLimitReceiverUid:self.session.sessionId]; [self.presenter getUserInfo:self.session.sessionId]; self.sessionNavView.userId = self.session.sessionId; } - (BOOL)isExistMessages:(NIMMessage *)message{ BOOL isExist = NO; NIMMessage * model; for (NIMMessage *item in self.messages.reverseObjectEnumerator.allObjects) { if ([item isKindOfClass:[NIMMessage class]] && [item.messageId isEqual:message.messageId]) { model = item; isExist = YES; break; } } if (model) { model = message; } return isExist; } - (BOOL)showChatRiskView:(UserInfoModel *)userInfo { NSString * cacheUid = [[SessionRiskCache shareCache] getCloseChatRiskAlert:self.session.sessionId]; if (cacheUid && [cacheUid isEqualToString:self.session.sessionId]) { return NO; } if ([[NIMSDK sharedSDK].userManager isMyFriend:self.session.sessionId] && !userInfo.banAccount) { return NO; } return YES; } #pragma mark - MessageProtocol - (void)onGetLimitChat:(ChatLimitModel *)chatLimit { BOOL chatDisabled = !chatLimit.chat && self.messages.count <= 0; if (chatDisabled) { SessionChatLimitView *chatLimitView = [[SessionChatLimitView alloc] initWithChatLimit:chatLimit]; self.sessionTableView.tableHeaderView = chatLimitView; } else { self.sessionTableView.tableHeaderView = nil; } self.toolbarView.sendDisabled = chatDisabled; } ///获取用户信息成功 - (void)onGetSessionUserInfoSuccess:(UserInfoModel *)userInfo { _userInfo = userInfo; if (userInfo.nick.length > 0) { self.sessionNavView.title = userInfo.nick; } if (userInfo.roomUid && self.openType == SessionListOpenTypeDefault) { self.followInRoomView.hidden = NO; self.followInRoomView.userInfo = userInfo; if ([self showChatRiskView:userInfo]) { self.riskAlertView.warning = userInfo.banAccount ? @" 温馨提醒:当前对方账号存在异常,请谨慎来往!" : @"温馨提示:对方和您非好友关系,请注意隐私安全!"; if (!self.riskAlertView.superview) { [self.view addSubview:self.riskAlertView]; [self.riskAlertView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.mas_equalTo(self.view); make.top.mas_equalTo(self.sessionNavView.mas_bottom); make.height.mas_equalTo(20); }]; } [self.followInRoomView mas_remakeConstraints:^(MASConstraintMaker *make) { make.left.right.mas_equalTo(self.view); make.top.mas_equalTo(self.riskAlertView.mas_bottom); make.height.mas_equalTo(80); }]; [self.sessionTableView mas_updateConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(self.sessionNavView.mas_bottom).mas_offset(100); }]; } else { [self.sessionTableView mas_updateConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(self.sessionNavView.mas_bottom).mas_offset(80); }]; } } else { if ([self showChatRiskView:userInfo]) { self.riskAlertView.warning = userInfo.banAccount ? @" 温馨提醒:当前对方账号存在异常,请谨慎来往!" : @"温馨提示:对方和您非好友关系,请注意隐私安全!"; if (!self.riskAlertView.superview) { [self.view addSubview:self.riskAlertView]; [self.riskAlertView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.mas_equalTo(self.view); make.top.mas_equalTo(self.sessionNavView.mas_bottom); make.height.mas_equalTo(20); }]; } } [self.sessionTableView mas_updateConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(self.sessionNavView.mas_bottom).mas_offset(20); }]; } } - (void)getFansLikeSuccess:(BOOL)isLike { self.sessionNavView.isLike = isLike; } - (void)attentionUserSuccess:(NSString *)uid { if ([uid isEqualToString:self.session.sessionId]) { self.sessionNavView.isLike = YES; } } #pragma mark - MessageCellDelegate - (void)updateMessageSuccess:(NIMMessage *)message { if ([message.session.sessionId isEqualToString:self.session.sessionId]) { [self.sessionTableView reloadData]; } } - (void)didTapAvatar:(NSString *)uid { if (uid.length > 0) { XPMineUserInfoViewController * infoVC = [[XPMineUserInfoViewController alloc] init]; infoVC.uid = uid.integerValue; [[self getKeyWindowNav] pushViewController:infoVC animated:YES]; } } #pragma mark - SessionNavViewDelegate - (void)sessionNavView:(SessionNavView *)view didClickLike:(UIButton *)sender { [self.presenter attentionUser:self.session.sessionId]; } - (void)sessionNavView:(SessionNavView *)view didClickBack:(UIButton *)sender { if (self.openType == SessionListOpenTypeRoom) { CATransition *transition = [CATransition animation]; transition.duration = 0.3f; transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; transition.type = kCATransitionPush; transition.subtype = kCATransitionFromLeft; [self.view.superview.layer addAnimation:transition forKey:nil]; [self removeFromParentViewController]; [self.view removeFromSuperview]; return; } [self.navigationController popViewControllerAnimated:YES]; } - (void)sessionNavView:(SessionNavView *)view didClickReport:(UIButton *)sender { SessionInfoViewController * reportVC = [[SessionInfoViewController alloc] init]; reportVC.userId = self.session.sessionId; [self.getKeyWindowNav pushViewController:reportVC animated:YES]; } #pragma mark - UITableViewDataSource - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.messages.count; } - (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath { NIMMessage * message = [self.messages safeObjectAtIndex1:indexPath.row]; NSString * identifier = [MessageCell cellContent:message]; ///从复用池中获取所需要的cell MessageCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier]; if (!cell) { ///如果没有的话 根据identifier 注册一下 重新获取一下即可 [tableView registerClass:[MessageCell class] forCellReuseIdentifier:identifier]; ///如果注册过了 就不需要判断cell是否为空 cell = [tableView dequeueReusableCellWithIdentifier:identifier]; } cell.delegate = self; [cell renderWithMessage:[self.messages safeObjectAtIndex1:indexPath.row]]; return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { NIMMessage *msg = [self.messages safeObjectAtIndex1:indexPath.row]; return [MessageCell measureHeight:msg]; } - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { [self.toolbarView foldMenuView]; } #pragma mark - NIMChatManagerDelegate - (void)onRecvMessages:(NSArray *)messages { NIMMessage *message = messages.firstObject; NIMSession *session = message.session; if (![session isEqual:self.session] || !messages.count) { return; } for (NIMMessage *message in messages) { if (message.isDeleted) { continue; } [self.messages addObject:message]; } [self.sessionTableView reloadData]; [self.sessionTableView nim_scrollToBottom:YES]; [[NIMSDK sharedSDK].conversationManager markAllMessagesReadInSession:self.session]; } - (void)sendMessage:(NIMMessage *)message didCompleteWithError:(NSError *)error { if (message.session.sessionType != NIMSessionTypeP2P) { return; } if (![self isExistMessages:message]) { [self.messages addObject:message]; } if (message.yidunAntiSpamRes) { NSDictionary * spamRes = message.yidunAntiSpamRes.toJSONObject; NSDictionary * spamResExt = ((NSString *)spamRes[@"ext"]).toJSONObject; if ([spamResExt[@"antispam"][@"suggestion"] intValue] == 2) { NSDictionary * dic = @{@"suggestion": @"2"}; message.localExt = dic; [[NIMSDK sharedSDK].conversationManager updateMessage:message forSession:self.session completion:nil]; } } [self.sessionTableView reloadData]; [self.sessionTableView nim_scrollToBottom:YES]; } - (void)fetchMessageAttachment:(NIMMessage *)message didCompleteWithError:(NSError *)error { if (![message.session isEqual:self.session]) return; for (NIMMessage * msg in self.messages) { if ([msg.messageId isEqualToString:message.messageId]) { NSInteger index = [self.messages indexOfObject:msg]; [self.messages replaceObjectAtIndex:index withObject:message]; [self.sessionTableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:UITableViewRowAnimationNone]; break; } } } #pragma mark - NIMConversationManagerDelegate - (void)messagesDeletedInSession:(NIMSession *)session { [self.messages removeAllObjects]; [self initData]; } #pragma mark - XPSessionMessageHeadViewDelegate - (void)onAvatarClick:(NSInteger)uid { if (self.openType == SessionListOpenTypeDefault) { XPMineUserInfoViewController * infoVC = [[XPMineUserInfoViewController alloc] init]; infoVC.uid = uid; [self.getKeyWindowNav pushViewController:infoVC animated:YES]; } } - (void)onFollowInRoom:(NSString *)roomUid { if (self.openType == SessionListOpenTypeDefault) { __block BOOL isSameRoom = NO; __block RoomInfoModel *roomModel; [self.getKeyWindowNav.viewControllers enumerateObjectsUsingBlock:^(__kindof UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { if ([obj isKindOfClass:[XPRoomViewController class]]) { XPRoomViewController * rooomVC = obj; [[self getKeyWindowNav] popToRootViewControllerAnimated:NO]; roomModel = rooomVC.getRoomInfo; if(rooomVC.getRoomInfo.uid == [roomUid integerValue]) { isSameRoom = YES; } else { [rooomVC exitRoom]; } *stop = YES; } }]; if (!isSameRoom) { if (roomModel.type == RoomType_MiniGame) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [XPRoomViewController openRoom:roomUid fromNick:self.userInfo.nick fromType:UserEnterRoomFromType_Follow_User fromUid:[NSString stringWithFormat:@"%ld", self.userInfo.uid] viewController:[[XCCurrentVCStackManager shareManager] getCurrentVC]]; }); } else { [XPRoomViewController openRoom:roomUid fromNick:self.userInfo.nick fromType:UserEnterRoomFromType_Follow_User fromUid:[NSString stringWithFormat:@"%ld", self.userInfo.uid] viewController:self]; } } } } - (void)didFailRetry:(NIMMessage *)message { if (message.isReceivedMsg) { [[NIMSDK sharedSDK].chatManager fetchMessageAttachment:message error:nil]; } else { [[NIMSDK sharedSDK].chatManager resendMessage:message error:nil]; } } #pragma mark - MessageToolbarViewDelegate - (void)keyboardWillChangeFrame:(CGRect)rect { CGFloat heightFromBottom = rect.size.height; heightFromBottom = MAX(0.0, heightFromBottom); [self.toolbarView mas_updateConstraints:^(MASConstraintMaker *make) { make.bottom.mas_equalTo(self.view).offset(-heightFromBottom); }]; [self.sessionTableView nim_scrollToBottom:NO]; } - (void)sendTextMessage:(NSString *)text { NIMMessage *message = [NIMMessageMaker msgWithText:text]; [[[NIMSDK sharedSDK] chatManager] sendMessage:message toSession:self.session error:nil]; } - (void)didSelectMenuItem:(MessageMenuModel *)info { UIWindow * currentWindow; for (int i = 0; i < [UIApplication sharedApplication].windows.count; i++) { UIWindow * window = [[UIApplication sharedApplication].windows safeObjectAtIndex1:i]; if(window.tag == 1000) { currentWindow = window; break; } } if (currentWindow) { currentWindow.windowLevel = -1; } switch (info.type) { case MessageMenuType_Photo: { TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:1 delegate:self]; imagePickerVc.modalPresentationStyle = UIModalPresentationOverFullScreen; imagePickerVc.allowPickingVideo = NO; imagePickerVc.allowTakeVideo = NO; imagePickerVc.naviBgColor = [ThemeColor appCellBackgroundColor]; imagePickerVc.naviTitleColor = [ThemeColor mainTextColor]; imagePickerVc.barItemTextColor = [ThemeColor mainTextColor]; [self presentViewController:imagePickerVc animated:YES completion:nil]; } break; case MessageMenuType_Gift: { XPSendGiftView * giftView = [[XPSendGiftView alloc] initWithType:SendGiftType_User uid:nil]; XPGiftUserInfoModel * userModel = [[XPGiftUserInfoModel alloc] init]; userModel.avatar = self.userInfo.avatar; userModel.nick = self.userInfo.nick; userModel.uid = self.session.sessionId.integerValue; userModel.isSelect = YES; [giftView configGiftUsers:@[userModel]]; [self presentViewController:giftView animated:YES completion:nil]; } break; default: break; } } - (void)audioRecordCompletion:(NSString *)recordPath { NIMMessage *message = [NIMMessageMaker msgWithAudio:recordPath]; [[[NIMSDK sharedSDK] chatManager] sendMessage:message toSession:self.session error:nil]; } #pragma mark - TZImagePickerControllerDelegate - (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto infos:(NSArray *)infos { [photos enumerateObjectsUsingBlock:^(UIImage * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { if (obj) { // 构造出具体会话 NIMMessage * message = [NIMMessageMaker msgWithImage:obj]; // 发送消息 [[NIMSDK sharedSDK].chatManager sendMessage:message toSession:self.session error:nil]; } }]; } #pragma mark - SessionRiskViewDelegate - (void)sessionRiskViewCloseButtonClick:(SessionRiskView *)view { [[SessionRiskCache shareCache] saveCloseRisk:self.session.sessionId]; [self.riskAlertView removeFromSuperview]; if (!self.followInRoomView.hidden) { [self.followInRoomView mas_remakeConstraints:^(MASConstraintMaker *make) { make.left.right.mas_equalTo(self.view); make.top.mas_equalTo(self.sessionNavView.mas_bottom); make.height.mas_equalTo(80); }]; [self.sessionTableView mas_updateConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(self.sessionNavView.mas_bottom).mas_offset(80); }]; } else { [self.sessionTableView mas_updateConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(self.sessionNavView.mas_bottom).mas_offset(0); }]; } } #pragma mark - Getters And Setters - (NSArray *)messages { if (!_messages) { _messages = [[NSMutableArray alloc] init]; } return _messages; } #pragma mark - Event Response - (void)closeKeyBoard { [self.view endEditing:YES]; [self.toolbarView foldMenuView]; } - (UITableView *)sessionTableView { if (!_sessionTableView) { _sessionTableView = [[UITableView alloc] init]; _sessionTableView.delegate = self; _sessionTableView.dataSource = self; _sessionTableView.backgroundColor = UIColor.clearColor; _sessionTableView.separatorStyle = UITableViewCellSeparatorStyleNone; _sessionTableView.showsVerticalScrollIndicator = NO; // UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(closeKeyBoard)]; // [_sessionTableView addGestureRecognizer:tap]; } return _sessionTableView; } - (XPSessionMessageHeadView *)followInRoomView { if (!_followInRoomView) { _followInRoomView = [[XPSessionMessageHeadView alloc] init]; _followInRoomView.delegate = self; _followInRoomView.hidden = YES; } return _followInRoomView; } - (SessionToolbarView *)toolbarView { if (!_toolbarView) { _toolbarView = [[SessionToolbarView alloc] init]; _toolbarView.delegate = self; } return _toolbarView; } - (SessionNavView *)sessionNavView { if (!_sessionNavView) { _sessionNavView = [[SessionNavView alloc] init]; _sessionNavView.delegate = self; } return _sessionNavView; } - (SessionRiskView *)riskAlertView { if (!_riskAlertView) { _riskAlertView = [[SessionRiskView alloc] init]; _riskAlertView.delegate = self; } return _riskAlertView; } @end