Files
peko-ios/YuMi/Modules/YMRoom/View/MessageContainerView/XPRoomMessageContainerView.m

1857 lines
74 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.

//
// YMRoomMessageView.m
// YUMI
//
// Created by YUMI on 2021/10/11.
//
#import "XPRoomMessageContainerView.h"
///Third
#import <Masonry/Masonry.h>
#import <NIMSDK/NIMSDK.h>
///Tool
#import "DJDKMIMOMColor.h"
#import "YUMIMacroUitls.h"
#import "XPRoomMessageConstant.h"
#import "XPRoomMessageParser.h"
#import "AccountInfoStorage.h"
#import "XPRoomMiniManager.h"
#import "PLTimeUtil.h"
#import "ClientConfig.h"
#import <POP.h>
#import "ThemeColor+Room.h"
#import "NSArray+Safe.h"
#import "Api+Room.h"
///Model
#import "RoomInfoModel.h"
#import "AttachmentModel.h"
#import "RoomFaceSendInfoModel.h"
#import "XPMessageRemoteExtModel.h"
#import "RoomPKChooseUserModel.h"
#import "CandyTreeResultModel.h"
#import "RoomSailingPrizeModel.h"
#import "UserInfoModel.h"
#import "XPMessageInfoModel.h"
#import "GiftReceiveInfoModel.h"
#import "XPGiftStorage.h"
///View
#import "XPRoomMessageTableViewCell.h"
#import "XPRoomMessageHeaderView.h"
#import "PIRoomMessagePhotoAlbumCell.h"
#import "PIRoomMessageUnlockPhotoAlbumView.h"
#import "PIRoomPhotoAlbumItemModel.h"
#import "SDPhotoBrowser.h"
#import "XPSkillCardPlayerManager.h"
// Boom
#import "BoomInfoModel.h"
// 红包
#import "LuckyPackageMessageTableViewCell.h"
// CP 进场提示
#import "CPEnterRoomTableViewCell.h"
#import "RoomEnterModel.h"
NSString * const kRoomShowTopicKey = @"kRoomShowTopicKey";
@interface XPRoomMessageContainerView ()<UITableViewDelegate, UITableViewDataSource, XPRoomMessageTableViewCellDelegate, UIGestureRecognizerDelegate,PIRoomMessagePhotoAlbumCellDelegate,SDPhotoBrowserDelegate,PIRoomMessageUnlockPhotoAlbumViewDelegate>
///房间的代理
@property (nonatomic,weak) id<RoomHostDelegate> hostDelegate;
///列表
@property (nonatomic,strong) UITableView *messageTableView;
///头部
@property (nonatomic,strong) XPRoomMessageHeaderView *headerView;
///底部有新的消息
@property (nonatomic,strong) UIButton *messageTipsBtn;
///有人at你
@property (nonatomic, strong) UIButton *atTipBtn;
///是否处于正在爬楼
@property (nonatomic,assign) BOOL isPending;
///是否是最小化进房的
@property (nonatomic,assign) BOOL isMiniEnter;
///数据源
@property (nonatomic,strong) NSMutableArray<XPMessageInfoModel *> *datasource;
@property (nonatomic,strong) NSMutableArray<XPMessageInfoModel *> *datasource_chat;
@property (nonatomic,strong) NSMutableArray<XPMessageInfoModel *> *datasource_gift;
///临时存放消息的数组
@property (nonatomic,strong) NSMutableArray *incomingMessages;
///有多少人at我
@property (nonatomic, assign) NSInteger atCount;
///@我的消息位置集合
@property (nonatomic, strong) NSMutableArray *locationArray;
///messageView 持有这个工具类 进行数据的分发操作
@property (nonatomic,strong) XPRoomMessageParser *messageParser;
@property(nonatomic,strong) PIRoomPhotoAlbumItemModel *lookUpModel;
@property(nonatomic,assign) BOOL isLoadHistoryMessage;
@property (nonatomic, assign) NSInteger displayType;
/// 清空公屏后下次追加强制走 reload避免插入动画与数据源不一致
@property (nonatomic, assign) BOOL forceReloadNextAppend;
/// 视图是否已经完成首次布局,未就绪期间的 UI 更新将排队
@property (atomic, assign) BOOL viewReady;
/// 是否正在刷新,避免嵌套批量更新
@property (atomic, assign) BOOL isFlushing;
/// 待执行的 UI 操作队列(在 viewReady 之前累积)
@property (nonatomic, strong) NSMutableArray<dispatch_block_t> *pendingOps;
@end
@implementation XPRoomMessageContainerView
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (instancetype)initWithDelegate:(id<RoomHostDelegate>)delegate {
self = [super init];
if (self) {
self.displayType = 1;
self.isLoadHistoryMessage = YES;
self.forceReloadNextAppend = NO;
self.hostDelegate = delegate;
[self initSubViews];
[self initSubViewConstraints];
}
return self;
}
/// 统一的安全插入实现:在数据与 UI 不一致时回退为 reload永不崩溃
- (void)safeApplyInsertsFromStartIndex:(NSInteger)startIndex
newItemsCount:(NSInteger)newItemsCount
needReload:(BOOL)needReload {
if (needReload || newItemsCount <= 0) {
[self.messageTableView reloadData];
return;
}
if (self.isFlushing) {
// 合并到下一帧,避免嵌套更新
dispatch_async(dispatch_get_main_queue(), ^{
[self safeApplyInsertsFromStartIndex:startIndex newItemsCount:newItemsCount needReload:NO];
});
return;
}
self.isFlushing = YES;
// 校验一致性
NSInteger beforeRows = [self.messageTableView numberOfRowsInSection:0];
NSInteger expectedRows = [self getCurrentDataSourceCount];
BOOL indexValid = (startIndex >= 0 && startIndex <= beforeRows);
BOOL countValid = (beforeRows + newItemsCount == expectedRows);
if (!indexValid || !countValid) {
[self.messageTableView reloadData];
self.isFlushing = NO;
return;
}
// 生成 indexPaths
NSMutableArray<NSIndexPath *> *indexPaths = [NSMutableArray array];
for (NSInteger i = 0; i < newItemsCount; i++) {
[indexPaths addObject:[NSIndexPath indexPathForRow:startIndex + i inSection:0]];
}
// 原子提交
[self.messageTableView beginUpdates];
@try {
[self.messageTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
} @catch (__unused NSException *e) {
[self.messageTableView reloadData];
} @finally {
[self.messageTableView endUpdates];
self.isFlushing = NO;
}
}
- (void)changeType:(NSInteger)type {
if (self.displayType == type) {
return;
}
self.displayType = type;
[self.messageTableView reloadData];
if (self.displayType == 1) {
self.messageTableView.tableHeaderView = self.headerView;
} else {
self.messageTableView.tableHeaderView = nil;
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self scrollToBottom:YES];
});
}
/// 更新其他 tag 的数据源,若传入空数组,则初始化并从 datasource 中获取数据
- (void)updateAllDataSource:(NSArray *)datas {
if (!datas || datas.count == 0) {
// 清空分类数据源
[self.datasource_chat removeAllObjects];
[self.datasource_gift removeAllObjects];
// 从主数据源重新构建分类数据源
datas = self.datasource;
}
for (XPMessageInfoModel *model in datas) {
switch (model.first) {
case NIMMessageTypeText:
case CustomMessageType_Face:
[self.datasource_chat addObject:model];
break;
case CustomMessageType_Gift:
case CustomMessageType_RoomBoom:
case CustomMessageType_Candy_Tree:
case CustomMessageType_Super_Gift:
case CustomMessageType_AllMicroSend:
[self.datasource_gift addObject:model];
break;
default:
break;
}
}
}
- (UIView *)listView {
return self;
}
- (void)showUserCard:(NSInteger)uid{
[self.messageParser showUserCard:uid];
}
#pragma mark - Response
- (void)messageTipsBtnAction:(UIButton *)sender {
self.isPending = NO;
self.messageTipsBtn.hidden = YES;
[self appendAndScrollToBottom];
}
- (void)atTipsBtnAction:(UIButton *)sender {
self.isPending = YES;
[self appendAndScrollToAtUser];
}
///追加数据源
- (void)appendAndScrollToAtUser {
// 在未就绪时排队
if (![NSThread isMainThread]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self appendAndScrollToAtUser];
});
return;
}
if (!self.viewReady) {
if (!self.pendingOps) self.pendingOps = [NSMutableArray array];
__weak typeof(self) weakSelf = self;
[self.pendingOps addObject:^{ __strong typeof(weakSelf) self = weakSelf; [self appendAndScrollToAtUser]; }];
return;
}
// 1. 检查 incomingMessages 是否为空
if (self.incomingMessages.count < 1) {
// 2. 安全检查 locationArray 是否为空
if (self.locationArray.count == 0) {
[self scrollToBottomWithTipsHidden:YES];
return;
}
// 3. 获取首个 @ 消息的索引,并进行安全检查
NSInteger index = [self safeGetIndexFromLocationArrayAt:0];
if (index == NSNotFound) {
[self scrollToBottomWithTipsHidden:YES];
return;
}
// 将 datasource 的索引转换为当前显示数据源的索引
NSInteger convertedIndex = [self convertDataSourceIndexToCurrentDisplayIndex:index];
if (convertedIndex == NSNotFound) {
[self scrollToBottomWithTipsHidden:YES];
} else {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:convertedIndex inSection:0];
NSInteger currentRows = [self getCurrentDataSourceCount];
if (currentRows > indexPath.row) {
[self.messageTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
if (currentRows == indexPath.row + 1) {
self.messageTipsBtn.hidden = YES;
self.isPending = NO;
}
} else {
[self scrollToBottomWithTipsHidden:YES];
}
}
[self safelyRemoveLocationAtIndex:0];
[self updateAtTipButton];
return;
}
// 4. 超长消息处理逻辑
BOOL needReloadData = NO;
if (self.datasource.count > kRoomMessageMaxLength) {
NSInteger removedCount = kRoomMessageMaxLength / 2;
[self safelyRemoveMessages:removedCount];
needReloadData = YES; // 标记需要重新加载数据
}
// 5. 在更新数据源之前获取当前行数
NSInteger currentRows = [self getCurrentDataSourceCount];
// 6. 插入新消息
NSMutableArray *tempNewDatas = @[].mutableCopy;
for (id item in self.incomingMessages) {
XPMessageInfoModel *model = [self parseMessage:item];
if (!model) continue;
[tempNewDatas addObject:model];
[self.datasource addObject:model];
[self processAtMentionsForMessage:item];
}
[self updateAllDataSource:tempNewDatas];
[self.incomingMessages removeAllObjects];
// 7. 更新 UITableView
[self safeApplyInsertsFromStartIndex:currentRows newItemsCount:tempNewDatas.count needReload:(needReloadData || self.forceReloadNextAppend)];
self.forceReloadNextAppend = NO;
// 6. 滚动到指定位置或底部
[self scrollToFirstLocationOrBottom];
}
- (void)scrollToBottomWithTipsHidden:(BOOL)hidden {
NSInteger rows = [self getCurrentDataSourceCount];
if (rows > 0) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:(rows - 1) inSection:0];
[self.messageTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
self.messageTipsBtn.hidden = hidden;
self.isPending = NO;
self.atCount = 0;
self.atTipBtn.hidden = hidden;
}
- (void)updateLocationArrayForMessageRemoval:(NSInteger)removedCount {
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
for (int i = 0; i < self.locationArray.count; i++) {
NSNumber *number = self.locationArray[i];
if (number.integerValue < removedCount) {
self.atCount--;
[indexSet addIndex:i];
}
}
[self.locationArray removeObjectsAtIndexes:indexSet];
for (int i = 0; i < self.locationArray.count; i++) {
NSNumber *number = self.locationArray[i];
self.locationArray[i] = @(number.integerValue - removedCount);
}
}
- (void)updateAtTipButton {
if (self.locationArray.count == 0) {
self.atTipBtn.hidden = YES;
} else {
[self.atTipBtn setTitle:[NSString stringWithFormat:YMLocalizedString(@"XPRoomMessageContainerView0"), (unsigned long)self.locationArray.count] forState:UIControlStateNormal];
self.atTipBtn.hidden = NO;
}
}
- (NSInteger)safeGetIndexFromLocationArrayAt:(NSUInteger)index {
if (index < self.locationArray.count) {
NSNumber *number = self.locationArray[index];
return [number intValue];
}
return NSNotFound; // 返回一个无效值,避免崩溃
}
- (void)safelyRemoveLocationAtIndex:(NSUInteger)index {
if (index < self.locationArray.count) {
[self.locationArray xpSafeRemoveObjectAtIndex:index];
self.atCount = MAX(0, self.atCount - 1);
}
}
- (void)safelyRemoveMessages:(NSInteger)count {
if (self.datasource.count >= count) {
// 获取要删除的消息
NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, count)];
NSArray *removedMessages = [self.datasource objectsAtIndexes:set];
// 从主数据源删除
[self.datasource removeObjectsAtIndexes:set];
// 从分类数据源中删除对应的消息
for (XPMessageInfoModel *removedModel in removedMessages) {
switch (removedModel.first) {
case NIMMessageTypeText:
case CustomMessageType_Face:
[self.datasource_chat removeObject:removedModel];
break;
case CustomMessageType_Gift:
case CustomMessageType_RoomBoom:
case CustomMessageType_Candy_Tree:
case CustomMessageType_Super_Gift:
case CustomMessageType_AllMicroSend:
[self.datasource_gift removeObject:removedModel];
break;
default:
break;
}
}
// 更新 locationArray
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
for (NSUInteger i = 0; i < self.locationArray.count; i++) {
NSNumber *number = self.locationArray[i];
if (number.integerValue < count) {
[indexSet addIndex:i];
} else {
self.locationArray[i] = @(number.integerValue - count);
}
}
[self.locationArray removeObjectsAtIndexes:indexSet];
}
}
- (XPMessageInfoModel *)parseMessage:(id)item {
if ([item isKindOfClass:[NIMMessage class]]) {
return [self.messageParser parseMessageAttribute:item];
} else if ([item isKindOfClass:[NIMBroadcastMessage class]]) {
return [self.messageParser parseBroadcastMessageAttribute:item];
}
return nil;
}
- (void)processAtMentionsForMessage:(id)item {
if ([item isKindOfClass:[NIMMessage class]]) {
NSArray *nickNameInfos = [(NIMMessage *)item remoteExt][@"atUids"];
if ([nickNameInfos isKindOfClass:[NSArray class]]) {
for (NSString *nick in nickNameInfos) {
if ([nick isEqualToString:[AccountInfoStorage instance].getUid]) {
[self.locationArray addObject:@(self.datasource.count - 1)];
}
}
}
}
}
/// 检查是否为礼物消息
- (BOOL)isGiftMessage:(id)messageData {
if ([messageData isKindOfClass:[NIMMessage class]]) {
NIMMessage *message = (NIMMessage *)messageData;
if ([message.messageObject isKindOfClass:[NIMCustomObject class]]) {
NIMCustomObject *obj = (NIMCustomObject *)message.messageObject;
if (obj.attachment && [obj.attachment isKindOfClass:[AttachmentModel class]]) {
AttachmentModel *attachment = (AttachmentModel *)obj.attachment;
return (attachment.first == CustomMessageType_Gift ||
attachment.first == CustomMessageType_AllMicroSend ||
attachment.first == CustomMessageType_Super_Gift);
}
}
}
return NO;
}
- (NSInteger)getCurrentDataSourceCount {
NSInteger count = 0;
switch (self.displayType) {
case 1:
count = self.datasource.count;
break;
case 2:
count = self.datasource_chat.count;
break;
case 3:
count = self.datasource_gift.count;
break;
default:
count = self.datasource.count;
break;
}
// 确保返回非负数
return MAX(0, count);
}
- (NSInteger)convertDataSourceIndexToCurrentDisplayIndex:(NSInteger)dataSourceIndex {
if (self.displayType == 1) {
return dataSourceIndex;
}
if (dataSourceIndex >= self.datasource.count) {
return NSNotFound;
}
XPMessageInfoModel *targetModel = [self.datasource objectAtIndex:dataSourceIndex];
if (!targetModel) {
return NSNotFound;
}
NSArray *currentDataSource = nil;
switch (self.displayType) {
case 2:
currentDataSource = self.datasource_chat;
break;
case 3:
currentDataSource = self.datasource_gift;
break;
default:
return dataSourceIndex;
}
// 在当前数据源中查找对应的模型
for (NSInteger i = 0; i < currentDataSource.count; i++) {
XPMessageInfoModel *model = [currentDataSource objectAtIndex:i];
if ([model isEqual:targetModel]) {
return i;
}
}
return NSNotFound;
}
- (void)scrollToFirstLocationOrBottom {
if (self.locationArray.count == 0) {
[self scrollToBottomWithTipsHidden:YES];
return;
}
NSInteger index = [self safeGetIndexFromLocationArrayAt:0];
if (index == NSNotFound) {
[self scrollToBottomWithTipsHidden:YES];
return;
}
// 将 datasource 的索引转换为当前显示数据源的索引
NSInteger convertedIndex = [self convertDataSourceIndexToCurrentDisplayIndex:index];
if (convertedIndex == NSNotFound) {
[self scrollToBottomWithTipsHidden:YES];
} else {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:convertedIndex inSection:0];
[self.messageTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
[self safelyRemoveLocationAtIndex:0];
}
[self updateAtTipButton];
}
#pragma mark - @我
///查找有多少人@我
- (void)findAtMeNumber {
if (self.incomingMessages.count) {
NIMMessage *message = self.incomingMessages.lastObject;
if (![message isKindOfClass:[NIMMessage class]] || ![message respondsToSelector:@selector(remoteExt)]) {
return;
}
id nickNameNifo = message.remoteExt[@"atUids"];
if ([nickNameNifo isKindOfClass:[NSArray class]]) {
for (NSString *nick in nickNameNifo) {
if ([nick isEqualToString:[AccountInfoStorage instance].getUid]) {
self.atCount += 1;
}
}
}
if (self.atCount > 0) {
self.atTipBtn.hidden = NO;
[self.atTipBtn setTitle:[NSString stringWithFormat:YMLocalizedString(@"XPRoomMessageContainerView2"), self.atCount] forState:UIControlStateNormal];
} else {
self.atTipBtn.hidden = YES;
}
}
}
#pragma mark - Private Method
- (void)initSubViews {
self.clipsToBounds = YES;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(addCustomMessage:) name:@"message" object:nil];
[self addSubview:self.messageTableView];
[self addSubview:self.messageTipsBtn];
[self addSubview:self.atTipBtn];
self.messageTableView.tableHeaderView = self.headerView;
// 标记未就绪,等待首次布局完成后再 flush 队列
self.viewReady = NO;
self.isFlushing = NO;
}
- (void)initSubViewConstraints {
RoomInfoModel *infoModel = self.hostDelegate.getRoomInfo;
CGFloat top = infoModel.type == RoomType_MiniGame ? 0 : 10;
[self.messageTableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self).offset(15);
make.bottom.trailing.mas_equalTo(self);
make.top.equalTo(self).mas_offset(top);
}];
[self.messageTipsBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(100);
make.height.mas_equalTo(30);
make.bottom.mas_equalTo(self.mas_bottom).offset(-5);
make.leading.mas_equalTo(self);
}];
[self.atTipBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(100);
make.height.mas_equalTo(30);
make.bottom.mas_equalTo(self.mas_bottom).offset(-5);
make.leading.mas_equalTo(self);
}];
// 首次约束完成后,标记就绪并 flush 排队操作
dispatch_async(dispatch_get_main_queue(), ^{
if (!self.viewReady) {
self.viewReady = YES;
if (self.pendingOps.count > 0) {
NSArray<dispatch_block_t> *ops = [self.pendingOps copy];
[self.pendingOps removeAllObjects];
for (void (^op)(void) in ops) { op(); }
}
}
});
}
///是否是当前房间
- (BOOL)isCurrentRoom:(NSString *)sessionId {
if ([sessionId isEqualToString:[NSString stringWithFormat:@"%ld", self.hostDelegate.getRoomInfo.roomId]]) {
return YES;
}
return NO;
}
///判断是否隐身进房
- (BOOL)handleHideEnter:(NIMMessage *)message {
NIMMessageChatroomExtension * messageExt = (NIMMessageChatroomExtension *)message.messageExt;
NSDictionary * dic = [(NSDictionary *)messageExt.roomExt.toJSONObject objectForKey:message.from];
XPMessageRemoteExtModel * extModel = [XPMessageRemoteExtModel modelWithJSON:dic];
return (extModel.enterHide || extModel.platformRole == 1);
}
#pragma mark - 添加数据并且做自动滚动
///添加信息
- (void)addRoomMessage:(id)messageData {
if(self.isLoadHistoryMessage == YES)return;
[self.incomingMessages addObject:messageData];
if ([messageData isKindOfClass:[NIMBroadcastMessage class]]) {
NIMBroadcastMessage *broadcastMessage = (NIMBroadcastMessage *)messageData;
[[XPRoomMiniManager shareManager] saveRoomMessage:broadcastMessage];
} else if ([messageData isKindOfClass:[NIMMessage class]]) {
NIMMessage *message = (NIMMessage *)messageData;
if (!self.isMiniEnter) {/// 最小化进房的话 不需要重新保存
if (self.hostDelegate.getRoomInfo.isCloseScreen) {
NIMCustomObject *obj = (NIMCustomObject *)message.messageObject;
if (obj.attachment != nil && [obj.attachment isKindOfClass:[AttachmentModel class]]) {
AttachmentModel *attachment = (AttachmentModel *)obj.attachment;
if (attachment.first == CustomMessageType_Update_RoomInfo &&
attachment.second == Custom_Message_Sub_Update_RoomInfo_MessageState){
[[XPRoomMiniManager shareManager] saveRoomMessage:message];
}
}
} else {
[[XPRoomMiniManager shareManager] saveRoomMessage:message];
}
}
}
if (self.isPending) {
self.messageTipsBtn.hidden = NO;
[self findAtMeNumber];
} else {
// 对于礼物消息,使用更平滑的更新策略
if ([self isGiftMessage:messageData]) {
// 稍微延迟处理礼物消息,让动画先执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self appendAndScrollToBottom];
});
} else {
[self appendAndScrollToBottom];
}
}
}
///追加数据源
- (void)appendAndScrollToBottom {
// 在未就绪时排队
if (![NSThread isMainThread]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self appendAndScrollToBottom];
});
return;
}
if (!self.viewReady) {
if (!self.pendingOps) self.pendingOps = [NSMutableArray array];
__weak typeof(self) weakSelf = self;
[self.pendingOps addObject:^{ __strong typeof(weakSelf) self = weakSelf; [self appendAndScrollToBottom]; }];
return;
}
if (self.incomingMessages.count < 1) {
///滚动到底部如果有人at自己后income消息在点击at按钮处做了拼接处理因为点击at按钮跳转到的是对应的at人消息如果后面有其他消息时点有更多按钮时需要滚动到最底部
[self scrollToBottom:YES];
return;
}
BOOL needReloadData = NO;
if (self.datasource.count > kRoomMessageMaxLength) {
NSInteger removedCount = kRoomMessageMaxLength / 2;
[self safelyRemoveMessages:removedCount];
needReloadData = YES; // 标记需要重新加载数据
}
// 在更新数据源之前获取当前行数
NSInteger currentRows = [self getCurrentDataSourceCount];
NSMutableArray *tempArray = @[].mutableCopy;
@kWeakify(self);
[self.incomingMessages enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
@kStrongify(self);
id model = nil;
if ([obj isKindOfClass:[NIMMessage class]]) {
model = [self.messageParser parseMessageAttribute:(NIMMessage *)obj];
} else if ([obj isKindOfClass:[NIMBroadcastMessage class]]) {
model = [self.messageParser parseBroadcastMessageAttribute:(NIMBroadcastMessage *)obj];
}
if (model) {
[tempArray addObject:model];
[self.datasource addObject:model];
}
}];
[self.incomingMessages removeAllObjects];
[self updateAllDataSource:tempArray];
// 如果有删除操作或清空标记,使用 reloadData否则使用增量更新
[self safeApplyInsertsFromStartIndex:currentRows newItemsCount:tempArray.count needReload:(needReloadData || self.forceReloadNextAppend)];
self.forceReloadNextAppend = NO;
//执行插入动画并滚动
// 延迟滚动执行,避免与礼物动画产生视觉冲突
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self scrollToBottom:NO];
});
}
///执行插入动画并滚动
- (void)scrollToBottom:(BOOL)animated {
NSArray *source = @[];
switch (self.displayType) {
case 1:
source = self.datasource;
break;
case 2:
source = self.datasource_chat;
break;
case 3:
source = self.datasource_gift;
break;
default:
source = self.datasource;
break;
}
if(source.count > 0){
NSIndexPath *ip = [NSIndexPath indexPathForRow:source.count-1 inSection:0]; //取最后一行数据
// 优化滚动动画,减少与布局更新的冲突
if (animated) {
// 使用 dispatch_async 确保布局更新完成后再滚动
dispatch_async(dispatch_get_main_queue(), ^{
[self.messageTableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionBottom animated:YES];
});
} else {
// 使用更平滑的滚动动画,减少与礼物动画的视觉冲突
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.messageTableView scrollToRowAtIndexPath:ip atScrollPosition:UITableViewScrollPositionBottom animated:NO];
} completion:nil];
}
self.atCount = 0;
self.atTipBtn.hidden = YES;
[self.locationArray removeAllObjects];
}
}
///自定义消息 是否可以加到 公屏 需要自己维护
- (BOOL)isCanDisplayMessage:(NIMMessage *)message {
NIMCustomObject *obj = (NIMCustomObject *)message.messageObject;
if (obj.attachment != nil && [obj.attachment isKindOfClass:[AttachmentModel class]]) {
AttachmentModel *attachment = (AttachmentModel *)obj.attachment;
if (attachment.first == CustomMessageType_Face && attachment.second == Custom_Message_Sub_Face_Send) {
return NO;
}
if (attachment.first == CustomMessageType_User_Enter_Room && attachment.second == Custom_Message_Sub_User_Enter_Room) {
return NO;
}
if (attachment.first == CustomMessageType_RedPacket) {
return NO;
}
// if (attachment.first == CustomMessageType_Face && attachment.second == Custom_Message_Sub_Face_Send) {
// if ([attachment.data[@"data"] isKindOfClass:[NSArray class]]) {
// NSArray * array = [RoomFaceSendInfoModel modelsWithArray:attachment.data[@"data"]];
// for (int i = 0; i< array.count; i++) {
// RoomFaceSendInfoModel * sendInfo = [array xpSafeObjectAtIndex:i];
// if (sendInfo.resultIndexes.count <=0) {
// return NO;
// }
// }
// }
// }
if (attachment.first == CustomMessageType_Room_PK && attachment.second == Custom_Message_Sub_Room_PK_Manager_Up_Mic) {
if (attachment.data && [attachment.data allKeys].count > 0) {
for (NSDictionary * dic in [attachment.data allValues]) {
RoomPKChooseUserModel * userModel = [RoomPKChooseUserModel modelWithDictionary:dic];
if (userModel.groupType == GroupType_Red || userModel.groupType == GroupType_Blue) {
return YES;
}
}
}
return NO;
} else if(attachment.first == CustomMessageType_Room_Sailing && (attachment.second == Custom_Message_Sub_Sailing_AllRoom_Notify || attachment.second == Custom_Message_Sub_Sailing_InRoom_NeedAllMicSend)) {
RoomSailingPrizeModel * prizeModel = [RoomSailingPrizeModel modelWithDictionary:attachment.data];
if (self.hostDelegate.getUserInfo.userLevelVo.experLevelSeq > prizeModel.userLevelLimit) {
return YES;
}
} else if(attachment.first == CustomMessageType_Room_Album){
return YES;
}else if(attachment.first == CustomMessageType_Gift) {
if(attachment.data[@"isRoomAlbum"] != nil){
BOOL isRoomAlbum = [attachment.data[@"isRoomAlbum"] boolValue];
if(isRoomAlbum == YES){
return NO;
}
}
GiftReceiveInfoModel *info = [GiftReceiveInfoModel modelWithJSON:attachment.data];
GiftInfoModel *giftInfo = info.gift == nil ? info.giftInfo : info.gift;
if (giftInfo == nil) {
giftInfo = [[XPGiftStorage shareStorage] findGiftInfo:info.giftId inRoom:@(info.roomUid).stringValue];
}
if (giftInfo.giftType == GiftType_super){
return NO;
}
}else if(attachment.first == CustomMessageType_AllMicroSend){
GiftReceiveInfoModel *info = [GiftReceiveInfoModel modelWithJSON:attachment.data];
GiftInfoModel *giftInfo = info.gift == nil ? info.giftInfo : info.gift;
if (giftInfo == nil) {
giftInfo = [[XPGiftStorage shareStorage] findGiftInfo:info.giftId inRoom:@(info.roomUid).stringValue];
}
if (giftInfo.giftType == GiftType_super){
return NO;
}
}
return [[[self supportMessageDic] objectForKey:@(attachment.first)] containsObject:@(attachment.second)];
}
return NO;
}
- (NSDictionary *)supportMessageDic {
return @{
@(CustomMessageType_AllMicroSend):
[NSSet setWithObjects:
@(Custom_Message_Sub_AllMicroSend),
@(Custom_Message_Sub_AllMicroLuckySend),
@(Custom_Message_Sub_AllBatchSend),
@(Custom_Message_Sub_AllBatchMicroLuckySend),
nil],
@(CustomMessageType_Gift):
[NSSet setWithObjects:
@(Custom_Message_Sub_Gift_Send),
@(Custom_Message_Sub_Gift_LuckySend),
nil],
@(CustomMessageType_Room_Tip):
[NSSet setWithObjects:
@(Custom_Message_Sub_Room_Tip_ShareRoom),
@(Custom_Message_Sub_Room_Tip_Attention_Owner),
nil],
@(CustomMessageType_Kick_User):
[NSSet setWithObjects:
@(Custom_Message_Sub_Kick_BeKicked),
@(Custom_Message_Sub_Kick_BlackList),
nil],
@(CustomMessageType_Queue):
[NSSet setWithObjects:
@(Custom_Message_Sub_Queue_Kick),
nil],
@(CustomMessageType_Look_Love):
[NSSet setWithObjects:
@(Custom_Message_Sub_Look_Love_Me),
@(Custom_Message_Sub_Look_Love_InRoom),
@(Custom_Message_Sub_Look_Love_AllRoom),
@(Custom_Message_Sub_Look_Love_AllRoom_Notify),
@(Custom_Message_Sub_Look_Love_InRoom_NeedAllMicSend),
nil],
@(CustomMessageType_Arrange_Mic):
[NSSet setWithObjects:
@(Custom_Message_Sub_Arrange_Mic_Mode_Open),
@(Custom_Message_Sub_Arrange_Mic_Mode_Close),
@(Custom_Message_Sub_Arrange_Mic_Free_Mic_Open),
@(Custom_Message_Sub_Arrange_Mic_Free_Mic_Close),
nil],
@(CustomMessageType_Update_RoomInfo):
[NSSet setWithObjects:
@(Custom_Message_Sub_Update_RoomInfo_MessageState),
@(Custom_Message_Sub_Update_RoomInfo_AnimateEffect),
nil],
@(CustomMessageType_Collection_Room):
[NSSet setWithObjects:
@(Custom_Message_Sub_Collect_Room_Tips),
nil],
@(CustomMessageType_RoomPlay_Dating):
[NSSet setWithObjects:
@(Custom_Message_Sub_Room_Play_Dating_Pick_Heart),
@(Custom_Message_Sub_Room_Play_Dating_Result_Mutual),
@(Custom_Message_Sub_Room_Play_Dating_Result_Not_Mutual),
nil],
@(CustomMessageType_Noble_VIP):
[NSSet setWithObjects:
@(Custom_Message_Sub_Room_Open_Noble_VIP),
@(Custom_Message_Sub_Room_Noble_LevelUp),
@(Custom_Message_Sub_AllRoom_Noble_LevelUp_Suspend),
nil],
@(CustomMessageType_Face):
[NSSet setWithObjects:
@(Custom_Message_Sub_Face_Send),
nil],
@(CustomMessageType_Anchor_FansTeam):
[NSSet setWithObjects:
@(Custom_Message_Sub_FansTeam_Open_Success),
@(Custom_Message_Sub_FansTeam_Open_Fail),
@(Custom_Message_Sub_FansTeam_Join_Success),
@(Custom_Message_Sub_FansTeam_Out_Success),
nil],
@(CustomMessageType_Hall_Super_Admin):
[NSSet setWithObjects:
@(Custom_Message_Sub_Hall_Super_Admin_Kick_Manager_Out_Room),
nil],
@(CustomMessageType_Room_PK):
[NSSet setWithObjects:
@(Custom_Message_Sub_Room_PK_Manager_Up_Mic),
@(Custom_Message_Sub_Room_PK_Mode_Close),
@(Custom_Message_Sub_Room_PK_Result),
@(Custom_Message_Sub_Room_PK_Mode_Open),
@(Custom_Message_Sub_Room_PK_Start),
@(Custom_Message_Sub_Room_PK_Re_Start),
nil],
@(CustomMessageType_LuckyBag):
[NSSet setWithObjects:
@(Custom_Message_Sub_Room_Gift_LuckBag_Server),
@(Custom_Message_Sub_Room_Gift_LuckBag_FullScree),
nil],
@(CustomMessageType_Gift_Compound):
[NSSet setWithObjects:@(Custom_Message_Sub_Gift_Compound),
nil],
@(CustomMessageType_Tarot):
[NSSet setWithObjects:
@(Custom_Message_Sub_Tarot_Novice),
@(Custom_Message_Sub_Tarot_Advanced),
@(Custom_Message_Sub_Tarot_Intermediate),
nil],
@(CustomMessageType_Room_Sailing):
[NSSet setWithObjects:
@(Custom_Message_Sub_Sailing_Me),
@(Custom_Message_Sub_Sailing_InRoom),
@(Custom_Message_Sub_Sailing_AllRoom),
@(Custom_Message_Sub_Sailing_AllRoom_Notify),
@(Custom_Message_Sub_Sailing_InRoom_NeedAllMicSend),
nil],
@(CustomMessageType_RedPacket):
[NSSet setWithObjects:
@(Custom_Message_Sub_OpenRedPacketSuccess),
@(Custom_Message_Sub_LuckyPackage),
nil],
@(CustomMessageType_General_Public_Screen):
[NSSet setWithObjects:
@(Custom_Message_Sub_General_Public_Screen_One_Room),
@(Custom_Message_Sub_General_Public_Screen_All_Room),
nil],
@(CustomMessageType_Super_Gift):
[NSSet setWithObjects:
@(Custom_Message_Sub_Super_Gift),
@(Custom_Message_Sub_Super_Gift_Room_Message),
nil],
};
}
- (void)addCustomMessage:(NSNotification *)notification {
self.isMiniEnter = NO;
if (self.hostDelegate.getRoomInfo.isCloseScreen) {return;}
NIMMessage * message = notification.object;
[self addRoomMessage:message];
}
///用户进入房间显示 房间话题
- (void)createUserEnterRoomAddRoomTopicMessage {
if ([ClientConfig shareConfig].configInfo.hideNoticeVersion == YES) {
return;
}
RoomInfoModel * roomInfo = self.hostDelegate.getRoomInfo;
if (roomInfo.uid == [AccountInfoStorage instance].getUid.integerValue) {return;}
NSString * key = [NSString stringWithFormat:@"%@_%@_%ld", kRoomShowTopicKey, [AccountInfoStorage instance].getUid, roomInfo.uid];
NSDate * oldDate = [[NSUserDefaults standardUserDefaults] valueForKey:key];
NSDate * date = [NSDate date];
if (roomInfo.introduction.length > 0) {
if (oldDate) {
NSDateComponents *compons = [PLTimeUtil compareTwoDate:date secondDate:[NSDate date]];
if (ABS(compons.year) > 0 || ABS(compons.month) > 0 || ABS(compons.day) > 0) {
NIMMessage * message = [[NIMMessage alloc] init];
NIMTipObject *tipObject = [[NIMTipObject alloc] init];
message.messageObject = tipObject;
NSString * content = [NSString stringWithFormat:YMLocalizedString(@"XPRoomMessageContainerView3"), roomInfo.introduction];
message.text = content;
message.localExt = @{@"isRoomTopic": @(NO)};
[message setValue:@(NIMMessageTypeTip) forKey:@"messageType"];
[message setValue:[NIMSession session:[NSString stringWithFormat:@"%ld", roomInfo.roomId] type:NIMSessionTypeChatroom] forKey:@"session"];
[self addRoomMessage:message];
[[NSUserDefaults standardUserDefaults] setValue:@(YES) forKey:key];
[[NSUserDefaults standardUserDefaults] synchronize];
}
} else {
NIMMessage * message = [[NIMMessage alloc] init];
NSString * content = [NSString stringWithFormat:YMLocalizedString(@"XPRoomMessageContainerView3"), roomInfo.introduction];
message.text = content;
message.localExt = @{@"isRoomTopic": @(NO)};
[message setValue:@(NIMMessageTypeTip) forKey:@"messageType"];
[message setValue:[NIMSession session:[NSString stringWithFormat:@"%ld", roomInfo.roomId] type:NIMSessionTypeChatroom] forKey:@"session"];
[self addRoomMessage:message];
[[NSUserDefaults standardUserDefaults] setValue:@(YES) forKey:key];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
NIMMessage * message = [[NIMMessage alloc] init];
NIMTipObject *tipObject = [[NIMTipObject alloc] init];
message.messageObject = tipObject;
message.text = YMLocalizedString(@"XPRoomMessageContainerView5");
message.localExt = @{@"isRoomTopic": @(YES)};
[message setValue:@(NIMMessageTypeTip) forKey:@"messageType"];
[message setValue:[NIMSession session:[NSString stringWithFormat:@"%ld", roomInfo.roomId] type:NIMSessionTypeChatroom] forKey:@"session"];
[self addRoomMessage:message];
}
- (void)handleBroadcastMessageAttachment:(AttachmentModel *)attachment; {
// if (attachment.first == CustomMessageType_RoomBoom) {
// Boom632Model *m = [Boom632Model modelWithJSON:attachment.data];
// NIMMessage * message = [[NIMMessage alloc] init];
// message.text = YMLocalizedString(@"XPRoomMessageContainerView5");
// message.localExt = @{@"isRoomTopic": @(YES)};
// [message setValue:@(NIMMessageTypeTip) forKey:@"messageType"];
// [message setValue:[NIMSession session:[NSString stringWithFormat:@"%ld", roomInfo.roomId] type:NIMSessionTypeChatroom] forKey:@"session"];
// [self addRoomMessage:message];
// }
}
- (void)handleBroadcastMessage:(NIMBroadcastMessage *)message {
[self addRoomMessage:message];
}
#pragma mark - RoomGuestDelegate
- (void)handleNIMCustomAttachment:(AttachmentModel *)attachment{
}
- (void)handleNIMCustomMessage:(NIMMessage *)message {
self.isMiniEnter = NO;
NIMCustomObject *obj = (NIMCustomObject *)message.messageObject;
// 这里其实是特殊处理
if (obj.attachment != nil && [obj.attachment isKindOfClass:[AttachmentModel class]]) {
AttachmentModel *attachment = (AttachmentModel *)obj.attachment;
switch (attachment.first) {
case CustomMessageType_Update_RoomInfo: {
if (attachment.second == Custom_Message_Sub_Update_RoomInfo_MessageState) {
[self.datasource removeAllObjects];
[self updateAllDataSource:nil];
[self.incomingMessages removeAllObjects];
[self.locationArray removeAllObjects];
self.forceReloadNextAppend = YES;
self.atCount = 0;
self.atTipBtn.hidden = YES;
self.messageTipsBtn.hidden = YES;
self.isPending = NO;
[[XPRoomMiniManager shareManager] resetLocalMessage];
[self addRoomMessage:message];
} else if (attachment.second == Custom_Message_Sub_Update_RoomInfo_CleanScreen) {
[self.datasource removeAllObjects];
[self updateAllDataSource:nil];
[self.incomingMessages removeAllObjects];
[self.locationArray removeAllObjects];
self.forceReloadNextAppend = YES;
self.atCount = 0;
self.atTipBtn.hidden = YES;
self.messageTipsBtn.hidden = YES;
self.isPending = NO;
[[XPRoomMiniManager shareManager] resetLocalMessage];
[self addRoomMessage:message];
}
}
break;
case CustomMessageType_Candy_Tree: {
if(attachment.second == Custom_Message_Sub_Candy_Tree_Me) {
CandyTreeGiftInfoModel * model = [CandyTreeGiftInfoModel modelWithDictionary:attachment.data];
if (model.uid.integerValue != [AccountInfoStorage instance].getUid.integerValue) {
return;
}
}
}
break;
case CustomMessageType_Kick_User: {
NSString * uid = [AccountInfoStorage instance].getUid;
NSString * roomId = [NSString stringWithFormat:@"%ld", self.hostDelegate.getRoomInfo.roomId];
NIMChatroomMembersByIdsRequest *request = [[NIMChatroomMembersByIdsRequest alloc]init];
request.roomId = roomId;
request.userIds = @[uid];
[[NIMSDK sharedSDK].chatroomManager fetchChatroomMembersByIds:request completion:^(NSError * _Nullable error, NSArray<NIMChatroomMember *> * _Nullable members) {
if (error == nil) {
NIMChatroomMember * member = members.firstObject;
BOOL isCreator = member.type == NIMChatroomMemberTypeCreator;
BOOL isManager = member.type == NIMChatroomMemberTypeManager;
if (isCreator || isManager) {
[self addRoomMessage:message];
}
}
}];
}
break;
case CustomMessageType_Hall_Super_Admin: {
[self addRoomMessage:message];
}
break;
case CustomMessageType_RedPacket: {
NSDictionary *data = attachment.data;
if (data) {
NSInteger currentRoomUID = self.hostDelegate.getRoomInfo.uid;
NSNumber *messageRoomUI = [data objectForKey:@"roomUid"];
if (messageRoomUI) {
if (currentRoomUID == messageRoomUI.integerValue) {
[self addRoomMessage:message];
}
}
}
}
break;
case CustomMessageType_User_Enter_Room: {
if (attachment.second == Custom_Message_Sub_Pic_Message) {
[self addRoomMessage:message];
} else if (attachment.second == Custom_Message_Sub_User_Enter_Room) {
RoomEnterModel *model = [RoomEnterModel modelWithJSON:attachment.data];
if (model.screenType == 1 || model.enterHide == 1) {
return;
}
for (int index = 0; index<model.cpList.count; index++) {
attachment.seq = index;
index += 1;
[(NIMCustomObject *)message.messageObject setAttachment:attachment];
[self addRoomMessage:message];
}
}
}
break;
case CustomMessageType_Face: {
// MARK: 只处理带结果内容的特殊表情
NSInteger delay = -1;
if ([message.messageObject isKindOfClass:[NIMCustomObject class]]) {
NIMCustomObject *obj = (NIMCustomObject *)message.messageObject;
AttachmentModel *attachment =(AttachmentModel *)obj.attachment;
if (attachment.second == Custom_Message_Sub_Face_Send) {
NSArray *datas = [attachment.data objectForKey:@"data"];
NSDictionary *obj = [datas xpSafeObjectAtIndex:0];
if (obj) {
NSArray *results = [obj objectForKey:@"resultIndexes"];
if ([results isKindOfClass:[NSArray class]] && results.count > 0) {
delay = 2.5;
}
}
}
}
if (delay > 0 ) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self addRoomMessage:message];
});
}
}
break;
default:
break;
}
}
if (self.hostDelegate.getRoomInfo.isCloseScreen) {
return;
}
// isCanDisplayMessage 是通用的过滤
if ([self isCanDisplayMessage:message]) {
[self addRoomMessage:message];
}
}
- (void)handleNIMNotificationMessage:(NIMMessage *)message {
self.isMiniEnter = NO;
NIMNotificationObject *notiMsg = (NIMNotificationObject *)message.messageObject;
if (![notiMsg.content isKindOfClass:[NIMChatroomNotificationContent class]]) {
return;
}
NIMChatroomNotificationContent *content = (NIMChatroomNotificationContent *)notiMsg.content;
RoomInfoModel *roomInfo = self.hostDelegate.getRoomInfo;
if (content.eventType == NIMChatroomEventTypeEnter) {
if (roomInfo.isCloseScreen) {
self.isLoadHistoryMessage = NO;
AttachmentModel *attachment = [[AttachmentModel alloc]init];
attachment.first = CustomMessageType_Update_RoomInfo;
attachment.second = Custom_Message_Sub_Update_RoomInfo_MessageState;
attachment.data = @{@"roomInfo":self.hostDelegate.getRoomInfo.model2dictionary};
NIMMessage *message = [[NIMMessage alloc]init];
NIMCustomObject *object = [[NIMCustomObject alloc] init];
object.attachment = attachment;
message.messageObject = object;
[self addRoomMessage:message];
return;
} else {
NIMChatroomNotificationMember *member = content.targets[0];
if (member.userId.integerValue == [AccountInfoStorage instance].getUid.integerValue) {
///自己进房成功后拉取历史消息
if(self.datasource.count > 0)
{
self.isLoadHistoryMessage = NO;
return;
}
[self handleFetchHistoryMessage:message];
} else {
self.isLoadHistoryMessage = NO;
BOOL hideEnter = [self handleHideEnter:message];
if (!hideEnter) {
///插入进房消息及房间公告提示
[self addRoomMessage:message];
}
}
}
} else if(content.eventType == NIMChatroomEventTypeInfoUpdated) {
if (roomInfo.isCloseScreen) {return;}
if (roomInfo.datingState == RoomDatingStateChangeType_Open) {
NSString * uid = [AccountInfoStorage instance].getUid;
NSString * roomId = [NSString stringWithFormat:@"%ld", roomInfo.roomId];
NIMChatroomMembersByIdsRequest *request = [[NIMChatroomMembersByIdsRequest alloc]init];
request.roomId = roomId;
request.userIds = @[uid];
[[NIMSDK sharedSDK].chatroomManager fetchChatroomMembersByIds:request completion:^(NSError * _Nullable error, NSArray<NIMChatroomMember *> * _Nullable members) {
if (error == nil) {
NIMChatroomMember * member = members.firstObject;
BOOL isCreator = member.type == NIMChatroomMemberTypeCreator;
BOOL isManager = member.type == NIMChatroomMemberTypeManager;
if (isCreator || isManager) {
[self addRoomMessage:message];
}
}
}];
}// else if (roomInfo)
}
}
- (void)handleFetchNewestMessage:(NIMMessage *)message {
RoomInfoModel * roomInfo = self.hostDelegate.getRoomInfo;
NSString *roomId = [NSString stringWithFormat:@"%ld", self.hostDelegate.getRoomInfo.roomId];
NIMHistoryMessageSearchOption *option = [[NIMHistoryMessageSearchOption alloc] init];
option.limit = [ClientConfig shareConfig].configInfo.roomMessageCount;
option.order = NIMMessageSearchOrderDesc;
option.messageTypes = @[@(NIMMessageTypeText),@(NIMMessageTypeCustom)];
@kWeakify(self);
[[NIMSDK sharedSDK].chatroomManager fetchMessageHistory:roomId
option:option
result:^(NSError * _Nullable error, NSArray<NIMMessage *> * _Nullable messages) {
@kStrongify(self);
if(error != nil){
self.isLoadHistoryMessage = NO;
}
if (self.datasource.count > kRoomMessageMaxLength) {
NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, kRoomMessageMaxLength/2)];
NSArray *needRemoveMsgArray = [self.datasource objectsAtIndexes:set];
[self.datasource removeObjectsInArray:needRemoveMsgArray];
}
// 执行插入
for (NIMMessage *item in messages.reverseObjectEnumerator) {
[self dealWithHistoryDataWithMessage:item];
}
self.isLoadHistoryMessage = NO;
BOOL hideEnter = [self handleHideEnter:message];
if (!hideEnter) {
///插入进房消息及房间公告提示
[self addRoomMessage:message];
}
if (!roomInfo.hasAnimationEffect) {
[self roomInfoNoGiftAnimationMessage:message];
}
[self createUserEnterRoomAddRoomTopicMessage];
[self updateAllDataSource:nil];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.messageTableView reloadData];
[self scrollToBottom:YES];
});
}];
}
- (void)handleFetchHistoryMessage:(NIMMessage *)message {
RoomInfoModel * roomInfo = self.hostDelegate.getRoomInfo;
NSString *roomId = [NSString stringWithFormat:@"%ld", self.hostDelegate.getRoomInfo.roomId];
NIMHistoryMessageSearchOption *option = [[NIMHistoryMessageSearchOption alloc] init];
option.limit = [ClientConfig shareConfig].configInfo.roomMessageCount;
option.startTime = self.hostDelegate.getRoomInfo.clearScreenTime / 1000.0;
option.order = NIMMessageSearchOrderAsc;
option.messageTypes = @[@(NIMMessageTypeText),@(NIMMessageTypeCustom)];
[[NIMSDK sharedSDK].chatroomManager fetchMessageHistory:roomId
option:option
result:^(NSError * _Nullable error, NSArray<NIMMessage *> * _Nullable messages) {
if(error != nil){
self.isLoadHistoryMessage = NO;
}
if (messages.count) {
//如果拉取的数量等于请求的数量说明这个时间点以后的消息数量大于等于需要拉取的数量直接拉取最新的50条
if (messages.count == [ClientConfig shareConfig].configInfo.roomMessageCount) {
[self handleFetchNewestMessage:message];
} else {
if (self.datasource.count > kRoomMessageMaxLength) {
NSIndexSet *set = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, kRoomMessageMaxLength/2)];
NSArray *needRemoveMsgArray = [self.datasource objectsAtIndexes:set];
[self.datasource removeObjectsInArray:needRemoveMsgArray];
}
// 执行插入
for (NIMMessage *item in messages) {
[self dealWithHistoryDataWithMessage:item];
}
self.isLoadHistoryMessage = NO;
BOOL hideEnter = [self handleHideEnter:message];
if (!hideEnter) {
///插入进房消息及房间公告提示
[self addRoomMessage:message];
}
if (!roomInfo.hasAnimationEffect) {
[self roomInfoNoGiftAnimationMessage:message];
}
[self createUserEnterRoomAddRoomTopicMessage];
[self updateAllDataSource:nil];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.messageTableView reloadData];
[self scrollToBottom:YES];
});
}
}else{
dispatch_async(dispatch_get_main_queue(), ^{///回到主线程
self.isLoadHistoryMessage = NO;
BOOL hideEnter = [self handleHideEnter:message];
if (!hideEnter) {
///插入进房消息及房间公告提示
[self addRoomMessage:message];
}
if (!roomInfo.hasAnimationEffect) {
[self roomInfoNoGiftAnimationMessage:message];
}
[self createUserEnterRoomAddRoomTopicMessage];
});
}
}];
}
-(void)dealWithHistoryDataWithMessage:(NIMMessage *)item{
BOOL isHaveSave = NO;
if(item.messageType == NIMMessageTypeText){
isHaveSave = YES;
}else if(item.messageType == NIMMessageTypeCustom){
NIMCustomObject *obj = (NIMCustomObject *)item.messageObject;
if (obj.attachment != nil && [obj.attachment isKindOfClass:[AttachmentModel class]]) {
AttachmentModel *attachment = (AttachmentModel *)obj.attachment;
if (attachment.first == CustomMessageType_Room_Album && attachment.second == Custom_Message_Sub_Room_Album) {
isHaveSave = YES;
} else if (attachment.first == CustomMessageType_User_Enter_Room && attachment.second == Custom_Message_Sub_Pic_Message) {
isHaveSave = YES;
}
}
}
if(isHaveSave == NO)return;
[self.datasource addObject:[self.messageParser parseMessageAttribute:item]];
if (!self.isMiniEnter) {/// 最小化进房的话 不需要重新保存
if (self.hostDelegate.getRoomInfo.isCloseScreen) {
NIMCustomObject *obj = (NIMCustomObject *)item.messageObject;
if (obj.attachment != nil && [obj.attachment isKindOfClass:[AttachmentModel class]]) {
AttachmentModel *attachment = (AttachmentModel *)obj.attachment;
if (attachment.first == CustomMessageType_Update_RoomInfo && attachment.second == Custom_Message_Sub_Update_RoomInfo_MessageState){
[[XPRoomMiniManager shareManager] saveRoomMessage:item];
}
}
} else {
[[XPRoomMiniManager shareManager] saveRoomMessage:item];
}
}
}
- (void)handleNIMTextMessage:(NIMMessage *)message {
self.isMiniEnter = NO;
if (self.hostDelegate.getRoomInfo.isCloseScreen) {return;}
[self addRoomMessage:message];
}
- (void)roomInfoNoGiftAnimationMessage:(NIMMessage *)message {
self.isMiniEnter = NO;
AttachmentModel *attachement = [[AttachmentModel alloc]init];
attachement.first = CustomMessageType_Update_RoomInfo;
attachement.second = Custom_Message_Sub_Update_RoomInfo_AnimateEffect;
NIMMessage *tempMessage = [[NIMMessage alloc]init];
NIMCustomObject *customObject = [[NIMCustomObject alloc]init];
customObject.attachment = attachement;
tempMessage.messageObject = customObject;
[self addRoomMessage:tempMessage];
}
- (void)onRoomMiniEntered {
self.isMiniEnter = YES;
self.isLoadHistoryMessage = NO;
///最小化进房 不需要请求任何接口 但是如果不加延迟的话 无法滚动到底部 原因还不知道
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSMutableArray *tempArray = @[].mutableCopy;
NSArray * temArray = [XPRoomMiniManager shareManager].getLocalCurrentRoomMessage;
[temArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
id model = nil;
if ([obj isKindOfClass:[NIMMessage class]]) {
model = [self.messageParser parseMessageAttribute:(NIMMessage *)obj];
} else if ([obj isKindOfClass:[NIMBroadcastMessage class]]) {
model = [self.messageParser parseBroadcastMessageAttribute:(NIMBroadcastMessage *)obj];
}
if (model) {
[tempArray addObject:model];
[self.datasource addObject:model];
}
}];
[self updateAllDataSource:tempArray];
[self.messageTableView reloadData];
//执行插入动画并滚动
[self scrollToBottom:YES];
});
}
- (void)onRoomEntered {
[[XPRoomMiniManager shareManager] resetLocalMessage];
self.headerView.bubbleColor = self.hostDelegate.getRoomInfo.type == RoomType_MiniGame ? [UIColor colorWithWhite:1 alpha:0.2] : [UIColor colorWithWhite:1 alpha:0.3];
}
- (void)onRoomUpdate {
///改变公屏的背景样式
if (self.hostDelegate.getRoomInfo.hadChangeRoomType) {
self.headerView.bubbleColor = self.hostDelegate.getRoomInfo.type == RoomType_MiniGame ? [UIColor colorWithWhite:1 alpha:0.2] : [UIColor colorWithWhite:1 alpha:0.3];
[self.messageTableView reloadData];
}
RoomInfoModel *infoModel = self.hostDelegate.getRoomInfo;
CGFloat top = infoModel.type == RoomType_MiniGame ? 0 : 10;
[self.messageTableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.mas_equalTo(self).offset(15);
make.bottom.trailing.mas_equalTo(self);
make.top.equalTo(self).mas_offset(top);
}];
}
#pragma mark - ScrollViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
// 手动拖拽开始
self.isPending = YES;
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
// 手动拖拽结束decelerate0松手时静止1松手时还在运动,会触发DidEndDecelerating方法
if (!decelerate) {
[self finishDraggingWith:scrollView];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
// 静止后触发(手动)
[self finishDraggingWith:scrollView];
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
if (!self.isPending && self.incomingMessages.count <= 0) {
[self scrollToBottom:YES];
}
}
/**
手动拖拽动作彻底完成(减速到零)
*/
- (void)finishDraggingWith:(UIScrollView *)scrollView {
CGFloat contentSizeH = scrollView.contentSize.height;
CGFloat contentOffsetY = scrollView.contentOffset.y;
CGFloat sizeH = scrollView.frame.size.height;
self.isPending = contentSizeH - contentOffsetY - sizeH > 20.0;
if (!self.isPending) {
self.messageTipsBtn.hidden = YES;
self.atTipBtn.hidden = YES;
self.atCount = 0;
[self.locationArray removeAllObjects];
if (self.incomingMessages.count > 0) {
[self appendAndScrollToBottom];
}
}
[[NSNotificationCenter defaultCenter] postNotificationName:@"roomMessageTabelViewStopScroll" object:nil];
}
#pragma mark - UITableViewDelegate And UITableViewDataSource
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray *source = @[];
switch (self.displayType) {
case 1:
source = self.datasource;
break;
case 2:
source = self.datasource_chat;
break;
case 3:
source = self.datasource_gift;
break;
default:
source = self.datasource;
break;
}
XPMessageInfoModel *model = [source xpSafeObjectAtIndex:indexPath.row];
// 预计算并缓存高度,避免使用 UITableViewAutomaticDimension
if (model.rowHeight <= 0) {
[self calculateRowHeightForModel:model];
}
if (model.first == CustomMessageType_RedPacket && model.second == Custom_Message_Sub_LuckyPackage) {
return model.rowHeight;
}
return model.rowHeight + 20;
}
// 新增方法:预计算行高
- (void)calculateRowHeightForModel:(XPMessageInfoModel *)model {
if (!model || !model.content) {
model.rowHeight = 44; // 默认高度
return;
}
// 使用 YYTextLayout 计算文本高度
YYTextContainer *container = [YYTextContainer new];
container.size = CGSizeMake(kRoomMessageMaxWidth - 24, MAXFLOAT);
container.maximumNumberOfRows = 0;
YYTextLayout *layout = [YYTextLayout layoutWithContainer:container text:model.content];
model.rowHeight = layout.textBoundingSize.height;
// 确保最小高度
if (model.rowHeight < 44) {
model.rowHeight = 44;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger count = 0;
switch (self.displayType) {
case 1:
count = self.datasource.count;
break;
case 2:
count = self.datasource_chat.count;
break;
case 3:
count = self.datasource_gift.count;
break;
default:
count = self.datasource.count;
break;
}
// 确保返回非负数
return MAX(0, count);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray *source = @[];
switch (self.displayType) {
case 1:
source = self.datasource;
break;
case 2:
source = self.datasource_chat;
break;
case 3:
source = self.datasource_gift;
break;
default:
source = self.datasource;
break;
}
XPMessageInfoModel *model = [source xpSafeObjectAtIndex:indexPath.row];
NSString *cellKey = [NSString isEmpty:model.cellKey] ? NSStringFromClass([XPRoomMessageTableViewCell class]) : model.cellKey;
if(model.first == CustomMessageType_Room_Album ||
(model.first == CustomMessageType_User_Enter_Room && model.second == Custom_Message_Sub_Pic_Message)){
PIRoomMessagePhotoAlbumCell * cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([PIRoomMessagePhotoAlbumCell class])];
cell.delegate = self;
cell.messageInfo = model;
cell.roomType = self.hostDelegate.getRoomInfo.type;
return cell;
} else if (model.first == CustomMessageType_RedPacket && model.second == Custom_Message_Sub_LuckyPackage) {
LuckyPackageMessageTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"LuckyPackageMessageTableViewCell" forIndexPath:indexPath];
cell.dataSource = model.customInfo;
return cell;
} else if (model.first == CustomMessageType_User_Enter_Room && model.second == Custom_Message_Sub_User_Enter_Room) {
CPEnterRoomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CPEnterRoomTableViewCell" forIndexPath:indexPath];
cell.cpIndex = [[model.customInfo objectForKey:@"cpIndex"] integerValue];
cell.dataSource = model.customInfo;
return cell;
}
else if (model.isBoom) {
cellKey = @"XPRoomMessageTableViewCell_Boom";
}
XPRoomMessageTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellKey];
cell.delegate = self;
cell.isLeftBigImage = model.isBoom;
cell.messageInfo = model;
cell.roomType = self.hostDelegate.getRoomInfo.type;
return cell;
}
//- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
// NSArray *source = @[];
// switch (self.displayType) {
// case 1:
// source = self.datasource;
// break;
// case 2:
// source = self.datasource_chat;
// break;
// case 3:
// source = self.datasource_gift;
// break;
//
// default:
// source = self.datasource;
// break;
// }
// XPMessageInfoModel *model = [source xpSafeObjectAtIndex:indexPath.row];
// if (model.rowHeight == 0) {
// model.rowHeight = CGRectGetHeight(cell.frame);
// }
//}
#pragma mark - XPRoomMessageTableViewCellDelegate
- (void)xPRoomMessageTableViewCellDidTapEmpty:(XPRoomMessageTableViewCell *)view {
if (self.delegate && [self.delegate respondsToSelector:@selector(xPRoomMessageContainerViewlDidTapEmpty:)]) {
[self.delegate xPRoomMessageContainerViewlDidTapEmpty:self];
}
}
-(void)clickDidTapEmptyAction{
if (self.delegate && [self.delegate respondsToSelector:@selector(xPRoomMessageContainerViewlDidTapEmpty:)]) {
[self.delegate xPRoomMessageContainerViewlDidTapEmpty:self];
}
}
#pragma mark - PIRoomMessagePhotoAlbumCell
- (void)pIRoomMessagePhotoAlbumCellDelegateDidTapEmpty:(PIRoomMessagePhotoAlbumCell *)view{
if (self.delegate && [self.delegate respondsToSelector:@selector(xPRoomMessageContainerViewlDidTapEmpty:)]) {
[self.delegate xPRoomMessageContainerViewlDidTapEmpty:self];
}
}
-(void)unlockAlbumHandleWithInfo:(PIRoomPhotoAlbumItemModel *_Nonnull)info{
PIRoomMessageUnlockPhotoAlbumView *albumView = [[PIRoomMessageUnlockPhotoAlbumView alloc]initWithFrame:CGRectMake(0, 0, KScreenWidth, KScreenHeight)];
albumView.albumModel = info;
albumView.delegate = self;
[TTPopup popupView:albumView style:TTPopupStyleAlert];
}
-(void)pIRoomMessagePhotoAlbumCell:(PIRoomMessagePhotoAlbumCell *_Nonnull)cell lookUpAlbumPhotoWithAlbumModel:(PIRoomPhotoAlbumItemModel *_Nonnull)albumModel;{
SDPhotoBrowser *browser = [[SDPhotoBrowser alloc]init];
browser.sourceImagesContainerView = cell;
browser.delegate = self;
browser.imageCount = 1;
browser.currentImageIndex = 0;
browser.isMe = NO;
self.lookUpModel = albumModel;
[browser show];
}
- (void)pIRoomMessagePhotoAlbumCell:(PIRoomMessagePhotoAlbumCell *)cell lookUpUserCardModel:(PIRoomPhotoAlbumItemModel *)albumModel{
[self.messageParser showUserCard:albumModel.uid.integerValue];
}
#pragma mark - PIRoomMessageUnlockPhotoAlbumViewDelegate
- (void)unlockRoomAlbumImageWithAlbumModel:(PIRoomPhotoAlbumItemModel *)albumModel{
[XNDJTDDLoadingTool showLoading];
[Api unlockRoomAlbumPhoto:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
[XNDJTDDLoadingTool hideHUD];
if(code == 200){
NSMutableDictionary *getData = [NSMutableDictionary dictionary];
[getData addEntriesFromDictionary:data.data];
AttachmentModel *attachment = [[AttachmentModel alloc] init];
attachment.first = CustomMessageType_Gift;
attachment.second = Custom_Message_Sub_Gift_Send;
NSDictionary *targetUsers = ((NSArray *)[getData objectForKey:@"targetUsers"]).firstObject;
[getData setObject:[targetUsers valueForKeyPath:@"uid"] forKey:@"targetUid"];
[getData setObject:[targetUsers valueForKeyPath:@"nick"] forKey:@"targetNick"];
[getData setObject:[targetUsers valueForKeyPath:@"avatar"] forKey:@"targetAvatar"];
[getData setObject:@(YES) forKey:@"isRoomAlbum"];
attachment.data = getData;
[self sendCustomMessage:attachment];
if(albumModel.ID != nil){
if(![[XPSkillCardPlayerManager shareInstance].photoIdList containsObject:albumModel.ID]){
if([XPSkillCardPlayerManager shareInstance].photoIdList == nil){
[XPSkillCardPlayerManager shareInstance].photoIdList = [NSMutableArray arrayWithArray:@[albumModel.ID]];
}else{
[[XPSkillCardPlayerManager shareInstance].photoIdList addObject:albumModel.ID];
}
}
[[NSNotificationCenter defaultCenter]postNotificationName:@"kGetgetUnlockRoomAlbumPhotoListNot" object:nil];
}
return;
}
[XNDJTDDLoadingTool showErrorWithMessage:msg];
} id:albumModel.ID roomUid:@(self.hostDelegate.getRoomInfo.uid).stringValue];
}
- (void)sendCustomMessage:(AttachmentModel *)attachment {
NSString *sessionID = [NSString stringWithFormat:@"%ld", [self.hostDelegate getRoomInfo].roomId];
NIMMessage *message = [[NIMMessage alloc]init];
NIMCustomObject *object = [[NIMCustomObject alloc] init];
object.attachment = attachment;
message.messageObject = object;
UserInfoModel *userInfo = [self.hostDelegate getUserInfo];
XPMessageRemoteExtModel *extModel = [[XPMessageRemoteExtModel alloc] init];
extModel.androidBubbleUrl = userInfo.androidBubbleUrl;
extModel.iosBubbleUrl = userInfo.iosBubbleUrl;
extModel.fromSayHelloChannel = userInfo.fromSayHelloChannel;
extModel.platformRole = userInfo.platformRole;
NSMutableDictionary *remoteExt = [NSMutableDictionary dictionaryWithObject:extModel.model2dictionary forKey:[NSString stringWithFormat:@"%ld", userInfo.uid]];
message.remoteExt = remoteExt;
NIMSessionType sessionType = NIMSessionTypeChatroom;
//构造会话
NIMSession *session = [NIMSession session:sessionID type:sessionType];
[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:nil];
}
#pragma mark - SDPhotoBrowserDelegate
- (NSURL *)photoBrowser:(SDPhotoBrowser *)browser highQualityImageURLForIndex:(NSInteger)index{
if(self.lookUpModel != nil){
return [[NSURL alloc]initWithString:self.lookUpModel.photoUrl];
}
return nil;
}
- (UIImage *)photoBrowser:(SDPhotoBrowser *)browser placeholderImageForIndex:(NSInteger)index {
return [UIImageConstant defaultBannerPlaceholder];
}
#pragma mark - Getters And Setters
- (UITableView *)messageTableView {
if (!_messageTableView) {
_messageTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
_messageTableView.delegate = self;
_messageTableView.dataSource = self;
// 移除 UITableViewAutomaticDimension使用预计算的高度
_messageTableView.rowHeight = 64; // 设置一个合理的默认高度
_messageTableView.tableFooterView = [UIView new];
_messageTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_messageTableView.backgroundColor = [UIColor clearColor];
_messageTableView.showsVerticalScrollIndicator = NO;
_messageTableView.clipsToBounds = NO;
_messageTableView.tag = 666;
if (@available(iOS 11.0, *)) {
_messageTableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
[_messageTableView registerClass:[XPRoomMessageTableViewCell class] forCellReuseIdentifier:@"XPRoomMessageTableViewCell_Boom"];
[_messageTableView registerClass:[XPRoomMessageTableViewCell class] forCellReuseIdentifier:NSStringFromClass([XPRoomMessageTableViewCell class])];
[_messageTableView registerClass:[XPRoomMessageTableViewCell class] forCellReuseIdentifier:@"ChatMessage"];
[_messageTableView registerClass:[PIRoomMessagePhotoAlbumCell class] forCellReuseIdentifier:NSStringFromClass([PIRoomMessagePhotoAlbumCell class])];
[_messageTableView registerClass:[LuckyPackageMessageTableViewCell class] forCellReuseIdentifier:@"LuckyPackageMessageTableViewCell"];
[_messageTableView registerClass:[CPEnterRoomTableViewCell class] forCellReuseIdentifier:@"CPEnterRoomTableViewCell"];
}
return _messageTableView;
}
- (XPRoomMessageHeaderView *)headerView {
if (!_headerView) {
_headerView = [[XPRoomMessageHeaderView alloc] init];
}
return _headerView;
}
- (UIButton *)messageTipsBtn {
if (!_messageTipsBtn) {
_messageTipsBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_messageTipsBtn setTitle:YMLocalizedString(@"XPRoomMessageContainerView6") forState:UIControlStateNormal];
[_messageTipsBtn setTitleColor:[DJDKMIMOMColor appMainColor] forState:UIControlStateNormal];
_messageTipsBtn.layer.cornerRadius = 15.0;
_messageTipsBtn.layer.masksToBounds = YES;
_messageTipsBtn.titleLabel.font = [UIFont systemFontOfSize:12.0];
_messageTipsBtn.backgroundColor = [UIColor whiteColor];
[_messageTipsBtn addTarget:self action:@selector(messageTipsBtnAction:) forControlEvents:UIControlEventTouchUpInside];
_messageTipsBtn.hidden = YES;
}
return _messageTipsBtn;
}
- (UIButton *)atTipBtn {
if (!_atTipBtn) {
_atTipBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_atTipBtn setTitle:YMLocalizedString(@"XPRoomMessageContainerView7") forState:UIControlStateNormal];
[_atTipBtn setTitleColor:[DJDKMIMOMColor appMainColor] forState:UIControlStateNormal];
_atTipBtn.layer.cornerRadius = 15.0;
_atTipBtn.layer.masksToBounds = YES;
_atTipBtn.titleLabel.font = [UIFont systemFontOfSize:12.0];
_atTipBtn.backgroundColor = [UIColor whiteColor];
[_atTipBtn addTarget:self action:@selector(atTipsBtnAction:) forControlEvents:UIControlEventTouchUpInside];
_atTipBtn.hidden = YES;
}
return _atTipBtn;
}
- (NSMutableArray<XPMessageInfoModel *> *)datasource {
if (!_datasource) {
_datasource = [NSMutableArray array];
}
return _datasource;
}
- (NSMutableArray<XPMessageInfoModel *> *)datasource_chat {
if (!_datasource_chat) {
_datasource_chat = [NSMutableArray array];
}
return _datasource_chat;
}
- (NSMutableArray<XPMessageInfoModel *> *)datasource_gift {
if (!_datasource_gift) {
_datasource_gift = [NSMutableArray array];
}
return _datasource_gift;
}
- (NSMutableArray *)incomingMessages {
if (!_incomingMessages) {
_incomingMessages = [NSMutableArray array];
}
return _incomingMessages;
}
- (NSMutableArray *)locationArray {
if (!_locationArray) {
_locationArray = [NSMutableArray array];
}
return _locationArray;
}
- (XPRoomMessageParser *)messageParser {
if (!_messageParser) {
_messageParser = [[XPRoomMessageParser alloc] init];
_messageParser.hostDelegate = self.hostDelegate;
}
return _messageParser;
}
@end