Commit 60b9453c by pierce

Fusi 新需求

parent 2ce49661
Showing with 550 additions and 166 deletions
......@@ -72,12 +72,12 @@
/// App Version
- (NSString *)appVersion {
return @"7600";
return @"7662";
}
/// App Dot Version
- (NSString *)appDotVersion {
return @"7.6.0.0";
return @"7.6.6.2";
}
/// App Id
......
......@@ -87,6 +87,7 @@
#define ROOM_CID_BECOME_FANS_GROUP_MEMBER 11010 // 用户成为粉丝团成员
#define ROOM_CID_RoomPopularChanged 11019 // 人气值变化消息
#define ROOM_CID_receivePatAudience 11025 // 接收撩一撩信息的cid
#define ROOM_CID_receiveAnchorRewardChanged 11200 // fusi直播奖励数据变化
// 更新直播间互动游戏开关状态socket
#define ROOM_CID_liveInteractionGameStateDidChanged 20204
......
......@@ -84,6 +84,8 @@ NS_ASSUME_NONNULL_BEGIN
- (void)fus_logSuccess;
- (void)fus_logOut;
- (void)fus_showVideoScoreQualityAlertView:(NSString *)sid;
#pragma mark - HTTP
/**
* 追踪包房
......
......@@ -19,6 +19,9 @@
@property (nonatomic) CGSize size;
@property (nonatomic) CGPoint origin;
// 动画抖动效果
+ (void)addAnimationShakeToView:(UIView *)target;
@end
......@@ -27,7 +30,4 @@
@property (nonatomic) CGFloat offsetX;
@property (nonatomic) CGFloat offsetY;
// 动画抖动效果
+ (void)addAnimationShakeToView:(UIView *)target;
@end
......@@ -106,6 +106,23 @@
return self.frame.origin;
}
// 动画抖动效果
+ (void)addAnimationShakeToView:(UIView *)target
{
if (![target isKindOfClass:[UIView class]]) {
return;
}
CAKeyframeAnimation *shakeAni = [CAKeyframeAnimation animation];
shakeAni.keyPath = @"transform.scale";
shakeAni.values = @[@(1.05), @(1.1),@(0.90), @(1.075), @(0.925), @(1.05), @(0.95), @(1.025), @(0.975), @(1.01), @(0.99), @(1)];
shakeAni.duration = 1;
shakeAni.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
shakeAni.repeatCount = 1;
[target.layer addAnimation:shakeAni forKey:nil];
}
@end
@implementation UIScrollView (FrameExtend)
......@@ -134,21 +151,4 @@
return self.contentOffset.y;
}
// 动画抖动效果
+ (void)addAnimationShakeToView:(UIView *)target
{
if (![target isKindOfClass:[UIView class]]) {
return;
}
CAKeyframeAnimation *shakeAni = [CAKeyframeAnimation animation];
shakeAni.keyPath = @"transform.scale";
shakeAni.values = @[@(1.05), @(1.1),@(0.90), @(1.075), @(0.925), @(1.05), @(0.95), @(1.025), @(0.975), @(1.01), @(0.99), @(1)];
shakeAni.duration = 1;
shakeAni.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
shakeAni.repeatCount = 1;
[target.layer addAnimation:shakeAni forKey:nil];
}
@end
......@@ -840,6 +840,8 @@
BED65C722C60C61C00668116 /* FUSShowRoomAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BED65C712C60C61C00668116 /* FUSShowRoomAssets.xcassets */; };
BED65CFF2C62148700668116 /* TTSDKLisenceFileForTest in Resources */ = {isa = PBXBuildFile; fileRef = BED65CFD2C62148700668116 /* TTSDKLisenceFileForTest */; };
BED65D002C62148700668116 /* TTSDKLisenceFile in Resources */ = {isa = PBXBuildFile; fileRef = BED65CFE2C62148700668116 /* TTSDKLisenceFile */; };
BEEAB2532D3380FA008CD059 /* FusAnchorRewardView.h in Headers */ = {isa = PBXBuildFile; fileRef = BEEAB2512D3380FA008CD059 /* FusAnchorRewardView.h */; };
BEEAB2542D3380FA008CD059 /* FusAnchorRewardView.m in Sources */ = {isa = PBXBuildFile; fileRef = BEEAB2522D3380FA008CD059 /* FusAnchorRewardView.m */; };
BEF675F12C6B156500A670FB /* 1_First_Kill_Animation_1@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = BEF673B22C6B156500A670FB /* 1_First_Kill_Animation_1@3x.png */; };
BEF675F22C6B156500A670FB /* 1_First_Kill_Animation_2@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = BEF673B32C6B156500A670FB /* 1_First_Kill_Animation_2@3x.png */; };
BEF675F32C6B156500A670FB /* 1_First_Kill_Animation_3@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = BEF673B42C6B156500A670FB /* 1_First_Kill_Animation_3@3x.png */; };
......@@ -2235,6 +2237,8 @@
BED65C712C60C61C00668116 /* FUSShowRoomAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = FUSShowRoomAssets.xcassets; sourceTree = "<group>"; };
BED65CFD2C62148700668116 /* TTSDKLisenceFileForTest */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TTSDKLisenceFileForTest; sourceTree = "<group>"; };
BED65CFE2C62148700668116 /* TTSDKLisenceFile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TTSDKLisenceFile; sourceTree = "<group>"; };
BEEAB2512D3380FA008CD059 /* FusAnchorRewardView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FusAnchorRewardView.h; sourceTree = "<group>"; };
BEEAB2522D3380FA008CD059 /* FusAnchorRewardView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FusAnchorRewardView.m; sourceTree = "<group>"; };
BEF673B22C6B156500A670FB /* 1_First_Kill_Animation_1@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "1_First_Kill_Animation_1@3x.png"; sourceTree = "<group>"; };
BEF673B32C6B156500A670FB /* 1_First_Kill_Animation_2@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "1_First_Kill_Animation_2@3x.png"; sourceTree = "<group>"; };
BEF673B42C6B156500A670FB /* 1_First_Kill_Animation_3@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "1_First_Kill_Animation_3@3x.png"; sourceTree = "<group>"; };
......@@ -4180,6 +4184,7 @@
BED657A62C5B745D00668116 /* FunctionView */ = {
isa = PBXGroup;
children = (
BEEAB2502D337F87008CD059 /* AnchorReward */,
BEB504712D2E8FD100EA6E6F /* LiveThemeView */,
BED655C12C5B745D00668116 /* Activity */,
BED655DC2C5B745D00668116 /* AudienceListView */,
......@@ -4603,6 +4608,15 @@
path = Others;
sourceTree = "<group>";
};
BEEAB2502D337F87008CD059 /* AnchorReward */ = {
isa = PBXGroup;
children = (
BEEAB2512D3380FA008CD059 /* FusAnchorRewardView.h */,
BEEAB2522D3380FA008CD059 /* FusAnchorRewardView.m */,
);
path = AnchorReward;
sourceTree = "<group>";
};
BEF673CD2C6B156500A670FB /* 1_First_Kill_Animation */ = {
isa = PBXGroup;
children = (
......@@ -5589,6 +5603,7 @@
BED65B062C5B746000668116 /* FUSRecommendedHosterView.h in Headers */,
BE189DC52C733B460008418B /* FSREffectViewController.h in Headers */,
BED658DE2C5B745E00668116 /* FUSAudiencePopView.h in Headers */,
BEEAB2532D3380FA008CD059 /* FusAnchorRewardView.h in Headers */,
BED65A572C5B745F00668116 /* FUSLiveTreasureBoxRecordDetailView.h in Headers */,
BED658C02C5B745E00668116 /* FUSLiveHalfWebViewCatalogueView.h in Headers */,
BED65A542C5B745F00668116 /* FUSLiveTreasureBoxListIconView.h in Headers */,
......@@ -6651,6 +6666,7 @@
BED6589A2C5B745E00668116 /* FUSLiveHelper.m in Sources */,
BE189E242C733B460008418B /* FSRLinkmicdictActiveMotorEffectView.m in Sources */,
BED65A2E2C5B745F00668116 /* FUSLiveShareHttpHelper.m in Sources */,
BEEAB2542D3380FA008CD059 /* FusAnchorRewardView.m in Sources */,
BED65ABF2C5B745F00668116 /* FUSHomeNovaListBroadcastHelper.m in Sources */,
BED6598C2C5B745E00668116 /* FUSLiveDewIncreaseView.m in Sources */,
BE189DE22C733B460008418B /* FSRSettingOnlineViewController.m in Sources */,
......
......@@ -5,12 +5,12 @@
"scale" : "1x"
},
{
"filename" : "live_start_live_tip_close@2x.png",
"filename" : "关闭@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "live_start_live_tip_close@3x.png",
"filename" : "关闭@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
......
......@@ -5,12 +5,12 @@
"scale" : "1x"
},
{
"filename" : "live_start_novaBuild_button_img_cn@2x.png",
"filename" : "live_anchor_reward_arrow_left@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "live_start_novaBuild_button_img_cn@3x.png",
"filename" : "live_anchor_reward_arrow_left@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
......
......@@ -5,12 +5,12 @@
"scale" : "1x"
},
{
"filename" : "live_start_novaBuild_button_img_en@2x.png",
"filename" : "live_anchor_reward_arrow_right@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "live_start_novaBuild_button_img_en@3x.png",
"filename" : "live_anchor_reward_arrow_right@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
......
......@@ -8,6 +8,14 @@
#import "FUSBaseModel.h"
extern NSString * const kFusiHTMLTagStartStr;
extern NSString * const kFusiHTMLBtnRegularStr;
extern NSString * const kFusiHTMLBtnReplaceTag;
extern NSString * const kFusiHTMLBtnParamAllTag;
extern NSString * const kFusiHTMLBtnParamLink;
extern NSString * const kFusiHTMLBtnParamText;
extern NSString * const kFusiHTMLBtnParamFinalRange;
typedef NS_ENUM(NSInteger,FUSLiveChatModelClickType) {
FUSLiveChatModelClickTypeDefault = 1,
FUSLiveChatModelClickTypeDewGift, // 点击跳转到露水礼物
......@@ -131,6 +139,9 @@ typedef NS_ENUM(NSInteger,FUSLiveChatModelClickType) {
/// 指令(100:拍一拍, 101:惩罚-去解救, 102: 回拍。103:觀看超過3分鐘)
@property (nonatomic, assign) NSInteger order;
/// HTML中自定义的按钮标签
@property (nonatomic, strong) NSMutableArray<NSMutableDictionary<NSString *, NSString *> *> *htmlBtnRangeArray;
/// 判定是否有通用点击事件,因为后台没有返回默认的order,需要自行判定归纳全是否是order通用点击
-(BOOL)ffisClickOrderModel;
......
......@@ -9,6 +9,14 @@
#import "FUSLiveChatModel.h"
#import "FUSFormatContentHelper.h"
NSString * const kFusiHTMLTagStartStr = @"<firefly_btn href=";
NSString * const kFusiHTMLBtnRegularStr = @"<firefly_btn href=(.*?)>(.*?)</firefly_btn>";
NSString * const kFusiHTMLBtnReplaceTag = @"##FIREFLYHTMLBTNTAG##";
NSString * const kFusiHTMLBtnParamAllTag = @"Alltag";
NSString * const kFusiHTMLBtnParamLink = @"Link";
NSString * const kFusiHTMLBtnParamText = @"Text";
NSString * const kFusiHTMLBtnParamFinalRange = @"FinalRange";
@implementation FUSLiveChatModel
- (instancetype)init
......@@ -75,6 +83,57 @@
}
}
}
/// 自定义标签解析
if ([_languageContent containsString:kFusiHTMLTagStartStr]) {
self.htmlBtnRangeArray = [NSMutableArray array];
NSString *regularStr = kFusiHTMLBtnRegularStr;
NSError *error;
// 正则搜索
NSRegularExpression *regularExpression = [NSRegularExpression regularExpressionWithPattern:regularStr
options:NSRegularExpressionCaseInsensitive
error:&error];
// 获取搜索结果数组
NSArray *matchArr = [regularExpression matchesInString:_languageContent options:NSMatchingReportProgress range:NSMakeRange(0, _languageContent.length)];
NSMutableArray *matchStrs = [NSMutableArray array];
for (NSTextCheckingResult *result in matchArr) {
if (result.range.location == kCFNotFound || (result.range.location + result.range.length) > _languageContent.length) {
continue;
}
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
NSString *originStr = [_languageContent substringWithRange:result.range];
NSInteger numberOfRanges = result.numberOfRanges;
if (result.numberOfRanges >= 2) {
NSRange linkRange = [result rangeAtIndex:1];
NSString *linkStr = [_languageContent substringWithRange:linkRange];
dict[kFusiHTMLBtnParamLink] = linkStr;
}
if (result.numberOfRanges >= 3) {
NSRange textRange = [result rangeAtIndex:2];
NSString *textStr = [_languageContent substringWithRange:textRange];
dict[kFusiHTMLBtnParamText] = textStr;
}
if (originStr) {
dict[kFusiHTMLBtnParamAllTag] = originStr;
[matchStrs addObject:originStr];
}
[self.htmlBtnRangeArray addObject:dict];
}
for (NSString *originStr in matchStrs) {
_languageContent = [_languageContent stringByReplacingOccurrencesOfString:originStr withString:kFusiHTMLBtnReplaceTag];
}
}
}
_languageContent = [FUSFormatContentHelper fus_replaceFusiLevelImg:_languageContent];
......
......@@ -605,15 +605,6 @@ NS_ASSUME_NONNULL_BEGIN
failure:(void(^)(NSString *msg, int code))failure;
/**
获取美颜配置参数
@param success 成功回调
@param failure 失败回调
*/
+ (void)fus_requestBeautyParamsWithSuccess:(void(^)(void))success
failure:(void(^)(NSString *msg, int code))failure;
/**
请求礼物面板广播消息数据
@param success 成功回调
......@@ -963,6 +954,13 @@ NS_ASSUME_NONNULL_BEGIN
+(void)fus_requestUserliveAssessHistoryGetListWithDateStr:(NSString *)dateStr
succeed:(void (^)(FUSUserLiveAssessHistoryModel *model))succeed
failure:(void(^)(NSString *msg,NSInteger code))failure;
/// 开播前或恢复直播调用
/// @param succeed 成功回调
/// @param failure 失败回调
+ (void)fus_getLiveBeforeInfosWithSucceed:(void (^)(NSDictionary *publicizeConfig, NSDictionary *gasStationConfig))succeed
failure:(void (^)(NSString *, int))failure;
@end
NS_ASSUME_NONNULL_END
......@@ -2625,63 +2625,20 @@
}
/**
获取美颜配置参数
/// 开播前或恢复直播调用
/// @param succeed 成功回调
/// @param failure 失败回调
+ (void)fus_getLiveBeforeInfosWithSucceed:(void (^)(NSDictionary *publicizeConfig, NSDictionary *gasStationConfig))succeed
failure:(void (^)(NSString *, int))failure {
@param success 成功回调
@param failure 失败回调
*/
+ (void)fus_requestBeautyParamsWithSuccess:(void(^)(void))success
failure:(void(^)(NSString *msg, int code))failure
{
[FUSRouter.userRouter fus_getUserCommonDataWithSuccess:^(NSDictionary * _Nonnull dataDict) {
[FUSHttpHelper postRequestBinaryWithUrl:FUSShowRoomURLs.fus_URL_Live_Before_GetLiveInfo params:@{} success:^(NSDictionary * _Nullable dataDict, int code) {
NSDictionary *liveGasStation = dataDict[@"liveGasStation"];
if (succeed) succeed(liveGasStation[@"publicizeConfig"],
liveGasStation[@"gasStationConfig"]);
} failure:^(NSString * _Nonnull msg, int code) {
} failure:^(NSDictionary * _Nullable dataDict, int code) {
if (failure) failure(dataDict[@"msg"],code);
}];
// // SDK 类型,1:七牛 SDK
// NSDictionary *params = @{@"sdktype":@"1",@"bid":[FUSShowRoomCenterBunble bundle].bundleIdentifier};
//
// [FUSHttpHelper postRequestBinaryWithUrl:FUSShowRoomURLs.fus_URL_LIVE_BEAUTY_PARAM params:params success:^(NSDictionary *dataDict, int code) {
//
// // 获取 Bitrate 配置
//// NSDictionary *bitrateDict = dataDict[@"bitrate"];
// // 获取 声网 配置
// NSDictionary *agoradata = dataDict[@"agoradata"];
// // 获取 字节 配置
// NSDictionary *bytedance = dataDict[@"bytedance"];
//
// // 存储美颜 key
// NSString *beautyKey = bytedance[@"beautyfilterkey"];
// if (beautyKey) {
// [[NSUserDefaults standardUserDefaults] setObject:beautyKey forKey:BEAUTY_ENGINE_KEY];
// }
//
// // 存储推流配置
// if (![NSDictionary isNullWithDictionary:bytedance]) {
// [[NSUserDefaults standardUserDefaults] setObject:bytedance forKey:LIVE_PUSH_CONFIG];
// NSString *bdurl = bytedance[@"bdurl"];
// NSString *bdurlmd5 = bytedance[@"bdurlmd5"];
// FUSDownloadResourceModel *downloadModel = [[FUSDownloadResourceModel alloc] init];
// downloadModel.resourceUrl = bdurl;
// downloadModel.unzipPath = BYTE_DANCE_FILTER_RESOURCE_PATH;
// downloadModel.md5 = bdurlmd5;
// downloadModel.downloadPriority = NSOperationQueuePriorityVeryHigh;
// [[FUSResourceDownloader shareInstance] fus_addDownloadResource:downloadModel];
// }
//
// // 存储推流配置
// if (![NSDictionary isNullWithDictionary:agoradata]) {
// [[NSUserDefaults standardUserDefaults] setObject:agoradata forKey:AGORA_PUSH_CONFIG];
// }
//
// } failure:^(NSDictionary *dataDict, int code) {
//
// if (failure) {
// failure(FAILURE_MESSAGE, code);
// }
//
// }];
}
@end
......@@ -799,7 +799,8 @@
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSMutableAttributedString *attributedString = [self createContentAttributedStringWithModel:model imageSize:CGSizeMake(imageW, imageH) fontSize:self.fontSize];
[self createContentAttributedStringWithModel:model imageSize:CGSizeMake(imageW, imageH) fontSize:self.fontSize complete:^(NSMutableAttributedString *attributedString) {
NSMutableAttributedString *contentAttr = [attributedString mutableCopy];
if (contentAttr.string.length != 0) {
......@@ -818,6 +819,7 @@
[self fus_rectWithAttr:attr completed:^(CGRect rect) {
complete([[NSMutableAttributedString alloc] initWithAttributedString:attr],rect);
}];
}];
});
}
......@@ -983,19 +985,81 @@
}
// 转换正文为富文本
- (NSMutableAttributedString *)createContentAttributedStringWithModel:(FUSLiveChatModel *)model
- (void)createContentAttributedStringWithModel:(FUSLiveChatModel *)model
imageSize:(CGSize)imageSize
fontSize:(CGFloat)fontSize {
fontSize:(CGFloat)fontSize
complete:(void(^)(NSMutableAttributedString *))complete {
// 添加 正文
FUSFormatContentModel *formatModel = [[FUSFormatContentModel alloc] init];
[formatModel fus_setValueWithModel:model];
NSMutableAttributedString *attributedString = [FUSFormatContentHelper fus_createContentAttributedStringWithModel:formatModel font:FUS_LIVE_FONT(fontSize) levelImageSize:imageSize];
__block NSMutableAttributedString *attributedString = [FUSFormatContentHelper fus_createContentAttributedStringWithModel:formatModel font:FUS_LIVE_FONT(fontSize) levelImageSize:imageSize];
if (model.htmlBtnRangeArray.count) {
dispatch_async(dispatch_get_main_queue(), ^{
attributedString = [self ff_convertHTMLBTNTag:attributedString htmlBtnRangeDict:model.htmlBtnRangeArray];
if (complete) {
complete(attributedString);
}
});
} else {
if (complete) {
complete(attributedString);
}
}
}
- (NSMutableAttributedString *)ff_convertHTMLBTNTag:(NSMutableAttributedString *)attributedString htmlBtnRangeDict:(NSMutableArray *)htmlBtnRangeArray {
if ([NSArray isNullWithArray:htmlBtnRangeArray]) {
return attributedString;
}
NSRange contentRange = [attributedString.string rangeOfString:kFusiHTMLBtnReplaceTag];
NSInteger i = 0;
while (contentRange.location != kCFNotFound && i < htmlBtnRangeArray.count) {
NSMutableDictionary *htmlDict = htmlBtnRangeArray[i];
[attributedString replaceCharactersInRange:contentRange withAttributedString:[self fus_getOrderBtnAtt:htmlDict[kFusiHTMLBtnParamText]]];
htmlDict[kFusiHTMLBtnParamFinalRange] = NSStringFromRange(NSMakeRange(contentRange.location, 1));
contentRange = [attributedString.string rangeOfString:kFusiHTMLBtnReplaceTag];
i++;
}
return attributedString;
}
/// 创建普通的按钮
- (NSAttributedString *)fus_getOrderBtnAtt:(NSString *)title {
UIView *btnView = [[UIView alloc] init];
btnView.backgroundColor = [UIColor fus_themeColor];
UILabel *patTitleLabel = [[UILabel alloc] init];
patTitleLabel.font = [UIFont fus_themeFont:_fontSize - 4];
patTitleLabel.textColor = [UIColor fus_textColorRich];
patTitleLabel.text = title;
[patTitleLabel sizeToFit];
patTitleLabel.x = 6;
patTitleLabel.centerY = (_fontSize + 2) / 2;
[btnView addSubview:patTitleLabel];
btnView.height = _fontSize + 2;
btnView.width = CGRectGetMaxX(patTitleLabel.frame) + 6;
btnView.layer.cornerRadius = btnView.height / 2;
UIView *btnBgView = [[UIView alloc] init];
btnBgView.userInteractionEnabled = NO;
btnBgView.frame = btnView.bounds;
btnBgView.width = btnView.width + 10;
btnBgView.height = btnView.height + 4;
[btnBgView addSubview:btnView];
btnView.center = btnBgView.center;
return [NSAttributedString attachmentStringWithContent:btnBgView contentMode:UIViewContentModeScaleAspectFit attachmentSize:btnBgView.size alignToFont:FUS_LIVE_FONT(self.fontSize) alignment:YYTextVerticalAlignmentCenter];
}
// 转换系统警告为富文本
- (NSAttributedString *)createSystemWarningAttributedStringWithModel:(FUSLiveChatModel *)model
......@@ -1119,7 +1183,7 @@
__weak typeof(self) weakSelf = self;
CGSize imageSize = CGSizeMake(45, 14);
NSMutableAttributedString *bulletAttr = [self createContentAttributedStringWithModel:message imageSize:imageSize fontSize:14];
[self createContentAttributedStringWithModel:message imageSize:imageSize fontSize:14 complete:^(NSMutableAttributedString *bulletAttr) {
dispatch_async(dispatch_get_main_queue(), ^{
FUSBulletModel *bulletModel = [[FUSBulletModel alloc] init];
......@@ -1150,6 +1214,7 @@
bulletModel.nickAttrRect = nickRect;
[[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_SHOW_BULLETS object:bulletModel];
});
}];
});
}
......
......@@ -8,9 +8,9 @@
import UIKit
import RxSwift
class FUSVideoEndScoreQualityAlertView: FUSBaseView {
@objcMembers public class FUSVideoEndScoreQualityAlertView: FUSBaseView {
static public func fus_create(dataList: [FUSAssessQualityInfoModel], showOn: UIView? = nil) {
@objc static public func fus_create(dataList: [FUSAssessQualityInfoModel], showOn: UIView? = nil) {
guard let showOnView = ((showOn != nil) ? showOn : UIViewController.fus_top()?.view) else { return }
let view = FUSVideoEndScoreQualityAlertView(frame: showOnView.bounds)
......@@ -33,7 +33,7 @@ class FUSVideoEndScoreQualityAlertView: FUSBaseView {
var qualityInfoViewList: [FFScoreQualityItemView] = .init()
let okBtn = FUSStyleButton(type: .custom)
override func makeUI() {
public override func makeUI() {
self.alpha = 0
self.backgroundColor = .fus_alertViewBlackBg()
......@@ -79,7 +79,7 @@ class FUSVideoEndScoreQualityAlertView: FUSBaseView {
}
}
override func bindViewModel() {
public override func bindViewModel() {
okBtn.rx.tap.subscribe(onNext: {[weak self] in
self?.fus_dismissWithAnimation()
......
//
// FusAnchorRewardView.h
// FUSShowRoomModule
//
// Created by pierce on 2025/1/12.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface FusAnchorRewardView : UIView
+ (void)fus_showAnchorRewardViewIfNeeded:(void(^)(FusAnchorRewardView *view))viewHasShowhandler;
@end
NS_ASSUME_NONNULL_END
//
// FusAnchorRewardView.m
// FUSShowRoomModule
//
// Created by pierce on 2025/1/12.
//
#import "FusAnchorRewardView.h"
#import <FUSFoundation/FUSFoundation.h>
#import <FUSCommon/FUSCommon.h>
#import "FUSLiveHttpHelper.h"
@interface FusAnchorRewardView()
@property (nonatomic, strong) UIButton *arrowBtn;
@property (nonatomic, strong) FUSWKWebView *webview;
/// 原始高度
@property (nonatomic, assign) CGFloat orginalHeight;
/// 最顶层的点击事件
@property (nonatomic, strong) UIButton *coverBtn;
@end
@implementation FusAnchorRewardView
+ (void)fus_showAnchorRewardViewIfNeeded:(void(^)(FusAnchorRewardView *view))viewHasShowhandler {
if (FUSLiveHelper.shareInstance.liveType != FUSLiveTypeAnchor) {
return;
}
[FUSLiveHttpHelper fus_getLiveBeforeInfosWithSucceed:^(NSDictionary * _Nonnull publicizeConfig, NSDictionary * _Nonnull gasStationConfig) {
if ([NSDictionary isNull:gasStationConfig]) {
return;
}
NSString *webUrl = gasStationConfig[@"webUrl"];
CGFloat webWidth = [gasStationConfig[@"webWidth"] floatValue];
CGFloat webHeight = [gasStationConfig[@"webHeight"] floatValue];
if ([NSString isNullWithString:webUrl]) {
return;
}
if ([webUrl containsString:@"?"]) {
webUrl = [NSString stringWithFormat:@"%@&titleHeight=33",webUrl];
} else {
webUrl = [NSString stringWithFormat:@"%@?titleHeight=33",webUrl];
}
CGFloat height = 120 / webWidth * webHeight;
FusAnchorRewardView *anchorRewardView = [[FusAnchorRewardView alloc] initWithFrame:CGRectMake(10, UIView.fus_SafeTop + 135, 120, height)];
anchorRewardView.orginalHeight = height;
[anchorRewardView.webview configWebViewWithWebUrl:webUrl];
[[[FUSLiveHelper shareInstance].currentFunctionView fus_viewWithLayer:FUSLiveFunctionLayerFunctionButtons] addSubview:anchorRewardView];
if (viewHasShowhandler) {
viewHasShowhandler(anchorRewardView);
}
} failure:^(NSString * _Nonnull msg, int code) {
}];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
/// 11200: fusi直播奖励数据变化
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveAnchorRewardChangedNotification:) name:STR(ROOM_CID_receiveAnchorRewardChanged) object:nil];
self.backgroundColor = UIColor.clearColor;
self.clipsToBounds = YES;
self.webview = [[FUSWKWebView alloc] initWithFrame:CGRectMake(0, 0, self.width - 14, self.height)];
self.webview.backgroundColor = UIColor.clearColor;
self.webview.wkWebView.scrollView.scrollEnabled = false;
self.webview.wkWebView.scrollView.maximumZoomScale = 1;
self.webview.wkWebView.scrollView.minimumZoomScale = 1;
self.webview.wkWebView.backgroundColor = UIColor.clearColor;
self.webview.wkWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
self.webview.hideProgress = YES;
[self addSubview:self.webview];
self.hidden = YES;
__weak typeof(self) weakSelf = self;
self.webview.loadingStateChangedHandler = ^(BOOL isLoading, BOOL isSucceed) {
if (!isLoading && isSucceed) {
weakSelf.hidden = NO;
}
};
self.webview.webEventHelper.webCidEventHandler = ^(NSDictionary *info, NSInteger cid) {
if (cid == 47) {
[UIView addAnimationShakeToView:weakSelf];
}
};
self.arrowBtn = [UIButton buttonWithType:UIButtonTypeCustom];
self.arrowBtn.frame = CGRectMake(self.webview.width, 0, 14, 17);
self.arrowBtn.centerY = 16.5;
[self.arrowBtn setImage:[FUSShowRoomCenterBunble imageNamed:@"live_anchor_reward_arrow_left"] forState:UIControlStateNormal];
[self.arrowBtn setImage:[FUSShowRoomCenterBunble imageNamed:@"live_anchor_reward_arrow_right"] forState:UIControlStateSelected];
[self.arrowBtn addTarget:self action:@selector(fus_clickArrowBtnAction:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.arrowBtn];
self.coverBtn = [UIButton buttonWithType:UIButtonTypeCustom];
self.coverBtn.frame = self.bounds;
self.coverBtn.hidden = YES;
[self.coverBtn addTarget:self action:@selector(fus_clickArrowBtnAction:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.coverBtn];
}
return self;
}
/**
11200: fusi直播奖励数据变化
*/
- (void)receiveAnchorRewardChangedNotification:(NSNotification *)notification
{
FUSSocketMessageModel *messageModel = notification.object;
// 检测
if (![[[[FUSLiveHelper shareInstance]roomInfoModel]roomId] isEqualToString:[NSString stringWithFormat:@"%d", messageModel.extend1]]) {
return;
}
NSString *jsString = [NSString stringWithFormat:@"LiveStreamingRewardsSocket('%@')",messageModel.jsondata];
[self.webview.wkWebView evaluateJavaScript:jsString completionHandler:nil];
}
- (void)fus_clickArrowBtnAction:(UIButton *)sender {
self.userInteractionEnabled = NO;
if (self.arrowBtn.isSelected == NO) {
self.arrowBtn.selected = YES;
// [UIView animateWithDuration:0.15 animations:^{
// self.height = 33;
// } completion:^(BOOL finished) {
CGFloat scaleFactor = 0.617;
CGSize originalSize = self.size;
[UIView animateWithDuration:0.3 animations:^{
self.height = 33;
self.transform = CGAffineTransformTranslate(CGAffineTransformScale(CGAffineTransformIdentity, scaleFactor, scaleFactor), -originalSize.width * scaleFactor / 2, -33 * scaleFactor / 2);
} completion:^(BOOL finished) {
self.userInteractionEnabled = YES;
self.coverBtn.hidden = NO;
}];
// }];
} else {
self.arrowBtn.selected = NO;
[UIView animateWithDuration:0.3 animations:^{
self.transform = CGAffineTransformIdentity;
self.height = self.orginalHeight;
} completion:^(BOOL finished) {
self.userInteractionEnabled = YES;
self.coverBtn.hidden = YES;
}];
}
}
@end
......@@ -98,6 +98,25 @@
MJWeakSelf
_contentLabel.textTapAction = ^(UIView * _Nonnull containerView, NSAttributedString * _Nonnull text, NSRange range, CGRect rect) {
if (weakSelf.model.htmlBtnRangeArray) {
for (NSDictionary *dict in weakSelf.model.htmlBtnRangeArray) {
NSString *rangeStr = dict[kFusiHTMLBtnParamFinalRange];
if (![NSString isNullWithString:rangeStr]) {
NSRange dictRange = NSRangeFromString(rangeStr);
if (range.location == dictRange.location) {
NSString *link = [dict[kFusiHTMLBtnParamLink] stringByRemovingPercentEncoding];
if (![NSString isNullWithString:rangeStr]) {
[FUSRouter.userRouter fus_handleTaskJumpActionWithJumpType:link tid:0 url:@"" success:^{
}];
return;
}
}
}
}
}
if (weakSelf.model.patStatus == 1 && range.location == weakSelf.patAudienceRange.location) {
// 撩一撩
if ([FUSLiveHelper shareInstance].liveType == FUSLiveTypeAudience) {
......
......@@ -31,6 +31,7 @@
#import "FUSLiveScrollView.h"
#import "FUSLiveAlertNotificationView.h"
#import "FUSLiveWecomeEnterView.h"
#import "FusAnchorRewardView.h"
#import "GGFPSLabel.h"
......@@ -1044,6 +1045,10 @@ UIGestureRecognizerDelegate
[self initPusherActivityView];
[self initPusherTableView];
[FusAnchorRewardView fus_showAnchorRewardViewIfNeeded:^(FusAnchorRewardView * _Nonnull view) {
view.y = weakSelf.pusherPopularView.bottom;
}];
}
/**
......@@ -1088,6 +1093,7 @@ UIGestureRecognizerDelegate
_pusherAcitivtyView.showStaticActivity = NO;
[_pusherContentView addSubview:_pusherAcitivtyView];
[_pusherAcitivtyView fus_loadActivityData];
}
- (void)initWelcomeView{
......
......@@ -13,7 +13,6 @@ import SnapKit
import FUSFoundation
import FUSCommon
//大小最好是(150 + 15)x(108 + 15)= 165 x 123
class FUSLiveStartV2ContentLiveActivityView: UIView {
/// 监听ishidden
......@@ -39,7 +38,7 @@ class FUSLiveStartV2ContentLiveActivityView: UIView {
imageBtn.imageView?.contentMode = .scaleAspectFit
imageBtn.snp.makeConstraints { make in
make.left.bottom.equalToSuperview()
make.size.equalTo(CGSizeMake(104, 92))
make.size.equalTo(CGSizeMake(96.0, 109))
}
closeBtn.setImage(FUSShowRoomCenterBunble.imageNamed("live_start_live_tip_close"), for: .normal)
......@@ -58,31 +57,58 @@ class FUSLiveStartV2ContentLiveActivityView: UIView {
fatalError("init(coder:) has not been implemented")
}
var jumpUrl:String?
func bindViewModel() {
//活动的显示图
if FUSCacheDataShare.shareStore().startLiveConfig.toDisplay == 0 {
self.isHidden = true
} else {
self.isHidden = false
self.imageBtn.setBackgroundImageWith(.init(string: FUSConfig.sharedInstanced().pathConfigs.bigDownloadPath(FUSCacheDataShare.shareStore().startLiveConfig.iconShowUrl)), for: .normal)
if FUSConfig.sharedInstanced().devConfigs.appStatus == false {
FUSLiveHttpHelper.fus_getLiveBeforeInfos { [weak self] publicizeConfig , _ in
if publicizeConfig.isEmpty == false,
let imgUrl = publicizeConfig["imgUrl"] as? String,
let imgWidth = publicizeConfig["imgWidth"] as? Double,
let imgHeight = publicizeConfig["imgHeight"] as? Double {
self?.imageBtn.snp.remakeConstraints { make in
make.left.bottom.equalToSuperview()
make.size.equalTo(CGSizeMake(96.0, 96.0 / imgWidth * imgHeight))
}
if FUSConfig.sharedInstanced().devConfigs.appStatus == true {
self.isHidden = true
self?.isHidden = false
self?.imageBtn.setBackgroundImageWith(.init(string: imgUrl), for: .normal)
self?.jumpUrl = publicizeConfig["jumpUrl"] as? String
}
} failure: { msg, code in
}
}
// //活动的显示图
// if FUSCacheDataShare.shareStore().startLiveConfig.toDisplay == 0 {
// self.isHidden = true
// } else {
// self.isHidden = false
// self.imageBtn.setBackgroundImageWith(.init(string: FUSConfig.sharedInstanced().pathConfigs.bigDownloadPath(FUSCacheDataShare.shareStore().startLiveConfig.iconShowUrl)), for: .normal)
// }
// if FUSConfig.sharedInstanced().devConfigs.appStatus == true {
// self.isHidden = true
// }
//
//关闭点击
closeBtn.rx.tap.subscribe(onNext: {
self.isHidden = true
}).disposed(by: disposeBag)
//点击活动
imageBtn.rx.tap.subscribe(onNext: {
imageBtn.rx.tap.subscribe(onNext: { [weak self] in
guard let vc = UIViewController.fus_top() else { return }
let url = FUSCacheDataShare.shareStore().startLiveConfig.iconClickUrl
guard let jumpUrl = self?.jumpUrl else { return }
let webVC = FUSWKWebViewController()
webVC.webUrlString = url
webVC.webUrlString = jumpUrl
vc.navigationController?.pushViewController(webVC, animated: true)
}).disposed(by: disposeBag)
......
......@@ -12,7 +12,7 @@ import FUSFoundation
import FUSCommon
///开播的回传模型
@objcMembers public class FUSLiveStartV2StartModel: NSObject{
@objcMembers public class FUSLiveStartV2StartModel: NSObject {
///标题
public var title = ""
///主题id
......@@ -67,7 +67,7 @@ import FUSCommon
let warningView = FUSLiveStartV2ContentWarningView()
// let liveStartPreviewEnterButton = FUSLiveStartPreviewEntranceButton(type: .custom)
let liveActivityView = FUSLiveStartV2ContentLiveActivityView(frame: CGRectZero)
let novaBuildBtn = UIButton(type: .custom)
// let novaBuildBtn = UIButton(type: .custom)
private func createUI(){
......@@ -115,20 +115,20 @@ import FUSCommon
make.top.equalTo(themeView.snp.bottom).offset(0)
}
novaBuildBtn.imageView?.contentMode = .scaleAspectFill
// 判断是否为中文区
if FUSLocalizationHelper.fus_currentLanguage().languageType == .chinese ||
FUSLocalizationHelper.fus_currentLanguage().languageType == .chinese ||
(FUSLocalizationHelper.fus_currentLanguage().key == FUSI_LANGUAGE_SYSTEM
&& FUSLocalizationHelper.fus_currentLanguage().languageType == .chinese){
novaBuildBtn.setBackgroundImage(FUSShowRoomCenterBunble.imageNamed("live_start_novaBuild_button_img_cn"), for: .normal)
}else {
novaBuildBtn.setBackgroundImage(FUSShowRoomCenterBunble.imageNamed("live_start_novaBuild_button_img_en"), for: .normal)
}
// novaBuildBtn.imageView?.contentMode = .scaleAspectFill
// // 判断是否为中文区
// if FUSLocalizationHelper.fus_currentLanguage().languageType == .chinese ||
// FUSLocalizationHelper.fus_currentLanguage().languageType == .chinese ||
// (FUSLocalizationHelper.fus_currentLanguage().key == FUSI_LANGUAGE_SYSTEM
// && FUSLocalizationHelper.fus_currentLanguage().languageType == .chinese){
// novaBuildBtn.setBackgroundImage(FUSShowRoomCenterBunble.imageNamed("live_start_novaBuild_button_img_cn"), for: .normal)
// }else {
// novaBuildBtn.setBackgroundImage(FUSShowRoomCenterBunble.imageNamed("live_start_novaBuild_button_img_en"), for: .normal)
// }
// contentView.addSubview(liveStartPreviewEnterButton)
contentView.addSubview(liveActivityView)
contentView.addSubview(novaBuildBtn)
// contentView.addSubview(novaBuildBtn)
// liveStartPreviewEnterButton.snp.makeConstraints { make in
// make.left.equalTo(themeView.snp.left)
......@@ -144,12 +144,12 @@ import FUSCommon
// make.size.equalTo(CGSizeMake(130, 123))
}
novaBuildBtn.snp.makeConstraints { make in
make.left.equalTo(themeView.snp.left)
make.top.equalTo(liveActivityView.snp.bottom).offset(14)
make.height.equalTo(38)
make.width.equalTo(104)
}
// novaBuildBtn.snp.makeConstraints { make in
// make.left.equalTo(themeView.snp.left)
// make.top.equalTo(liveActivityView.snp.bottom).offset(14)
// make.height.equalTo(38)
// make.width.equalTo(104)
// }
contentView.addSubview(bottomView)
bottomView.snp.makeConstraints { make in
......@@ -176,13 +176,11 @@ import FUSCommon
self?.startLiveModel.accept(model)
}
}
//TODO:跳转: added By Pidan
// warningView.clickHandler = { model, index in
// Mediator.PersonalPage?.handleTaskJumpAction(jumpType: model.jumpurl, tid: 0, url: model.jumpval, succeed: {
//
// })
// }
warningView.clickHandler = { model, index in
FUSRouter.userRouter().fus_handleTaskJumpAction(withJumpType: model.jumpurl, tid: 0, url: model.jumpval) {
}
}
//关闭按钮
closeBtn.rx.tap.subscribe(onNext: {[weak self] in
......@@ -243,30 +241,30 @@ import FUSCommon
// }
// }).disposed(by: disposeBag)
//新星养成
self.novaBuildBtn.isHidden = FUSCacheDataShare.shareStore().startLiveConfig.novaToDisplay == 0
self.liveActivityView.isHiddenObserver.subscribe(onNext: {[weak self] isHidden in
guard let self = self else { return }
self.novaBuildBtn.snp.remakeConstraints({ make in
make.left.equalTo(self.themeView.snp.left)
if isHidden {
make.top.equalTo(self.themeView.snp.bottom).offset(14)
}else {
make.top.equalTo(self.liveActivityView.snp.bottom).offset(14)
}
make.height.equalTo(38)
make.width.equalTo(104)
})
}).disposed(by: disposeBag)
self.novaBuildBtn.rx.tap.subscribe(onNext: {
guard let vc = UIViewController.fus_top() else { return }
let url = FUSCacheDataShare.shareStore().startLiveConfig.novaToShowUrl
let webVC = FUSWKWebViewController()
webVC.webUrlString = url
vc.navigationController?.pushViewController(webVC, animated: true)
}).disposed(by: disposeBag)
// //新星养成
// self.novaBuildBtn.isHidden = FUSCacheDataShare.shareStore().startLiveConfig.novaToDisplay == 0
//
// self.liveActivityView.isHiddenObserver.subscribe(onNext: {[weak self] isHidden in
// guard let self = self else { return }
// self.novaBuildBtn.snp.remakeConstraints({ make in
// make.left.equalTo(self.themeView.snp.left)
// if isHidden {
// make.top.equalTo(self.themeView.snp.bottom).offset(14)
// }else {
// make.top.equalTo(self.liveActivityView.snp.bottom).offset(14)
// }
// make.height.equalTo(38)
// make.width.equalTo(104)
// })
// }).disposed(by: disposeBag)
//
// self.novaBuildBtn.rx.tap.subscribe(onNext: {
// guard let vc = UIViewController.fus_top() else { return }
// let url = FUSCacheDataShare.shareStore().startLiveConfig.novaToShowUrl
// let webVC = FUSWKWebViewController()
// webVC.webUrlString = url
// vc.navigationController?.pushViewController(webVC, animated: true)
// }).disposed(by: disposeBag)
}
func bindModel(){
......
......@@ -312,6 +312,9 @@ NS_ASSUME_NONNULL_BEGIN
/// 开始直播前准备
+ (NSString *)fus_URL_Live_Before_Ready;
/// 开播前或恢复直播调用
+ (NSString *)fus_URL_Live_Before_GetLiveInfo;
#pragma mark -- PK
// 房间PK信息
+ (NSString *)fus_URL_LIVE_PK_ROOM_PK_INFO;
......
......@@ -567,6 +567,10 @@
return [FUSConfig.sharedInstanced.pathConfigs apiUrl:@"/live/before/ready"];
}
/// 开播前或恢复直播调用
+ (NSString *)fus_URL_Live_Before_GetLiveInfo {
return [FUSConfig.sharedInstanced.pathConfigs apiUrl:@"/live/before/getLiveInfo"];
}
#pragma mark -- PK
// 房间PK信息
......
......@@ -207,9 +207,6 @@
// 更新红包资源
[FUSTreasureBoxHttpHelper fus_updateRedPackageResources];
[FUSLiveHttpHelper fus_requestBeautyParamsWithSuccess:nil
failure:nil];
[FUSLiveHttpHelper fus_requestLiveGameListSuccess:^(NSArray<FUSLiveGameModel *> *gameList) {
[FUSLiveConfigsDataCenter shareInstance].gameList = gameList;
} failure:^(NSString *mgs, int code) {
......@@ -425,6 +422,16 @@
[[UIViewController fus_topViewController].navigationController pushViewController:vc animated:YES];
}
- (void)fus_showVideoScoreQualityAlertView:(NSString *)sid {
[FUSLiveHttpHelper fus_requestUserliveAssessQualityInfoWithSid:sid succeed:^(NSArray<FUSAssessQualityInfoModel *> * _Nonnull modelList) {
[FUSVideoEndScoreQualityAlertView fus_createWithDataList:modelList showOn:[UIViewController fus_topViewController].view];
} failure:^(NSString * _Nonnull msg, NSInteger code) {
}];
}
#pragma mark - HTTP
/**
* 追踪包房
......
......@@ -357,7 +357,8 @@
// 更新语音签名文字链数据
[FUSCommonCacheOperate updateVoiceSignDemoTipsCache];
[FUSCommonCacheOperate fus_getLiveStartConfigWithType:ReadServerBegin success:nil failure:nil];
// Fusi v1.2 版本去掉这个换成了:/live/before/getLiveInfo 这个请求,在开播的时候调用: added By Pidan
// [FUSCommonCacheOperate fus_getLiveStartConfigWithType:ReadServerBegin success:nil failure:nil];
// 标记自动登录
[[NSUserDefaults standardUserDefaults] setObject:@(1) forKey:FUSUserUDKeys.fus_AUTO_LOGIN_MARK_BOOL];
......
......@@ -708,6 +708,21 @@
}
- (void)fus_handleTaskJumpActionWithJumpType:(NSString *)jumpType tid:(NSInteger)tid url:(NSString *)url success:(void (^)(void))success{
NSMutableDictionary *querys = [NSMutableDictionary dictionary];
NSURL *jumpURL = [NSURL URLWithString:jumpType];
NSURLComponents *jumpURLComponents = [NSURLComponents componentsWithURL:jumpURL resolvingAgainstBaseURL:NO];
if (jumpURLComponents.queryItems.count > 0) {
for (NSURLQueryItem *item in jumpURLComponents.queryItems) {
querys[item.name] = item.value;
}
jumpURLComponents.queryItems = nil;
jumpType = jumpURLComponents.URL.absoluteString ?: jumpType;
}
if ([jumpType isEqualToString:@"yazhai://_bind_phone"]) {
FUSPhoneAuthViewController *phoneBindingVc = [[FUSPhoneAuthViewController alloc]init];
......@@ -812,6 +827,11 @@
FUSVerifyVersionController *vc = [[FUSVerifyVersionController alloc] init];
vc.hidesBottomBarWhenPushed = YES;
[[UIViewController fus_topViewController].navigationController pushViewController:vc animated:YES];
} else if ([jumpType isEqualToString:@"yazhai://_anchor_center"]) {
NSString *sid = querys[@"sid"];
if ([NSString isNullWithString:sid] == NO) {
[FUSRouter.liveRouter fus_showVideoScoreQualityAlertView:sid];
}
}
}
......
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