Files
featherVoice/TUIKit/TUIChat/UI_Minimalist/Chat/TUIBaseChatViewController_Minimalist.m
2025-08-08 10:49:36 +08:00

1508 lines
68 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// TUIBaseChatViewController_Minimalist.m
// UIKit
//
// Created by annidyfeng on 2019/5/21.
// Copyright © 2023 Tencent. All rights reserved.
//
#import "TUIBaseChatViewController_Minimalist.h"
#import <AVFoundation/AVFoundation.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import <Photos/Photos.h>
#import <TIMCommon/NSString+TUIEmoji.h>
#import <TIMCommon/TIMDefine.h>
#import <TIMCommon/TIMInputViewMoreActionProtocol.h>
#import <TIMCommon/TUIBubbleMessageCell_Minimalist.h>
#import <TIMCommon/TUIFloatViewController.h>
#import <TIMCommon/TUIGroupAvatar+Helper.h>
#import <TUICore/NSDictionary+TUISafe.h>
#import <TUICore/TUICore.h>
#import <TUICore/TUILogin.h>
#import <TUICore/TUIThemeManager.h>
#import <TUICore/TUITool.h>
#import <TUICore/NSString+TUIUtil.h>
#import "ReactiveObjC/ReactiveObjC.h"
#import "TUIAIDenoiseSignatureManager.h"
#import "TUIBaseMessageController_Minimalist.h"
#import "TUICameraViewController.h"
#import "TUIChatConfig.h"
#import "TUIChatDataProvider.h"
#import "TUIChatMediaDataProvider.h"
#import "TUIChatModifyMessageHelper.h"
#import "TUICloudCustomDataTypeCenter.h"
#import "TUIFileMessageCellData.h"
#import "TUIImageMessageCellData.h"
#import "TUIJoinGroupMessageCell_Minimalist.h"
#import "TUIMessageController_Minimalist.h"
#import "TUIMessageDataProvider.h"
#import "TUIMessageMultiChooseView_Minimalist.h"
#import "TUIMessageReadViewController_Minimalist.h"
#import "TUIReplyMessageCellData.h"
#import "TUITextMessageCellData.h"
#import "TUIVideoMessageCellData.h"
#import "TUIVoiceMessageCellData.h"
#import "UIAlertController+TUICustomStyle.h"
static UIView *gCustomTopView;
static UIView *gTopExentsionView;
static UIView *gGroupPinTopView;
static CGRect gCustomTopViewRect;
@interface TUIBaseChatViewController_Minimalist () <TUIBaseMessageControllerDelegate_Minimalist,
TUIInputControllerDelegate_Minimalist,
UIImagePickerControllerDelegate,
UIDocumentPickerDelegate,
UINavigationControllerDelegate,
TUIMessageMultiChooseViewDelegate_Minimalist,
TUIChatBaseDataProviderDelegate,
TUINotificationProtocol,
TUIJoinGroupMessageCellDelegate_Minimalist,
V2TIMConversationListener,
TUINavigationControllerDelegate,
V2TIMSDKListener,
TUIChatMediaDataListener,
TIMInputViewMoreActionProtocol>
@property(nonatomic, strong) TUINaviBarIndicatorView *titleView;
@property(nonatomic, strong) TUIMessageMultiChooseView_Minimalist *multiChooseView;
@property(nonatomic, assign) BOOL responseKeyboard;
@property(nonatomic, assign) BOOL isPageAppears;
@property(nonatomic, strong) TUIChatDataProvider *dataProvider;
@property(nonatomic, assign) BOOL firstAppear;
@property(nonatomic, copy) NSString *mainTitle;
@property(nonatomic, strong) UIImageView *backgroudView;
@property(nonatomic, strong) UIImageView *avatarView;
@property(nonatomic, strong) UILabel *mainTitleLabel;
@property(nonatomic, strong) UILabel *subTitleLabel;
@property(nonatomic, strong) TUIChatMediaDataProvider *mediaProvider;
@end
@implementation TUIBaseChatViewController_Minimalist
#pragma mark - Life Cycle
- (instancetype)init {
self = [super init];
if (self) {
[TUIBaseChatViewController_Minimalist createCachePath];
[[TUIAIDenoiseSignatureManager sharedInstance] updateSignature];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reloadTopViewsAndMessagePage)
name:TUICore_TUIChatExtension_ChatViewTopArea_ChangedNotification
object:nil];
[TUIChatMediaSendingManager.sharedInstance addCurrentVC:self];
}
return self;
}
- (void)setTitle:(NSString *)title {
self.mainTitle = title;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setupTopViews];
self.firstAppear = YES;
self.view.backgroundColor = TIMCommonDynamicColor(@"controller_bg_color", @"#FFFFFF");
self.edgesForExtendedLayout = UIRectEdgeNone;
[self configBackgroundView];
[self configNotify];
// setup UI
[self setupNavigator];
if (gCustomTopView) {
[self setupCustomTopView];
}
[self setupMessageController];
[self setupInputMoreMenu];
[self setupInputController];
// data provider
self.dataProvider = [[TUIChatDataProvider alloc] init];
self.dataProvider.delegate = self;
[[V2TIMManager sharedInstance] addIMSDKListener:self];
}
- (void)dealloc {
[TUICore unRegisterEventByObject:self];
}
- (void)willMoveToParentViewController:(UIViewController *)parent {
if (parent == nil) {
[self saveDraft];
}
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self configTopViewsViewWillAppear];
}
- (void)configTopViewsViewWillAppear {
if (gCustomTopView.superview != self.view) {
if (CGRectEqualToRect(gCustomTopView.frame, CGRectZero)) {
gCustomTopView.frame = CGRectMake(0, CGRectGetMaxY(gTopExentsionView.frame), gCustomTopViewRect.size.width, gCustomTopViewRect.size.height);
}
[self.view addSubview:gCustomTopView];
}
if (gTopExentsionView.superview != self.view) {
[self.view addSubview:gTopExentsionView];
}
if (gGroupPinTopView.superview != self.view && self.conversationData.groupID.length > 0){
[self.view addSubview:gGroupPinTopView];
}
[self reloadTopViewsAndMessagePage];
}
- (void)setupTopViews {
if (gTopExentsionView) {
[gTopExentsionView removeFromSuperview];
}
else {
gTopExentsionView = [[UIView alloc] init];
gTopExentsionView.clipsToBounds = YES;
}
if (gGroupPinTopView) {
[gGroupPinTopView removeFromSuperview];
}
else {
gGroupPinTopView = [[UIView alloc] init];
gGroupPinTopView.clipsToBounds = YES;
}
if (gTopExentsionView) {
[self setupTopExentsionView];
}
if (gCustomTopView) {
[self setupCustomTopView];
gCustomTopView.frame = CGRectMake(0, CGRectGetMaxY(gTopExentsionView.frame), gCustomTopViewRect.size.width, gCustomTopViewRect.size.height);
}
if (gGroupPinTopView && self.conversationData.groupID.length > 0) {
[self setupGroupPinTopView];
gGroupPinTopView.frame = CGRectMake(0, CGRectGetMaxY(gCustomTopView.frame), gGroupPinTopView.frame.size.width, gGroupPinTopView.frame.size.height);;
}
}
- (void)reloadTopViewsAndMessagePage {
gCustomTopView.frame = CGRectMake(0, CGRectGetMaxY(gTopExentsionView.frame), gCustomTopView.frame.size.width, gCustomTopView.frame.size.height);
if (gGroupPinTopView) {
gGroupPinTopView.frame = CGRectMake(0, CGRectGetMaxY(gCustomTopView.frame), gGroupPinTopView.frame.size.width, gGroupPinTopView.frame.size.height);;
}
CGFloat topMarginByCustomView = [self topMarginByCustomView];
if (_messageController.view.mm_y != topMarginByCustomView) {
CGFloat textViewHeight = TUIChatConfig.defaultConfig.enableMainPageInputBar? TTextView_Height:0;
_messageController.view.frame = CGRectMake(0, topMarginByCustomView, self.view.mm_w,
self.view.mm_h - textViewHeight - Bottom_SafeHeight - topMarginByCustomView);
[self.messageController scrollToBottom:YES];
}
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.responseKeyboard = YES;
self.isPageAppears = YES;
if (self.firstAppear) {
[self loadDraft];
self.firstAppear = NO;
}
self.mainTitleLabel.text = [self getMainTitleLabelText];
[self configHeadImageView:self.conversationData];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
self.responseKeyboard = NO;
self.isPageAppears = NO;
[self openMultiChooseBoard:NO];
[self.messageController enableMultiSelectedMode:NO];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}
- (void)setupNavigator {
TUINavigationController *naviController = (TUINavigationController *)self.navigationController;
if ([naviController isKindOfClass:TUINavigationController.class]) {
naviController.uiNaviDelegate = self;
UIImage *backimg = TIMCommonDynamicImage(@"nav_back_img", TIMCommonImagePath(@"nav_back"));
backimg = [backimg rtl_imageFlippedForRightToLeftLayoutDirection];
naviController.navigationItemBackArrowImage = backimg;
}
UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 24, 24)];
[backButton addTarget:self action:@selector(backButtonClick) forControlEvents:UIControlEventTouchUpInside];
UIImage *imgicon = [[TUIImageCache sharedInstance] getResourceFromCache:TUIChatImagePath_Minimalist(@"vc_back")];
imgicon = [imgicon rtl_imageFlippedForRightToLeftLayoutDirection];
[backButton setImage:imgicon forState:UIControlStateNormal];
UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
UIView *infoView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScale390(200), 40)];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(rightBarButtonClick)];
[infoView addGestureRecognizer:tap];
UIImageView *avatarView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
avatarView.image = self.conversationData.avatarImage;
avatarView.layer.cornerRadius = avatarView.frame.size.width / 2.0;
avatarView.layer.masksToBounds = YES;
[infoView addSubview:avatarView];
self.avatarView = avatarView;
UILabel *mainTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(avatarView.mm_x + avatarView.mm_w + kScale390(8), 0, kScale390(200), 20)];
mainTitleLabel.font = [UIFont boldSystemFontOfSize:14];
mainTitleLabel.text = [self getMainTitleLabelText];
mainTitleLabel.rtlAlignment = TUITextRTLAlignmentLeading;
[infoView addSubview:mainTitleLabel];
self.mainTitleLabel = mainTitleLabel;
self.subTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(mainTitleLabel.mm_x, 20, mainTitleLabel.mm_w, 20)];
self.subTitleLabel.font = [UIFont systemFontOfSize:12];
self.subTitleLabel.rtlAlignment = TUITextRTLAlignmentLeading;
[self updateSubTitleLabelText];
[infoView addSubview:self.subTitleLabel];
if (isRTL()) {
[avatarView resetFrameToFitRTL];
[mainTitleLabel resetFrameToFitRTL];
[self.subTitleLabel resetFrameToFitRTL];
}
__weak typeof(self) weakSelf = self;
[[RACObserve(_conversationData, otherSideTyping) distinctUntilChanged] subscribeNext:^(id otherSideTyping) {
BOOL otherSideTypingFlag = [otherSideTyping boolValue];
if (!otherSideTypingFlag) {
[weakSelf updateSubTitleLabelText];
}
else {
NSString *typingText = [NSString stringWithFormat:@"%@...", TIMCommonLocalizableString(TUIKitTyping)];
weakSelf.subTitleLabel.text = typingText;
}
}];
UIBarButtonItem *infoViewItem = [[UIBarButtonItem alloc] initWithCustomView:infoView];
self.navigationItem.leftBarButtonItems = @[ backButtonItem, infoViewItem ];
CGSize itemSize = CGSizeMake(30, 24);
NSMutableArray *rightBarButtonList = [NSMutableArray array];
NSMutableDictionary *param = [NSMutableDictionary dictionary];
if (self.conversationData.userID.length > 0) {
param[TUICore_TUIChatExtension_NavigationMoreItem_UserID] = self.conversationData.userID;
} else if (self.conversationData.groupID.length > 0) {
param[TUICore_TUIChatExtension_NavigationMoreItem_GroupID] = self.conversationData.groupID;
}
param[TUICore_TUIChatExtension_NavigationMoreItem_ItemSize] = NSStringFromCGSize(itemSize);
param[TUICore_TUIChatExtension_NavigationMoreItem_FilterVideoCall] = @(!TUIChatConfig.defaultConfig.enableVideoCall);
param[TUICore_TUIChatExtension_NavigationMoreItem_FilterAudioCall] = @(!TUIChatConfig.defaultConfig.enableAudioCall);
NSArray<TUIExtensionInfo *> *extensionList = [TUICore getExtensionList:TUICore_TUIChatExtension_NavigationMoreItem_MinimalistExtensionID param:param];
for (TUIExtensionInfo *info in extensionList) {
if (info.icon && info.onClicked) {
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, itemSize.width, itemSize.height)];
button.tui_extValueObj = info;
[button addTarget:self action:@selector(rightBarButtonClick:) forControlEvents:UIControlEventTouchUpInside];
[button setImage:info.icon forState:UIControlStateNormal];
UIBarButtonItem *rightItem = [[UIBarButtonItem alloc] initWithCustomView:button];
[rightBarButtonList addObject:rightItem];
}
}
if (rightBarButtonList.count > 0) {
self.navigationItem.rightBarButtonItems = rightBarButtonList.reverseObjectEnumerator.allObjects;
}
}
- (NSString *)getMainTitleLabelText {
if (self.conversationData.title.length > 0) {
return self.conversationData.title;
} else if (self.conversationData.groupID.length > 0) {
return self.conversationData.groupID;
} else {
return self.conversationData.userID;
}
}
- (void)updateSubTitleLabelText {
if (![TUIConfig defaultConfig].displayOnlineStatusIcon) {
self.subTitleLabel.text = @"";
return;
}
if (self.conversationData.userID.length > 0) {
[[V2TIMManager sharedInstance] getUserStatus:@[ self.conversationData.userID ]
succ:^(NSArray<V2TIMUserStatus *> *result) {
V2TIMUserStatus *status = result.firstObject;
self.subTitleLabel.text = [self getUserStatus:status];
}
fail:nil];
} else if (self.conversationData.groupID.length > 0) {
[[V2TIMManager sharedInstance] getGroupMemberList:self.conversationData.groupID
filter:V2TIM_GROUP_MEMBER_FILTER_ALL
nextSeq:0
succ:^(uint64_t nextSeq, NSArray<V2TIMGroupMemberFullInfo *> *memberList) {
NSMutableString *title = [NSMutableString string];
int memberCount = 0;
for (V2TIMGroupMemberFullInfo *info in memberList) {
NSString *name = (info.nameCard ?: info.nickName) ?: info.userID;
[title appendString:[NSString stringWithFormat:@"%@", name]];
if (++memberCount >= 5) {
break;
}
}
if (title.length > 0) {
title = [NSMutableString stringWithString:[title substringToIndex:title.length - 1]];
}
self.subTitleLabel.text = title;
}
fail:nil];
}
}
- (void)onUserStatusChanged:(NSArray<V2TIMUserStatus *> *)userStatusList {
if (!self.conversationData.userID) {
return;
}
for (V2TIMUserStatus *status in userStatusList) {
if ([status.userID isEqualToString:self.conversationData.userID]) {
self.subTitleLabel.text = [self getUserStatus:status];
break;
}
}
}
- (NSString *)getUserStatus:(V2TIMUserStatus *)status {
NSString *title = nil;
switch (status.statusType) {
case V2TIM_USER_STATUS_UNKNOWN: {
title = TIMCommonLocalizableString(TUIKitUserStatusUnknown);
} break;
case V2TIM_USER_STATUS_ONLINE: {
title = TIMCommonLocalizableString(TUIKitUserStatusOnline);
} break;
case V2TIM_USER_STATUS_OFFLINE: {
title = TIMCommonLocalizableString(TUIKitUserStatusOffline);
} break;
case V2TIM_USER_STATUS_UNLOGINED: {
title = TIMCommonLocalizableString(TUIKitUserStatusUnlogined);
} break;
default:
break;
}
return title;
}
- (void)backButtonClick {
[self.messageController readReport];
[self.navigationController popViewControllerAnimated:YES];
}
- (void)rightBarButtonClick:(UIButton *)button {
TUIExtensionInfo *info = button.tui_extValueObj;
if (info == nil || ![info isKindOfClass:TUIExtensionInfo.class] || info.onClicked == nil) {
return;
}
NSMutableDictionary *param = [NSMutableDictionary dictionary];
if (self.conversationData.userID.length > 0) {
param[TUICore_TUIChatExtension_NavigationMoreItem_UserID] = self.conversationData.userID;
} else if (self.conversationData.groupID.length > 0) {
param[TUICore_TUIChatExtension_NavigationMoreItem_GroupID] = self.conversationData.groupID;
}
if (self.navigationController) {
param[TUICore_TUIChatExtension_NavigationMoreItem_PushVC] = self.navigationController;
}
info.onClicked(param);
}
- (void)setupMessageController {
TUIMessageController_Minimalist *vc = [[TUIMessageController_Minimalist alloc] init];
vc.hightlightKeyword = self.highlightKeyword;
vc.locateMessage = self.locateMessage;
vc.isMsgNeedReadReceipt = self.conversationData.msgNeedReadReceipt && [TUIChatConfig defaultConfig].msgNeedReadReceipt;
_messageController = vc;
_messageController.delegate = self;
[_messageController setConversation:self.conversationData];
CGFloat textViewHeight = TUIChatConfig.defaultConfig.enableMainPageInputBar? TTextView_Height:0;
_messageController.view.frame = CGRectMake(0, [self topMarginByCustomView], self.view.frame.size.width,
self.view.frame.size.height - textViewHeight - Bottom_SafeHeight - [self topMarginByCustomView]);
[self addChildViewController:_messageController];
[self.view addSubview:_messageController.view];
[_messageController didMoveToParentViewController:self];
}
- (void)setupTopExentsionView {
if (gTopExentsionView.superview != self.view) {
[self.view addSubview:gTopExentsionView];
}
gTopExentsionView.frame = CGRectMake(0, 0, self.view.frame.size.width, 0);
NSMutableDictionary *param = [NSMutableDictionary dictionary];
if (self.conversationData.userID.length > 0) {
param[TUICore_TUIChatExtension_ChatViewTopArea_ChatID] = self.conversationData.userID;
param[TUICore_TUIChatExtension_ChatViewTopArea_IsGroup] = @"0";
} else if (self.conversationData.groupID.length > 0) {
param[TUICore_TUIChatExtension_ChatViewTopArea_IsGroup] = @"1";
param[TUICore_TUIChatExtension_ChatViewTopArea_ChatID] = self.conversationData.groupID;
}
[TUICore raiseExtension:TUICore_TUIChatExtension_ChatViewTopArea_MinimalistExtensionID parentView:gTopExentsionView param:param];
}
- (void)setupGroupPinTopView {
if (gGroupPinTopView.superview != self.view) {
[self.view addSubview:gGroupPinTopView];
}
gGroupPinTopView.backgroundColor = [UIColor clearColor];
gGroupPinTopView.frame = CGRectMake(0, 0, self.view.frame.size.width, 0);
}
- (void)setupCustomTopView {
if (gCustomTopView.superview != self.view) {
[self.view addSubview:gCustomTopView];
}
}
- (void)setupInputController {
_inputController = [[TUIInputController_Minimalist alloc] init];
_inputController.delegate = self;
_inputController.view.frame =
CGRectMake(0, self.view.frame.size.height - TTextView_Height - Bottom_SafeHeight, self.view.frame.size.width, TTextView_Height + Bottom_SafeHeight);
_inputController.view.autoresizingMask = UIViewAutoresizingFlexibleTopMargin;
[self addChildViewController:_inputController];
[self.view addSubview:_inputController.view];
_inputController.view.hidden = !TUIChatConfig.defaultConfig.enableMainPageInputBar;
}
- (void)setupInputMoreMenu {
id<TUIChatInputBarConfigDataSource> dataSource = [TUIChatConfig defaultConfig].inputBarDataSource;
if (dataSource && [dataSource respondsToSelector:@selector(inputBarShouldHideItemsInMoreMenuOfModel:)]) {
TUIChatInputBarMoreMenuItem tag = [dataSource inputBarShouldHideItemsInMoreMenuOfModel:self.conversationData];
self.conversationData.enableFile = !(tag & TUIChatInputBarMoreMenuItem_File);
self.conversationData.enableAlbum = !(tag & TUIChatInputBarMoreMenuItem_Album);
self.conversationData.enableTakePhoto = !(tag & TUIChatInputBarMoreMenuItem_TakePhoto);
self.conversationData.enableRecordVideo = !(tag & TUIChatInputBarMoreMenuItem_RecordVideo);
self.conversationData.enableWelcomeCustomMessage = !(tag & TUIChatInputBarMoreMenuItem_CustomMessage);
}
if (dataSource && [dataSource respondsToSelector:@selector(inputBarShouldAddNewItemsToMoreListOfModel:)]) {
NSArray *items = [dataSource inputBarShouldAddNewItemsToMoreListOfModel:self.conversationData];
if ([items isKindOfClass:NSArray.class]) {
self.conversationData.customizedNewItemsInMoreMenu = items;
}
}
}
- (void)configHeadImageView:(TUIChatConversationModel *)convData {
/**
* Setup default avatar
*/
if (convData.groupID.length > 0) {
/**
* If it is a group, change the group default avatar to the last used avatar
*/
convData.avatarImage = [TUIGroupAvatar getNormalGroupCacheAvatar:convData.groupID groupType:convData.groupType];
}
@weakify(self);
[[RACObserve(convData, faceUrl) distinctUntilChanged] subscribeNext:^(NSString *faceUrl) {
@strongify(self);
NSString *groupID = convData.groupID ?: @"";
NSString *pFaceUrl = convData.faceUrl ?: @"";
NSString *groupType = convData.groupType ?: @"";
UIImage *originAvatarImage = nil;
if (convData.groupID.length > 0) {
originAvatarImage = convData.avatarImage ?: DefaultGroupAvatarImageByGroupType(groupType);
} else {
originAvatarImage = convData.avatarImage ?: DefaultAvatarImage;
}
NSDictionary *param = @{
@"groupID" : groupID,
@"faceUrl" : pFaceUrl,
@"groupType" : groupType,
@"originAvatarImage" : originAvatarImage,
};
[TUIGroupAvatar configAvatarByParam:param targetView:self.avatarView];
}];
}
- (void)configBackgroundView {
self.backgroudView = [[UIImageView alloc] init];
self.backgroudView.backgroundColor =
TUIChatConfig.defaultConfig.backgroudColor ? TUIChatConfig.defaultConfig.backgroudColor : TUIChatDynamicColor(@"chat_controller_bg_color", @"#FFFFFF");
NSString *conversationID = [self getConversationID];
NSString *imgUrl = [self getBackgroundImageUrlByConversationID:conversationID];
if (TUIChatConfig.defaultConfig.backgroudImage) {
self.backgroudView.backgroundColor = UIColor.clearColor;
self.backgroudView.image = TUIChatConfig.defaultConfig.backgroudImage;
} else if (IS_NOT_EMPTY_NSSTRING(imgUrl)) {
[self.backgroudView sd_setImageWithURL:[NSURL URLWithString:imgUrl] placeholderImage:nil];
}
CGFloat textViewHeight = TUIChatConfig.defaultConfig.enableMainPageInputBar? TTextView_Height:0;
self.backgroudView.frame =
CGRectMake(0, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height - textViewHeight - Bottom_SafeHeight);
[self.view insertSubview:self.backgroudView atIndex:0];
}
- (void)configNotify {
[[V2TIMManager sharedInstance] addConversationListener:self];
[TUICore registerEvent:TUICore_TUIConversationNotify subKey:TUICore_TUIConversationNotify_ClearConversationUIHistorySubKey object:self];
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(onFriendInfoChanged:) name:@"FriendInfoChangedNotification" object:nil];
[TUICore registerEvent:TUICore_TUIContactNotify subKey:TUICore_TUIContactNotify_UpdateConversationBackgroundImageSubKey object:self];
}
#pragma mark - Public Methods
- (void)sendMessage:(V2TIMMessage *)message {
[self.messageController sendMessage:message];
}
- (void)sendMessage:(V2TIMMessage *)message placeHolderCellData:(TUIMessageCellData *)placeHolderCellData {
[self.messageController sendMessage:message placeHolderCellData:placeHolderCellData];
}
- (void)saveDraft {
NSString *content = [self.inputController.inputBar.inputTextView.textStorage tui_getPlainString];
TUIReplyPreviewData *previewData = nil;
if (self.inputController.referenceData) {
previewData = self.inputController.referenceData;
} else if (self.inputController.replyData) {
previewData = self.inputController.replyData;
}
if (previewData) {
NSDictionary *dict = @{
@"content" : content ?: @"",
@"messageReply" : @{
@"messageID" : previewData.msgID ?: @"",
@"messageAbstract" : [previewData.msgAbstract ?: @"" getInternationalStringWithfaceContent],
@"messageSender" : previewData.sender ?: @"",
@"messageType" : @(previewData.type),
@"messageTime" :
@(previewData.originMessage.timestamp ? [previewData.originMessage.timestamp timeIntervalSince1970] : 0), // Compatible for web
@"messageSequence" : @(previewData.originMessage.seq), // Compatible for web
@"version" : @(kDraftMessageReplyVersion),
},
};
NSMutableDictionary *mudic = [NSMutableDictionary dictionaryWithDictionary:dict];
if (IS_NOT_EMPTY_NSSTRING(previewData.messageRootID)) {
[mudic setObject:previewData.messageRootID forKey:@"messageRootID"];
}
NSError *error = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:mudic options:0 error:&error];
if (error == nil) {
content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
}
[TUIChatDataProvider saveDraftWithConversationID:self.conversationData.conversationID Text:content];
}
- (void)loadDraft {
NSString *draft = self.conversationData.draftText;
if (draft.length == 0) {
return;
}
NSError *error = nil;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:[draft dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&error];
if (error || jsonDict == nil) {
NSMutableAttributedString *formatEmojiString = [draft getAdvancedFormatEmojiStringWithFont:kTUIInputNoramlFont
textColor:kTUIInputNormalTextColor
emojiLocations:nil];
[self.inputController.inputBar addDraftToInputBar:formatEmojiString];
return;
}
/**
* Display draft
*/
NSString *draftContent = [jsonDict.allKeys containsObject:@"content"] ? jsonDict[@"content"] : @"";
NSMutableAttributedString *formatEmojiString = [draftContent getAdvancedFormatEmojiStringWithFont:kTUIInputNoramlFont
textColor:kTUIInputNormalTextColor
emojiLocations:nil];
[self.inputController.inputBar addDraftToInputBar:formatEmojiString];
NSString *messageRootID = [jsonDict.allKeys containsObject:@"messageRootID"] ? jsonDict[@"messageRootID"] : @"";
/**
* Display message reply preview bar
*/
if ([jsonDict isKindOfClass:NSDictionary.class] && [jsonDict.allKeys containsObject:@"messageReply"]) {
NSDictionary *reply = jsonDict[@"messageReply"];
if ([reply isKindOfClass:NSDictionary.class] && [reply.allKeys containsObject:@"messageID"] && [reply.allKeys containsObject:@"messageAbstract"] &&
[reply.allKeys containsObject:@"messageSender"] && [reply.allKeys containsObject:@"messageType"] && [reply.allKeys containsObject:@"version"]) {
NSInteger version = [reply[@"version"] integerValue];
if (version <= kDraftMessageReplyVersion) {
if (IS_NOT_EMPTY_NSSTRING(messageRootID)) {
TUIReplyPreviewData *replyData = [[TUIReplyPreviewData alloc] init];
replyData.msgID = reply[@"messageID"];
replyData.msgAbstract = reply[@"messageAbstract"];
replyData.sender = reply[@"messageSender"];
replyData.type = [reply[@"messageType"] integerValue];
replyData.messageRootID = messageRootID;
[self.inputController showReplyPreview:replyData];
} else {
TUIReferencePreviewData *replyData = [[TUIReferencePreviewData alloc] init];
replyData.msgID = reply[@"messageID"];
replyData.msgAbstract = reply[@"messageAbstract"];
replyData.sender = reply[@"messageSender"];
replyData.type = [reply[@"messageType"] integerValue];
[self.inputController showReferencePreview:replyData];
}
}
}
}
}
+ (void)setCustomTopView:(UIView *)view {
gCustomTopView = view;
gCustomTopViewRect = view.frame;
gCustomTopView.clipsToBounds = YES;
}
+ (UIView *)customTopView {
return gCustomTopView;
}
+ (UIView *)groupPinTopView {
return gGroupPinTopView;
}
+ (UIView *)topAreaBottomView {
if (gGroupPinTopView) {
return gGroupPinTopView;
}
if (gCustomTopView) {
return gCustomTopView;
}
if (gTopExentsionView) {
return gTopExentsionView;
}
return nil;
}
#pragma mark - Getters & Setters
- (void)setConversationData:(TUIChatConversationModel *)conversationData {
_conversationData = conversationData;
if (!IS_NOT_EMPTY_NSSTRING(_conversationData.title) || !IS_NOT_EMPTY_NSSTRING(conversationData.faceUrl)) {
[self checkTitle:YES];
}
}
- (CGFloat)topMarginByCustomView {
CGFloat gCutomTopViewH = gCustomTopView && gCustomTopView.superview ? gCustomTopView.mm_h : 0 ;
CGFloat gTopExtsionH = gTopExentsionView && gTopExentsionView.superview ? gTopExentsionView.mm_h : 0;
CGFloat gGroupPinTopViewH = gGroupPinTopView && gGroupPinTopView.superview ? gGroupPinTopView.mm_h : 0;
CGFloat height = gCutomTopViewH + gTopExtsionH + gGroupPinTopViewH;
return height;
}
#pragma mark - Event Response
- (void)checkTitle:(BOOL)force {
if (force || self.conversationData.title.length == 0) {
if (self.conversationData.userID.length > 0) {
self.conversationData.title = self.conversationData.userID;
@weakify(self);
[TUIChatDataProvider getFriendInfoWithUserId:self.conversationData.userID
SuccBlock:^(V2TIMFriendInfoResult *_Nonnull friendInfoResult) {
@strongify(self);
if (friendInfoResult.relation & V2TIM_FRIEND_RELATION_TYPE_IN_MY_FRIEND_LIST &&
friendInfoResult.friendInfo.friendRemark.length > 0) {
self.conversationData.title = friendInfoResult.friendInfo.friendRemark;
self.conversationData.faceUrl = friendInfoResult.friendInfo.userFullInfo.faceURL;
} else {
[TUIChatDataProvider
getUserInfoWithUserId:self.conversationData.userID
SuccBlock:^(V2TIMUserFullInfo *_Nonnull userInfo) {
if (userInfo.nickName.length > 0) {
self.conversationData.title = userInfo.nickName;
self.conversationData.faceUrl = userInfo.faceURL;
}
}
failBlock:nil];
}
}
failBlock:nil];
} else if (self.conversationData.groupID.length > 0) {
[TUIChatDataProvider getGroupInfoWithGroupID:self.conversationData.groupID
SuccBlock:^(V2TIMGroupInfoResult *_Nonnull groupResult) {
if (groupResult.info.groupName.length > 0) {
self.conversationData.title = groupResult.info.groupName;
self.conversationData.faceUrl = groupResult.info.faceURL;
self.conversationData.groupType = groupResult.info.groupType;
}
}
failBlock:nil];
}
}
}
- (void)leftBarButtonClick {
[self.navigationController popViewControllerAnimated:YES];
}
- (void)rightBarButtonClick {
//When pushing a new VC, the keyboard needs to be hidden.
[self.inputController reset];
if (_conversationData.userID.length > 0) {
[self getUserOrFriendProfileVCWithUserID:self.conversationData.userID
succBlock:^(UIViewController *_Nonnull vc) {
[self.navigationController pushViewController:vc animated:YES];
}
failBlock:^(int code, NSString *_Nonnull desc) {
[TUITool makeToastError:code msg:desc];
}];
} else {
NSDictionary *param = @{TUICore_TUIContactObjectFactory_GetGroupInfoVC_GroupID : self.conversationData.groupID};
[self.navigationController pushViewController:TUICore_TUIContactObjectFactory_GetGroupInfoVC_Minimalist param:param forResult:nil];
}
}
- (void)getUserOrFriendProfileVCWithUserID:(NSString *)userID succBlock:(void (^)(UIViewController *vc))succ failBlock:(nullable V2TIMFail)fail {
NSDictionary *param = @{
TUICore_TUIContactObjectFactory_GetUserOrFriendProfileVCMethod_UserIDKey: userID ? : @"",
TUICore_TUIContactObjectFactory_GetUserOrFriendProfileVCMethod_SuccKey: succ ? : ^(UIViewController *vc){},
TUICore_TUIContactObjectFactory_GetUserOrFriendProfileVCMethod_FailKey: fail ? : ^(int code, NSString * desc){}
};
[TUICore createObject:TUICore_TUIContactObjectFactory_Minimalist key:TUICore_TUIContactObjectFactory_GetUserOrFriendProfileVCMethod param:param];
}
#pragma mark - TUICore notify
- (void)onNotifyEvent:(NSString *)key subKey:(NSString *)subKey object:(id)anObject param:(NSDictionary *)param {
if ([key isEqualToString:TUICore_TUIConversationNotify] && [subKey isEqualToString:TUICore_TUIConversationNotify_ClearConversationUIHistorySubKey]) {
[self.messageController clearUImsg];
} else if ([key isEqualToString:TUICore_TUIContactNotify] && [subKey isEqualToString:TUICore_TUIContactNotify_UpdateConversationBackgroundImageSubKey]) {
NSString *conversationID = param[TUICore_TUIContactNotify_UpdateConversationBackgroundImageSubKey_ConversationID];
if (IS_NOT_EMPTY_NSSTRING(conversationID)) {
[self updateBackgroundImageUrlByConversationID:conversationID];
}
}
}
- (void)updateBackgroundImageUrlByConversationID:(NSString *)conversationID {
if ([[self getConversationID] isEqualToString:conversationID]) {
self.backgroudView.backgroundColor = UIColor.clearColor;
NSString *imgUrl = [self getBackgroundImageUrlByConversationID:conversationID];
if (IS_NOT_EMPTY_NSSTRING(imgUrl)) {
[self.backgroudView sd_setImageWithURL:[NSURL URLWithString:imgUrl] placeholderImage:nil];
} else {
self.backgroudView.image = nil;
}
}
}
- (NSString *)getBackgroundImageUrlByConversationID:(NSString *)targerConversationID {
if (targerConversationID.length == 0) {
return nil;
}
NSDictionary *dict = [NSUserDefaults.standardUserDefaults objectForKey:@"conversation_backgroundImage_map"];
if (dict == nil) {
dict = @{};
}
NSString *conversationID_UserID = [NSString stringWithFormat:@"%@_%@", targerConversationID, [TUILogin getUserID]];
if (![dict isKindOfClass:NSDictionary.class] || ![dict.allKeys containsObject:conversationID_UserID]) {
return nil;
}
return [dict objectForKey:conversationID_UserID];
}
- (NSString *)getConversationID {
NSString *conversationID = @"";
if (self.conversationData.conversationID.length > 0) {
conversationID = self.conversationData.conversationID;
} else if (self.conversationData.userID.length > 0) {
conversationID = [NSString stringWithFormat:@"c2c_%@", self.conversationData.userID];
} else if (self.conversationData.groupID.length > 0) {
conversationID = [NSString stringWithFormat:@"group_%@", self.conversationData.groupID];
}
return conversationID;
}
#pragma mark - TUIInputControllerDelegate
- (void)inputController:(TUIInputController_Minimalist *)inputController didChangeHeight:(CGFloat)height {
if (!self.responseKeyboard) {
return;
}
[UIView animateWithDuration:0.3
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
CGRect msgFrame = self.messageController.view.frame;
msgFrame.size.height = self.view.frame.size.height - height - [self topMarginByCustomView];
self.messageController.view.frame = msgFrame;
CGRect inputFrame = self.inputController.view.frame;
inputFrame.origin.y = msgFrame.origin.y + msgFrame.size.height;
inputFrame.size.height = height;
self.inputController.view.frame = inputFrame;
[self.messageController scrollToBottom:NO];
}
completion:nil];
}
- (void)inputController:(TUIInputController_Minimalist *)inputController didSendMessage:(V2TIMMessage *)msg {
[self.messageController sendMessage:msg];
}
- (void)inputControllerDidSelectMoreButton:(TUIInputController_Minimalist *)inputController {
NSArray *items = [self.dataProvider getInputMoreActionItemList:self.conversationData.userID
groupID:self.conversationData.groupID
conversationModel:self.conversationData
pushVC:self.navigationController
actionController:self];
if (items.count) {
UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[alertVC configItems:items];
[alertVC addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Cancel) style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alertVC animated:YES completion:nil];
}
}
- (void)inputControllerDidSelectCamera:(TUIInputController_Minimalist *)inputController {
[self.mediaProvider takePicture];
}
- (void)inputControllerDidInputAt:(TUIInputController_Minimalist *)inputController {
/**
* Handle to GroupChatVC
*/
}
- (void)inputController:(TUIInputController_Minimalist *)inputController didDeleteAt:(NSString *)atText {
/**
* Handle to GroupChatVC
*/
}
- (void)inputControllerBeginTyping:(TUIInputController_Minimalist *)inputController {
// for C2CChatVC
}
- (void)inputControllerEndTyping:(TUIInputController_Minimalist *)inputController {
// for C2CChatVC
}
#pragma mark - TUIBaseMessageControllerDelegate
- (void)didTapInMessageController:(TUIBaseMessageController_Minimalist *)controller {
[self.inputController reset];
}
- (BOOL)messageController:(TUIBaseMessageController_Minimalist *)controller willShowMenuInCell:(TUIMessageCell *)cell {
if ([self.inputController.inputBar.inputTextView isFirstResponder]) {
self.inputController.inputBar.inputTextView.overrideNextResponder = cell;
return YES;
}
return NO;
}
- (TUIMessageCellData *)messageController:(TUIBaseMessageController_Minimalist *)controller onNewMessage:(V2TIMMessage *)message {
return nil;
}
- (TUIMessageCell *)messageController:(TUIBaseMessageController_Minimalist *)controller onShowMessageData:(TUIMessageCellData *)data {
return nil;
}
- (void)messageController:(TUIBaseMessageController_Minimalist *)controller willDisplayCell:(TUIMessageCell *)cell withData:(TUIMessageCellData *)cellData {
if ([cell isKindOfClass:[TUIJoinGroupMessageCell_Minimalist class]]) {
TUIJoinGroupMessageCell_Minimalist *joinCell = (TUIJoinGroupMessageCell_Minimalist *)cell;
joinCell.joinGroupDelegate = self;
}
}
- (void)messageController:(TUIBaseMessageController_Minimalist *)controller onSelectMessageAvatar:(TUIMessageCell *)cell {
NSString *userID = nil;
if (cell.messageData.innerMessage.groupID.length > 0) {
userID = cell.messageData.innerMessage.sender;
} else {
if (cell.messageData.isUseMsgReceiverAvatar) {
if (cell.messageData.innerMessage.isSelf) {
userID = cell.messageData.innerMessage.userID;
} else {
userID = V2TIMManager.sharedInstance.getLoginUser;
}
} else {
userID = cell.messageData.innerMessage.sender;
}
}
if (userID == nil) {
return;
}
[self getUserOrFriendProfileVCWithUserID:userID
succBlock:^(UIViewController *vc) {
[self.navigationController pushViewController:vc animated:YES];
}
failBlock:nil];
}
- (void)messageController:(TUIBaseMessageController_Minimalist *)controller onSelectMessageContent:(TUIMessageCell *)cell {
cell.disableDefaultSelectAction = NO;
if (cell.disableDefaultSelectAction) {
return;
}
}
- (void)messageController:(TUIBaseMessageController_Minimalist *)controller onSelectMessageMenu:(NSInteger)menuType withData:(TUIMessageCellData *)data {
[self onSelectMessageMenu:menuType withData:data];
}
- (void)didHideMenuInMessageController:(TUIBaseMessageController_Minimalist *)controller {
self.inputController.inputBar.inputTextView.overrideNextResponder = nil;
}
- (void)messageController:(TUIBaseMessageController_Minimalist *)controller onReEditMessage:(TUIMessageCellData *)data {
V2TIMMessage *message = data.innerMessage;
if (message.elemType == V2TIM_ELEM_TYPE_TEXT) {
NSString *text = message.textElem.text;
self.inputController.inputBar.inputTextView.text = text;
[self.inputController.inputBar.inputTextView becomeFirstResponder];
}
}
- (CGFloat)getTopMarginByCustomView {
return [self topMarginByCustomView];
}
- (void)messageController:(TUIBaseMessageController_Minimalist *)controller onSelectMessageWhenMultiCheckboxAppear:(TUIMessageCellData *)data {
if (_multiChooseView) {
NSArray *uiMsgs = [self.messageController multiSelectedResult:TUIMultiResultOptionAll];
_multiChooseView.selectedCountLabel.text =
[NSString stringWithFormat:@"%lu %@", (unsigned long)uiMsgs.count, TIMCommonLocalizableString(TUIKitSelected)];
}
}
#pragma mark - TUIChatBaseDataProviderDelegate
- (NSString *)dataProvider:(TUIChatBaseDataProvider *)dataProvider mergeForwardTitleWithMyName:(NSString *)name {
return [self forwardTitleWithMyName:name];
}
- (NSString *)dataProvider:(TUIChatBaseDataProvider *)dataProvider mergeForwardMsgAbstactForMessage:(V2TIMMessage *)message {
return @"";
}
- (void)dataProvider:(TUIChatBaseDataProvider *)dataProvider sendMessage:(V2TIMMessage *)message {
[self.messageController sendMessage:message];
}
- (void)onSelectPhotoMoreCellData {
[self.mediaProvider selectPhoto];
}
- (void)onTakePictureMoreCellData {
[self.mediaProvider takePicture];
}
- (void)onTakeVideoMoreCellData {
[self.mediaProvider takeVideo];
}
- (void)onSelectFileMoreCellData {
[self.mediaProvider selectFile];
}
#pragma mark - TUINavigationControllerDelegate
- (void)navigationControllerDidClickLeftButton:(TUINavigationController *)controller {
if (controller.currentShowVC == self) {
[self.messageController readReport];
}
}
- (void)navigationControllerDidSideSlideReturn:(TUINavigationController *)controller fromViewController:(UIViewController *)fromViewController {
if ([fromViewController isEqual:self]) {
[self.messageController readReport];
}
}
#pragma mark - MessageMenu
- (void)onSelectMessageMenu:(NSInteger)menuType withData:(TUIMessageCellData *)data {
if (menuType == 0) {
[self openMultiChooseBoard:YES];
} else if (menuType == 1) {
if (data == nil) {
return;
}
NSMutableArray *uiMsgs = [NSMutableArray arrayWithArray:@[ data ]];
[self prepareForwardMessages:uiMsgs];
}
}
- (void)openMultiChooseBoard:(BOOL)open {
[self.view endEditing:YES];
if (_multiChooseView) {
[_multiChooseView removeFromSuperview];
}
if (open) {
_multiChooseView = [[TUIMessageMultiChooseView_Minimalist alloc] init];
_multiChooseView.frame = UIScreen.mainScreen.bounds;
_multiChooseView.delegate = self;
_multiChooseView.titleLabel.text = self.conversationData.title;
_multiChooseView.selectedCountLabel.text = [NSString stringWithFormat:@"%d %@", 1, TIMCommonLocalizableString(TUIKitSelected)];
if (@available(iOS 12.0, *)) {
if (@available(iOS 13.0, *)) {
// > ios 12
[UIApplication.sharedApplication.keyWindow addSubview:_multiChooseView];
} else {
// ios = 12
UIView *view = self.navigationController.view;
if (view == nil) {
view = self.view;
}
[view addSubview:_multiChooseView];
}
} else {
// < ios 12
[UIApplication.sharedApplication.keyWindow addSubview:_multiChooseView];
}
} else {
[self.messageController enableMultiSelectedMode:NO];
}
}
- (void)messageMultiChooseViewOnCancelClicked:(TUIMessageMultiChooseView_Minimalist *)multiChooseView {
[self openMultiChooseBoard:NO];
[self.messageController enableMultiSelectedMode:NO];
}
- (void)messageMultiChooseViewOnRelayClicked:(TUIMessageMultiChooseView_Minimalist *)multiChooseView {
NSArray *uiMsgs = [self.messageController multiSelectedResult:TUIMultiResultOptionAll];
[self prepareForwardMessages:uiMsgs];
}
- (void)messageMultiChooseViewOnDeleteClicked:(TUIMessageMultiChooseView_Minimalist *)multiChooseView {
NSArray *uiMsgs = [self.messageController multiSelectedResult:TUIMultiResultOptionAll];
if (uiMsgs.count == 0) {
[TUITool makeToast:TIMCommonLocalizableString(TUIKitRelayNoMessageTips)];
return;
}
[self.messageController deleteMessages:uiMsgs];
[self openMultiChooseBoard:NO];
[self.messageController enableMultiSelectedMode:NO];
}
- (void)prepareForwardMessages:(NSArray<TUIMessageCellData *> *)uiMsgs {
if (uiMsgs.count == 0) {
[TUITool makeToast:TIMCommonLocalizableString(TUIKitRelayNoMessageTips)];
return;
}
BOOL hasUnsupportMsg = NO;
for (TUIMessageCellData *data in uiMsgs) {
if (data.status != Msg_Status_Succ) {
hasUnsupportMsg = YES;
break;
}
}
if (hasUnsupportMsg) {
UIAlertController *vc = [UIAlertController alertControllerWithTitle:TIMCommonLocalizableString(TUIKitRelayUnsupportForward)
message:nil
preferredStyle:UIAlertControllerStyleAlert];
[vc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Confirm)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action){
}]];
[self presentViewController:vc animated:YES completion:nil];
return;
}
__weak typeof(self) weakSelf = self;
UIAlertController *tipsVc = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
// Forward one-by-one
[tipsVc
tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(TUIKitRelayOneByOneForward)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
if (uiMsgs.count <= 30) {
[weakSelf selectTarget:NO toForwardMessage:uiMsgs orForwardText:nil];
return;
}
UIAlertController *vc =
[UIAlertController alertControllerWithTitle:TIMCommonLocalizableString(TUIKitRelayOneByOnyOverLimit)
message:nil
preferredStyle:UIAlertControllerStyleAlert];
[vc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Cancel)
style:UIAlertActionStyleDefault
handler:nil]];
[vc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(TUIKitRelayCombineForwad)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
[weakSelf selectTarget:YES
toForwardMessage:uiMsgs
orForwardText:nil];
}]];
[weakSelf presentViewController:vc animated:YES completion:nil];
}]];
// Merge-forward
[tipsVc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(TUIKitRelayCombineForwad)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action) {
[weakSelf selectTarget:YES toForwardMessage:uiMsgs orForwardText:nil];
}]];
[tipsVc tuitheme_addAction:[UIAlertAction actionWithTitle:TIMCommonLocalizableString(Cancel) style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:tipsVc animated:YES completion:nil];
}
- (void)selectTarget:(BOOL)mergeForward toForwardMessage:(NSArray<TUIMessageCellData *> *)uiMsgs orForwardText:(NSString *)forwardText {
__weak typeof(self) weakSelf = self;
UIViewController *vc = (UIViewController *)[TUICore createObject:TUICore_TUIConversationObjectFactory_Minimalist
key:TUICore_TUIConversationObjectFactory_ConversationSelectVC_Minimalist
param:nil];
vc.navigateValueCallback = ^(NSDictionary *param) {
NSArray<NSDictionary *> *selectList = param[TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList];
NSMutableArray<TUIChatConversationModel *> *targetList = [NSMutableArray arrayWithCapacity:selectList.count];
for (NSDictionary *selectItem in selectList) {
TUIChatConversationModel *model = [TUIChatConversationModel new];
model.title = selectItem[TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_Title];
model.userID = selectItem[TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_UserID];
model.groupID = selectItem[TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_GroupID];
model.conversationID = selectItem[TUICore_TUIConversationObjectFactory_ConversationSelectVC_ResultList_ConversationID];
[targetList addObject:model];
}
if (uiMsgs.count > 0) {
[weakSelf forwardMessages:uiMsgs toTargets:targetList merge:mergeForward];
} else if (forwardText.length > 0) {
[weakSelf forwardText:forwardText toConverations:targetList];
}
};
TUIFloatViewController *floatVC = [[TUIFloatViewController alloc] init];
[floatVC appendChildViewController:(id)vc topMargin:kScale390(87.5)];
[floatVC.topGestureView setTitleText:@""
subTitleText:@""
leftBtnText:TIMCommonLocalizableString(TUIKitCreateCancel)
rightBtnText:TIMCommonLocalizableString(MultiSelect)];
floatVC.topGestureView.subTitleLabel.hidden = YES;
[self presentViewController:floatVC
animated:YES
completion:^{
[weakSelf openMultiChooseBoard:NO];
[weakSelf.messageController enableMultiSelectedMode:NO];
}];
}
- (void)forwardMessages:(NSArray<TUIMessageCellData *> *)uiMsgs toTargets:(NSArray<TUIChatConversationModel *> *)targets merge:(BOOL)merge {
if (uiMsgs.count == 0 || targets.count == 0) {
return;
}
@weakify(self);
[self.dataProvider getForwardMessageWithCellDatas:uiMsgs
toTargets:targets
Merge:merge
ResultBlock:^(TUIChatConversationModel *_Nonnull targetConversation, NSArray<V2TIMMessage *> *_Nonnull msgs) {
@strongify(self);
TUIChatConversationModel *convCellData = targetConversation;
NSTimeInterval timeInterval = convCellData.groupID.length ? 0.09 : 0.05;
TUISendMessageAppendParams *appendParams = [[TUISendMessageAppendParams alloc] init];
appendParams.isSendPushInfo = YES;
appendParams.isOnlineUserOnly = NO;
appendParams.priority = V2TIM_PRIORITY_NORMAL;
/**
*
* Forward to currernt chat vc
*/
if ([convCellData.conversationID isEqualToString:self.conversationData.conversationID]) {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
for (V2TIMMessage *imMsg in msgs) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.messageController sendMessage:imMsg];
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[NSThread sleepForTimeInterval:timeInterval];
}
});
return;
}
/**
* Forward to other chat user
*/
for (V2TIMMessage *message in msgs) {
message.needReadReceipt = self.conversationData.msgNeedReadReceipt && [TUIChatConfig defaultConfig].msgNeedReadReceipt;
[TUIMessageDataProvider sendMessage:message
toConversation:convCellData
appendParams:appendParams
Progress:nil
SuccBlock:^{
/**
* Messages sent to other chats need to broadcast the message sending status, which is convenient to refresh the message status after
* entering the corresponding chat
*/
[NSNotificationCenter.defaultCenter postNotificationName:TUIKitNotification_onMessageStatusChanged object:message];
}
FailBlock:^(int code, NSString *desc) {
[NSNotificationCenter.defaultCenter postNotificationName:TUIKitNotification_onMessageStatusChanged object:message];
}];
/**
* The delay here is to ensure the order of the receiving end as much as possible when forwarding in batches one by one
*/
[NSThread sleepForTimeInterval:timeInterval];
}
}
fail:^(int code, NSString *desc) {
NSLog(@"%@", desc);
NSAssert(NO, desc);
}];
}
- (NSString *)forwardTitleWithMyName:(NSString *)nameStr {
return @"";
}
#pragma mark - Message reply
- (void)messageController:(TUIBaseMessageController_Minimalist *)controller onRelyMessage:(nonnull TUIMessageCellData *)data {
@weakify(self);
[self.inputController exitReplyAndReference:^{
@strongify(self);
NSString *desc = @"";
desc = [self replyReferenceMessageDesc:data];
TUIReplyPreviewData *replyData = [[TUIReplyPreviewData alloc] init];
replyData.msgID = data.msgID;
replyData.msgAbstract = desc;
replyData.sender = data.senderName;
replyData.type = (NSInteger)data.innerMessage.elemType;
replyData.originMessage = data.innerMessage;
NSMutableDictionary *cloudResultDic = [[NSMutableDictionary alloc] initWithCapacity:5];
if (replyData.originMessage.cloudCustomData) {
NSDictionary *originDic = [TUITool jsonData2Dictionary:replyData.originMessage.cloudCustomData];
if (originDic && [originDic isKindOfClass:[NSDictionary class]]) {
[cloudResultDic addEntriesFromDictionary:originDic];
}
}
NSString *messageParentReply = cloudResultDic[@"messageReply"];
NSString *messageRootID = [messageParentReply valueForKey:@"messageRootID"];
if (!IS_NOT_EMPTY_NSSTRING(messageRootID)) {
/**
* If the original message does not have messageRootID, you need to make the msgID of the current original message as the root
*/
if (IS_NOT_EMPTY_NSSTRING(replyData.originMessage.msgID)) {
messageRootID = replyData.originMessage.msgID;
}
}
replyData.messageRootID = messageRootID;
[self.inputController showReplyPreview:replyData];
}];
}
- (NSString *)replyReferenceMessageDesc:(TUIMessageCellData *)data {
NSString *desc = @"";
if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_FILE) {
desc = data.innerMessage.fileElem.filename;
} else if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_MERGER) {
desc = data.innerMessage.mergerElem.title;
} else if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_CUSTOM) {
desc = [TUIMessageDataProvider getDisplayString:data.innerMessage];
} else if (data.innerMessage.elemType == V2TIM_ELEM_TYPE_TEXT) {
desc = data.innerMessage.textElem.text;
}
return desc;
}
#pragma mark - Message quote
- (void)messageController:(TUIBaseMessageController_Minimalist *)controller onReferenceMessage:(TUIMessageCellData *)data {
@weakify(self);
[self.inputController exitReplyAndReference:^{
@strongify(self);
NSString *desc = @"";
desc = [self replyReferenceMessageDesc:data];
TUIReferencePreviewData *referenceData = [[TUIReferencePreviewData alloc] init];
referenceData.msgID = data.msgID;
referenceData.msgAbstract = desc;
referenceData.sender = data.senderName;
referenceData.type = (NSInteger)data.innerMessage.elemType;
referenceData.originMessage = data.innerMessage;
[self.inputController showReferencePreview:referenceData];
}];
}
#pragma mark - Translation forward
- (void)messageController:(TUIBaseMessageController_Minimalist *)controller onForwardText:(NSString *)text {
if (text.length == 0) {
return;
}
[self selectTarget:NO toForwardMessage:nil orForwardText:text];
}
- (void)forwardText:(NSString *)text toConverations:(NSArray<TUIChatConversationModel *> *)conversations {
TUISendMessageAppendParams *appendParams = [[TUISendMessageAppendParams alloc] init];
appendParams.isSendPushInfo = YES;
appendParams.isOnlineUserOnly = NO;
appendParams.priority = V2TIM_PRIORITY_NORMAL;
for (TUIChatConversationModel *conversation in conversations) {
V2TIMMessage *message = [[V2TIMManager sharedInstance] createTextMessage:text];
dispatch_async(dispatch_get_main_queue(), ^{
if ([conversation.conversationID isEqualToString:self.conversationData.conversationID]) {
// Send text to myself
[self.messageController sendMessage:message];
} else {
// Send to other conversation
message.needReadReceipt = self.conversationData.msgNeedReadReceipt && [TUIChatConfig defaultConfig].msgNeedReadReceipt;
[TUIMessageBaseDataProvider sendMessage:message
toConversation:conversation
appendParams:appendParams
Progress:nil
SuccBlock:^{
[NSNotificationCenter.defaultCenter postNotificationName:TUIKitNotification_onMessageStatusChanged object:message];
}
FailBlock:^(int code, NSString *desc) {
[NSNotificationCenter.defaultCenter postNotificationName:TUIKitNotification_onMessageStatusChanged object:message];
}];
}
});
}
}
#pragma mark - Private Methods
+ (void)createCachePath {
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:TUIKit_Image_Path]) {
[fileManager createDirectoryAtPath:TUIKit_Image_Path withIntermediateDirectories:YES attributes:nil error:nil];
}
if (![fileManager fileExistsAtPath:TUIKit_Video_Path]) {
[fileManager createDirectoryAtPath:TUIKit_Video_Path withIntermediateDirectories:YES attributes:nil error:nil];
}
if (![fileManager fileExistsAtPath:TUIKit_Voice_Path]) {
[fileManager createDirectoryAtPath:TUIKit_Voice_Path withIntermediateDirectories:YES attributes:nil error:nil];
}
if (![fileManager fileExistsAtPath:TUIKit_File_Path]) {
[fileManager createDirectoryAtPath:TUIKit_File_Path withIntermediateDirectories:YES attributes:nil error:nil];
}
if (![fileManager fileExistsAtPath:TUIKit_DB_Path]) {
[fileManager createDirectoryAtPath:TUIKit_DB_Path withIntermediateDirectories:YES attributes:nil error:nil];
}
}
#pragma mark - TUIJoinGroupMessageCellDelegate
- (void)didTapOnRestNameLabel:(TUIJoinGroupMessageCell_Minimalist *)cell withIndex:(NSInteger)index {
NSString *userId = cell.joinData.userIDList[index];
[self getUserOrFriendProfileVCWithUserID:userId
succBlock:^(UIViewController *vc) {
[self.navigationController pushViewController:vc animated:YES];
}
failBlock:^(int code, NSString *desc) {
[TUITool makeToastError:code msg:desc];
}];
}
#pragma mark - V2TIMConversationListener
- (void)onConversationChanged:(NSArray<V2TIMConversation *> *)conversationList {
for (V2TIMConversation *conv in conversationList) {
if ([conv.conversationID isEqualToString:self.conversationData.conversationID]) {
if (!self.conversationData.otherSideTyping) {
self.conversationData.title = conv.showName;
}
break;
}
}
}
#pragma mark - FriendInfoChangedNotification
- (void)onFriendInfoChanged:(NSNotification *)notice {
[self checkTitle:YES];
}
#pragma mark - Media Provider
- (void)sendPlaceHolderUIMessage:(TUIMessageCellData *)cellData {
[self.messageController sendPlaceHolderUIMessage:cellData];
}
- (TUIChatMediaDataProvider *)mediaProvider {
if (_mediaProvider == nil) {
_mediaProvider = [[TUIChatMediaDataProvider alloc] init];
_mediaProvider.listener = self;
_mediaProvider.presentViewController = self;
_mediaProvider.conversationID = _conversationData.conversationID;
}
return _mediaProvider;
}
- (void)onProvideImage:(NSString *)imageUrl {
V2TIMMessage *message = [V2TIMManager.sharedInstance createImageMessage:imageUrl];
[self sendMessage:message];
}
- (void)onProvideImageError:(NSString *)errorMessage {
[TUITool makeToast:errorMessage];
}
- (void)onProvidePlaceholderVideoSnapshot:(NSString *)snapshotUrl
SnapImage:(UIImage *)image
Completion:(void (^__nullable)(BOOL finished, TUIMessageCellData *placeHolderCellData))completion {
TUIMessageCellData *videoCellData = [TUIVideoMessageCellData placeholderCellDataWithSnapshotUrl:snapshotUrl thubImage:image];
[self.messageController sendPlaceHolderUIMessage:videoCellData];
if (completion) {
completion(YES,videoCellData);
}
}
- (void)onProvideVideo:(NSString *)videoUrl
snapshot:(NSString *)snapshotUrl
duration:(NSInteger)duration
placeHolderCellData:(TUIMessageCellData *)placeHolderCellData {
V2TIMMessage *message = [V2TIMManager.sharedInstance createVideoMessage:videoUrl
type:videoUrl.pathExtension
duration:(int)duration
snapshotPath:snapshotUrl];
[self sendMessage:message placeHolderCellData:placeHolderCellData];
}
- (void)onProvideVideoError:(NSString *)errorMessage {
[TUITool makeToast:errorMessage];
}
- (void)onProvideFile:(NSString *)fileUrl filename:(NSString *)filename fileSize:(NSInteger)fileSize {
V2TIMMessage *message = [V2TIMManager.sharedInstance createFileMessage:fileUrl fileName:filename];
[self sendMessage:message];
}
- (void)onProvideFileError:(NSString *)errorMessage {
[TUITool makeToast:errorMessage];
}
- (NSString *)currentConversationID {
return self.conversationData.conversationID;
}
- (BOOL)isPageAppears {
return self.responseKeyboard;
}
@end