Files
peko-ios/YuMi/Modules/YMRoom/View/SendGiftView/GiftComboManager.m
2024-12-11 10:48:01 +08:00

521 lines
16 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.

//
// GiftComboManager.m
// YuMi
//
// Created by P on 2024/9/5.
//
// 处理连击面板逻辑
#import "GiftComboManager.h"
#import "Api+Gift.h"
#import "UserInfoModel.h"
#import "AttachmentModel.h"
#import "XPGiftCountModel.h"
#import "GiftReceiveInfoModel.h"
#import "XPMessageRemoteExtModel.h"
#import "GiftComboFlagView.h"
@interface GiftComboManager ()
@property (nonatomic, assign) BOOL enableCombo;
@property (nonatomic, strong) NSMutableArray *requestQueue;
// 用来存储 GiftReceiveInfoModel 和 NSDictionary 的元数据队列
@property (nonatomic, strong) NSMutableArray *giftComboQueue;
@property (nonatomic, strong) NSMutableArray *comboFlagQueue;
@property (nonatomic, strong) NSTimer *comboFlagTimer;
@property (nonatomic, strong) UIView *containerView;
// 定时器,处理请求的调度器
@property (nonatomic, strong) dispatch_source_t timer;
@property (nonatomic, copy) NSArray *sendGiftToUIDs;
@property (nonatomic, assign) GiftSourceType giftSourceType;
@property (nonatomic, strong) GiftInfoModel *giftInfo;
@property (nonatomic, assign) RoomSendGiftType roomSendGiftType;
@property (nonatomic, copy) NSString *roomUID;
@property (nonatomic, copy) NSString *giftNumPerTimes;
@property (nonatomic, strong) UserInfoModel *sendGiftUserInfo;
@property (nonatomic, copy) NSString *sessionID;
@property (nonatomic, strong) XPGiftCountModel *countModel;
@property (nonatomic, assign) NSInteger combo;
@property (nonatomic, assign) bool isCombing;
@property (nonatomic, copy) void (^actionCallback)(ComboActionType type);
@property (nonatomic, copy) NSString *errorMessage;
@property (nonatomic, strong) NSMutableArray<GiftComboFlagView *> *activeViews; // 用于存储最多2个活跃的动画视图
@end
@implementation GiftComboManager
#pragma mark - 单例方法
+ (instancetype)sharedManager {
static GiftComboManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
sharedInstance.giftComboQueue = [NSMutableArray array];
sharedInstance.activeViews = [NSMutableArray array];
sharedInstance.comboFlagQueue = [NSMutableArray array];
sharedInstance.requestQueue = [NSMutableArray array];
[sharedInstance startProcessingGiftComboFlagQueue];
});
return sharedInstance;
}
//// 添加 GiftReceiveInfoModel 和 metadata 到队列
- (void)addGiftComboWithInfo:(GiftReceiveInfoModel *)info andMetadata:(NSDictionary *)metadata {
if (info && metadata) {
// 将元数据打包成字典并添加到队列
@synchronized (self) {
NSDictionary *comboData = @{@"info": info, @"metadata": metadata};
[self.giftComboQueue addObject:comboData];
}
// 启动定时器
[self startProcessingQueue];
}
}
- (void)addComboFromNIMAttachment:(AttachmentModel *)attachment {
if (attachment) {
// 将元数据打包成字典并添加到队列
@synchronized (self) {
[self.giftComboQueue addObject:attachment];
}
// 启动定时器
[self startProcessingQueue];
}
}
// 开始连击,重置
- (void)resetCombo {
_combo = 1;
_errorMessage = @"";
if (self.actionCallback && self.enableCombo) {
self.actionCallback(ComboAction_ShowPanel);
self.isCombing = YES;
}
if (self.handleRoomUIChanged) {
self.handleRoomUIChanged(YES);
}
}
- (void)registerActions:(void (^)(ComboActionType))action {
_combo = 1;
_errorMessage = @"";
self.actionCallback = action;
}
- (void)forceRemove {
if (self.actionCallback) {
self.actionCallback(ComboAction_RemovePanel);
}
if (self.handleRoomUIChanged) {
self.handleRoomUIChanged(NO);
}
self.isCombing = NO;
}
- (NSInteger)loadComboCountFromSendGiftView {
if (!self.enableCombo) {
return 0;
}
NSInteger temp = self.combo;
self.combo += 1;
return temp;
}
- (NSInteger)loadComboCount {
return self.combo;
}
- (NSInteger)loadTotalGiftNum {
return self.combo * self.countModel.giftNumber.integerValue * self.sendGiftToUIDs.count;
}
- (BOOL)isGiftCombing {
return self.isCombing;
}
#pragma mark - 处理飘屏逻辑
- (void)receiveGiftInfoForDisplayComboFlags:(GiftReceiveInfoModel *)receiveInfo
container:(UIView *)container {
self.containerView = container;
[self.giftComboQueue addObject:receiveInfo];
[self startProcessingGiftComboFlagQueue];
}
- (void)startProcessingGiftComboFlagQueue {
self.comboFlagTimer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(processGiftFlagQueue) userInfo:nil repeats:YES];
}
- (void)stopProcessingGiftComboFlagQueue {
[self.comboFlagTimer invalidate];
self.comboFlagTimer = nil;
}
- (void)processGiftFlagQueue {
if (self.giftComboQueue.count == 0) {
return;
}
GiftReceiveInfoModel *receiveInfo = [self.giftComboQueue firstObject];
[self.giftComboQueue removeObjectAtIndex:0];
[self handleGiftInfo:receiveInfo];
}
- (void)handleGiftInfo:(GiftReceiveInfoModel *)receiveInfo {
if (receiveInfo.comboCount < 1) {
// 不正常的数据,不处理
return;
}
if ([self updateExistingViewWithModel:receiveInfo]) {
// 如果更新了现有视图,就不需要创建新视图
return;
}
if (self.activeViews.count >= 2) {
GiftComboFlagView *oldestView = [self.activeViews firstObject];
[self animateRemoveView:oldestView];
}
CGFloat positionY = kGetScaleWidth(380);
CGFloat positionX = isMSRTL() ? -self.containerView.bounds.size.width : self.containerView.bounds.size.width;
GiftComboFlagView *flagView = [[GiftComboFlagView alloc] initWithFrame:CGRectMake(positionX,
positionY,
kGetScaleWidth(300),
50)];
@kWeakify(self);
@kWeakify(flagView);
[flagView setTimerEnd:^{
@kStrongify(self);
@kStrongify(flagView);
[self animateRemoveView:flagView];
}];
[flagView updateReceiveInfoModel:receiveInfo animationType:0];
[self allCurrentFlagMoveDown];
[self.containerView addSubview:flagView];
[self.activeViews insertObject:flagView atIndex:0];
[self animateView:flagView positionY:positionY];
}
- (void)allCurrentFlagMoveDown {
CGFloat positionY = kGetScaleWidth(380);
[self.activeViews enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(GiftComboFlagView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
dispatch_async(dispatch_get_main_queue(), ^{
CGRect rect = obj.frame;
rect.origin.y = positionY + idx * 50;
obj.frame = rect;
});
}];
}
- (void)animateView:(GiftComboFlagView *)flagView positionY:(CGFloat)positionY {
[UIView animateWithDuration:0.1
animations:^{
if (isMSRTL()) {
// 获取屏幕宽度
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
// 计算新的 x 坐标:屏幕宽度 - 视图宽度 - 右边距
CGFloat newX = screenWidth - 20 - kGetScaleWidth(300);
// 更新 flagView 的 frame使右边距离屏幕右边 20 像素
flagView.frame = CGRectMake(newX, positionY, kGetScaleWidth(300), 50);
} else {
flagView.frame = CGRectMake(20, positionY, kGetScaleWidth(300), 50);
}
} completion:^(BOOL finished) {
[self allCurrentFlagMoveDown];
}];
}
- (void)animateRemoveView:(GiftComboFlagView *)flagView {
[flagView removeFromSuperview];
if ([self.activeViews containsObject:flagView]) {
[self.activeViews removeObject:flagView];
flagView = nil;
}
[self allCurrentFlagMoveDown];
}
// 更新现有 View 的数据,如果存在相同的 ID
- (BOOL)updateExistingViewWithModel:(GiftReceiveInfoModel *)model {
NSInteger index = 0;
for (GiftComboFlagView *existingFlag in self.activeViews) {
if (existingFlag.superview == nil) {
continue;
}
if ([existingFlag.receiveInfo isEqual:model]) {
[self updateComboFlag:existingFlag with:model];
return YES;
}
index++;
}
return NO;
}
- (void)updateComboFlag:(GiftComboFlagView *)flagView with:(GiftReceiveInfoModel *)model {
[flagView updateReceiveInfoModel:model animationType:1];
}
// MARK: Logic is 连击面板出现后,每点击一次,就触发一次面板最后的请求,请求成功后,构造云信消息体,消息体进入队列并按 0.2s 一次的频率发送消息
#pragma mark - 管理队列
// 开始处理队列
- (void)startProcessingQueue {
if (self.timer) {
return; // 如果定时器已经在运行,直接返回
}
// 创建 GCD 定时器
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
// 设置定时器时间间隔:每 0.2 秒执行一次
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 0.2 * NSEC_PER_SEC, 0.01 * NSEC_PER_SEC);
// 定时器触发的事件处理
dispatch_source_set_event_handler(self.timer, ^{
[self processRequestQueue];
[self processGiftComboQueue];
});
// **立即执行一次处理方法**
[self processRequestQueue];
[self processGiftComboQueue];
// 启动定时器
dispatch_resume(self.timer);
}
// 停止处理队列
- (void)stopProcessingQueue {
if (self.timer) {
if (self.requestQueue.count == 0 && self.giftComboQueue.count == 0) {
// 取消定时器
dispatch_source_cancel(self.timer);
// 设置取消回调,在资源完全释放后将 timer 置为 nil
@kWeakify(self);
dispatch_source_set_cancel_handler(self.timer, ^{
@kStrongify(self);
self.timer = nil;
});
}
}
}
// 处理队列中的第一个请求
- (void)processGiftComboQueue {
@synchronized (self) {
if (self.giftComboQueue.count > 0) {
// 获取并移除队列中的第一个元数据
AttachmentModel *attachment = [self.giftComboQueue firstObject];
[self.giftComboQueue removeObjectAtIndex:0];
// 处理逻辑
[self processGiftComboWith:attachment];
} else {
[self stopProcessingQueue];
}
}
}
// 处理元数据的实际逻辑
- (void)processGiftComboWith:(AttachmentModel *)info {
[self sendCustomMessage:info];
}
- (NSString *)loadErrorMessage {
return self.errorMessage;
}
- (void)processRequestQueue {
@synchronized (self) {
if (self.requestQueue.count > 0) {
// 获取并移除队列中的第一个元数据
NSDictionary *dic = [self.requestQueue firstObject];
[self handleSendGift:dic];
[self.requestQueue removeObject:dic];
} else {
[self stopProcessingQueue];
}
}
}
#pragma mark - Gift meta data
- (void)enableToCombo:(BOOL)enable {
self.enableCombo = enable;
}
- (BOOL)loadEnable {
return self.enableCombo;
}
- (void)saveSendGiftTo:(NSArray *)UIDs
{
_sendGiftToUIDs = UIDs;
}
- (void)saveGiftSourceType:(GiftSourceType)type
{
_giftSourceType = type;
}
- (void)saveSendGiftInfo:(GiftInfoModel *)model
{
_giftInfo = model;
}
- (void)saveSendGiftType:(RoomSendGiftType)type
{
_roomSendGiftType = type;
}
- (void)saveRoomUID:(NSString *)roomUID {
_roomUID = roomUID;
}
- (void)saveSendGiftNum:(NSString *)numString
{
_giftNumPerTimes = numString;
}
- (void)saveUserInfo:(UserInfoModel *)userInfo {
_sendGiftUserInfo = userInfo;
}
- (void)saveSessionID:(NSString *)sessionID {
_sessionID = sessionID;
}
- (void)saveGiftCountModel:(XPGiftCountModel *)model {
_countModel = model;
}
#pragma mark - XPGiftPresenter
- (void)sendGift {
NSString *allUIDs = @"";
for (NSString *item in self.sendGiftToUIDs) {
if (allUIDs.length > 0) {
allUIDs = [allUIDs stringByAppendingString:@","];
}
allUIDs = [allUIDs stringByAppendingString:item];
}
NSDictionary *dic = @{
@"targetUids":allUIDs,
@"giftNum":self.giftNumPerTimes,
@"sendType":[NSString stringWithFormat:@"%ld", GiftSendType_OnMic],
@"giftId":[NSString stringWithFormat:@"%ld", self.giftInfo.giftId],
@"giftSource":[NSString stringWithFormat:@"%ld", self.giftSourceType],
@"giftType":[NSString stringWithFormat:@"%ld", self.roomSendGiftType],
@"roomUid":self.roomUID
};
[self.requestQueue addObject:dic];
[self startProcessingQueue];
}
- (void)handleSendGift:(NSDictionary *)dic {
NSString *allUIDs = [dic objectForKey:@"targetUids"];
@kWeakify(self);
[Api requestSendGift:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
@kStrongify(self);
if (!self) {
return;
}
if (code == 200) {
GiftReceiveInfoModel *receive = [GiftReceiveInfoModel modelWithJSON:data.data];
receive.sourceType = [[dic objectForKey:@"giftSource"] integerValue];
receive.roomSendGiftType = [[dic objectForKey:@"giftType"] integerValue];
NSArray *array = [allUIDs componentsSeparatedByString:@","];
receive.receiveGiftNumberUser = array.count;
[self handleSendGiftSuccess:receive sourceData:data];
[[NSNotificationCenter defaultCenter] postNotificationName:@"receiveLuckGiftWinning"
object:@{@"CurrentGold": receive.userPurse.diamonds,
@"Price": @(receive.gift.goldPrice * receive.giftNum * array.count),
@"isFromWinning":@(NO)}];
} else {
self.errorMessage = msg;
if (self.actionCallback) {
self.actionCallback(ComboAction_Error);
}
[self forceRemove];
}
}
targetUids:allUIDs
giftNum:self.giftNumPerTimes
sendType:[dic objectForKey:@"sendType"]
giftId:[dic objectForKey:@"giftId"]
giftSource:[dic objectForKey:@"giftSource"]
giftType:[dic objectForKey:@"giftType"]
roomUid:[dic objectForKey:@"roomUid"]
msg:@""
uid:[AccountInfoStorage instance].getUid];
}
- (void)handleSendGiftSuccess:(GiftReceiveInfoModel *)receive
sourceData:(BaseModel *)response {
if (self.actionCallback) {
self.actionCallback(ComboAction_Combo_Count_Update);
}
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithDictionary:response.data];
[dic setObject:@(self.combo) forKey:@"comboCount"];
self.sendGiftReceiveInfo = receive;
if (self.handleComboSuccess) {
self.handleComboSuccess(receive, dic);
}
if (self.actionCallback) {
self.actionCallback(ComboAction_Update_After_Send_Success);
}
}
- (void)sendCustomMessage:(AttachmentModel *)attachment {
NIMMessage *message = [[NIMMessage alloc]init];
NIMCustomObject *object = [[NIMCustomObject alloc] init];
object.attachment = attachment;
message.messageObject = object;
UserInfoModel *userInfo = self.sendGiftUserInfo;
XPMessageRemoteExtModel *extModel = [[XPMessageRemoteExtModel alloc] init];
extModel.androidBubbleUrl = userInfo.androidBubbleUrl;
extModel.iosBubbleUrl = userInfo.iosBubbleUrl;
extModel.fromSayHelloChannel = userInfo.fromSayHelloChannel;
NSMutableDictionary *remoteExt = [NSMutableDictionary dictionaryWithObject:extModel.model2dictionary forKey:[NSString stringWithFormat:@"%ld", userInfo.uid]];
message.remoteExt = remoteExt;
//构造会话
NIMSession *session = [NIMSession session:self.sessionID type:NIMSessionTypeChatroom];
[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:nil];
}
@end