Commit ba85dfc7 by suolong

限时表演观众获取票api

parent 2ec7e932
Showing with 753 additions and 109 deletions
......@@ -86,8 +86,11 @@
@end
@interface FUSRoomTicketShowConfig : FUSBaseModel
/// 开启集票时的最小目标票数(字符串形式,服务端下发)
@property (nonatomic, copy) NSString *collectTicketMin;
/// 开启集票时的最大目标票数(字符串形式,服务端下发)
@property (nonatomic, copy) NSString *collectTicketMax;
/// 集票配置提示文案(用于客户端弹窗/入口展示)
@property (nonatomic, copy) NSString *collectTicketTips;
@end
......@@ -151,6 +154,9 @@
@property (nonatomic, copy) NSString *publishUrl; // 拉流地址
/// 是否处于集票中(0:否、1:是),进房后用于决定是否展示“限时表演”相关入口
@property (nonatomic, assign) NSInteger collectTicket;
/// 人气值对象)(score:人气值、lev:人气值等级、change:变化数量)
@property (nonatomic, copy) NSDictionary *popular;
......@@ -177,8 +183,10 @@
@property (nonatomic, copy) NSArray<NSString *> *reminderKeys;
/// 从用户资料中同步并补齐“我的房间”基础字段(用于创建/进入自己的房间等场景)
- (void)fus_setModelWithMyRoomInfos;
/// 使用另一个房间模型更新当前实例(用于重连/切房等场景的数据同步)
-(void)fus_setupWithModel:(FUSRoomInfoModel *)model;
@end
......@@ -22,6 +22,7 @@
@property (nonatomic, copy) NSString *guard; //贵人
@property (nonatomic, copy) NSString *isnew; //是否是新人
@property (nonatomic, copy) NSDictionary *privilege; // VIP 身份数据包含:richPower:是否有富豪权益
@property (nonatomic, assign) NSInteger inVip; // 是否VIP/是否SVIP(服务端字段,0否,1VIP,2SVIP等)
@property (nonatomic, copy) NSString *isdisplay; // 是否显示(是否机器人)(0:否、1:是)
@property (nonatomic, copy) NSString *isHide; //是否是神秘人
......
......@@ -48,6 +48,27 @@ NS_ASSUME_NONNULL_BEGIN
/// 响应描述
@property (nonatomic, copy) NSString *msg;
/// socket:数据类型(观众端=1,主播端=4)
@property (nonatomic, assign) NSInteger dataType;
/// socket:房间ID(部分推送平铺字段)
@property (nonatomic, copy) NSString *roomId;
/// socket:频道ID(部分推送平铺字段)
@property (nonatomic, copy) NSString *channelId;
/// socket:回合ID(部分推送平铺字段)
@property (nonatomic, copy) NSString *roundId;
/// socket:当前阶段状态(0:集票中 1:待表演 2:表演中 9999:已结束)
@property (nonatomic, assign) NSInteger showStatus;
/// socket:阶段时长(毫秒)
@property (nonatomic, assign) NSInteger showStatusTime;
/// socket:剩余时间(毫秒)
@property (nonatomic, assign) NSInteger remaintime;
/// 集票目标总数
@property (nonatomic, assign) NSInteger targetTicketNum;
......@@ -57,6 +78,12 @@ NS_ASSUME_NONNULL_BEGIN
/// 集票表演主题标题
@property (nonatomic, copy) NSString *showTheme;
/// socket:购买用户信息(赠票/购票的用户)
@property (nonatomic, strong, nullable) FUSOnlineUserModel *buyUser;
/// socket:buyUser 的购买数量
@property (nonatomic, assign) NSInteger buyNum;
/// 阶段信息
@property (nonatomic, strong, nullable) FUSTicketShowCollectTicketStageDataModel *stageData;
......
......@@ -16,7 +16,8 @@
+ (NSDictionary<NSString *,id> *)modelContainerPropertyGenericClass {
return @{@"stageData": [FUSTicketShowCollectTicketStageDataModel class],
@"mvpInfo": [FUSTicketShowCollectTicketMVPInfoModel class]};
@"mvpInfo": [FUSTicketShowCollectTicketMVPInfoModel class],
@"buyUser": [FUSOnlineUserModel class]};
}
@end
......@@ -946,6 +946,16 @@ NS_ASSUME_NONNULL_BEGIN
succeed:(void (^)(FUSTicketShowCollectTicketToggleResultModel *model))succeed
failure:(void (^)(NSString *msg, NSInteger code))failure;
/// Ticket Show - 视图 - 获取表演票的集票信息(观众端进房刷新)
/// @param roomId 房间ID
/// @param channelId 频道ID
/// @param roundId 回合ID
+ (void)fus_ticketShowProgressGetInfoWithRoomId:(NSString *)roomId
channelId:(NSString *)channelId
roundId:(NSString *)roundId
succeed:(void (^)(FUSTicketShowCollectTicketToggleResultModel *model))succeed
failure:(void (^)(NSString *msg, NSInteger code))failure;
/// 开始直播前准备
/// @param succeed 成功回调
......
......@@ -3090,6 +3090,35 @@
if (failure) failure(dataDict[@"msg"], code);
}];
}
+ (void)fus_ticketShowProgressGetInfoWithRoomId:(NSString *)roomId
channelId:(NSString *)channelId
roundId:(NSString *)roundId
succeed:(void (^)(FUSTicketShowCollectTicketToggleResultModel * _Nonnull))succeed
failure:(void (^)(NSString * _Nonnull, NSInteger))failure {
NSString *uid = FUSCacheDataShare.shareStore.userDetailInfo.uid;
if ([NSString isNull:uid]
|| [NSString isNull:roomId]
|| [NSString isNull:channelId]
|| [NSString isNull:roundId]) {
if (failure) failure([NSString fus_localString:@"参数错误"], -3);
return;
}
NSDictionary *params = @{
@"uid": uid,
@"roomId": roomId,
@"channelId": channelId,
@"roundId": roundId
};
[FUSHttpHelper postRequestBinaryWithUrl:FUSShowRoomURLs.fus_URL_TicketShow_Progress_GetInfo params:params success:^(NSDictionary * _Nullable dataDict, int code) {
FUSTicketShowCollectTicketToggleResultModel *model = [FUSTicketShowCollectTicketToggleResultModel fus_modelWithDict:dataDict];
if (succeed) succeed(model);
} failure:^(NSDictionary * _Nullable dataDict, int code) {
if (failure) failure(dataDict[@"msg"], code);
}];
}
/// 获取用户直播评分历史列表
+ (void)fus_requestUserliveAssessHistoryGetListWithDateStr:(NSString *)dateStr succeed:(void (^)(FUSUserLiveAssessHistoryModel * _Nonnull))succeed failure:(void (^)(NSString * _Nonnull, NSInteger))failure{
NSDictionary *parm = @{@"dateStr": dateStr};
......
......@@ -11,6 +11,8 @@
#import "FUSLiveGiftView.h"
#import "FUSLiveChatTableView.h"
@class FUSRoomInfoModel;
@interface FUSLiveChatInputHelper : NSObject <FUSLiveChatInputViewDelegate, FUSLiveBottomToolViewDelegate>
/**
......@@ -57,4 +59,8 @@
/// 销毁限时表演集票磨砂条(确认结束直播后调用)
- (void)fus_destroyShowTimeFrostedIfNeeded;
/// 进房/重连后根据房间状态同步“限时表演”集票磨砂条的展示与隐藏
/// @param roomInfoModel 进房接口返回的房间模型(collectTicket=1 表示当前处于集票中)
- (void)fus_syncShowTimeFrostedWithRoomInfoModel:(FUSRoomInfoModel *)roomInfoModel;
@end
......@@ -21,6 +21,7 @@
#import "FUSLiveHelper.h"
#import "FUSLiveHttpHelper.h"
#import "FUSTicketShowCollectTicketToggleResultModel.h"
#import "FUSOnlineUserModel.h"
#import <FirebaseAnalytics/FirebaseAnalytics.h>
......@@ -52,6 +53,8 @@
@property (nonatomic, copy) NSString *showTimeRoundId;
@property (nonatomic, assign) NSInteger showTimeLastFinalTicketNum;
@end
@implementation FUSLiveChatInputHelper
......@@ -65,6 +68,7 @@
_chatInputView = chatInputView;
_chatInputView.delegate = self;
_isBeautyFace = YES;
_showTimeLastFinalTicketNum = -1;
[self registerReceiptNotification];
......@@ -136,23 +140,15 @@
return;
}
// 兼容两种结构:
// 1) payload 直接平铺在 jsonDict
// 2) payload 放在 jsonDict.data 内
// 11038:payload 直接平铺在 jsonDict(无 data 包裹)
NSDictionary *payload = jsonDict;
NSDictionary *dataDict = payload[@"data"];
if ([dataDict isKindOfClass:NSDictionary.class] && dataDict.count > 0) {
payload = dataDict;
}
// 目前先只实现 dataType=1(主播开启集票/推送当前集票阶段信息)
NSInteger dataType = [payload[@"dataType"] integerValue];
if (dataType <= 0) {
dataType = [jsonDict[@"dataType"] integerValue];
}
if (dataType != 1) {
if (FUSLiveHelper.shareInstance.liveType == FUSLiveTypeAnchor) {
if (dataType != 4) {
return;
}
}
// 复用 toggle 接口返回模型:字段与 socket 推送结构保持一致(targetTicketNum/finalTicketNum/stageData/mvpInfo 等)
FUSTicketShowCollectTicketToggleResultModel *model = [FUSTicketShowCollectTicketToggleResultModel fus_modelWithDict:payload];
......@@ -177,9 +173,17 @@
self.showTimeFrostedView = bar;
// roundId 用于主播端取消集票
self.showTimeRoundId = (model.stageData.roundId.length > 0 ? model.stageData.roundId : @"");
NSString *previousRoundId = (self.showTimeRoundId.length > 0 ? self.showTimeRoundId : @"");
NSInteger previousFinalTicketNum = self.showTimeLastFinalTicketNum;
NSString *incomingRoundId = (model.stageData.roundId.length > 0 ? model.stageData.roundId : model.roundId);
self.showTimeRoundId = (incomingRoundId.length > 0 ? incomingRoundId : @"");
NSInteger stageStatus = (model.stageData ? model.stageData.showStatus : model.showStatus);
if (FUSLiveHelper.shareInstance.liveType == FUSLiveTypeAnchor) {
bar.displayMode = FUSLiveShowTimeCollectFrostedDisplayModeAnchor;
bar.actionHandler = nil;
bar.actionTitle = nil;
bar.stageStatus = stageStatus;
__weak typeof(self) weakSelf = self;
bar.cancelHandler = ^{
__strong typeof(weakSelf) cancelSelf = weakSelf;
......@@ -194,8 +198,8 @@
}
FUSRoomInfoModel *roomInfoModel = FUSLiveHelper.shareInstance.roomInfoModel;
NSString *cancelRoomId = (model.stageData.roomId.length > 0 ? model.stageData.roomId : roomInfoModel.roomId);
NSString *cancelChannelId = (model.stageData.channelId.length > 0 ? model.stageData.channelId : roomInfoModel.channelId);
NSString *cancelRoomId = (model.stageData.roomId.length > 0 ? model.stageData.roomId : (model.roomId.length > 0 ? model.roomId : roomInfoModel.roomId));
NSString *cancelChannelId = (model.stageData.channelId.length > 0 ? model.stageData.channelId : (model.channelId.length > 0 ? model.channelId : roomInfoModel.channelId));
if ([NSString isNull:cancelRoomId] || [NSString isNull:cancelChannelId]) {
[FUSDialogView fus_showDialog:[NSString fus_localString:@"当前房间信息异常"]];
return;
......@@ -207,6 +211,7 @@
[FUSLoadingView fus_dismissProgressView];
[cancelSelf fus_destroyShowTimeFrostedIfNeeded];
cancelSelf.showTimeRoundId = @"";
cancelSelf.showTimeLastFinalTicketNum = -1;
});
} failure:^(NSString * _Nonnull msg, NSInteger code) {
dispatch_async(dispatch_get_main_queue(), ^{
......@@ -216,7 +221,21 @@
}];
};
} else {
bar.displayMode = FUSLiveShowTimeCollectFrostedDisplayModeAudience;
bar.cancelHandler = nil;
bar.stageStatus = stageStatus;
NSString *actionTitle = ([NSString stringWithObject:model.mvpInfo.mvpUserId].length > 0 ? [NSString fus_localString:@"抢当MVP"] : [NSString fus_localString:@"购票支持"]);
bar.actionTitle = actionTitle;
__weak typeof(self) weakSelf = self;
bar.actionHandler = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) {
return;
}
[FUSDialogView fus_showDialog:[NSString fus_localString:@"购票接口未接入"]];
};
}
NSInteger target = MAX(0, model.targetTicketNum);
......@@ -226,26 +245,60 @@
NSString *remainingText = [NSString stringWithFormat:[NSString fus_localString:@"还差 %zd 张!"], (NSInteger)remaining];
NSString *themeText = (model.showTheme.length > 0 ? model.showTheme : [NSString fus_localString:@"限时表演"]);
// 后端给的是“阶段总时长 showStatusTime”和“剩余时间 remaintime”(毫秒)
// 这里换算成开始时间戳(秒),用于磨砂条内部倒计时展示
NSTimeInterval nowSec = [[NSDate date] timeIntervalSince1970];
NSTimeInterval startTimestamp = nowSec;
NSInteger durationMs = model.stageData.showStatusTime;
NSInteger remainMs = model.stageData.remaintime;
if (durationMs > 0 && remainMs >= 0 && remainMs <= durationMs) {
NSTimeInterval elapsedSec = ((NSTimeInterval)(durationMs - remainMs)) / 1000.0;
startTimestamp = nowSec - MAX(0, elapsedSec);
NSInteger newTicketCount = MAX(0, model.buyNum);
if (newTicketCount <= 0 && previousFinalTicketNum >= 0 && previousRoundId.length > 0 && [previousRoundId isEqualToString:self.showTimeRoundId]) {
newTicketCount = MAX(0, finalNum - previousFinalTicketNum);
}
self.showTimeLastFinalTicketNum = finalNum;
FUSOnlineUserModel *giftUser = model.buyUser;
if (!giftUser) {
NSDictionary *giftUserDict = nil;
NSArray<NSString *> *candidateUserKeys = @[@"buyUser", @"giftUserInfo", @"ticketUserInfo", @"userInfo", @"buyUserInfo", @"contributeUserInfo", @"sendUserInfo", @"user"];
for (NSString *key in candidateUserKeys) {
id value = payload[key];
if ([value isKindOfClass:NSDictionary.class] && ((NSDictionary *)value).count > 0) {
giftUserDict = (NSDictionary *)value;
break;
}
}
if (!giftUserDict) {
id ticketUser = payload[@"ticketUser"];
if ([ticketUser isKindOfClass:NSDictionary.class] && ((NSDictionary *)ticketUser).count > 0) {
giftUserDict = (NSDictionary *)ticketUser;
}
}
giftUser = (giftUserDict ? [FUSOnlineUserModel fus_modelWithDict:giftUserDict] : nil);
}
// MVP 用户信息用于顶部展示(无则不展示)
FUSLiveShowTimeCollectFrostedState showState = (newTicketCount > 0 ? FUSLiveShowTimeCollectFrostedStateNewTicket : FUSLiveShowTimeCollectFrostedStateStarted);
// MVP 用户信息用于右侧头像展示(无则不展示)
FUSOnlineUserModel *mvpUser = model.mvpInfo.mvpUserInfo;
[bar fus_updateWithState:FUSLiveShowTimeCollectFrostedStateStarted
if (bar.displayMode == FUSLiveShowTimeCollectFrostedDisplayModeAudience) {
NSInteger durationMs = (model.stageData ? model.stageData.showStatusTime : model.showStatusTime);
NSInteger remainMs = (model.stageData ? model.stageData.remaintime : model.remaintime);
NSInteger remainSeconds = -1;
if (durationMs > 0 && remainMs >= 0 && remainMs <= durationMs) {
remainSeconds = (NSInteger)llround(((NSTimeInterval)remainMs) / 1000.0);
}
[bar fus_updateAudienceWithState:showState
themeText:themeText
progress:progress
remainingText:remainingText
startTimestamp:startTimestamp
newTicketCount:0
userModel:mvpUser];
countdownRemainingSeconds:remainSeconds
newTicketCount:newTicketCount
giftUserModel:giftUser
mvpUserModel:mvpUser];
} else {
[bar fus_updateWithState:showState
themeText:themeText
progress:progress
remainingText:remainingText
newTicketCount:newTicketCount
giftUserModel:giftUser
mvpUserModel:mvpUser];
}
});
}
......@@ -592,7 +645,8 @@
UIView *popContainer = (containerView ?: [UIViewController fus_topViewController].view);
__weak typeof(self) weakSelf = self;
FUSRoomTicketShowConfig *ticketShowConfig = [FUSLiveHelper shareInstance].liveBeforeReadyInfo.ticketShowConfig;
FUSLiveShowTimePopView *popView = [FUSLiveShowTimePopView fus_showOnView:popContainer ticketShowConfig:ticketShowConfig];
NSString *defaultThemeText = [FUSLiveHelper shareInstance].roomInfoModel.introduce;
FUSLiveShowTimePopView *popView = [FUSLiveShowTimePopView fus_showOnView:popContainer ticketShowConfig:ticketShowConfig themeText:defaultThemeText];
popView.startCollectHandler = ^(NSString * _Nonnull themeText, NSInteger ticketCount) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) {
......@@ -681,20 +735,11 @@
NSInteger remaining = MAX(0, target - finalNum);
CGFloat progress = (target > 0 ? ((CGFloat)finalNum / (CGFloat)target) : 0);
NSString *remainingText = [NSString stringWithFormat:[NSString fus_localString:@"还差 %zd 张!"], (NSInteger)remaining];
NSTimeInterval nowSec = [[NSDate date] timeIntervalSince1970];
NSTimeInterval startTimestamp = nowSec;
NSInteger durationMs = model.stageData.showStatusTime;
NSInteger remainMs = model.stageData.remaintime;
if (durationMs > 0 && remainMs >= 0 && remainMs <= durationMs) {
NSTimeInterval elapsedSec = ((NSTimeInterval)(durationMs - remainMs)) / 1000.0;
startTimestamp = nowSec - MAX(0, elapsedSec);
}
[bar fus_updateWithState:FUSLiveShowTimeCollectFrostedStateStarted
themeText:(model.showTheme.length > 0 ? model.showTheme : finalTheme)
progress:progress
remainingText:remainingText
startTimestamp:startTimestamp
newTicketCount:0
userModel:nil];
});
......@@ -999,6 +1044,7 @@
}
#pragma mark - 限时表演磨砂条
/// 主动销毁“限时表演”集票磨砂条(用于退出直播/切房等需要强制清理 UI 的场景)
- (void)fus_destroyShowTimeFrostedIfNeeded {
if (self.showTimeFrostedView) {
[self.showTimeFrostedView fus_dismiss];
......@@ -1006,4 +1052,140 @@
}
}
/// 进房/重连后,用房间的 collectTicket 状态决定是否需要展示“限时表演”集票入口(观众端)
- (void)fus_syncShowTimeFrostedWithRoomInfoModel:(FUSRoomInfoModel *)roomInfoModel {
if (!roomInfoModel) {
[self fus_destroyShowTimeFrostedIfNeeded];
return;
}
if (FUSLiveHelper.shareInstance.liveType != FUSLiveTypeAudience) {
[self fus_destroyShowTimeFrostedIfNeeded];
return;
}
if (roomInfoModel.collectTicket != 1) {
[self fus_destroyShowTimeFrostedIfNeeded];
return;
}
UIView *onView = self.showTimeFrostedView.superview;
if (!onView) {
onView = [[FUSLiveHelper shareInstance].currentFunctionView fus_viewWithLayer:FUSLiveFunctionLayerFunctionButtons];
}
if (!onView) {
onView = [FUSLiveHelper shareInstance].currentLiveVCView;
}
if (!onView) {
onView = [UIViewController fus_topViewController].view;
}
if (!onView) {
return;
}
FUSLiveShowTimeCollectFrostedView *bar = [FUSLiveShowTimeCollectFrostedView fus_showOnView:onView];
self.showTimeFrostedView = bar;
bar.displayMode = FUSLiveShowTimeCollectFrostedDisplayModeAudience;
bar.cancelHandler = nil;
bar.stageStatus = 0;
bar.actionTitle = [NSString fus_localString:@"购票支持"];
__weak typeof(self) weakSelf = self;
bar.actionHandler = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) {
return;
}
[FUSDialogView fus_showDialog:[NSString fus_localString:@"购票接口未接入"]];
};
NSString *themeText = (![NSString isNull:roomInfoModel.introduce] ? roomInfoModel.introduce : [NSString fus_localString:@"限时表演"]);
[bar fus_updateWithState:FUSLiveShowTimeCollectFrostedStateStarted
themeText:themeText
progress:0
remainingText:[NSString fus_localString:@"集票进行中"]
newTicketCount:0
userModel:nil];
NSString *roomId = roomInfoModel.roomId;
NSString *channelId = roomInfoModel.channelId;
NSString *roundId = roomInfoModel.roundId;
if ([NSString isNull:roomId] || [NSString isNull:channelId] || [NSString isNull:roundId]) {
return;
}
[FUSLiveHttpHelper fus_ticketShowProgressGetInfoWithRoomId:roomId channelId:channelId roundId:roundId succeed:^(FUSTicketShowCollectTicketToggleResultModel * _Nonnull model) {
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) {
return;
}
if (FUSLiveHelper.shareInstance.liveType != FUSLiveTypeAudience) {
return;
}
NSString *currentRoomId = FUSLiveHelper.shareInstance.roomInfoModel.roomId;
if (![NSString isNull:currentRoomId] && ![currentRoomId isEqualToString:roomId]) {
return;
}
FUSLiveShowTimeCollectFrostedView *currentBar = strongSelf.showTimeFrostedView;
if (!currentBar || currentBar.displayMode != FUSLiveShowTimeCollectFrostedDisplayModeAudience) {
return;
}
NSInteger stageStatus = (model.stageData ? model.stageData.showStatus : model.showStatus);
if (stageStatus == 9999) {
[strongSelf fus_destroyShowTimeFrostedIfNeeded];
return;
}
NSString *incomingRoundId = (model.stageData.roundId.length > 0 ? model.stageData.roundId : model.roundId);
strongSelf.showTimeRoundId = (incomingRoundId.length > 0 ? incomingRoundId : @"");
NSInteger target = MAX(0, model.targetTicketNum);
NSInteger finalNum = MAX(0, model.finalTicketNum);
strongSelf.showTimeLastFinalTicketNum = finalNum;
if (target <= 0) {
return;
}
NSInteger remaining = MAX(0, target - finalNum);
CGFloat progress = MIN(1.0, MAX(0.0, (CGFloat)finalNum / (CGFloat)target));
NSString *remainingText = [NSString stringWithFormat:[NSString fus_localString:@"还差 %zd 张!"], (NSInteger)remaining];
NSString *finalThemeText = nil;
if (model.showTheme.length > 0) {
finalThemeText = model.showTheme;
} else if (![NSString isNull:roomInfoModel.introduce]) {
finalThemeText = roomInfoModel.introduce;
} else {
finalThemeText = [NSString fus_localString:@"限时表演"];
}
currentBar.stageStatus = stageStatus;
currentBar.actionTitle = ([NSString stringWithObject:model.mvpInfo.mvpUserId].length > 0 ? [NSString fus_localString:@"抢当MVP"] : [NSString fus_localString:@"购票支持"]);
NSInteger durationMs = (model.stageData ? model.stageData.showStatusTime : model.showStatusTime);
NSInteger remainMs = (model.stageData ? model.stageData.remaintime : model.remaintime);
NSInteger remainSeconds = -1;
if (durationMs > 0 && remainMs >= 0 && remainMs <= durationMs) {
remainSeconds = (NSInteger)llround(((NSTimeInterval)remainMs) / 1000.0);
}
FUSLiveShowTimeCollectFrostedState showState = (finalNum >= target ? FUSLiveShowTimeCollectFrostedStateCompleted : FUSLiveShowTimeCollectFrostedStateStarted);
[currentBar fus_updateAudienceWithState:showState
themeText:finalThemeText
progress:progress
remainingText:remainingText
countdownRemainingSeconds:remainSeconds
newTicketCount:0
giftUserModel:model.buyUser
mvpUserModel:model.mvpInfo.mvpUserInfo];
});
} failure:^(NSString * _Nonnull msg, NSInteger code) {
}];
}
@end
......@@ -14,6 +14,14 @@ typedef NS_ENUM(NSInteger, FUSLiveShowTimeCollectFrostedState) {
FUSLiveShowTimeCollectFrostedStateCompleted,
};
/// 磨砂条展示模式
typedef NS_ENUM(NSInteger, FUSLiveShowTimeCollectFrostedDisplayMode) {
/// 主播端:展示取消入口/新票提示
FUSLiveShowTimeCollectFrostedDisplayModeAnchor = 0,
/// 用户端:展示购票入口/抢当 MVP
FUSLiveShowTimeCollectFrostedDisplayModeAudience,
};
/// 限时表演集票磨砂条:展示集票进度/倒计时/取消入口
@interface FUSLiveShowTimeCollectFrostedView : UIView
......@@ -26,20 +34,52 @@ typedef NS_ENUM(NSInteger, FUSLiveShowTimeCollectFrostedState) {
/// @param themeText 表演主题
/// @param progress 0~1 进度
/// @param remainingText 剩余票提示,如“还差 49 张!”
/// @param startTimestamp 集票开始时间戳(秒)
/// @param newTicketCount 新票数(state=NewTicket 时使用)
/// @param userModel 新票贡献用户信息(nil 时不展示标题栏用户信息
/// @param userModel 新票贡献用户信息(兼容旧接口;推荐使用带 giftUserModel/mvpUserModel 的新方法
- (void)fus_updateWithState:(FUSLiveShowTimeCollectFrostedState)state
themeText:(NSString *)themeText
progress:(CGFloat)progress
remainingText:(NSString *)remainingText
startTimestamp:(NSTimeInterval)startTimestamp
newTicketCount:(NSInteger)newTicketCount
userModel:(FUSOnlineUserModel * _Nullable)userModel;
/// 更新展示内容(推荐:分别传入赠票用户与 MVP 用户)
/// @param giftUserModel 赠票用户信息(主播端顶部左侧展示)
/// @param mvpUserModel MVP 用户信息(右侧 MVP 头像展示,nil 表示不展示)
- (void)fus_updateWithState:(FUSLiveShowTimeCollectFrostedState)state
themeText:(NSString *)themeText
progress:(CGFloat)progress
remainingText:(NSString *)remainingText
newTicketCount:(NSInteger)newTicketCount
giftUserModel:(FUSOnlineUserModel * _Nullable)giftUserModel
mvpUserModel:(FUSOnlineUserModel * _Nullable)mvpUserModel;
/// 用户端更新展示内容(用于用户中途进房时同步倒计时)
/// @param countdownRemainingSeconds 距离结束剩余秒数;<0 表示未知,内部按 10 分钟从当前开始计时
- (void)fus_updateAudienceWithState:(FUSLiveShowTimeCollectFrostedState)state
themeText:(NSString *)themeText
progress:(CGFloat)progress
remainingText:(NSString *)remainingText
countdownRemainingSeconds:(NSInteger)countdownRemainingSeconds
newTicketCount:(NSInteger)newTicketCount
giftUserModel:(FUSOnlineUserModel * _Nullable)giftUserModel
mvpUserModel:(FUSOnlineUserModel * _Nullable)mvpUserModel;
/// 取消按钮点击回调(预留服务端入口)
@property (nonatomic, copy, nullable) void (^cancelHandler)(void);
/// 展示模式(默认主播端)
@property (nonatomic, assign) FUSLiveShowTimeCollectFrostedDisplayMode displayMode;
/// 用户端按钮点击回调(购票/抢当 MVP),仅 displayMode=Audience 时使用
@property (nonatomic, copy, nullable) void (^actionHandler)(void);
/// 用户端按钮文案(如“购票支持”“抢当MVP”)
@property (nonatomic, copy, nullable) NSString *actionTitle;
/// 阶段状态(来自服务端 stageData.showStatus,用于展示“表演中”等状态标签)
@property (nonatomic, assign) NSInteger stageStatus;
/// 移除磨砂条
- (void)fus_dismiss;
......
......@@ -11,6 +11,25 @@
static const CGFloat kFUSShowTimeFrostedHeight = 120.0;
static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
@interface FUSShowTimePaddingLabel : UILabel
@property (nonatomic, assign) UIEdgeInsets textInsets;
@end
@implementation FUSShowTimePaddingLabel
- (CGSize)intrinsicContentSize {
CGSize size = [super intrinsicContentSize];
size.width += self.textInsets.left + self.textInsets.right;
size.height += self.textInsets.top + self.textInsets.bottom;
return size;
}
- (void)drawTextInRect:(CGRect)rect {
[super drawTextInRect:UIEdgeInsetsInsetRect(rect, self.textInsets)];
}
@end
@interface FUSLiveShowTimeCollectFrostedView ()
/// 磨砂容器(承载毛玻璃效果)
......@@ -31,6 +50,10 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
@property (nonatomic, strong) UIImageView *avatarView;
/// 顶部昵称
@property (nonatomic, strong) UILabel *topTitleLabel;
/// 顶部在线等级(lev)
@property (nonatomic, strong) UILabel *levLabel;
/// 顶部 VIP/SVIP 标识(由 inVip/等级决定)
@property (nonatomic, strong) UIImageView *vipIconView;
/// 新票图标
@property (nonatomic, strong) UIImageView *ticketIconView;
/// 新票数提示(+N)
......@@ -39,7 +62,9 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
/// 分割线以下内容容器(用于垂直居中进度圈)
@property (nonatomic, strong) UIView *bottomContentView;
/// 表演主题
@property (nonatomic, strong) UILabel *themeLabel;
@property (nonatomic, strong) CBAutoScrollLabel *themeLabel;
/// 阶段状态标签(如“表演中”)
@property (nonatomic, strong) UILabel *statusTagLabel;
/// 倒计时图标
@property (nonatomic, strong) UIImageView *countdownIconView;
/// 还差票数提示
......@@ -51,12 +76,18 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
/// 右侧 MVP 标识
@property (nonatomic, strong) UIImageView *mvpIconView;
/// 用户端:MVP 头像
@property (nonatomic, strong) UIImageView *mvpAvatarView;
/// 用户端:MVP 角标
@property (nonatomic, strong) UILabel *mvpTagLabel;
/// 取消集票按钮
@property (nonatomic, strong) UIButton *cancelBtn;
/// 集票开始时间戳(秒)
@property (nonatomic, assign) NSTimeInterval startTimestamp;
/// 倒计时基准对应的阶段状态(避免上票推送导致 startTimestamp 反复被覆盖)
@property (nonatomic, assign) NSInteger countdownStageStatus;
/// 倒计时刷新计时器
@property (nonatomic, strong) dispatch_source_t countdownTimer;
......@@ -87,6 +118,8 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = UIColor.clearColor;
_displayMode = FUSLiveShowTimeCollectFrostedDisplayModeAnchor;
_countdownStageStatus = -1;
UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
self.blurView = [[UIVisualEffectView alloc] initWithEffect:effect];
......@@ -106,10 +139,31 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
self.topTitleLabel.font = [UIFont fus_themeFont:12];
self.topTitleLabel.textColor = [UIColor colorWithWhite:1 alpha:0.85];
self.topTitleLabel.text = [NSString fus_localString:@"我是昵称"];
self.topTitleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[self.topTitleLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
[self.blurView.contentView addSubview:self.topTitleLabel];
FUSShowTimePaddingLabel *levLabel = [[FUSShowTimePaddingLabel alloc] init];
levLabel.textInsets = UIEdgeInsetsMake(0, 6, 0, 6);
levLabel.layer.cornerRadius = 9;
levLabel.layer.masksToBounds = YES;
levLabel.backgroundColor = [UIColor colorWithWhite:1 alpha:0.9];
self.levLabel = levLabel;
self.levLabel.font = [UIFont fus_themeFont:11];
self.levLabel.textColor = [UIColor colorWithWhite:0 alpha:0.8];
self.levLabel.textAlignment = NSTextAlignmentCenter;
self.levLabel.text = @"0";
[self.levLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.levLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.blurView.contentView addSubview:self.levLabel];
self.vipIconView = [[UIImageView alloc] init];
self.vipIconView.contentMode = UIViewContentModeScaleAspectFit;
self.vipIconView.hidden = YES;
[self.blurView.contentView addSubview:self.vipIconView];
self.ticketIconView = [[UIImageView alloc] init];
self.ticketIconView.image = [FUSShowRoomCenterBunble imageNamed:@"Live_bottom_ticket"];
self.ticketIconView.image = [FUSShowRoomCenterBunble imageNamed:@"home_list_ticket"];
self.ticketIconView.hidden = YES;
[self.blurView.contentView addSubview:self.ticketIconView];
......@@ -136,9 +190,25 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
self.bottomContentView = [[UIView alloc] init];
[self.blurView.contentView addSubview:self.bottomContentView];
self.themeLabel = [[UILabel alloc] init];
self.statusTagLabel = [[UILabel alloc] init];
self.statusTagLabel.hidden = YES;
self.statusTagLabel.text = @"";
self.statusTagLabel.textAlignment = NSTextAlignmentCenter;
self.statusTagLabel.font = [UIFont fus_themeBoldFont:10];
self.statusTagLabel.textColor = UIColor.blackColor;
self.statusTagLabel.backgroundColor = [UIColor colorWithHex:@"#52DDE2"];
self.statusTagLabel.layer.cornerRadius = 8;
self.statusTagLabel.layer.masksToBounds = YES;
[self.blurView.contentView addSubview:self.statusTagLabel];
self.themeLabel = [[CBAutoScrollLabel alloc] init];
self.themeLabel.font = [UIFont fus_themeBoldFont:16];
self.themeLabel.textColor = UIColor.whiteColor;
self.themeLabel.scrollDirection = CBAutoScrollDirectionLeft;
self.themeLabel.scrollSpeed = 30;
self.themeLabel.pauseInterval = 1.5;
self.themeLabel.labelSpacing = 20;
[self.themeLabel observeApplicationNotifications];
self.themeLabel.text = [NSString fus_localString:@"我是表演主题表演主题"];
[self.blurView.contentView addSubview:self.themeLabel];
......@@ -155,6 +225,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
self.countdownLabel = [[UILabel alloc] init];
self.countdownLabel.font = [UIFont fus_themeFont:12];
self.countdownLabel.textColor = [UIColor colorWithWhite:1 alpha:0.85];
self.countdownLabel.textAlignment = NSTextAlignmentRight;
self.countdownLabel.text = @"";
[self.blurView.contentView addSubview:self.countdownLabel];
......@@ -162,32 +233,36 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
self.mvpIconView.image = [FUSShowRoomCenterBunble imageNamed:@"Live_bottom_mvp"];
[self.blurView.contentView addSubview:self.mvpIconView];
self.mvpAvatarView = [[UIImageView alloc] init];
self.mvpAvatarView.hidden = YES;
self.mvpAvatarView.backgroundColor = [UIColor colorWithWhite:1 alpha:0.2];
self.mvpAvatarView.layer.cornerRadius = 21;
self.mvpAvatarView.layer.masksToBounds = YES;
[self.blurView.contentView addSubview:self.mvpAvatarView];
self.mvpTagLabel = [[UILabel alloc] init];
self.mvpTagLabel.hidden = YES;
self.mvpTagLabel.textAlignment = NSTextAlignmentCenter;
self.mvpTagLabel.font = [UIFont fus_themeBoldFont:9];
self.mvpTagLabel.textColor = UIColor.blackColor;
self.mvpTagLabel.backgroundColor = [UIColor colorWithHex:@"#52DDE2"];
self.mvpTagLabel.text = @"MVP";
self.mvpTagLabel.layer.cornerRadius = 7;
self.mvpTagLabel.layer.masksToBounds = YES;
[self.blurView.contentView addSubview:self.mvpTagLabel];
[self.blurView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self);
}];
[self.cancelBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.blurView.contentView).offset(10);
make.right.equalTo(self.blurView.contentView).offset(-12);
make.size.mas_equalTo(CGSizeMake(76, 26));
}];
[self.topDividerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.blurView.contentView);
make.top.equalTo(self.cancelBtn.mas_bottom).offset(8);
make.height.mas_equalTo(0.5);
}];
[self.bottomContentView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.bottom.equalTo(self.blurView.contentView);
make.top.equalTo(self.topDividerView.mas_bottom);
}];
[self.themeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.progressContainer.mas_right).offset(12);
make.top.equalTo(self.bottomContentView).offset(8);
make.height.mas_equalTo(22);
[self.statusTagLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.greaterThanOrEqualTo(self.progressContainer.mas_right).offset(12);
make.right.equalTo(self.themeLabel.mas_left).offset(-6);
make.centerY.equalTo(self.themeLabel);
make.height.mas_equalTo(16);
}];
[self.statusTagLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.statusTagLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.remainingLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.themeLabel);
......@@ -197,14 +272,10 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
[self.remainingLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.remainingLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.mvpIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.blurView.contentView).offset(-12);
make.centerY.equalTo(self.remainingLabel);
make.size.mas_equalTo(CGSizeMake(44, 20));
}];
[self.themeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.lessThanOrEqualTo(self.mvpIconView.mas_left).offset(-12);
[self.mvpTagLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(self.mvpAvatarView);
make.bottom.equalTo(self.mvpAvatarView).offset(3);
make.size.mas_equalTo(CGSizeMake(34, 14));
}];
[self.progressContainer mas_makeConstraints:^(MASConstraintMaker *make) {
......@@ -215,7 +286,7 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
}];
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.progressContainer.mas_right).offset(12);
make.left.equalTo(self.blurView.contentView).offset(12);
make.top.equalTo(self.blurView.contentView).offset(10);
make.size.mas_equalTo(CGSizeMake(22, 22));
}];
......@@ -224,19 +295,31 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
make.left.equalTo(self.avatarView.mas_right).offset(6);
make.centerY.equalTo(self.avatarView);
make.height.mas_equalTo(18);
make.right.lessThanOrEqualTo(self.cancelBtn.mas_left).offset(-8);
}];
[self.ticketIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.topTitleLabel.mas_right).offset(6);
[self.levLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.topTitleLabel.mas_right).offset(4);
make.centerY.equalTo(self.topTitleLabel);
make.size.mas_equalTo(CGSizeMake(28, 16));
make.height.mas_equalTo(18);
}];
[self.vipIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.levLabel.mas_right).offset(4);
make.centerY.equalTo(self.topTitleLabel);
make.height.mas_equalTo(14);
make.width.mas_equalTo(0);
}];
[self.ticketIconView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.vipIconView.mas_right).offset(6);
make.centerY.equalTo(self.avatarView).offset(-0.5);
make.size.mas_equalTo(CGSizeMake(23, 13.5));
make.right.lessThanOrEqualTo(self.cancelBtn.mas_left).offset(-4);
}];
[self.showTimeNewTicketLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.ticketIconView.mas_right).offset(4);
make.centerY.equalTo(self.topTitleLabel);
make.centerY.equalTo(self.avatarView).offset(-0.5);
make.height.mas_equalTo(18);
make.right.lessThanOrEqualTo(self.cancelBtn.mas_left).offset(-8);
}];
......@@ -251,16 +334,17 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
make.left.equalTo(self.countdownIconView.mas_right).offset(4);
make.centerY.equalTo(self.remainingLabel);
make.height.mas_equalTo(18);
make.right.lessThanOrEqualTo(self.mvpIconView.mas_left).offset(-8);
make.width.mas_equalTo(44);
}];
[self.countdownLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.countdownLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self fus_applyLayoutForDisplayMode];
[self fus_updateWithState:FUSLiveShowTimeCollectFrostedStateStarted
themeText:self.themeLabel.text
progress:0.05
remainingText:self.remainingLabel.text
startTimestamp:[[NSDate date] timeIntervalSince1970]
newTicketCount:0
userModel:nil];
......@@ -344,26 +428,97 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
themeText:(NSString *)themeText
progress:(CGFloat)progress
remainingText:(NSString *)remainingText
startTimestamp:(NSTimeInterval)startTimestamp
newTicketCount:(NSInteger)newTicketCount
userModel:(FUSOnlineUserModel *)userModel {
self.themeLabel.text = (themeText.length > 0 ? themeText : [NSString fus_localString:@"限时表演"]);
[self fus_updateWithState:state
themeText:themeText
progress:progress
remainingText:remainingText
newTicketCount:newTicketCount
giftUserModel:userModel
mvpUserModel:nil];
}
- (void)fus_updateWithState:(FUSLiveShowTimeCollectFrostedState)state
themeText:(NSString *)themeText
progress:(CGFloat)progress
remainingText:(NSString *)remainingText
newTicketCount:(NSInteger)newTicketCount
giftUserModel:(FUSOnlineUserModel *)giftUserModel
mvpUserModel:(FUSOnlineUserModel *)mvpUserModel {
NSString *decodedThemeText = nil;
if (themeText.length > 0) {
decodedThemeText = themeText.stringByRemovingPercentEncoding ?: themeText;
}
NSString *finalThemeText = (decodedThemeText.length > 0 ? decodedThemeText : [NSString fus_localString:@"限时表演"]);
if (![self.themeLabel.text isEqualToString:finalThemeText]) {
[self.themeLabel setText:finalThemeText refreshLabels:YES];
}
self.mvpIconView.hidden = YES;
if (mvpUserModel) {
self.mvpAvatarView.hidden = NO;
self.mvpTagLabel.hidden = NO;
if (mvpUserModel.face.length > 0) {
[self.mvpAvatarView setWebImageWithSubURLString:mvpUserModel.face placeholder:nil];
}
} else {
self.mvpAvatarView.hidden = YES;
self.mvpTagLabel.hidden = YES;
}
[self.mvpAvatarView mas_updateConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(self.mvpAvatarView.hidden ? CGSizeZero : CGSizeMake(42, 42));
}];
if (userModel) {
if (giftUserModel) {
self.avatarView.hidden = NO;
self.topTitleLabel.hidden = NO;
self.mvpIconView.hidden = NO;
NSString *nickname = (userModel.nickname.length > 0 ? userModel.nickname : userModel.uid);
self.levLabel.hidden = NO;
NSString *nickname = (giftUserModel.nickname.length > 0 ? giftUserModel.nickname : giftUserModel.uid);
self.topTitleLabel.text = (nickname.length > 0 ? nickname : [NSString fus_localString:@""]);
if (userModel.face.length > 0) {
[self.avatarView setWebImageWithSubURLString:userModel.face placeholder:nil];
NSString *levText = (giftUserModel.lev.length > 0 ? giftUserModel.lev : @"0");
self.levLabel.text = levText;
NSInteger inVip = giftUserModel.inVip;
if (inVip <= 0 && [giftUserModel.privilege isKindOfClass:NSDictionary.class]) {
inVip = [giftUserModel.privilege[@"richPower"] integerValue];
}
if (inVip <= 0) {
inVip = giftUserModel.isVip.integerValue;
}
BOOL isSVIP = (inVip == 1);
if (isSVIP) {
NSInteger levelValue = giftUserModel.level.integerValue;
if (levelValue <= 0) {
levelValue = 1;
}
self.vipIconView.image = [UIImage fus_imageWithLevel:levelValue];
self.vipIconView.hidden = (self.vipIconView.image == nil);
[self.vipIconView mas_updateConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(self.vipIconView.hidden ? 0 : 28);
}];
} else {
self.vipIconView.image = nil;
self.vipIconView.hidden = YES;
[self.vipIconView mas_updateConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(0);
}];
}
if (giftUserModel.face.length > 0) {
[self.avatarView setWebImageWithSubURLString:giftUserModel.face placeholder:nil];
}
} else {
self.avatarView.hidden = YES;
self.topTitleLabel.hidden = YES;
self.ticketIconView.hidden = YES;
self.showTimeNewTicketLabel.hidden = YES;
self.mvpIconView.hidden = YES;
self.levLabel.hidden = YES;
self.vipIconView.image = nil;
self.vipIconView.hidden = YES;
[self.vipIconView mas_updateConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(0);
}];
}
CGFloat clamped = MIN(1.0, MAX(0.0, progress));
......@@ -371,17 +526,29 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
self.progressLabel.text = [NSString stringWithFormat:@"%zd%%", (NSInteger)llround(clamped * 100)];
self.remainingLabel.text = (remainingText.length > 0 ? remainingText : @"");
self.startTimestamp = startTimestamp;
if (state == FUSLiveShowTimeCollectFrostedStateCompleted) {
self.startTimestamp = 0;
self.countdownStageStatus = -1;
} else {
NSTimeInterval nowSec = [[NSDate date] timeIntervalSince1970];
if (self.startTimestamp <= 0) {
self.startTimestamp = nowSec;
self.countdownStageStatus = self.stageStatus;
} else if (self.countdownStageStatus != self.stageStatus) {
self.startTimestamp = nowSec;
self.countdownStageStatus = self.stageStatus;
}
}
if (state == FUSLiveShowTimeCollectFrostedStateCompleted) {
[self fus_stopCountdownTimer];
} else {
[self fus_startCountdownTimerIfNeeded];
}
if (userModel && state == FUSLiveShowTimeCollectFrostedStateNewTicket && newTicketCount > 0) {
if (giftUserModel && newTicketCount > 0) {
self.ticketIconView.hidden = NO;
self.showTimeNewTicketLabel.hidden = NO;
self.showTimeNewTicketLabel.text = [NSString stringWithFormat:@"+%zd", (NSInteger)newTicketCount];
self.showTimeNewTicketLabel.text = [NSString stringWithFormat:@"x%zd", (NSInteger)newTicketCount];
} else {
self.ticketIconView.hidden = YES;
self.showTimeNewTicketLabel.hidden = YES;
......@@ -395,9 +562,90 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
}
}
- (void)fus_updateAudienceWithState:(FUSLiveShowTimeCollectFrostedState)state
themeText:(NSString *)themeText
progress:(CGFloat)progress
remainingText:(NSString *)remainingText
countdownRemainingSeconds:(NSInteger)countdownRemainingSeconds
newTicketCount:(NSInteger)newTicketCount
giftUserModel:(FUSOnlineUserModel *)giftUserModel
mvpUserModel:(FUSOnlineUserModel *)mvpUserModel {
[self fus_updateWithState:state
themeText:themeText
progress:progress
remainingText:remainingText
newTicketCount:newTicketCount
giftUserModel:giftUserModel
mvpUserModel:mvpUserModel];
if (self.displayMode != FUSLiveShowTimeCollectFrostedDisplayModeAudience) {
return;
}
if (state == FUSLiveShowTimeCollectFrostedStateCompleted) {
return;
}
if (countdownRemainingSeconds < 0) {
return;
}
NSInteger clampedRemain = MAX(0, MIN(kFUSShowTimeCollectSeconds, countdownRemainingSeconds));
NSTimeInterval nowSec = [[NSDate date] timeIntervalSince1970];
NSTimeInterval base = nowSec - MAX(0, (NSTimeInterval)kFUSShowTimeCollectSeconds - (NSTimeInterval)clampedRemain);
if (self.startTimestamp <= 0) {
self.startTimestamp = base;
self.countdownStageStatus = self.stageStatus;
[self fus_startCountdownTimerIfNeeded];
return;
}
if (self.countdownStageStatus != self.stageStatus) {
self.startTimestamp = base;
self.countdownStageStatus = self.stageStatus;
[self fus_startCountdownTimerIfNeeded];
return;
}
}
- (void)setCancelHandler:(void (^)(void))cancelHandler {
_cancelHandler = [cancelHandler copy];
self.cancelBtn.hidden = (_cancelHandler == nil);
[self fus_refreshActionButtonVisibility];
}
- (void)setDisplayMode:(FUSLiveShowTimeCollectFrostedDisplayMode)displayMode {
_displayMode = displayMode;
[self fus_applyLayoutForDisplayMode];
[self fus_refreshActionButtonVisibility];
}
- (void)setActionHandler:(void (^)(void))actionHandler {
_actionHandler = [actionHandler copy];
[self fus_refreshActionButtonVisibility];
}
- (void)setActionTitle:(NSString *)actionTitle {
_actionTitle = [actionTitle copy];
if (self.displayMode == FUSLiveShowTimeCollectFrostedDisplayModeAudience && actionTitle.length > 0) {
[self.cancelBtn setTitle:actionTitle forState:UIControlStateNormal];
}
[self fus_refreshActionButtonVisibility];
}
- (void)setStageStatus:(NSInteger)stageStatus {
_stageStatus = stageStatus;
if (self.displayMode != FUSLiveShowTimeCollectFrostedDisplayModeAudience) {
self.statusTagLabel.text = @"";
self.statusTagLabel.hidden = YES;
return;
}
NSString *text = @"";
if (stageStatus == 2) {
text = [NSString fus_localString:@"表演中"];
} else if (stageStatus == 1) {
text = [NSString fus_localString:@"待表演"];
}
self.statusTagLabel.text = text;
self.statusTagLabel.hidden = (text.length == 0);
}
/// 开始倒计时刷新(每秒刷新显示,触底自动停止)
......@@ -456,6 +704,14 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
/// 点击取消:预留给服务端取消接口
- (void)fus_onClickCancel {
if (self.displayMode == FUSLiveShowTimeCollectFrostedDisplayModeAudience) {
if (self.actionHandler) {
self.actionHandler();
return;
}
[FUSDialogView fus_showDialog:[NSString fus_localString:@"功能开发中"]];
return;
}
if (self.cancelHandler) {
self.cancelHandler();
return;
......@@ -464,6 +720,71 @@ static const NSInteger kFUSShowTimeCollectSeconds = 10 * 60;
[self fus_dismiss];
}
/// 根据展示模式调整布局(主播端/用户端差异)
- (void)fus_applyLayoutForDisplayMode {
self.topDividerView.hidden = NO;
[self.cancelBtn mas_remakeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.blurView.contentView).offset(10);
make.right.equalTo(self.blurView.contentView).offset(-12);
make.size.mas_equalTo(CGSizeMake(76, 26));
}];
[self.topDividerView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.right.equalTo(self.blurView.contentView);
make.top.equalTo(self.cancelBtn.mas_bottom).offset(8);
make.height.mas_equalTo(0.5);
}];
[self.bottomContentView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.right.bottom.equalTo(self.blurView.contentView);
make.top.equalTo(self.topDividerView.mas_bottom);
}];
[self.mvpAvatarView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.blurView.contentView).offset(-12);
make.top.equalTo(self.topDividerView.mas_bottom).offset(8);
make.size.mas_equalTo(CGSizeMake(42, 42));
}];
[self.themeLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.progressContainer.mas_right).offset(12);
make.top.equalTo(self.bottomContentView).offset(8);
make.height.mas_equalTo(22);
make.right.equalTo(self.mvpAvatarView.mas_left).offset(-12);
}];
[self.countdownLabel mas_updateConstraints:^(MASConstraintMaker *make) {
make.right.lessThanOrEqualTo(self.mvpAvatarView.mas_left).offset(-8);
}];
[self.mvpIconView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeZero);
make.top.left.equalTo(self.blurView.contentView);
}];
[self setNeedsLayout];
[self layoutIfNeeded];
[self.themeLabel refreshLabels];
}
/// 刷新按钮显隐与文案(避免用户端 cancelHandler=nil 时按钮被隐藏)
- (void)fus_refreshActionButtonVisibility {
BOOL showBtn = NO;
if (self.displayMode == FUSLiveShowTimeCollectFrostedDisplayModeAudience) {
showBtn = (self.actionHandler != nil || self.actionTitle.length > 0);
if (self.actionTitle.length > 0) {
[self.cancelBtn setTitle:self.actionTitle forState:UIControlStateNormal];
} else {
[self.cancelBtn setTitle:[NSString fus_localString:@"购票支持"] forState:UIControlStateNormal];
}
} else {
showBtn = (self.cancelHandler != nil);
[self.cancelBtn setTitle:[NSString fus_localString:@"取消集票"] forState:UIControlStateNormal];
}
self.cancelBtn.hidden = !showBtn;
}
- (void)fus_dismiss {
[self fus_stopCountdownTimer];
[self removeFromSuperview];
......
......@@ -10,12 +10,14 @@ typedef void(^FUSLiveShowTimeStartCollectHandler)(NSString *themeText, NSInteger
/// “限时表演”底部弹窗(集票数量配置)
@interface FUSLiveShowTimePopView : UIView
/// 在指定容器上展示弹窗
/// - Parameter onView: 承载弹窗的父视图
+ (instancetype)fus_showOnView:(UIView *)onView;
/// 在指定容器上展示弹窗,并传入配置
+ (instancetype)fus_showOnView:(UIView *)onView ticketShowConfig:(nullable FUSRoomTicketShowConfig *)ticketShowConfig;
/// 在指定容器上展示弹窗,并传入配置与默认主题文案
/// - Parameters:
/// - onView: 承载弹窗的父视图
/// - ticketShowConfig: 集票数量范围等配置
/// - themeText: 默认主题文案(例如主播标题 introduce),为空则保持当前展示值
+ (instancetype)fus_showOnView:(UIView *)onView
ticketShowConfig:(nullable FUSRoomTicketShowConfig *)ticketShowConfig
themeText:(nullable NSString *)themeText;
/// 点击“开启集票”后的回调(外部决定怎么展示集票磨砂条/调用接口)
@property (nonatomic, copy, nullable) FUSLiveShowTimeStartCollectHandler startCollectHandler;
......
......@@ -53,14 +53,12 @@ static const NSInteger kFUSShowTimeTicketMax = 50;
@implementation FUSLiveShowTimePopView
/// 创建并展示弹窗
+ (instancetype)fus_showOnView:(UIView *)onView {
return [self fus_showOnView:onView ticketShowConfig:nil];
}
+ (instancetype)fus_showOnView:(UIView *)onView ticketShowConfig:(nullable FUSRoomTicketShowConfig *)ticketShowConfig {
+ (instancetype)fus_showOnView:(UIView *)onView
ticketShowConfig:(nullable FUSRoomTicketShowConfig *)ticketShowConfig
themeText:(nullable NSString *)themeText {
FUSLiveShowTimePopView *view = [[FUSLiveShowTimePopView alloc] initWithFrame:onView.bounds];
[view fus_updateWithTicketShowConfig:ticketShowConfig];
[view fus_updateThemeTextIfNeeded:themeText];
[onView addSubview:view];
[view fus_show];
return view;
......@@ -427,4 +425,13 @@ static const NSInteger kFUSShowTimeTicketMax = 50;
}
}
- (void)fus_updateThemeTextIfNeeded:(nullable NSString *)themeText {
if (self.themeTextField.text.length > 0) {
return;
}
if (![NSString isNull:themeText]) {
self.themeTextField.text = themeText;
}
}
@end
......@@ -6127,6 +6127,7 @@ BDAlphaPlayerMetalViewDelegate
[self fus_reloadRealtimeActivityWebView];
[self fus_refreshLiveTheme];
[self fus_refreshLiveTopicWindow];
[self.chatInputHelper fus_syncShowTimeFrostedWithRoomInfoModel:model];
if (![model.roomId isEqualToString:[[FUSCacheDataShare shareStore]userDetailInfo].uid] &&
model.liked &&
......
......@@ -589,6 +589,9 @@ NS_ASSUME_NONNULL_BEGIN
/// Ticket Show - 功能 - 主播端切换限时表演的集票(开始/取消)
+ (NSString *)fus_URL_TicketShow_CollectTicket_Toggle;
/// Ticket Show - 视图 - 获取表演票的集票信息(观众端进房刷新)
+ (NSString *)fus_URL_TicketShow_Progress_GetInfo;
@end
......
......@@ -970,6 +970,12 @@
return [FUSConfig.sharedInstanced.pathConfigs apiUrl:@"/ticketshow/collectTicket/toggle"];
}
/// Ticket Show - 视图 - 获取表演票的集票信息(观众端进房刷新)
+ (NSString *)fus_URL_TicketShow_Progress_GetInfo
{
return [FUSConfig.sharedInstanced.pathConfigs apiUrl:@"/ticketshow/progress/getinfo"];
}
@end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment