883 lines
28 KiB
Objective-C
883 lines
28 KiB
Objective-C
//
|
||
// GiftComboManager.m
|
||
// YuMi
|
||
//
|
||
// Created by P on 2024/9/5.
|
||
//
|
||
// 处理连击面板逻辑
|
||
|
||
#import "GiftComboManager.h"
|
||
|
||
#import <Foundation/Foundation.h>
|
||
#import <UIKit/UIKit.h>
|
||
#import <Bugly/Bugly.h>
|
||
#import "BuglyManager.h"
|
||
|
||
#import "Api+Gift.h"
|
||
|
||
#import "UserInfoModel.h"
|
||
#import "AttachmentModel.h"
|
||
#import "XPGiftCountModel.h"
|
||
#import "GiftReceiveInfoModel.h"
|
||
#import "XPMessageRemoteExtModel.h"
|
||
|
||
#import "GiftComboFlagView.h"
|
||
|
||
// 通知常量实现
|
||
NSString * const kBoomStateForceResetNotification = @"BoomStateForceResetNotification";
|
||
|
||
@interface GiftComboManager ()
|
||
|
||
@property (nonatomic, assign) BOOL enableCombo;
|
||
|
||
@property (nonatomic, strong) NSMutableArray *requestQueue;
|
||
|
||
// 用来存储 GiftReceiveInfoModel 和 NSDictionary 的元数据队列
|
||
@property (nonatomic, strong) NSMutableArray *networkQueue; // 网络通信队列(AttachmentModel)
|
||
@property (nonatomic, strong) NSMutableArray *uiQueue; // UI动画队列(GiftReceiveInfoModel)
|
||
@property (nonatomic, strong) dispatch_source_t comboFlagTimer;
|
||
@property (nonatomic, strong) UIView *containerView;
|
||
|
||
// 定时器,处理请求的调度器
|
||
@property (nonatomic, strong) dispatch_source_t timer;
|
||
|
||
// 新增:后台处理队列
|
||
@property (nonatomic, strong) dispatch_queue_t backgroundQueue;
|
||
@property (nonatomic, strong) dispatch_queue_t networkProcessingQueue;
|
||
|
||
@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 - 单例方法
|
||
|
||
- (void)dealloc {
|
||
|
||
// 🔥 修复:确保所有timer都被停止
|
||
[self forceStopAllTimers];
|
||
|
||
// 停止UI队列处理
|
||
[self stopProcessingUIQueue];
|
||
|
||
// 清空所有队列
|
||
[self clearAllQueues];
|
||
|
||
// 重置状态
|
||
self.isCombing = NO;
|
||
self.enableCombo = NO;
|
||
self.actionCallback = nil;
|
||
|
||
}
|
||
|
||
+ (instancetype)sharedManager {
|
||
static GiftComboManager *sharedInstance = nil;
|
||
static dispatch_once_t onceToken;
|
||
dispatch_once(&onceToken, ^{
|
||
sharedInstance = [[self alloc] init];
|
||
// sharedInstance.giftComboQueue = [NSMutableArray array];
|
||
sharedInstance.networkQueue = [NSMutableArray array];
|
||
sharedInstance.uiQueue = [NSMutableArray array];
|
||
sharedInstance.activeViews = [NSMutableArray array];
|
||
sharedInstance.requestQueue = [NSMutableArray array];
|
||
|
||
// 初始化后台处理队列
|
||
sharedInstance.backgroundQueue = dispatch_queue_create("com.yumi.giftcombo.background", DISPATCH_QUEUE_CONCURRENT);
|
||
sharedInstance.networkProcessingQueue = dispatch_queue_create("com.yumi.giftcombo.network", DISPATCH_QUEUE_SERIAL);
|
||
|
||
[sharedInstance startProcessingUIQueue];
|
||
});
|
||
return sharedInstance;
|
||
}
|
||
|
||
//// 添加 GiftReceiveInfoModel 和 metadata 到队列
|
||
- (void)addGiftComboWithInfo:(GiftReceiveInfoModel *)info andMetadata:(NSDictionary *)metadata {
|
||
if (info && metadata) {
|
||
// 将元数据打包成字典并添加到队列
|
||
@synchronized (self) {
|
||
NSDictionary *comboData = @{@"info": info, @"metadata": metadata};
|
||
[self.networkQueue addObject:comboData];
|
||
}
|
||
// 启动定时器
|
||
[self startProcessingQueue];
|
||
}
|
||
}
|
||
|
||
- (void)addComboFromNIMAttachment:(AttachmentModel *)attachment {
|
||
if (attachment) {
|
||
// 将元数据打包成字典并添加到队列
|
||
@synchronized (self) {
|
||
[self.networkQueue addObject:attachment];
|
||
}
|
||
// 启动定时器
|
||
[self startProcessingQueue];
|
||
}
|
||
}
|
||
|
||
// 开始连击,重置
|
||
- (void)reset {
|
||
|
||
// 🔥 修复:检查是否正在重置过程中
|
||
if (self.isCombing && self.combo == 0) {
|
||
return;
|
||
}
|
||
|
||
// 🔥 修复:如果actionCallback为空,延迟执行reset
|
||
if (!self.actionCallback) {
|
||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||
[self reset];
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 🔥 修复:确保enableCombo状态正确
|
||
if (!self.enableCombo) {
|
||
self.enableCombo = YES;
|
||
}
|
||
|
||
// 确保连击计数正确重置为 1
|
||
_combo = 1;
|
||
_errorMessage = @"";
|
||
|
||
// 验证重置后的状态
|
||
|
||
// 发送通知,让 GiftComboView 重置显示
|
||
[[NSNotificationCenter defaultCenter] postNotificationName:@"ComboCountReset" object:nil];
|
||
|
||
// 🔥 修复:确保状态正确设置后再触发回调
|
||
self.isCombing = YES;
|
||
|
||
// 检查是否应该显示连击面板 - 确保在主线程执行UI回调
|
||
if (self.actionCallback) {
|
||
@kWeakify(self);
|
||
[self safeExecuteUIBlock:^{
|
||
@kStrongify(self);
|
||
if (self && self.actionCallback) {
|
||
self.actionCallback(ComboAction_ShowPanel);
|
||
}
|
||
}];
|
||
} else {
|
||
}
|
||
|
||
if (self.handleRoomUIChanged) {
|
||
@kWeakify(self);
|
||
[self safeExecuteUIBlock:^{
|
||
@kStrongify(self);
|
||
if (self && self.handleRoomUIChanged) {
|
||
self.handleRoomUIChanged(YES);
|
||
}
|
||
}];
|
||
}
|
||
}
|
||
|
||
- (void)registerActions:(void (^)(ComboActionType))action {
|
||
_combo = 1;
|
||
_errorMessage = @"";
|
||
self.actionCallback = action;
|
||
}
|
||
|
||
// 新增:实现其他简化接口方法
|
||
- (void)activate {
|
||
self.enableCombo = YES;
|
||
}
|
||
|
||
- (void)deactivate {
|
||
self.enableCombo = NO;
|
||
}
|
||
|
||
- (BOOL)isActive {
|
||
return self.isCombing && self.enableCombo;
|
||
}
|
||
|
||
- (NSInteger)currentCount {
|
||
// 确保连击计数最少为 1
|
||
if (self.combo < 1) {
|
||
self.combo = 1;
|
||
}
|
||
return self.combo;
|
||
}
|
||
|
||
- (void)incrementCount {
|
||
self.combo += 1;
|
||
}
|
||
|
||
- (void)clear {
|
||
|
||
[self forceBoomStateReset];
|
||
|
||
// 🔥 修复:确保在主线程执行UI回调,并检查回调是否有效
|
||
if (self.actionCallback) {
|
||
@kWeakify(self);
|
||
[self safeExecuteUIBlock:^{
|
||
@kStrongify(self);
|
||
if (self && self.actionCallback) {
|
||
self.actionCallback(ComboAction_RemovePanel);
|
||
}
|
||
}];
|
||
}
|
||
}
|
||
|
||
- (void)send {
|
||
[self sendGift];
|
||
}
|
||
|
||
- (NSDictionary *)stateInfo {
|
||
return [self getComboStateInfo];
|
||
}
|
||
|
||
- (BOOL)canStartCombo {
|
||
return self.enableCombo && self.giftInfo != nil && self.sendGiftToUIDs.count > 0;
|
||
}
|
||
|
||
- (void)validateState {
|
||
[self validateAndFixComboCount];
|
||
}
|
||
|
||
- (void)handleError:(NSError *)error {
|
||
self.errorMessage = error.localizedDescription;
|
||
}
|
||
|
||
- (NSString *)lastErrorMessage {
|
||
return self.errorMessage ?: @"";
|
||
}
|
||
|
||
- (void)clearError {
|
||
self.errorMessage = @"";
|
||
}
|
||
|
||
|
||
|
||
- (void)forceBoomStateReset {
|
||
|
||
// 1. 立即停止所有定时器(无条件停止)
|
||
[self forceStopAllTimers];
|
||
|
||
// 2. 清空所有队列
|
||
[self clearAllQueues];
|
||
|
||
// 3. 重置所有状态标志
|
||
self.isCombing = NO;
|
||
|
||
// 4. 重置combo计数为0
|
||
_combo = 0;
|
||
|
||
// 5. 发送通知(优先级最高,通知所有相关组件)
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
[[NSNotificationCenter defaultCenter] postNotificationName:kBoomStateForceResetNotification
|
||
object:nil];
|
||
});
|
||
|
||
// 6. 强制恢复UI(确保底部栏和侧栏显示)
|
||
if (self.handleRoomUIChanged) {
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
self.handleRoomUIChanged(NO);
|
||
});
|
||
}
|
||
}
|
||
|
||
// 无条件停止定时器
|
||
- (void)forceStopAllTimers {
|
||
|
||
// 停止主定时器
|
||
if (self.timer) {
|
||
dispatch_source_cancel(self.timer);
|
||
self.timer = nil;
|
||
}
|
||
|
||
// 停止UI队列定时器
|
||
if (self.comboFlagTimer) {
|
||
dispatch_source_cancel(self.comboFlagTimer);
|
||
self.comboFlagTimer = nil;
|
||
}
|
||
}
|
||
|
||
// 清空所有队列
|
||
- (void)clearAllQueues {
|
||
@synchronized (self) {
|
||
[self.requestQueue removeAllObjects];
|
||
[self.networkQueue removeAllObjects];
|
||
[self.uiQueue removeAllObjects];
|
||
}
|
||
}
|
||
|
||
- (NSInteger)loadTotalGiftNum {
|
||
return self.combo * self.countModel.giftNumber.integerValue * self.sendGiftToUIDs.count;
|
||
}
|
||
|
||
// 新增:检查连击状态是否有效
|
||
- (BOOL)isComboStateValid {
|
||
@synchronized (self) {
|
||
return self.isCombing &&
|
||
self.enableCombo &&
|
||
self.combo > 0 &&
|
||
self.giftInfo != nil &&
|
||
self.sendGiftToUIDs.count > 0;
|
||
}
|
||
}
|
||
|
||
// 新增:获取连击状态信息
|
||
- (NSDictionary *)getComboStateInfo {
|
||
@synchronized (self) {
|
||
return @{
|
||
@"isCombing": @(self.isCombing),
|
||
@"enableCombo": @(self.enableCombo),
|
||
@"combo": @(self.combo),
|
||
@"hasGiftInfo": @(self.giftInfo != nil),
|
||
@"targetCount": @(self.sendGiftToUIDs.count),
|
||
@"errorMessage": self.errorMessage ?: @""
|
||
};
|
||
}
|
||
}
|
||
|
||
// 新增:验证并修复连击计数
|
||
- (void)validateAndFixComboCount {
|
||
@synchronized (self) {
|
||
if (self.combo < 1) {
|
||
self.combo = 1;
|
||
}
|
||
|
||
if (self.combo > 1000) {
|
||
self.combo = 100;
|
||
}
|
||
}
|
||
}
|
||
|
||
#pragma mark - 处理飘屏逻辑
|
||
- (void)receiveGiftInfoForDisplayComboFlags:(GiftReceiveInfoModel *)receiveInfo
|
||
container:(UIView *)container {
|
||
self.containerView = container;
|
||
[self.uiQueue addObject:receiveInfo];
|
||
[self startProcessingUIQueue];
|
||
}
|
||
|
||
- (void)removeComboFlag {
|
||
self.containerView = nil;
|
||
[self stopProcessingQueue];
|
||
[self stopProcessingUIQueue];
|
||
}
|
||
|
||
- (void)startProcessingUIQueue {
|
||
if (self.comboFlagTimer) {
|
||
return;
|
||
}
|
||
|
||
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
|
||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
|
||
|
||
dispatch_source_set_timer(timer,
|
||
DISPATCH_TIME_NOW,
|
||
0.2 * NSEC_PER_SEC,
|
||
0.01 * NSEC_PER_SEC);
|
||
|
||
@kWeakify(self);
|
||
dispatch_source_set_event_handler(timer, ^{
|
||
@kStrongify(self);
|
||
[self processGiftFlagQueue];
|
||
});
|
||
|
||
dispatch_resume(timer);
|
||
self.comboFlagTimer = timer;
|
||
}
|
||
|
||
- (void)stopProcessingUIQueue {
|
||
if (self.comboFlagTimer) {
|
||
dispatch_source_cancel(self.comboFlagTimer);
|
||
|
||
@kWeakify(self);
|
||
dispatch_source_set_cancel_handler(self.comboFlagTimer, ^{
|
||
@kStrongify(self);
|
||
self.comboFlagTimer = nil;
|
||
});
|
||
}
|
||
}
|
||
|
||
- (void)processGiftFlagQueue {
|
||
@synchronized (self) {
|
||
if (self.uiQueue.count == 0) {
|
||
return;
|
||
}
|
||
|
||
GiftReceiveInfoModel *receiveInfo = [self.uiQueue firstObject];
|
||
[self.uiQueue xpSafeRemoveObjectAtIndex:0];
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
[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.25s 一次的频率发送消息
|
||
#pragma mark - 管理队列
|
||
|
||
// 开始处理队列
|
||
- (void)startProcessingQueue {
|
||
if (self.timer) {
|
||
return; // 如果定时器已经在运行,直接返回
|
||
}
|
||
|
||
// 创建 GCD 定时器 - 使用后台队列避免主线程阻塞
|
||
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.backgroundQueue);
|
||
|
||
//#if DEBUG
|
||
// dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC, 0.01 * NSEC_PER_SEC);
|
||
//#else
|
||
// 优化:减少间隔提高响应速度,从0.25秒改为0.1秒
|
||
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC, 0.01 * NSEC_PER_SEC);
|
||
//#endif
|
||
|
||
|
||
// 定时器触发的事件处理 - 在后台队列执行
|
||
@kWeakify(self);
|
||
dispatch_source_set_event_handler(self.timer, ^{
|
||
@kStrongify(self);
|
||
if (!self) {
|
||
return;
|
||
}
|
||
[self processRequestQueue];
|
||
[self processGiftComboQueue];
|
||
});
|
||
|
||
// **立即执行一次处理方法**
|
||
[self processRequestQueue];
|
||
[self processGiftComboQueue];
|
||
|
||
// 启动定时器
|
||
dispatch_resume(self.timer);
|
||
}
|
||
|
||
// 停止处理队列
|
||
- (void)stopProcessingQueue {
|
||
if (self.timer) {
|
||
// 🔥 修复:无条件停止timer,不依赖队列状态
|
||
dispatch_source_cancel(self.timer);
|
||
|
||
// 设置取消回调,在资源完全释放后将 timer 置为 nil
|
||
@kWeakify(self);
|
||
dispatch_source_set_cancel_handler(self.timer, ^{
|
||
@kStrongify(self);
|
||
if (self) {
|
||
self.timer = nil;
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// 处理队列中的第一个请求
|
||
- (void)processGiftComboQueue {
|
||
@synchronized (self) {
|
||
if (self.networkQueue.count > 0) {
|
||
// 获取并移除队列中的第一个网络数据
|
||
id networkData = [self.networkQueue firstObject];
|
||
[self.networkQueue xpSafeRemoveObjectAtIndex:0];
|
||
|
||
// 处理逻辑
|
||
if ([networkData isKindOfClass:[AttachmentModel class]]) {
|
||
dispatch_async(self.networkProcessingQueue, ^{
|
||
[self processGiftComboWith:(AttachmentModel *)networkData];
|
||
});
|
||
} else if ([networkData isKindOfClass:[NSDictionary class]]) {
|
||
// 处理包含info和metadata的字典
|
||
// NSDictionary *comboData = (NSDictionary *)networkData;
|
||
// GiftReceiveInfoModel *info = comboData[@"info"];
|
||
// NSDictionary *metadata = comboData[@"metadata"];
|
||
// 这里可以添加相应的处理逻辑
|
||
}
|
||
} 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 xpSafeObjectAtIndex:0];
|
||
if (dic) {
|
||
// 优化:在后台队列处理API请求,避免阻塞主线程
|
||
dispatch_async(self.networkProcessingQueue, ^{
|
||
[self handleSendGift:dic];
|
||
});
|
||
[self.requestQueue removeObject:dic];
|
||
}
|
||
} else {
|
||
[self stopProcessingQueue];
|
||
}
|
||
}
|
||
}
|
||
|
||
#pragma mark - Gift meta data
|
||
|
||
// 统一配置方法,替代多个save方法
|
||
- (void)configureWithGiftInfo:(GiftInfoModel *)giftInfo
|
||
targetUIDs:(NSArray *)UIDs
|
||
roomUID:(NSString *)roomUID
|
||
sessionID:(NSString *)sessionID
|
||
userInfo:(UserInfoModel *)userInfo
|
||
countModel:(XPGiftCountModel *)countModel
|
||
sourceType:(GiftSourceType)sourceType
|
||
sendType:(RoomSendGiftType)sendType
|
||
giftNum:(NSString *)giftNum {
|
||
|
||
|
||
self.giftInfo = giftInfo;
|
||
self.sendGiftToUIDs = UIDs;
|
||
self.roomUID = roomUID;
|
||
self.sessionID = sessionID;
|
||
self.sendGiftUserInfo = userInfo;
|
||
self.countModel = countModel;
|
||
self.giftSourceType = sourceType;
|
||
self.roomSendGiftType = sendType;
|
||
self.giftNumPerTimes = giftNum;
|
||
}
|
||
|
||
- (BOOL)loadEnable {
|
||
return self.enableCombo;
|
||
}
|
||
|
||
#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"];
|
||
// NSString *giftId = [dic objectForKey:@"giftId"];
|
||
|
||
@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 {
|
||
// 区分错误类型,优化恢复策略
|
||
if (code > 500 && code < 600) {
|
||
// 服务器错误,可能是临时问题,保持连击状态
|
||
#if DEBUG
|
||
self.errorMessage = [NSString stringWithFormat:@"服务器繁忙,请稍后重试 - %@", msg];
|
||
#else
|
||
self.errorMessage = @"Over Heat & try later";
|
||
// 使用 BuglyManager 统一上报礼物发送错误
|
||
NSString *uid = [AccountInfoStorage instance].getUid ?: @"未知用户";
|
||
NSMutableDictionary *userInfo = [@{@"targetUids": allUIDs,
|
||
@"giftNum": self.giftNumPerTimes} mutableCopy];
|
||
[userInfo addEntriesFromDictionary:dic];
|
||
[userInfo setObject:msg forKey:@"error message"];
|
||
|
||
[[BuglyManager sharedManager] reportNetworkError:uid
|
||
api:@"gift/sendV5"
|
||
code:code
|
||
userInfo:userInfo];
|
||
#endif
|
||
// 临时错误,不重置连击状态,允许用户重试
|
||
} else if (code == 31005) {
|
||
// 余额不足,需要重置连击状态
|
||
self.errorMessage = YMLocalizedString(@"XPCandyTreeInsufficientBalanceView1");
|
||
[self clear];
|
||
} else if (code == 8535) {
|
||
// VIP等级不足,需要重置连击状态, 但不可能出现
|
||
self.errorMessage = @"";
|
||
[self clear];
|
||
} else {
|
||
// 其他错误,根据错误类型决定是否重置连击状态
|
||
self.errorMessage = msg;
|
||
// 对于网络错误等临时问题,保持连击状态
|
||
if (code >= 1000 && code < 2000) {
|
||
// 网络相关错误,保持连击状态
|
||
} else {
|
||
// 其他错误,重置连击状态
|
||
[self clear];
|
||
}
|
||
}
|
||
|
||
// 确保在主线程执行UI回调
|
||
if (self.actionCallback) {
|
||
@kWeakify(self);
|
||
[self safeExecuteUIBlock:^{
|
||
@kStrongify(self);
|
||
self.actionCallback(ComboAction_Error);
|
||
}];
|
||
}
|
||
}
|
||
}
|
||
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 {
|
||
|
||
// 验证连击计数有效性
|
||
[self validateAndFixComboCount];
|
||
|
||
// 在API成功时递增combo计数
|
||
if (self.isCombing) {
|
||
self.combo += 1;
|
||
|
||
// 更新UI显示 - 确保在主线程执行UI回调
|
||
if (self.actionCallback) {
|
||
@kWeakify(self);
|
||
[self safeExecuteUIBlock:^{
|
||
@kStrongify(self);
|
||
self.actionCallback(ComboAction_Combo_Count_Update);
|
||
}];
|
||
}
|
||
}
|
||
|
||
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithDictionary:response.data];
|
||
|
||
// 确保连击计数最少为 1
|
||
NSInteger comboToSet = self.combo;
|
||
if (comboToSet < 1) {
|
||
comboToSet = 1;
|
||
}
|
||
|
||
[dic setObject:@(comboToSet) forKey:@"comboCount"];
|
||
|
||
self.sendGiftReceiveInfo = receive;
|
||
|
||
if (self.handleComboSuccess) {
|
||
self.handleComboSuccess(receive, dic);
|
||
}
|
||
|
||
// 确保在主线程执行UI回调
|
||
if (self.actionCallback) {
|
||
@kWeakify(self);
|
||
[self safeExecuteUIBlock:^{
|
||
@kStrongify(self);
|
||
self.actionCallback(ComboAction_Update_After_Send_Success);
|
||
}];
|
||
}
|
||
|
||
}
|
||
|
||
- (void)sendCustomMessage:(AttachmentModel *)attachment {
|
||
|
||
NIMMessage *message = [[NIMMessage alloc]init];
|
||
message.setting.quickDeliveryEnabled = YES; // 开启快速投递
|
||
|
||
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;
|
||
extModel.platformRole = userInfo.platformRole;
|
||
NSMutableDictionary *remoteExt = [NSMutableDictionary dictionaryWithObject:extModel.model2dictionary forKey:[NSString stringWithFormat:@"%ld", userInfo.uid]];
|
||
message.remoteExt = remoteExt;
|
||
|
||
//构造会话
|
||
NIMSession *session = [NIMSession session:self.sessionID type:NIMSessionTypeChatroom];
|
||
|
||
// 优化:确保在主线程发送云信消息,因为云信SDK可能期望在主线程调用
|
||
[self safeExecuteUIBlock:^{
|
||
NSError *error = nil;
|
||
[[NIMSDK sharedSDK].chatManager sendMessage:message toSession:session error:&error];
|
||
if (error) {
|
||
} else {
|
||
}
|
||
}];
|
||
}
|
||
|
||
// 新增:辅助方法,统一处理UI回调的线程安全
|
||
- (void)safeExecuteUIBlock:(void (^)(void))uiBlock {
|
||
if (!uiBlock) return;
|
||
|
||
if ([NSThread isMainThread]) {
|
||
uiBlock();
|
||
} else {
|
||
dispatch_async(dispatch_get_main_queue(), uiBlock);
|
||
}
|
||
}
|
||
// 🔧 新增:状态通知方法实现
|
||
- (void)setUserComboState:(BOOL)isCombo forUser:(NSString *)uid {
|
||
if (!uid || uid.length == 0) {
|
||
return;
|
||
}
|
||
|
||
|
||
// 通过通知中心通知动画管理器
|
||
[[NSNotificationCenter defaultCenter] postNotificationName:@"GiftComboStateChanged"
|
||
object:nil
|
||
userInfo:@{
|
||
@"uid": uid,
|
||
@"isCombo": @(isCombo)
|
||
}];
|
||
}
|
||
|
||
- (void)clearUserComboState:(NSString *)uid {
|
||
[self setUserComboState:NO forUser:uid];
|
||
}
|
||
|
||
@end
|