You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
478 lines
19 KiB
478 lines
19 KiB
// |
|
// ChatViewController.m |
|
// LekangGuard |
|
// |
|
// Created by ecell on 2023/1/3. |
|
// |
|
|
|
#import "ChatViewController.h" |
|
#import <SDWebImage/UIButton+WebCache.h> |
|
#import "AppDelegate.h" |
|
#import "XHDisplayTextViewController.h" |
|
#import "XHDisplayMediaViewController.h" |
|
#import "XHDisplayLocationViewController.h" |
|
#import "XHAudioPlayerHelper.h" |
|
#import "LatestLocationModel.h" |
|
#import "CJFileUtility.h" |
|
#import "VoiceConverter.h" |
|
#import <AVFoundation/AVFoundation.h> |
|
|
|
|
|
@interface ChatViewController () <XHAudioPlayerHelperDelegate,UIActionSheetDelegate> |
|
|
|
@property (nonatomic, strong) NSArray *emotionManagers; |
|
@property (nonatomic ,strong) UIBarButtonItem *rightBarBtn; |
|
|
|
@property (nonatomic ,assign) NSInteger page; |
|
|
|
@end |
|
|
|
@implementation ChatViewController |
|
|
|
- (void)viewDidLoad |
|
{ |
|
[super viewDidLoad]; |
|
self.allowsSendMultiMedia = NO; |
|
self.allowsSendVoice = YES; |
|
self.page = 1; |
|
self.view.backgroundColor = RGB(241, 241, 241); |
|
self.zx_navTitle = GJText(@"微聊"); |
|
kWeakSelf(self) |
|
[self zx_setRightBtnWithImg:ImageName_(@"icon_delect_1") clickedBlock:^(ZXNavItemBtn * _Nonnull btn) { |
|
|
|
EasyAlertView *alertV = [EasyAlertView alertViewWithTitle:GJText(@"是否清除聊天信息") subtitle:nil AlertViewType:AlertViewTypeSystemAlert config:nil]; |
|
[alertV addAlertItem:^EasyAlertItem *{ |
|
return [EasyAlertItem itemWithTitle:GJText(@"取消") type:AlertItemTypeSystemCancel callback:nil]; |
|
}]; |
|
[alertV addAlertItem:^EasyAlertItem *{ |
|
return [EasyAlertItem itemWithTitle:GJText(@"确定") type:AlertItemTypeSystemDefault callback:^(EasyAlertView *showview, long index) { |
|
[weakself DeleteChatMessage]; |
|
}]; |
|
}]; |
|
[alertV showAlertView]; |
|
}]; |
|
self.messageInputView.multiMediaSendButton.hidden = YES; |
|
self.messageInputView.faceSendButton.hidden = YES; |
|
self.messageInputView.inputTextView.hidden = YES; |
|
self.messageInputView.voiceChangeButton.hidden = YES; |
|
// UIApplication *app = [UIApplication sharedApplication]; |
|
// [[NSNotificationCenter defaultCenter] addObserver:self |
|
// selector:@selector(applicationWillResignActive:) |
|
// name:UIApplicationWillResignActiveNotification |
|
// object:app]; |
|
// |
|
// [[NSNotificationCenter defaultCenter] addObserver:self |
|
// selector:@selector(applicationDidBecomeActive:) |
|
// name:UIApplicationDidBecomeActiveNotification |
|
// object:app]; |
|
// |
|
//MARK :防止在ios 11 的系统中刷新Tableview 之后的抖动 |
|
|
|
self.messageTableView.frame = CGRectMake(0, iPhoneX_TabbarHeight, SCREEN_WIDTH, SCREEN_HEIGHT-iPhoneX_NavHeight); |
|
self.messageTableView.estimatedRowHeight = 0; |
|
self.messageTableView.estimatedSectionHeaderHeight = 0; |
|
self.messageTableView.estimatedSectionFooterHeight = 0; |
|
[self.messageTableView setBackgroundColor:KKClearColor]; |
|
|
|
[self.messageTableView bindHeadRefreshHandler:^{ |
|
[UICommon feedbackGenerator]; |
|
[weakself QueryChatListModel:TableisReload_h pageSize:KSizeNO current:self.page]; |
|
|
|
} themeColor:KKMainColor refreshStyle:KafkaRefreshStyleReplicatorWoody]; |
|
|
|
|
|
[UICommon MessageUpload:@"加载中"]; |
|
[weakself.messageTableView.headRefreshControl beginRefreshing]; |
|
} |
|
|
|
|
|
/// 收到推送聊天信息 |
|
- (void)pusChatMessage:(NSDictionary *)chatModel |
|
{ |
|
NSDictionary *dic = chatModel[@"message"]; |
|
NSString *uid = dic[@"senderID"]; |
|
/// 防止同设备两个不同账号切换后,消息推送重复拉取数据 |
|
if (![uid isEqualToString:APIManager.sharedManager.loginModel.openid]) |
|
[self QueryChatListModel:TableisReload_f pageSize:1 current:1]; |
|
} |
|
|
|
#pragma mark 查询微聊数据 |
|
/// 查询微聊数据 |
|
- (void)QueryChatListModel:(TableisReload)isReload pageSize:(NSInteger)pageSize current:(NSInteger)current |
|
{ |
|
kWeakSelf(self) |
|
NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; |
|
[parameters setValue:@(current) forKey:@"current"]; |
|
[parameters setValue:APIManager.sharedManager.deviceModel.groupid forKey:@"groupId"]; |
|
[parameters setValue:@(pageSize) forKey:@"pageSize"]; |
|
[[[APIManager sharedManager] APGET:QueryChat_URL parameters:parameters resultClass:BaseModel.class] subscribeNext:^(BaseModel *model) { |
|
[UICommon HidenLoading]; |
|
if (isReload == TableisReload_d) |
|
[weakself.modelListArr removeAllObjects]; |
|
if (model.records.count > 0) |
|
{ |
|
NSMutableArray *messageArr = [NSMutableArray arrayWithArray:[[model.records reverseObjectEnumerator] allObjects]];//倒序操作 |
|
[messageArr enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL * _Nonnull stop) { |
|
XHMessage *message = [weakself ModelConversion:obj]; |
|
if (isReload == TableisReload_f && messageArr.count > 0) |
|
[self.modelListArr insertObject:message atIndex:self.modelListArr.count]; |
|
else |
|
[self.modelListArr insertObject:message atIndex:idx]; |
|
|
|
XHMessage *msg = weakself.modelListArr[idx]; |
|
NSLog(@"%@",[UICommon getDateFormatWithStr:@"YYYY-MM-dd HH:mm:ss" date:msg.timestamp]); |
|
}]; |
|
|
|
|
|
self.page ++; |
|
//NSLog(@"%@",messageArr); |
|
} |
|
weakself.messages = weakself.modelListArr; |
|
NSInteger rows = 0; |
|
if (weakself.modelListArr.count > 0) |
|
{ |
|
rows = isReload == TableisReload_f ? model.records.count - 1 : weakself.modelListArr.count-1; |
|
if (isReload == TableisReload_h && model.records.count <= 0) |
|
rows = 0; |
|
} |
|
[self reloadTable:rows]; |
|
self.zx_navRightBtn.hidden = weakself.modelListArr.count > 0 ? NO : YES; |
|
[weakself.messageTableView.headRefreshControl endRefreshing]; |
|
|
|
} error:^(NSError * _Nullable error) { |
|
NSDictionary *dic = error.userInfo; |
|
[UICommon MessageErrorText:dic[NSLocalizedDescriptionKey]]; |
|
}]; |
|
} |
|
|
|
|
|
|
|
/** |
|
* 发送语音消息的回调方法 |
|
* |
|
* @param voicePath 目标语音本地路径 |
|
* @param voiceDuration 目标语音时长 |
|
* @param sender 发送者的名字 |
|
* @param date 发送时间 |
|
*/ |
|
- (void)didSendVoice:(NSString *)voicePath voiceDuration:(NSNumber *)voiceDuration fromSender:(NSString *)sender onDate:(NSDate *)date { |
|
if ([voiceDuration doubleValue] < 1) { |
|
[UICommon MessageErrorText:@"录音时间太短"]; |
|
return; |
|
} |
|
|
|
XHMessage *message = [[XHMessage alloc] initWithVoicePath:voicePath |
|
voiceUrl:nil |
|
voiceDuration:F(@"%@", voiceDuration) |
|
sender:sender |
|
timestamp:date]; |
|
// message.avatar = ImageName_(APIManager.sharedManager.deviceModel.image); |
|
message.avatarUrl = APIManager.sharedManager.deviceModel.image; |
|
message.senderID = APIManager.sharedManager.loginModel.openid; |
|
|
|
[self wavToAmr:voicePath]; |
|
voicePath = [CJFileUtility changeFileExt:voicePath ext:@"amr"]; |
|
|
|
kWeakSelf(self) |
|
NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; |
|
[parameters setValue:@(voiceDuration.integerValue) forKey:@"duration"]; |
|
[parameters setValue:APIManager.sharedManager.deviceModel.groupid forKey:@"groupId"]; |
|
[parameters setValue:@(3) forKey:@"myType"]; |
|
// [parameters setValue:@"" forKey:@"content"]; |
|
|
|
[[[APIManager sharedManager] uploadFileWithURL:AddChat_URL parameters:parameters filePath:voicePath fileData:nil] subscribeNext:^(NSArray *x) { |
|
|
|
[UICommon HidenLoading]; |
|
XHMessage *model = [self ModelConversion:x[0]]; |
|
NSString *fileName = [NSString stringWithFormat:@"%@.wav",model.Id]; |
|
NSString *wavPath = [CJFileUtility documentsPathSubDir:@"msg_audio" andFile:fileName]; |
|
[CJFileUtility moveFile:[CJFileUtility changeFileExt:voicePath ext:@"wav"] to:wavPath]; |
|
model.voicePath = wavPath; |
|
[weakself.modelListArr addObject:model]; |
|
weakself.messages = weakself.modelListArr; |
|
[self reloadTable:weakself.modelListArr.count-1]; |
|
//[weakself addMessage:model]; |
|
self.zx_navRightBtn.hidden = weakself.modelListArr.count > 0 ? NO : YES; |
|
[weakself finishSendMessageWithBubbleMessageType:XHBubbleMessageMediaTypeVoice]; |
|
} error:^(NSError * _Nullable error) { |
|
NSDictionary *dic = error.userInfo; |
|
[UICommon MessageErrorText:dic[NSLocalizedDescriptionKey]]; |
|
|
|
}]; |
|
} |
|
|
|
/** |
|
* 按下錄音按鈕 "準備" 錄音 |
|
*/ |
|
- (void)prepareRecordingVoiceActionWithCompletion:(BOOL (^)(void))completion |
|
{ |
|
WEAKSELF |
|
[weakSelf.currentSelectedCell.messageBubbleView.animationVoiceImageView stopAnimating]; |
|
[[XHAudioPlayerHelper shareInstance] stopAudio]; |
|
[super prepareRecordingVoiceActionWithCompletion:completion]; |
|
} |
|
|
|
/** |
|
* 配置Cell的样式或者字体 |
|
* |
|
* @param cell 目标Cell |
|
* @param indexPath 目标Cell所在位置IndexPath |
|
*/ |
|
- (void)configureCell:(XHMessageTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath |
|
{ |
|
XHMessage *message = self.modelListArr[indexPath.row]; |
|
[cell.avatarButton sd_setImageWithURL:[NSURL URLWithString:message.avatarUrl] forState:0 placeholderImage:message.sendType == 1 ? ImageName_(@"icon_unclick_waigong_head_portrait") : ImageName_(@"icon_boy_head_portrait")]; |
|
|
|
if (self.currentSelectedCell == cell && [[XHAudioPlayerHelper shareInstance] isPlaying]) { |
|
[self.currentSelectedCell.messageBubbleView.animationVoiceImageView startAnimating]; |
|
}else{ |
|
[self.currentSelectedCell.messageBubbleView.animationVoiceImageView stopAnimating]; |
|
} |
|
} |
|
|
|
/** |
|
* 点击多媒体消息的时候统一触发这个回调 |
|
* |
|
* @param message 被操作的目标消息Model |
|
* @param indexPath 该目标消息在哪个IndexPath里面 |
|
* @param messageTableViewCell 目标消息在该Cell上 |
|
*/ |
|
- (void)multiMediaMessageDidSelectedOnMessage:(id <XHMessageModel>)message |
|
atIndexPath:(NSIndexPath *)indexPath |
|
onMessageTableViewCell:(XHMessageTableViewCell *)messageTableViewCell |
|
{ |
|
kWeakSelf(self) |
|
if (!message) { |
|
[self.messageInputView.inputTextView resignFirstResponder]; |
|
return; |
|
} |
|
UIViewController *disPlayViewController; |
|
switch (message.messageMediaType) |
|
{ |
|
case XHBubbleMessageMediaTypeVideo: |
|
case XHBubbleMessageMediaTypePhoto: |
|
{ |
|
|
|
XHDisplayMediaViewController *messageDisplayTextView = [[XHDisplayMediaViewController alloc] init]; |
|
messageDisplayTextView.message = message; |
|
disPlayViewController = messageDisplayTextView; |
|
break; |
|
} |
|
break; |
|
case XHBubbleMessageMediaTypeVoice: |
|
{ |
|
DLog(@"message : %@", message.voicePath); |
|
messageTableViewCell.messageBubbleView.voiceUnreadDotImageView.hidden = YES; |
|
|
|
[[XHAudioPlayerHelper shareInstance] setDelegate:(id<NSFileManagerDelegate>)self]; |
|
if (self.currentSelectedCell) { |
|
[self.currentSelectedCell.messageBubbleView.animationVoiceImageView stopAnimating]; |
|
} |
|
if (self.currentSelectedCell == messageTableViewCell) |
|
{ |
|
[messageTableViewCell.messageBubbleView.animationVoiceImageView stopAnimating]; |
|
[[XHAudioPlayerHelper shareInstance] stopAudio]; |
|
self.currentSelectedCell = nil; |
|
} |
|
else |
|
{ |
|
self.currentSelectedCell = messageTableViewCell; |
|
[weakself P_playVoiceWithMessage:message block:^(NSString *wavPath) { |
|
if (weakself.currentSelectedCell == messageTableViewCell){ |
|
[messageTableViewCell.messageBubbleView.animationVoiceImageView startAnimating]; |
|
[[XHAudioPlayerHelper shareInstance] managerAudioWithFileName:wavPath toPlay:YES]; |
|
if (message.bubbleMessageType == 1 && message.isRead == NO) |
|
[weakself UpdateByIdMessage:message]; |
|
} |
|
}]; |
|
} |
|
break; |
|
} |
|
break; |
|
case XHBubbleMessageMediaTypeEmotion: |
|
DLog(@"facePath : %@", message.emotionPath); |
|
break; |
|
case XHBubbleMessageMediaTypeLocalPosition: { |
|
// DLog(@"facePath : %@", message.localPositionPhoto); |
|
|
|
LatestLocationModel *locate = [[LatestLocationModel alloc] init]; |
|
locate.longitude = @([message location].coordinate.longitude); |
|
locate.latitude = @([message location].coordinate.latitude); |
|
locate.addr = [message geolocations]; |
|
locate.updateTime = [UICommon getDateFormatWithStr:@"YYYY-MM-dd HH:mm:ss" date:message.timestamp]; |
|
|
|
break; |
|
} |
|
default: |
|
break; |
|
} |
|
if (disPlayViewController) { |
|
[self.navigationController pushViewController:disPlayViewController animated:YES]; |
|
} |
|
} |
|
|
|
|
|
#pragma 播放语音 |
|
|
|
- (void)P_playVoiceWithMessage:(XHMessage *)message block:(void(^)(NSString *wavPath))block |
|
{ |
|
NSString *path = [CJFileUtility documentsPathSubDir:@"msg_audio" andFile:message.Id]; |
|
NSString *wavPath = [path stringByAppendingPathExtension:@"wav"]; |
|
NSString *amrPath = [path stringByAppendingPathExtension:@"amr"]; |
|
|
|
if ([CJFileUtility fileExists:wavPath] || [CJFileUtility fileExists:amrPath]) |
|
{ |
|
if (![CJFileUtility fileExists:wavPath] && [CJFileUtility fileExists:amrPath]) |
|
{ |
|
[self amrToWav:amrPath]; |
|
} |
|
block (wavPath); |
|
} |
|
else |
|
{ |
|
WEAKSELF |
|
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:message.text]]; |
|
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; |
|
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) { |
|
|
|
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) { |
|
/* 设定下载到的位置 */ |
|
return [NSURL fileURLWithPath:amrPath]; |
|
|
|
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) { |
|
NSLog(@"下载完成"); |
|
if (![CJFileUtility fileExists:wavPath] && [CJFileUtility fileExists:amrPath]) { |
|
[weakSelf amrToWav:amrPath]; |
|
} |
|
block(wavPath); |
|
}]; |
|
[downloadTask resume]; |
|
} |
|
} |
|
|
|
#pragma mark XHAudioPlayerHelperDelegate |
|
- (void)didAudioPlayerStopPlay:(AVAudioPlayer *)audioPlayer |
|
{ |
|
if (!self.currentSelectedCell) |
|
{ |
|
return; |
|
} |
|
[self.currentSelectedCell.messageBubbleView.animationVoiceImageView stopAnimating]; |
|
self.currentSelectedCell = nil; |
|
} |
|
|
|
|
|
|
|
#pragma mark 根据消息id更新当前消息为已读状态 |
|
/// 根据消息id更新当前消息为已读状态 |
|
- (void)UpdateByIdMessage:(XHMessage *)message |
|
{ |
|
NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; |
|
[parameters setValue:message.Id forKey:@"id"]; |
|
[[[APIManager sharedManager] APGET:UpdateById_URL parameters:parameters resultClass:nil] subscribeNext:^(id _Nullable x) { |
|
[UICommon HidenLoading]; |
|
message.isRead = YES; |
|
self.messageCoutnUpdata(); |
|
} error:^(NSError * _Nullable error) { |
|
NSDictionary *dic = error.userInfo; |
|
[UICommon MessageErrorText:dic[NSLocalizedDescriptionKey]]; |
|
}]; |
|
} |
|
|
|
|
|
#pragma mark 删除微聊数据 |
|
/// 删除微聊数据 |
|
- (void)DeleteChatMessage |
|
{ |
|
[UICommon MessageUpload:@"加载中"]; |
|
NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; |
|
[parameters setValue:APIManager.sharedManager.deviceModel.groupid forKey:@"groupId"]; |
|
[[[APIManager sharedManager] APGET:DeleteChat_URL parameters:parameters resultClass:nil] subscribeNext:^(id _Nullable x) { |
|
[UICommon HidenLoading]; |
|
[UICommon MessageSuccessText:@"删除成功"]; |
|
self.page = 1; |
|
[self QueryChatListModel:TableisReload_d pageSize:KSizeNO current:1]; |
|
} error:^(NSError * _Nullable error) { |
|
NSDictionary *dic = error.userInfo; |
|
[UICommon MessageErrorText:dic[NSLocalizedDescriptionKey]]; |
|
}]; |
|
} |
|
|
|
|
|
/// 刷新table |
|
/// @param row 滚动到指定行 |
|
- (void)reloadTable:(NSInteger)row |
|
{ |
|
[self.messageTableView reloadData]; |
|
if (self.modelListArr.count > 0) |
|
[self.messageTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0] |
|
atScrollPosition:UITableViewScrollPositionTop |
|
animated:NO]; |
|
} |
|
|
|
|
|
/// 本地数据模型转XHMessage |
|
/// @param obj 数据 |
|
- (XHMessage *)ModelConversion:(id)obj |
|
{ |
|
XHMessage *message = [[XHMessage alloc] init]; |
|
ChatModel *chatModel = [ChatModel yy_modelWithJSON:obj]; |
|
message.text = chatModel.content; |
|
message.isRead = chatModel.readStatus; |
|
message.timestamp = [UICommon getTimeStrWithString:chatModel.createTime]; |
|
message.avatarUrl = chatModel.image; |
|
message.voiceDuration = chatModel.duration; |
|
message.messageMediaType = chatModel.myType; |
|
message.senderID = chatModel.sendUser; |
|
message.bubbleMessageType = [chatModel.sendUser isEqualToString:APIManager.sharedManager.loginModel.openid] ? 0 : 1 ; |
|
message.sender = chatModel.nickName; |
|
message.Id = chatModel.Id; |
|
message.sendType = chatModel.sendType; |
|
if (chatModel.image.length <= 0 && [chatModel.sendUser isEqualToString:APIManager.sharedManager.loginModel.openid]) |
|
message.avatarUrl = APIManager.sharedManager.userModel.image; |
|
|
|
return message; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
- (void)wavToAmr:(NSString *)path |
|
{ |
|
//转格式 |
|
if ([CJFileUtility fileExists:path]) |
|
{ |
|
NSString *armPath = [CJFileUtility changeFileExt:path ext:@"amr"]; |
|
[VoiceConverter wavToAmr:path amrSavePath:armPath]; |
|
|
|
} |
|
} |
|
|
|
//转换后删掉amr文件 |
|
- (void)amrToWav:(NSString *)path |
|
{ |
|
//转格式 |
|
if ([CJFileUtility fileExists:path]) { |
|
NSString *wavPath = [CJFileUtility changeFileExt:path ext:@"wav"]; |
|
[VoiceConverter amrToWav:path wavSavePath:wavPath]; |
|
[CJFileUtility deleteFile:path]; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
@end |
|
|
|
|
|
|
|
/* |
|
#pragma mark - Navigation |
|
|
|
// In a storyboard-based application, you will often want to do a little preparation before navigation |
|
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { |
|
// Get the new view controller using [segue destinationViewController]. |
|
// Pass the selected object to the new view controller. |
|
} |
|
*/
|
|
|