// // TUIGroupChatViewController.m // TXIMSDK_TUIKit_iOS // // Created by kayev on 2021/6/17. // Copyright © 2023 Tencent. All rights reserved. // #import "ReactiveObjC/ReactiveObjC.h" #import #import #import #import #import #import #import "TUIBaseChatViewController+ProtectedAPI.h" #import "TUIGroupChatViewController.h" #import "TUIGroupPendencyController.h" #import "TUIGroupPendencyDataProvider.h" #import "TUILinkCellData.h" #import "TUIMessageDataProvider.h" #import "TUITextMessageCellData.h" #import "TUIChatFlexViewController.h" #import "TUIMessageController.h" #import "TUIGroupPinCell.h" #import "TUIGroupPinPageViewController.h" @interface TUIGroupChatViewController () //@property (nonatomic, strong) UIButton *atBtn; @property(nonatomic, strong) UIView *tipsView; @property(nonatomic, strong) UILabel *pendencyLabel; @property(nonatomic, strong) UIButton *pendencyBtn; @property(nonatomic, strong) TUIGroupPendencyDataProvider *pendencyViewModel; @property(nonatomic, strong) NSMutableArray *atUserList; @property(nonatomic, assign) BOOL responseKeyboard; @property(nonatomic, strong) TUIGroupPinCellView *oneGroupPinView; @property(nonatomic, strong) NSArray *groupPinList; @property(nonatomic, strong) TUIGroupPinPageViewController *pinPageVC; @end @implementation TUIGroupChatViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.view.backgroundColor = [UIColor clearColor]; [self setupTipsView]; [self setupGroupPinTips]; [[V2TIMManager sharedInstance] addGroupListener:self]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshTipsView) name:TUICore_TUIChatExtension_ChatViewTopArea_ChangedNotification object:nil]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self refreshTipsView]; } - (void)dealloc { [TUICore unRegisterEventByObject:self]; } - (void)setupTipsView { self.tipsView = [[UIView alloc] initWithFrame:CGRectZero]; self.tipsView.backgroundColor = RGB(246, 234, 190); [self.view addSubview:self.tipsView]; self.tipsView.mm_height(24).mm_width(self.view.mm_w); self.pendencyLabel = [[UILabel alloc] initWithFrame:CGRectZero]; [self.tipsView addSubview:self.pendencyLabel]; self.pendencyLabel.font = [UIFont systemFontOfSize:12]; self.pendencyBtn = [UIButton buttonWithType:UIButtonTypeSystem]; [self.tipsView addSubview:self.pendencyBtn]; [self.pendencyBtn setTitle:TIMCommonLocalizableString(TUIKitChatPendencyTitle) forState:UIControlStateNormal]; [self.pendencyBtn.titleLabel setFont:[UIFont systemFontOfSize:12]]; [self.pendencyBtn addTarget:self action:@selector(openPendency:) forControlEvents:UIControlEventTouchUpInside]; [self.pendencyBtn sizeToFit]; self.tipsView.alpha = 0; @weakify(self); [RACObserve(self.pendencyViewModel, unReadCnt) subscribeNext:^(NSNumber *unReadCnt) { @strongify(self); if ([unReadCnt intValue]) { self.pendencyLabel.text = [NSString stringWithFormat:TIMCommonLocalizableString(TUIKitChatPendencyRequestToJoinGroupFormat), unReadCnt]; [self.pendencyLabel sizeToFit]; CGFloat gap = (self.tipsView.mm_w - self.pendencyLabel.mm_w - self.pendencyBtn.mm_w - 8) / 2; self.pendencyLabel.mm_left(gap).mm__centerY(self.tipsView.mm_h / 2); self.pendencyBtn.mm_hstack(8); self.tipsView.alpha = 1; [self refreshTipsView]; } else { self.tipsView.alpha = 0; } }]; [self getPendencyList]; } - (void)refreshTipsView { UIView *topView = [TUIGroupChatViewController topAreaBottomView]; CGRect transRect = [topView convertRect:topView.bounds toView:self.view]; self.tipsView.frame = CGRectMake(0, transRect.origin.y + transRect.size.height, self.tipsView.frame.size.width, self.tipsView.frame.size.height); } - (void)setupGroupPinTips { self.oneGroupPinView = [[TUIGroupPinCellView alloc] init]; CGFloat margin = 0 ; self.oneGroupPinView.frame = CGRectMake(0, margin, self.view.frame.size.width, 62); UIView *topView = [TUIGroupChatViewController groupPinTopView]; for (UIView *subview in topView.subviews) { [subview removeFromSuperview]; } topView.frame = CGRectMake(0, 0, self.view.mm_w, 0); [topView addSubview:self.oneGroupPinView]; @weakify(self); self.oneGroupPinView.isFirstPage = YES; self.oneGroupPinView.onClickCellView = ^(V2TIMMessage *originMessage) { @strongify(self); if (self.groupPinList.count >= 2) { [self gotoDetailPopPinPage]; } else { [self jump2GroupPinHighlightLine:originMessage]; } }; self.oneGroupPinView.onClickRemove = ^(V2TIMMessage *originMessage) { @strongify(self); [self.messageController unPinGroupMessage:originMessage]; }; self.messageController.pinGroupMessageChanged = ^(NSArray * _Nonnull groupPinList) { @strongify(self); if (groupPinList.count > 0 ) { if (!self.oneGroupPinView.superview) { [topView addSubview:self.oneGroupPinView]; } TUIMessageCellData * cellData = [TUIMessageDataProvider getCellData:[groupPinList lastObject]]; [self.oneGroupPinView fillWithData:cellData]; if (groupPinList.count >= 2) { [self.oneGroupPinView showMultiAnimation]; topView.frame = CGRectMake(0, 0, self.view.mm_w, self.oneGroupPinView.frame.size.height +20 + margin); self.oneGroupPinView.removeButton.hidden = YES; } else { [self.oneGroupPinView hiddenMultiAnimation]; topView.frame = CGRectMake(0, 0, self.view.mm_w, self.oneGroupPinView.frame.size.height + margin); if ([self.messageController isCurrentUserRoleSuperAdminInGroup]) { self.oneGroupPinView.removeButton.hidden = NO; } else { self.oneGroupPinView.removeButton.hidden = YES; } } } else { [self.oneGroupPinView removeFromSuperview]; topView.frame = CGRectMake(0, 0, self.view.mm_w, 0); } self.groupPinList = groupPinList; [[NSNotificationCenter defaultCenter] postNotificationName:TUICore_TUIChatExtension_ChatViewTopArea_ChangedNotification object:nil]; if (self.pinPageVC) { NSMutableArray *formatGroupPinList = [NSMutableArray arrayWithArray:groupPinList.reverseObjectEnumerator.allObjects]; self.pinPageVC.groupPinList = formatGroupPinList; self.pinPageVC.canRemove = [self.messageController isCurrentUserRoleSuperAdminInGroup]; if (groupPinList.count > 0) { [self reloadPopPinPage]; } else { [self.pinPageVC dismissViewControllerAnimated:NO completion:nil]; } } }; self.messageController.groupRoleChanged = ^(V2TIMGroupMemberRole role) { @strongify(self); self.messageController.pinGroupMessageChanged(self.groupPinList); if (self.pinPageVC) { self.pinPageVC.canRemove = [self.messageController isCurrentUserRoleSuperAdminInGroup]; [self.pinPageVC.tableview reloadData]; } }; } - (void)gotoDetailPopPinPage { TUIGroupPinPageViewController *vc = [[TUIGroupPinPageViewController alloc] init]; self.pinPageVC = vc; NSMutableArray *formatGroupPinList = [NSMutableArray arrayWithArray:self.groupPinList.reverseObjectEnumerator.allObjects]; vc.groupPinList = formatGroupPinList; vc.canRemove = [self.messageController isCurrentUserRoleSuperAdminInGroup]; vc.view.frame = self.view.frame; CGFloat cellHight = (62); CGFloat maxOnePage = 4; float height = (self.groupPinList.count) * cellHight; height = MIN(cellHight * maxOnePage , height); UIView *topView = [TUIGroupChatViewController groupPinTopView]; CGRect transRect = [topView convertRect:topView.bounds toView:[UIApplication sharedApplication].delegate.window]; vc.modalPresentationStyle = UIModalPresentationOverFullScreen; @weakify(self); [self presentViewController:vc animated:NO completion:^{ vc.tableview.frame = CGRectMake(0, CGRectGetMinY(transRect), self.view.frame.size.width, 60); vc.customArrowView.frame = CGRectMake(0, CGRectGetMaxY(vc.tableview.frame), vc.tableview.frame.size.width, 0); vc.bottomShadow.frame = CGRectMake(0, CGRectGetMaxY(vc.customArrowView.frame), vc.tableview.frame.size.width, 0); [UIView animateWithDuration:0.3 animations:^{ vc.tableview.frame = CGRectMake(0, CGRectGetMinY(transRect), self.view.frame.size.width, height); vc.customArrowView.frame = CGRectMake(0, CGRectGetMaxY(vc.tableview.frame), vc.tableview.frame.size.width, 40); vc.bottomShadow.frame = CGRectMake(0, CGRectGetMaxY(vc.customArrowView.frame), vc.tableview.frame.size.width, self.view.frame.size.height); UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:vc.customArrowView.bounds byRoundingCorners:(UIRectCornerBottomLeft | UIRectCornerBottomRight) cornerRadii:CGSizeMake(10.0, 10.0)]; CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init]; maskLayer.frame = vc.customArrowView.bounds; maskLayer.path = maskPath.CGPath; vc.customArrowView.layer.mask = maskLayer; }]; }]; vc.onClickRemove = ^(V2TIMMessage *originMessage) { @strongify(self); [self.messageController unPinGroupMessage:originMessage]; }; vc.onClickCellView = ^(V2TIMMessage *originMessage) { @strongify(self); [self jump2GroupPinHighlightLine:originMessage]; }; } - (void)jump2GroupPinHighlightLine:(V2TIMMessage *)originMessage { TUIMessageController *msgVC = (TUIMessageController *)self.messageController; NSString * originMsgID = originMessage.msgID; [msgVC findMessages:@[originMsgID ?: @""] callback:^(BOOL success, NSString * _Nonnull desc, NSArray * _Nonnull messages) { if (success) { V2TIMMessage *message = messages.firstObject; if (message && message.status == V2TIM_MSG_STATUS_SEND_SUCC ) { [msgVC locateAssignMessage:originMessage matchKeyWord:@""]; } else { [TUITool makeToast:TIMCommonLocalizableString(TUIKitReplyMessageNotFoundOriginMessage)]; } } }]; } - (void)reloadPopPinPage { CGFloat cellHight = (62); CGFloat maxOnePage = 4; float height = (self.groupPinList.count) * cellHight; height = MIN(cellHight * maxOnePage , height); self.pinPageVC.tableview.frame = CGRectMake(0, self.pinPageVC.tableview.frame.origin.y, self.view.frame.size.width, height); self.pinPageVC.customArrowView.frame = CGRectMake(0, CGRectGetMaxY(self.pinPageVC.tableview.frame), self.pinPageVC.tableview.frame.size.width, 40); self.pinPageVC.bottomShadow.frame = CGRectMake(0, CGRectGetMaxY(self.pinPageVC.customArrowView.frame), self.pinPageVC.tableview.frame.size.width, self.view.frame.size.height); [self.pinPageVC.tableview reloadData]; } - (void)getPendencyList { if (self.conversationData.groupID.length > 0) [self.pendencyViewModel loadData]; } - (void)openPendency:(id)sender { TUIGroupPendencyController *vc = [[TUIGroupPendencyController alloc] init]; @weakify(self); vc.cellClickBlock = ^(TUIGroupPendencyCell *_Nonnull cell) { if (cell.pendencyData.isRejectd || cell.pendencyData.isAccepted) { // After selecting, you will no longer enter the details page. return; } @strongify(self); [[V2TIMManager sharedInstance] getUsersInfo:@[ cell.pendencyData.fromUser ] succ:^(NSArray *profiles) { // Show user profile VC NSDictionary *param = @{ TUICore_TUIContactObjectFactory_UserProfileController_UserProfile : profiles.firstObject, TUICore_TUIContactObjectFactory_UserProfileController_PendencyData : cell.pendencyData, TUICore_TUIContactObjectFactory_UserProfileController_ActionType : @(3) }; [self.navigationController pushViewController:TUICore_TUIContactObjectFactory_UserProfileController_Classic param:param forResult:nil]; } fail:nil]; }; vc.viewModel = self.pendencyViewModel; [self.navigationController pushViewController:vc animated:YES]; } - (void)setConversationData:(TUIChatConversationModel *)conversationData { [super setConversationData:conversationData]; if (self.conversationData.groupID.length > 0) { _pendencyViewModel = [TUIGroupPendencyDataProvider new]; _pendencyViewModel.groupId = conversationData.groupID; } self.atUserList = [NSMutableArray array]; } #pragma mark - V2TIMGroupListener - (void)onReceiveJoinApplication:(NSString *)groupID member:(V2TIMGroupMemberInfo *)member opReason:(NSString *)opReason { [self getPendencyList]; } - (void)onGroupInfoChanged:(NSString *)groupID changeInfoList:(NSArray *)changeInfoList { if (![groupID isEqualToString:self.conversationData.groupID]) { return; } for (V2TIMGroupChangeInfo *changeInfo in changeInfoList) { if (changeInfo.type == V2TIM_GROUP_INFO_CHANGE_TYPE_NAME) { self.conversationData.title = changeInfo.value; return; } } } #pragma mark - TUIInputControllerDelegate - (void)inputController:(TUIInputController *)inputController didSendMessage:(V2TIMMessage *)msg { /** * If the text message has @ user, createTextAtMessage is required */ if (msg.elemType == V2TIM_ELEM_TYPE_TEXT) { NSMutableArray *atUserList = [NSMutableArray array]; for (TUIUserModel *model in self.atUserList) { if (model.userId) { [atUserList addObject:model.userId]; } } if (atUserList.count > 0) { NSData *cloudCustomData = msg.cloudCustomData; msg = [[V2TIMManager sharedInstance] createTextAtMessage:msg.textElem.text atUserList:atUserList]; msg.cloudCustomData = cloudCustomData; } /** * After the message is sent, the atUserList need to be reset */ [self.atUserList removeAllObjects]; } [super inputController:inputController didSendMessage:msg]; } - (void)inputControllerDidInputAt:(TUIInputController *)inputController { [super inputControllerDidInputAt:inputController]; /** * Input of @ character detected */ if (self.conversationData.groupID.length > 0) { if ([self.navigationController.topViewController isKindOfClass:NSClassFromString(@"TUISelectGroupMemberViewController")]) { return; } //When pushing a new VC, the keyboard needs to be hidden. [self.inputController reset]; __weak typeof(self) weakSelf = self; NSMutableDictionary *param = [NSMutableDictionary dictionary]; param[TUICore_TUIContactObjectFactory_SelectGroupMemberVC_GroupID] = self.conversationData.groupID; param[TUICore_TUIContactObjectFactory_SelectGroupMemberVC_Name] = TIMCommonLocalizableString(TUIKitAtSelectMemberTitle); param[TUICore_TUIContactObjectFactory_SelectGroupMemberVC_OptionalStyle] = @(1); [self.navigationController pushViewController:TUICore_TUIContactObjectFactory_SelectGroupMemberVC_Classic param:param forResult:^(NSDictionary *_Nonnull param) { NSArray *modelList = [param tui_objectForKey:TUICore_TUIContactObjectFactory_SelectGroupMemberVC_ResultUserList asClass:NSArray.class]; NSMutableString *atText = [[NSMutableString alloc] init]; for (int i = 0; i < modelList.count; i++) { TUIUserModel *model = modelList[i]; if (![model isKindOfClass:TUIUserModel.class]) { NSAssert(NO, @"Error data-type in modelList"); continue; } [weakSelf.atUserList addObject:model]; [atText appendString:[NSString stringWithFormat:@"@%@ ", model.name]]; } NSAttributedString *spaceString = [[NSAttributedString alloc] initWithString:atText attributes:@{NSFontAttributeName : kTUIInputNoramlFont, NSForegroundColorAttributeName : kTUIInputNormalTextColor}]; [weakSelf.inputController.inputBar addWordsToInputBar:spaceString ]; }]; } } - (void)inputController:(TUIInputController *)inputController didDeleteAt:(NSString *)atText { [super inputController:inputController didDeleteAt:atText]; for (TUIUserModel *user in self.atUserList) { if ([atText rangeOfString:user.name].location != NSNotFound) { [self.atUserList removeObject:user]; break; } } } - (void)inputController:(TUIInputController *)inputController didSelectMoreCell:(TUIInputMoreCell *)cell { [super inputController:inputController didSelectMoreCell:cell]; } #pragma mark - TUIBaseMessageControllerDelegate - (void)messageController:(TUIBaseMessageController *)controller onLongSelectMessageAvatar:(TUIMessageCell *)cell { if (!cell || !cell.messageData || !cell.messageData.identifier) { return; } if ([cell.messageData.identifier isEqualToString:[TUILogin getUserID]]) { return; } BOOL atUserExist = NO; for (TUIUserModel *model in self.atUserList) { if ([model.userId isEqualToString:cell.messageData.identifier]) { atUserExist = YES; break; } } if (!atUserExist) { TUIUserModel *user = [[TUIUserModel alloc] init]; user.userId = cell.messageData.identifier; user.name = cell.messageData.senderName; [self.atUserList addObject:user]; NSString *nameString = [NSString stringWithFormat:@"@%@ ", user.name]; UIFont *textFont = kTUIInputNoramlFont; NSAttributedString *spaceString = [[NSAttributedString alloc] initWithString:nameString attributes:@{NSFontAttributeName : textFont}]; [self.inputController.inputBar addWordsToInputBar:spaceString ]; } } #pragma mark - Override Methods - (NSString *)forwardTitleWithMyName:(NSString *)nameStr { return TIMCommonLocalizableString(TUIKitRelayGroupChatHistory); } @end