1.代码提交

This commit is contained in:
HiBit 2025-12-08 10:34:34 +08:00
parent 5fb7a4d9bc
commit 6d48234e48
154 changed files with 5750 additions and 287 deletions

View File

@ -141,7 +141,6 @@
3754ACF02ED94A4C009EBCAD /* SRFeedBackController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754ACEF2ED94A4C009EBCAD /* SRFeedBackController.swift */; };
3754ACF22ED975B9009EBCAD /* SRRewardController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754ACF12ED975B9009EBCAD /* SRRewardController.swift */; };
3754ACF42ED98609009EBCAD /* SRCoinPackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754ACF32ED98609009EBCAD /* SRCoinPackCell.swift */; };
3754ACF62ED9A2D4009EBCAD /* SRCoinPackTitleview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754ACF52ED9A2D4009EBCAD /* SRCoinPackTitleview.swift */; };
3754ACF82ED9A32C009EBCAD /* SRCoinPackHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754ACF72ED9A32C009EBCAD /* SRCoinPackHeaderView.swift */; };
3754ACFC2ED9A36C009EBCAD /* SRCoinsPackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754ACFB2ED9A36C009EBCAD /* SRCoinsPackModel.swift */; };
3754ACFE2ED9A3A0009EBCAD /* SRCoinsPackReceiveModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754ACFD2ED9A3A0009EBCAD /* SRCoinsPackReceiveModel.swift */; };
@ -164,6 +163,35 @@
3754AD3C2EDD9C01009EBCAD /* SRThirdModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AD3B2EDD9C01009EBCAD /* SRThirdModel.swift */; };
3754AD3E2EDD9C88009EBCAD /* SRLogin+FB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AD3D2EDD9C88009EBCAD /* SRLogin+FB.swift */; };
3754AEF82EDE94CD009EBCAD /* SRUserRewardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AEF72EDE94CD009EBCAD /* SRUserRewardCell.swift */; };
3754AEFA2EDEB4F4009EBCAD /* SRUserListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AEF92EDEB4F4009EBCAD /* SRUserListViewController.swift */; };
3754AEFD2EDEB515009EBCAD /* SRUserHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AEFB2EDEB515009EBCAD /* SRUserHeaderView.swift */; };
3754AEFE2EDEB515009EBCAD /* SRUserListHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AEFC2EDEB515009EBCAD /* SRUserListHeaderView.swift */; };
3754AF012EDED6FD009EBCAD /* SRPayManger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF002EDED6FD009EBCAD /* SRPayManger.swift */; };
3754AF042EDED773009EBCAD /* SRIapManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF032EDED773009EBCAD /* SRIapManager.swift */; };
3754AF082EDED91C009EBCAD /* SRPayDateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF072EDED91C009EBCAD /* SRPayDateModel.swift */; };
3754AF0A2EDED9A8009EBCAD /* SRStoreAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF092EDED9A8009EBCAD /* SRStoreAPI.swift */; };
3754AF0C2EDEDA49009EBCAD /* SRPayDataRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF0B2EDEDA49009EBCAD /* SRPayDataRequest.swift */; };
3754AF0E2EDEDAA9009EBCAD /* SRPayAlertModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF0D2EDEDAA9009EBCAD /* SRPayAlertModel.swift */; };
3754AF102EDEDD21009EBCAD /* SRWaitRestoreModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF0F2EDEDD21009EBCAD /* SRWaitRestoreModel.swift */; };
3754AF122EDEDEBF009EBCAD /* SRIapOrderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF112EDEDEBF009EBCAD /* SRIapOrderModel.swift */; };
3754AF142EDEE1AC009EBCAD /* SRStatAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF132EDEE1AC009EBCAD /* SRStatAPI.swift */; };
3754AF162EDEE43E009EBCAD /* SRIapVerifyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF152EDEE43E009EBCAD /* SRIapVerifyModel.swift */; };
3754AF192EDEF0AF009EBCAD /* SRStoreController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF182EDEF0AF009EBCAD /* SRStoreController.swift */; };
3754AF1C2EDEF112009EBCAD /* SRStoreCoinsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF1B2EDEF112009EBCAD /* SRStoreCoinsView.swift */; };
3754AF202EDEF133009EBCAD /* SRStoreVipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF1F2EDEF133009EBCAD /* SRStoreVipView.swift */; };
3754AF242EDEF38C009EBCAD /* SRBigCoinViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF232EDEF38C009EBCAD /* SRBigCoinViewCell.swift */; };
3754AF262EDEF3A7009EBCAD /* SRSmallCoinViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF252EDEF3A7009EBCAD /* SRSmallCoinViewCell.swift */; };
3754AF282EDEF3CC009EBCAD /* SRPackCoinViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF272EDEF3CC009EBCAD /* SRPackCoinViewCell.swift */; };
3754AF2A2EDEF425009EBCAD /* SRStoreCoinsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF292EDEF425009EBCAD /* SRStoreCoinsCell.swift */; };
3754AF2C2EDEF4D5009EBCAD /* SRStoreVipCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754AF2B2EDEF4D5009EBCAD /* SRStoreVipCell.swift */; };
3754B00B2EE12040009EBCAD /* SRCoinsPackClaimListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754B00A2EE12040009EBCAD /* SRCoinsPackClaimListView.swift */; };
3754B00F2EE120C5009EBCAD /* SRCoinsPackClaimListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754B00E2EE120C5009EBCAD /* SRCoinsPackClaimListCell.swift */; };
3754B0112EE1248D009EBCAD /* SRCoinsPackBuyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754B0102EE1248D009EBCAD /* SRCoinsPackBuyView.swift */; };
3754B0152EE12521009EBCAD /* SRCoinPackConfirmView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754B0142EE12521009EBCAD /* SRCoinPackConfirmView.swift */; };
3754B0172EE12633009EBCAD /* SRCoinPackConfirmItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754B0162EE12633009EBCAD /* SRCoinPackConfirmItemView.swift */; };
3754B0192EE1267B009EBCAD /* SRCoinPackConfirmItem1View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754B0182EE1267B009EBCAD /* SRCoinPackConfirmItem1View.swift */; };
3754B01B2EE12690009EBCAD /* SRCoinPackConfirmItem2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754B01A2EE12690009EBCAD /* SRCoinPackConfirmItem2View.swift */; };
3754B0312EE2C9D4009EBCAD /* SRVideoRechargeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3754B0302EE2C9D4009EBCAD /* SRVideoRechargeView.swift */; };
3779D0612ECF1CB8006B1698 /* SRShortHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3779D0602ECF1CB8006B1698 /* SRShortHeaderView.swift */; };
47BB39E2DD30787FA591F8EB /* Pods_SynthReel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9255BF4D4B1CFDDB5CFFB43 /* Pods_SynthReel.framework */; };
/* End PBXBuildFile section */
@ -304,7 +332,6 @@
3754ACEF2ED94A4C009EBCAD /* SRFeedBackController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRFeedBackController.swift; sourceTree = "<group>"; };
3754ACF12ED975B9009EBCAD /* SRRewardController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRRewardController.swift; sourceTree = "<group>"; };
3754ACF32ED98609009EBCAD /* SRCoinPackCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCoinPackCell.swift; sourceTree = "<group>"; };
3754ACF52ED9A2D4009EBCAD /* SRCoinPackTitleview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCoinPackTitleview.swift; sourceTree = "<group>"; };
3754ACF72ED9A32C009EBCAD /* SRCoinPackHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCoinPackHeaderView.swift; sourceTree = "<group>"; };
3754ACFB2ED9A36C009EBCAD /* SRCoinsPackModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCoinsPackModel.swift; sourceTree = "<group>"; };
3754ACFD2ED9A3A0009EBCAD /* SRCoinsPackReceiveModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCoinsPackReceiveModel.swift; sourceTree = "<group>"; };
@ -320,6 +347,35 @@
3754AD3B2EDD9C01009EBCAD /* SRThirdModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRThirdModel.swift; sourceTree = "<group>"; };
3754AD3D2EDD9C88009EBCAD /* SRLogin+FB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SRLogin+FB.swift"; sourceTree = "<group>"; };
3754AEF72EDE94CD009EBCAD /* SRUserRewardCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRUserRewardCell.swift; sourceTree = "<group>"; };
3754AEF92EDEB4F4009EBCAD /* SRUserListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRUserListViewController.swift; sourceTree = "<group>"; };
3754AEFB2EDEB515009EBCAD /* SRUserHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRUserHeaderView.swift; sourceTree = "<group>"; };
3754AEFC2EDEB515009EBCAD /* SRUserListHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRUserListHeaderView.swift; sourceTree = "<group>"; };
3754AF002EDED6FD009EBCAD /* SRPayManger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRPayManger.swift; sourceTree = "<group>"; };
3754AF032EDED773009EBCAD /* SRIapManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRIapManager.swift; sourceTree = "<group>"; };
3754AF072EDED91C009EBCAD /* SRPayDateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRPayDateModel.swift; sourceTree = "<group>"; };
3754AF092EDED9A8009EBCAD /* SRStoreAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRStoreAPI.swift; sourceTree = "<group>"; };
3754AF0B2EDEDA49009EBCAD /* SRPayDataRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRPayDataRequest.swift; sourceTree = "<group>"; };
3754AF0D2EDEDAA9009EBCAD /* SRPayAlertModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRPayAlertModel.swift; sourceTree = "<group>"; };
3754AF0F2EDEDD21009EBCAD /* SRWaitRestoreModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRWaitRestoreModel.swift; sourceTree = "<group>"; };
3754AF112EDEDEBF009EBCAD /* SRIapOrderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRIapOrderModel.swift; sourceTree = "<group>"; };
3754AF132EDEE1AC009EBCAD /* SRStatAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRStatAPI.swift; sourceTree = "<group>"; };
3754AF152EDEE43E009EBCAD /* SRIapVerifyModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRIapVerifyModel.swift; sourceTree = "<group>"; };
3754AF182EDEF0AF009EBCAD /* SRStoreController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRStoreController.swift; sourceTree = "<group>"; };
3754AF1B2EDEF112009EBCAD /* SRStoreCoinsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRStoreCoinsView.swift; sourceTree = "<group>"; };
3754AF1F2EDEF133009EBCAD /* SRStoreVipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRStoreVipView.swift; sourceTree = "<group>"; };
3754AF232EDEF38C009EBCAD /* SRBigCoinViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRBigCoinViewCell.swift; sourceTree = "<group>"; };
3754AF252EDEF3A7009EBCAD /* SRSmallCoinViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRSmallCoinViewCell.swift; sourceTree = "<group>"; };
3754AF272EDEF3CC009EBCAD /* SRPackCoinViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRPackCoinViewCell.swift; sourceTree = "<group>"; };
3754AF292EDEF425009EBCAD /* SRStoreCoinsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRStoreCoinsCell.swift; sourceTree = "<group>"; };
3754AF2B2EDEF4D5009EBCAD /* SRStoreVipCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRStoreVipCell.swift; sourceTree = "<group>"; };
3754B00A2EE12040009EBCAD /* SRCoinsPackClaimListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCoinsPackClaimListView.swift; sourceTree = "<group>"; };
3754B00E2EE120C5009EBCAD /* SRCoinsPackClaimListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCoinsPackClaimListCell.swift; sourceTree = "<group>"; };
3754B0102EE1248D009EBCAD /* SRCoinsPackBuyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCoinsPackBuyView.swift; sourceTree = "<group>"; };
3754B0142EE12521009EBCAD /* SRCoinPackConfirmView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCoinPackConfirmView.swift; sourceTree = "<group>"; };
3754B0162EE12633009EBCAD /* SRCoinPackConfirmItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCoinPackConfirmItemView.swift; sourceTree = "<group>"; };
3754B0182EE1267B009EBCAD /* SRCoinPackConfirmItem1View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCoinPackConfirmItem1View.swift; sourceTree = "<group>"; };
3754B01A2EE12690009EBCAD /* SRCoinPackConfirmItem2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRCoinPackConfirmItem2View.swift; sourceTree = "<group>"; };
3754B0302EE2C9D4009EBCAD /* SRVideoRechargeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRVideoRechargeView.swift; sourceTree = "<group>"; };
3779D0602ECF1CB8006B1698 /* SRShortHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SRShortHeaderView.swift; sourceTree = "<group>"; };
59DC746604B26E9FF802D317 /* Pods-SynthReel.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SynthReel.debug.xcconfig"; path = "Target Support Files/Pods-SynthReel/Pods-SynthReel.debug.xcconfig"; sourceTree = "<group>"; };
AA88214030574193B51DE563 /* Pods-SynthReel.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SynthReel.release.xcconfig"; path = "Target Support Files/Pods-SynthReel/Pods-SynthReel.release.xcconfig"; sourceTree = "<group>"; };
@ -550,6 +606,7 @@
3754ACD42ED82722009EBCAD /* SRDetailRecommendview.swift */,
3754ACD62ED82774009EBCAD /* SRDetailRecommendCell.swift */,
3754AD192EDD745A009EBCAD /* SRVideoLockView.swift */,
3754B0302EE2C9D4009EBCAD /* SRVideoRechargeView.swift */,
);
path = V;
sourceTree = "<group>";
@ -639,6 +696,8 @@
03E9A7D42EC4764A000D1067 /* Libs */ = {
isa = PBXGroup;
children = (
3754AF022EDED751009EBCAD /* SRAppstore */,
3754AEFF2EDED691009EBCAD /* payManger */,
3754AD342EDD9A8F009EBCAD /* SRLogin */,
3754ACD82ED83724009EBCAD /* FSPagerView */,
3754ACCF2ED81F2F009EBCAD /* Alert */,
@ -715,6 +774,8 @@
03B1A9292ECC072C006C353F /* SRShortApi.swift */,
03B1A8E42EC715DA006C353F /* SRHomeApi.swift */,
03E9A7FC2EC5763B000D1067 /* SRUserApi.swift */,
3754AF092EDED9A8009EBCAD /* SRStoreAPI.swift */,
3754AF132EDEE1AC009EBCAD /* SRStatAPI.swift */,
);
path = API;
sourceTree = "<group>";
@ -740,6 +801,7 @@
370D2F0E2ED4532A00571E77 /* VC */ = {
isa = PBXGroup;
children = (
3754AEF92EDEB4F4009EBCAD /* SRUserListViewController.swift */,
370D2F0F2ED4534500571E77 /* SRUserViewController.swift */,
370D2F1D2ED54C7F00571E77 /* SRHelpCenterController.swift */,
370D2F1F2ED54C8F00571E77 /* SRAboutUsController.swift */,
@ -753,6 +815,8 @@
370D2F132ED457C900571E77 /* view */ = {
isa = PBXGroup;
children = (
3754AEFB2EDEB515009EBCAD /* SRUserHeaderView.swift */,
3754AEFC2EDEB515009EBCAD /* SRUserListHeaderView.swift */,
370D2F142ED457F000571E77 /* SRUserTopCell.swift */,
370D2F192ED45CCA00571E77 /* SRUserSettingCell.swift */,
370D2F242ED5807600571E77 /* SRAboutHeaderVIew.swift */,
@ -801,6 +865,7 @@
3754ACDD2ED93C14009EBCAD /* Coinpack */ = {
isa = PBXGroup;
children = (
3754B0092EE11F71009EBCAD /* M */,
3754ACDF2ED93C36009EBCAD /* view */,
3754ACDE2ED93C2A009EBCAD /* VC */,
);
@ -818,8 +883,14 @@
3754ACDF2ED93C36009EBCAD /* view */ = {
isa = PBXGroup;
children = (
3754ACF52ED9A2D4009EBCAD /* SRCoinPackTitleview.swift */,
3754ACF72ED9A32C009EBCAD /* SRCoinPackHeaderView.swift */,
3754B00A2EE12040009EBCAD /* SRCoinsPackClaimListView.swift */,
3754B00E2EE120C5009EBCAD /* SRCoinsPackClaimListCell.swift */,
3754B0102EE1248D009EBCAD /* SRCoinsPackBuyView.swift */,
3754B0142EE12521009EBCAD /* SRCoinPackConfirmView.swift */,
3754B0162EE12633009EBCAD /* SRCoinPackConfirmItemView.swift */,
3754B0182EE1267B009EBCAD /* SRCoinPackConfirmItem1View.swift */,
3754B01A2EE12690009EBCAD /* SRCoinPackConfirmItem2View.swift */,
);
path = view;
sourceTree = "<group>";
@ -839,6 +910,8 @@
3754ACF92ED9A34E009EBCAD /* Store */ = {
isa = PBXGroup;
children = (
3754AF1A2EDEF105009EBCAD /* Views */,
3754AF172EDEF09C009EBCAD /* VC */,
3754ACFA2ED9A359009EBCAD /* Model */,
);
path = Store;
@ -847,8 +920,9 @@
3754ACFA2ED9A359009EBCAD /* Model */ = {
isa = PBXGroup;
children = (
3754ACFB2ED9A36C009EBCAD /* SRCoinsPackModel.swift */,
3754ACFD2ED9A3A0009EBCAD /* SRCoinsPackReceiveModel.swift */,
3754AF072EDED91C009EBCAD /* SRPayDateModel.swift */,
3754AF0D2EDEDAA9009EBCAD /* SRPayAlertModel.swift */,
3754AF152EDEE43E009EBCAD /* SRIapVerifyModel.swift */,
);
path = Model;
sourceTree = "<group>";
@ -881,6 +955,56 @@
path = SRLogin;
sourceTree = "<group>";
};
3754AEFF2EDED691009EBCAD /* payManger */ = {
isa = PBXGroup;
children = (
3754AF002EDED6FD009EBCAD /* SRPayManger.swift */,
);
path = payManger;
sourceTree = "<group>";
};
3754AF022EDED751009EBCAD /* SRAppstore */ = {
isa = PBXGroup;
children = (
3754AF032EDED773009EBCAD /* SRIapManager.swift */,
3754AF0B2EDEDA49009EBCAD /* SRPayDataRequest.swift */,
3754AF0F2EDEDD21009EBCAD /* SRWaitRestoreModel.swift */,
3754AF112EDEDEBF009EBCAD /* SRIapOrderModel.swift */,
);
path = SRAppstore;
sourceTree = "<group>";
};
3754AF172EDEF09C009EBCAD /* VC */ = {
isa = PBXGroup;
children = (
3754AF182EDEF0AF009EBCAD /* SRStoreController.swift */,
);
path = VC;
sourceTree = "<group>";
};
3754AF1A2EDEF105009EBCAD /* Views */ = {
isa = PBXGroup;
children = (
3754AF1B2EDEF112009EBCAD /* SRStoreCoinsView.swift */,
3754AF1F2EDEF133009EBCAD /* SRStoreVipView.swift */,
3754AF232EDEF38C009EBCAD /* SRBigCoinViewCell.swift */,
3754AF252EDEF3A7009EBCAD /* SRSmallCoinViewCell.swift */,
3754AF272EDEF3CC009EBCAD /* SRPackCoinViewCell.swift */,
3754AF292EDEF425009EBCAD /* SRStoreCoinsCell.swift */,
3754AF2B2EDEF4D5009EBCAD /* SRStoreVipCell.swift */,
);
path = Views;
sourceTree = "<group>";
};
3754B0092EE11F71009EBCAD /* M */ = {
isa = PBXGroup;
children = (
3754ACFB2ED9A36C009EBCAD /* SRCoinsPackModel.swift */,
3754ACFD2ED9A3A0009EBCAD /* SRCoinsPackReceiveModel.swift */,
);
path = M;
sourceTree = "<group>";
};
3779D05F2ECF1C8D006B1698 /* V */ = {
isa = PBXGroup;
children = (
@ -1046,15 +1170,20 @@
03B1A8FB2EC818BE006C353F /* UIStackView+SRAdd.swift in Sources */,
03B1A8E12EC6D6D3006C353F /* SRHomeMenuDataSource.swift in Sources */,
03E9A7F82EC4AA54000D1067 /* SRUserDefaultsKey.swift in Sources */,
3754B0312EE2C9D4009EBCAD /* SRVideoRechargeView.swift in Sources */,
3754AF1C2EDEF112009EBCAD /* SRStoreCoinsView.swift in Sources */,
03B1A8452EC5CBBB006C353F /* SRNavigationController.swift in Sources */,
03B1A9092ECAAF55006C353F /* SRHomeTopChartsView.swift in Sources */,
03E9A7E42EC49593000D1067 /* SRUrlPath.swift in Sources */,
3754AF122EDEDEBF009EBCAD /* SRIapOrderModel.swift in Sources */,
370D2EF92ED000D200571E77 /* SRHistoryViewController.swift in Sources */,
3754B0112EE1248D009EBCAD /* SRCoinsPackBuyView.swift in Sources */,
03B1A9462ECC5679006C353F /* SRSearchResultCell.swift in Sources */,
03B1A9502ECEB4E6006C353F /* SRRecommendPlayerViewController.swift in Sources */,
03B1A83E2EC5C91E006C353F /* SRTool.swift in Sources */,
03B1A9172ECAF14F006C353F /* SRHomeHotView.swift in Sources */,
03B1A8FD2EC81C62006C353F /* SRHomeBannerCell.swift in Sources */,
3754AF2A2EDEF425009EBCAD /* SRStoreCoinsCell.swift in Sources */,
03E9A7DB2EC485BE000D1067 /* SRNetwork.swift in Sources */,
370D2F152ED457F000571E77 /* SRUserTopCell.swift in Sources */,
3754ACBD2ED5B6B6009EBCAD /* UINavigationBar+SRAdd.swift in Sources */,
@ -1066,10 +1195,13 @@
370D2F072ED3FEFA00571E77 /* SRFavoritesCell.swift in Sources */,
3754ACE42ED93E3B009EBCAD /* SRBaseWebViewController.swift in Sources */,
03B1A8E72EC7175D006C353F /* SRCategoryModel.swift in Sources */,
3754B0152EE12521009EBCAD /* SRCoinPackConfirmView.swift in Sources */,
370D2F2F2ED5AB2500571E77 /* SRViralHitController.swift in Sources */,
3754ACDA2ED8374D009EBCAD /* SRPagerViewTransformer.swift in Sources */,
3754AF282EDEF3CC009EBCAD /* SRPackCoinViewCell.swift in Sources */,
3754ACFE2ED9A3A0009EBCAD /* SRCoinsPackReceiveModel.swift in Sources */,
3754ACF22ED975B9009EBCAD /* SRRewardController.swift in Sources */,
3754AEFA2EDEB4F4009EBCAD /* SRUserListViewController.swift in Sources */,
03B1A94A2ECC79AB006C353F /* SREpSelectorView.swift in Sources */,
03B1A94E2ECD604B006C353F /* SREpSelectorCell.swift in Sources */,
03B1A9262ECBFF31006C353F /* SRShortPlayerViewModel.swift in Sources */,
@ -1093,6 +1225,8 @@
370D2F092ED44A6D00571E77 /* SRListMenuCell.swift in Sources */,
03980F532ECEDEAB0006E317 /* SRRecommendPlayerControlView.swift in Sources */,
03B1A90F2ECAC768006C353F /* SRHomeBingeWorthyView.swift in Sources */,
3754B0192EE1267B009EBCAD /* SRCoinPackConfirmItem1View.swift in Sources */,
3754AF162EDEE43E009EBCAD /* SRIapVerifyModel.swift in Sources */,
3754ACFC2ED9A36C009EBCAD /* SRCoinsPackModel.swift in Sources */,
03B1A9072EC86656006C353F /* SRGradientView.swift in Sources */,
370D2F292ED58EC400571E77 /* SRTopChartsViewController.swift in Sources */,
@ -1103,18 +1237,24 @@
03B1A8402EC5CA37006C353F /* AppDelegate+Config.swift in Sources */,
03B1A9152ECAEE63006C353F /* SRHomeViralHitsCell.swift in Sources */,
03B1A8472EC5CBCF006C353F /* SRViewController.swift in Sources */,
3754AF192EDEF0AF009EBCAD /* SRStoreController.swift in Sources */,
370D2F052ED3FEE700571E77 /* SRHistoryCell.swift in Sources */,
3754AF202EDEF133009EBCAD /* SRStoreVipView.swift in Sources */,
3754ACEA2ED94065009EBCAD /* SRBaseWebViewController + Script.swift in Sources */,
3754AD3A2EDD9BB1009EBCAD /* SRLogin+apple.swift in Sources */,
03B1A9032EC8555B006C353F /* SRHomeYouLikeView.swift in Sources */,
03B1A9362ECC1D1D006C353F /* SRSearchRecordView.swift in Sources */,
3754AF262EDEF3A7009EBCAD /* SRSmallCoinViewCell.swift in Sources */,
03B1A8D82EC6D051006C353F /* SRCollectionView.swift in Sources */,
03B1A90D2ECAC51A006C353F /* NSNumber+SRAdd.swift in Sources */,
3754AF082EDED91C009EBCAD /* SRPayDateModel.swift in Sources */,
03E9A7DD2EC485E1000D1067 /* SRNetworkModel.swift in Sources */,
3754ACEC2ED943AB009EBCAD /* SRAppWebViewController.swift in Sources */,
03B1A9442ECC4ED9006C353F /* SRSearchResultView.swift in Sources */,
3754ACBF2ED5B839009EBCAD /* SRNavgationTitleView.swift in Sources */,
3754B00B2EE12040009EBCAD /* SRCoinsPackClaimListView.swift in Sources */,
03B1A8FF2EC81C92006C353F /* SRImageView.swift in Sources */,
3754B00F2EE120C5009EBCAD /* SRCoinsPackClaimListCell.swift in Sources */,
03B1A83B2EC5C8E0006C353F /* SRToast.swift in Sources */,
03B1A9302ECC10D1006C353F /* SRSearchViewController.swift in Sources */,
03B1A8432EC5CB99006C353F /* SRTabBarController.swift in Sources */,
@ -1123,26 +1263,31 @@
03B1A8ED2EC72C1F006C353F /* SRHomeModuleItem.swift in Sources */,
370D2F102ED4534500571E77 /* SRUserViewController.swift in Sources */,
03B1A9112ECAC927006C353F /* SRHomeBingeWorthyCell.swift in Sources */,
3754AF0C2EDEDA49009EBCAD /* SRPayDataRequest.swift in Sources */,
03E9A7F22EC4A8F6000D1067 /* UserDefaults+SRAdd.swift in Sources */,
370D2F2B2ED597F700571E77 /* SRTopChartsCell.swift in Sources */,
3754AD3C2EDD9C01009EBCAD /* SRThirdModel.swift in Sources */,
03B1A93A2ECC3F54006C353F /* SRSearchViewModel.swift in Sources */,
03E9A7F62EC4A9B1000D1067 /* SRUserInfo.swift in Sources */,
3754AF0A2EDED9A8009EBCAD /* SRStoreAPI.swift in Sources */,
03B1A8F92EC813BC006C353F /* SRScrollView.swift in Sources */,
03B1A8E92EC721CD006C353F /* SRLabel.swift in Sources */,
3754AD1A2EDD745A009EBCAD /* SRVideoLockView.swift in Sources */,
03B1A9282ECC05B1006C353F /* SRShortDetailModel.swift in Sources */,
03980F592ECEED190006E317 /* SRMyShortViewController.swift in Sources */,
3754AEFD2EDEB515009EBCAD /* SRUserHeaderView.swift in Sources */,
3754AF012EDED6FD009EBCAD /* SRPayManger.swift in Sources */,
3754AEFE2EDEB515009EBCAD /* SRUserListHeaderView.swift in Sources */,
3754ACD32ED82113009EBCAD /* SRGradientbutton.swift in Sources */,
03B1A92A2ECC0738006C353F /* SRShortApi.swift in Sources */,
03B1A90B2ECAB2EA006C353F /* SRHomeTopChartsContentView.swift in Sources */,
370D2F0C2ED44ACB00571E77 /* SRListMenuDataSource.swift in Sources */,
03B1A94C2ECC7A2D006C353F /* SRPanModalContentView.swift in Sources */,
03B1A9052EC857B3006C353F /* SRHomeYouLikeCell.swift in Sources */,
3754AF042EDED773009EBCAD /* SRIapManager.swift in Sources */,
03B1A92E2ECC0D7E006C353F /* SRShortDetailControlView.swift in Sources */,
03B1A9382ECC210D006C353F /* SRSearchRecordCell.swift in Sources */,
03E9A7D72EC47A23000D1067 /* SRResponseCryptor.swift in Sources */,
3754ACF62ED9A2D4009EBCAD /* SRCoinPackTitleview.swift in Sources */,
03B1A8502EC5DB2E006C353F /* SRHomeViewController.swift in Sources */,
03B1A8E52EC715E1006C353F /* SRHomeApi.swift in Sources */,
3754AD022EDA8AF7009EBCAD /* SRLoginController.swift in Sources */,
@ -1150,8 +1295,10 @@
03B1A8F12EC72DD7006C353F /* SRHomeViewModel.swift in Sources */,
03E9A7FD2EC57658000D1067 /* SRUserApi.swift in Sources */,
03E9A7E72EC49819000D1067 /* SRDefine.swift in Sources */,
3754AF2C2EDEF4D5009EBCAD /* SRStoreVipCell.swift in Sources */,
03B1A9012EC852B2006C353F /* SRHomeModuleView.swift in Sources */,
03E9A7EF2EC4A8AF000D1067 /* SRAccountManager.swift in Sources */,
3754AF102EDEDD21009EBCAD /* SRWaitRestoreModel.swift in Sources */,
03B1A8F52EC81277006C353F /* SRHomeBannerView.swift in Sources */,
3754AD312EDD939A009EBCAD /* SRUserLoginView.swift in Sources */,
3754AD1C2EDD77BA009EBCAD /* SRVideoUnlockResult.swift in Sources */,
@ -1163,11 +1310,14 @@
03B1A8552EC5E434006C353F /* UIFont+SRAdd.swift in Sources */,
3779D0612ECF1CB8006B1698 /* SRShortHeaderView.swift in Sources */,
03980F512ECEBEE20006E317 /* SRRecommendPlayerCell.swift in Sources */,
3754AF242EDEF38C009EBCAD /* SRBigCoinViewCell.swift in Sources */,
03B1A91B2ECAFFD6006C353F /* UIView+SRAdd.swift in Sources */,
03B1A9222ECB456C006C353F /* SRDetailPlayerViewController.swift in Sources */,
3754B0172EE12633009EBCAD /* SRCoinPackConfirmItemView.swift in Sources */,
03B1A91F2ECB2A0E006C353F /* SRHomeBannerMiniCell.swift in Sources */,
3754ACC12ED69105009EBCAD /* SRTopChartsHeaderView.swift in Sources */,
03B1A93E2ECC4568006C353F /* SRTableView.swift in Sources */,
3754AF0E2EDEDAA9009EBCAD /* SRPayAlertModel.swift in Sources */,
03980F4F2ECEB91C0006E317 /* SRRecommendPlayerViewModel.swift in Sources */,
03B1A9422ECC4632006C353F /* SRHotSearchCell.swift in Sources */,
03B1A9342ECC12D9006C353F /* SRSearchTextView.swift in Sources */,
@ -1176,6 +1326,8 @@
03B1A93C2ECC406E006C353F /* SRHotSearchView.swift in Sources */,
03B1A8E32EC6F577006C353F /* SRHomeMenuCell.swift in Sources */,
3754AD362EDD9AA7009EBCAD /* SRLogin.swift in Sources */,
3754AF142EDEE1AC009EBCAD /* SRStatAPI.swift in Sources */,
3754B01B2EE12690009EBCAD /* SRCoinPackConfirmItem2View.swift in Sources */,
3754ACE12ED93C4D009EBCAD /* SRCoinPackController.swift in Sources */,
3754ACD52ED82722009EBCAD /* SRDetailRecommendview.swift in Sources */,
);
@ -1229,7 +1381,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.drama.hive;
PRODUCT_BUNDLE_IDENTIFIER = com.SynthReel.Tv.lssj;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
STRING_CATALOG_GENERATE_SYMBOLS = YES;
@ -1271,7 +1423,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.drama.hive;
PRODUCT_BUNDLE_IDENTIFIER = com.SynthReel.Tv.lssj;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
STRING_CATALOG_GENERATE_SYMBOLS = YES;

View File

@ -0,0 +1,67 @@
//
// SRStatAPI.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRStatAPI: NSObject {
enum EventKey: String {
case payError = "pay_error" //
case payCallback = "pay_callback" //
case payRestore = "pay_restore" //restore
case forceUpdate = "force_update"//
case payCancel = "pay_cancel" //
case payTemplateDialog = "pay_template_dialog"//
}
static func requestEventStat(orderCode: String?, shortPlayId: String?, videoId: String?, eventKey: EventKey, errorMsg: String?, otherParamenters: [String : Any]? = nil) async {
var eventName = ""
switch eventKey {
case .payRestore:
eventName = "pay restore"
case .payCallback:
eventName = "pay callback failed"
case .forceUpdate:
eventName = "force update"
case .payCancel:
eventName = "user pay canceled"
default:
eventName = "platform pay failed"
}
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/createOrder")
param.method = .post
param.parameters = [
"userId" : SRAccountManager.manager.userInfo?.customer_id ?? "",
"short_play_video_id" : videoId ?? "0",
"short_play_id" : shortPlayId ?? "0",
"event_key" : eventKey.rawValue,
"order_code": orderCode ?? "",
"event_name" : eventName,
]
if let errorMsg = errorMsg {
param.parameters?["error_msg"] = errorMsg
}
if let otherParamenters = otherParamenters {
otherParamenters.forEach {
param.parameters?[$0] = $1
}
}
SRNetwork.request(parameters: param) { (_: SRNetwork.Response<String>) in
continuation.resume()
}
}
}
}

View File

@ -0,0 +1,93 @@
//
// SRStoreAPI.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import SmartCodable
class SRStoreAPI: NSObject {
enum BuyType: String, SmartCaseDefaultable {
case coins = "coins"
case subVip = "sub_vip"
case subCoins = "sub_coins"
case vip = "vip"
}
static func requestPayTemplate() async -> SRPayDateModel? {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/paySettingsV4")
param.method = .post
param.parameters = [
"discount" : "1",
"purchases_token" : SRPayManger.manager.getAppStoreReceipt() ?? "",
]
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRPayDateModel>) in
continuation.resume(returning: response.data)
}
}
}
static func requestCreateOrder(payId: String, shortPlayId: String, videoId: String, isDiscount: Bool = false, identifierDiscount: String? = nil) async -> SRIapOrderModel? {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/createOrder")
param.method = .post
param.parameters = [
"payment_channel" : "apple",
"short_play_id" : shortPlayId,
"video_id" : videoId,
"pay_setting_id" : payId,
"is_discount" : isDiscount ? 1 : 0,
"product_discount" : identifierDiscount ?? "",
]
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRIapOrderModel>) in
continuation.resume(returning: response.data)
}
}
}
static func requestVerifyOrder(parameters: [String : Any]) async -> SRIapVerifyModel? {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/applePaid")
param.method = .post
param.parameters = parameters
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRIapVerifyModel>) in
continuation.resume(returning: response.data)
}
}
}
static func requestCoinsPackData() async -> SRCoinsPackModel? {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/getReceiveDayCoinInfo")
param.method = .get
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRCoinsPackModel>) in
continuation.resume(returning: response.data)
}
}
}
static func requestReceiveCoinsPackCoins(id: String?) async -> Bool {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/getReceiveDayCoinInfo")
param.method = .post
param.parameters = [
"id" : id ?? ""
]
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<String>) in
if response.isSuccess {
continuation.resume(returning: true)
}else{
continuation.resume(returning: false)
}
}
}
}
}

View File

@ -12,3 +12,12 @@ let kSRAccountTokenDefaultsKey = "kSRAccountTokenDefaultsKey"
///
let kSRUserInfoDefaultsKey = "kSRUserInfoDefaultsKey"
let kSRWaitRestoreIAPDefaultsKey = "kSRWaitRestoreIAPDefaultsKey"
let kSRApnsAlertDefaultsKey = "kSRApnsAlertDefaultsKey"
let kSRVersionUpdateAlertDefaultsKey = "kSRVersionUpdateAlertDefaultsKey"
let kSRHasBeenOpenedAPPDefaultsKey = "kSRHasBeenOpenedAPPDefaultsKey"

View File

@ -13,7 +13,8 @@ import SmartCodable
extension String {
var localized: String {
return String(localized: LocalizationValue(self))
let value = String(localized: LocalizationValue(self))
return value.replacingOccurrences(of: "<br>", with: "\n")
}
func localizedReplace(text: String) -> String {

View File

@ -7,11 +7,13 @@
//
let SRBaseURL = "https://api-synthreeltv.synthreeltv.com"
//let SRBaseURL = "https://api-univicos.univicos.com"
let SRUrlPathPrefix = "/th"
//let SRUrlPathPrefix = "/uni"
let SRWebBaseURL = "https://www.synthreeltv.com"
let SRCampaignWebURL = "https://campaign.synthreeltv.com"
//@"https://api-univicos.univicos.com/uni";
///
let kSRFeedBackHomeWebUrl = SRCampaignWebURL + "/pages/leave/index"

View File

@ -12,6 +12,7 @@ import SnapKit
class SRPanModalContentView: HWPanModalContentView {
var mainScrollView: UIScrollView?
var contentHeight = UIScreen.height * (2 / 3)
@ -56,9 +57,10 @@ class SRPanModalContentView: HWPanModalContentView {
//MARK: HWPanModalPresentable
override func panScrollable() -> UIScrollView? {
return nil
return mainScrollView
}
override func longFormHeight() -> PanModalHeight {
return PanModalHeightMake(.content, contentHeight)
}

View File

@ -10,15 +10,144 @@ import UIKit
class SRCoinPackController: SRViewController {
private var model: SRCoinsPackModel? {
didSet {
headerView.model = model
claimView.dataArr = model?.receive_list ?? []
}
}
private var payDataModel : SRPayDateModel? {
didSet {
var arr: [SRPayItem] = []
payDataModel?.list_coins?.forEach {
if $0.buy_type == .subCoins {
arr.append($0)
}
}
self.buyView.dataArr = arr
}
}
private var payDataRequest: SRPayDataRequest?
lazy var topImageview = UIImageView.init(image: .topCoinPack)
private lazy var bgIconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "查看图片 25"))
return imageView
}()
private lazy var scrollView: SRScrollView = {
let scrollView = SRScrollView()
return scrollView
}()
private lazy var headerView: SRCoinPackHeaderView = {
let view = SRCoinPackHeaderView()
view.clickClaimButton = { [weak self] in
self?.requestReceiveCoins(nil)
}
return view
}()
private lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.spacing = 19
return stackView
}()
private lazy var claimView: SRCoinsPackClaimListView = {
let view = SRCoinsPackClaimListView()
view.clickClaimButton = { [weak self] id in
guard let self = self else { return }
self.requestReceiveCoins(id)
}
return view
}()
private lazy var buyView: SRCoinsPackBuyView = {
let view = SRCoinsPackBuyView()
view.buyFinishHandle = { [weak self] in
self?.requestCoinsPackData()
}
return view
}()
private lazy var tipView: UIView = {
let view = UIView()
view.addSubview(tipTitleLabel)
view.addSubview(tipTextLabel)
tipTitleLabel.snp.makeConstraints { make in
make.top.equalToSuperview()
make.left.equalToSuperview().offset(16)
}
tipTextLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.right.lessThanOrEqualToSuperview().offset(-16)
make.top.equalTo(tipTitleLabel.snp.bottom).offset(4)
make.bottom.equalToSuperview()
}
return view
}()
private lazy var tipTitleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .medium)
label.textColor = .white.withAlphaComponent(0.5)
label.text = "coins_pack_tips_title".localized
return label
}()
private weak var recommandView: UIView?
private lazy var tipTextLabel: UILabel = {
let att = NSMutableAttributedString(string: "coins_pack_tips".localized)
att.yy_lineSpacing = 3
let label = UILabel()
label.font = .font(ofSize: 10, weight: .regular)
label.textColor = .white.withAlphaComponent(0.5)
label.attributedText = att
label.numberOfLines = 0
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
sr_setui()
()
self.navigationItem.title = "My Refills".localized
payDataModel = SRIapManager.manager.payDateModel
sr_setui()
requestCoinsPackData()
updateLayout()
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
private func updateLayout() {
stackView.sr_removeAllArrangedSubview()
if self.claimView.dataArr.count > 0 {
stackView.addArrangedSubview(self.claimView)
} else if self.buyView.dataArr.count > 0 {
stackView.addArrangedSubview(self.buyView)
}
stackView.addArrangedSubview(tipView)
}
}
extension SRCoinPackController {
@ -28,5 +157,90 @@ extension SRCoinPackController {
topImageview.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
}
view.addSubview(bgIconImageView)
view.addSubview(scrollView)
scrollView.addSubview(headerView)
scrollView.addSubview(stackView)
bgIconImageView.snp.makeConstraints { make in
make.right.equalToSuperview()
make.top.equalToSuperview()
}
scrollView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
make.bottom.equalToSuperview()
}
headerView.snp.makeConstraints { make in
make.left.centerX.equalToSuperview()
make.top.equalToSuperview()
}
stackView.snp.makeConstraints { make in
make.left.centerX.equalToSuperview()
make.top.equalTo(headerView.snp.bottom).offset(16)
make.bottom.equalToSuperview().offset(-(UIScreen.safeBottom + 10))
}
}
}
extension SRCoinPackController {
private func requestCoinsPackData() {
Task {
let model = await SRStoreAPI.requestCoinsPackData()
guard let model = model else { return }
self.model = model
if (model.receive_list?.count ?? 0) == 0 {
self.requestPayData()
}
self.updateLayout()
}
}
private func requestPayData() {
self.payDataRequest = SRPayDataRequest()
self.payDataRequest?.requestProducts { [weak self] model in
guard let self = self else { return }
guard let model = model else { return }
self.payDataModel = model
self.updateLayout()
}
}
private func requestReceiveCoins(_ id: String?) {
Task {
let sucess = await SRStoreAPI.requestReceiveCoinsPackCoins(id: id)
self.requestCoinsPackData()
if sucess {
self.showVideoRecommand()
}
}
}
///
private func showVideoRecommand() {
guard self.recommandView == nil else { return }
Task {
let (model, _, _) = await SRShortApi.requestRecommand()
guard let model = model else { return }
guard self.recommandView == nil else { return }
let view = SRDetailRecommendview()
view.dataArr = model
view.didSelectedVideo = { [weak self] model in
guard let self = self else { return }
let vc = SRDetailPlayerViewController()
vc.shortId = model.short_play_id
self.navigationController?.pushViewController(vc, animated: true)
}
view.show(in: SRTool.keyWindow)
self.recommandView = view
}
}
}

View File

@ -0,0 +1,82 @@
//
// SRCoinPackConfirmItem1View.swift
// SynthReel
//
// Created by CSGY on 2025/12/4.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinPackConfirmItem1View: SRCoinPackConfirmItemView {
override init(frame: CGRect) {
super.init(frame: frame)
lazy var bgIconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "getBg"))
return imageView
}()
titleLabel.text = "synthreel_bonus_you_get".localized
let view1 = getItemView(image: UIImage(named: "dailyIocn"), text: "synthreel_weekly_refill_package".localized)
let view2 = getItemView(image: UIImage(named: "rewardicon"), text: "synthreel_dailu_bonuses".localized)
addSubview(bgIconImageView)
addSubview(view1)
addSubview(view2)
bgIconImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
view1.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.top.equalTo(titleLabel.snp.bottom).offset(8)
make.height.equalTo(83)
make.bottom.equalToSuperview().offset(-16)
}
view2.snp.makeConstraints { make in
make.left.equalTo(view1.snp.right).offset(12)
make.right.equalToSuperview().offset(-12)
make.width.height.top.equalTo(view1)
}
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func getItemView(image: UIImage?, text: String) -> UIView {
let view = UIImageView()
view.image = .itemBg
let imageView = UIImageView(image: image)
imageView.setContentHuggingPriority(.required, for: .horizontal)
imageView.setContentCompressionResistancePriority(.required, for: .horizontal)
let label = UILabel()
label.numberOfLines = 0
label.font = .font(ofSize: 12, weight: .init(800))
label.textColor = .white
label.text = text
label.textAlignment = .center
view.addSubview(imageView)
view.addSubview(label)
imageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(5)
}
label.snp.makeConstraints { make in
make.left.right.equalToSuperview().inset(10)
make.top.equalTo(imageView.snp.bottom).offset(10)
}
return view
}
}

View File

@ -0,0 +1,134 @@
//
// SRCoinPackConfirmItem2View.swift
// SynthReel
//
// Created by CSGY on 2025/12/4.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinPackConfirmItem2View: SRCoinPackConfirmItemView {
var list: [String]? {
didSet {
self.stackView.sr_removeAllArrangedSubview()
list?.forEach {
let view = SRCoinPackConfirmItem2View.ItemView()
view.text = $0
self.stackView.addArrangedSubview(view)
}
}
}
private lazy var bgIconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "receiveBg"))
return imageView
}()
private lazy var lineView: UIView = {
let view = UIView()
view.backgroundColor = .white.withAlphaComponent(0.1)
return view
}()
private lazy var stackView: UIStackView = {
let view = UIStackView()
view.axis = .vertical
view.spacing = 24
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.titleLabel.text = "synthreel_coin_bag_buy_tip_title".localized
fa_setupLayout()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SRCoinPackConfirmItem2View {
private func fa_setupLayout() {
addSubview(bgIconImageView)
addSubview(lineView)
addSubview(stackView)
bgIconImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
lineView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.centerX.equalToSuperview()
make.top.equalTo(titleLabel.snp.bottom).offset(16)
make.height.equalTo(1)
}
stackView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.top.equalTo(lineView.snp.bottom).offset(16)
make.bottom.equalToSuperview().offset(-16)
}
}
}
extension SRCoinPackConfirmItem2View {
class ItemView: UIView {
var text: String? {
didSet {
textLabel.text = text
}
}
private lazy var iconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "coins_icon_06"))
return imageView
}()
private lazy var textLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 14, weight: .regular)
label.textColor = .white
label.numberOfLines = 0
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(iconImageView)
addSubview(textLabel)
iconImageView.snp.makeConstraints { make in
make.left.equalToSuperview()
make.top.equalToSuperview()
}
textLabel.snp.makeConstraints { make in
make.left.equalTo(iconImageView.snp.right).offset(12)
make.right.lessThanOrEqualToSuperview()
make.top.equalToSuperview()
make.bottom.equalToSuperview()
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}

View File

@ -0,0 +1,41 @@
//
// SRCoinPackConfirmItemView.swift
// SynthReel
//
// Created by CSGY on 2025/12/4.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinPackConfirmItemView: UIView {
lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 18, weight: .bold)
label.textColor = .white
label.textAlignment = .center
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .black
layer.cornerRadius = 8
layer.masksToBounds = true
addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.right.lessThanOrEqualToSuperview().offset(-16)
make.top.equalToSuperview().offset(16)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,207 @@
//
// SRCoinPackConfirmView.swift
// SynthReel
//
// Created by CSGY on 2025/12/4.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import HWPanModal
class SRCoinPackConfirmView: SRPanModalContentView {
var buyFinishHandle: (() -> Void)?
var shortPlayId: String?
var videoId: String?
var model: SRPayItem? {
didSet {
item2View.list = model?.ext_info?.sub_coins_txt_list
var price = model?.price ?? ""
if model?.discount_type == 1, let introductoryPrice = model?.introductionaryOffer {
price = introductoryPrice.price.stringValue
} else if model?.discount_type == 2, let discount = model?.promotionalOffers?.first {
price = discount.price.stringValue
}
priceLabel.text = "\(model?.currency ?? "")\(price)/\(model?.getTimeString() ?? "")"
}
}
// private lazy var bgImageView: UIImageView = {
// let imageView = UIImageView(image: UIImage(named: ""))
// return imageView
// }()
private lazy var bgIconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: ""))
return imageView
}()
private lazy var titleLabel: SRLabel = {
let label = SRLabel()
label.font = .font(ofSize: 24, weight: .init(700))
label.textColors = [UIColor._4_CFFD_4.cgColor, UIColor._51_D_4_FF.cgColor]
label.textStartPoint = .init(x: 0.5, y: 0)
label.textEndPoint = .init(x: 0.5, y: 1)
label.text = "synthreel_what_you_get".localized
return label
}()
private lazy var closeButton: UIButton = {
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
Task {
await self.dismiss(animated: true)
}
}))
button.setImage(UIImage(named: "Close"), for: .normal)
return button
}()
private lazy var scrollView: SRScrollView = {
let scrollView = SRScrollView()
scrollView.addObserver(self, forKeyPath: "contentSize", context: nil)
return scrollView
}()
private lazy var item1View: SRCoinPackConfirmItem1View = {
let view = SRCoinPackConfirmItem1View()
return view
}()
private lazy var item2View: SRCoinPackConfirmItem2View = {
let view = SRCoinPackConfirmItem2View()
return view
}()
private lazy var priceLabel: SRLabel = {
let label = SRLabel()
label.font = .font(ofSize: 18, weight: .bold)
label.textColors = [UIColor.srBlue.cgColor, UIColor.srBlue.cgColor]
label.textStartPoint = .init(x: 0, y: 0.5)
label.textEndPoint = .init(x: 1, y: 0.5)
return label
}()
private lazy var continueButton: UIButton = {
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
guard let model = self.model else { return }
SRIapManager.manager.start(model: model, shortPlayId: self.shortPlayId, videoId: self.videoId) { [weak self] finish in
guard let self = self else { return }
if finish {
self.buyFinishHandle?()
Task {
await self.dismiss(animated: true)
}
}
}
}))
button.setTitle("synthreel_continue".localized, for: .normal)
button.setTitleColor(.black, for: .normal)
button.titleLabel?.font = .font(ofSize: 14, weight: .bold)
button.setBackgroundImage(.continueBG, for: .normal)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .clear
sr_setupLayout()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "contentSize" {
self.panModalSetNeedsLayoutUpdate()
}
}
override func allowsTapBackgroundToDismiss() -> Bool {
return false
}
override func cornerRadius() -> CGFloat {
return 0
}
override func longFormHeight() -> PanModalHeight {
let height = 96 + scrollView.contentSize.height + 92 + 10
return PanModalHeightMake(.content, height + UIScreen.safeBottom)
}
}
extension SRCoinPackConfirmView {
private func sr_setupLayout() {
addSubview(bgImageView)
bgImageView.addSubview(bgIconImageView)
addSubview(titleLabel)
addSubview(closeButton)
addSubview(scrollView)
scrollView.addSubview(item1View)
scrollView.addSubview(item2View)
addSubview(priceLabel)
addSubview(continueButton)
bgImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
bgIconImageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview()
}
titleLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(55)
}
closeButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-16)
make.top.equalToSuperview().offset(24)
}
scrollView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalToSuperview().offset(96)
make.bottom.equalToSuperview().offset(-(92 + UIScreen.safeBottom + 10))
}
item1View.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.top.equalToSuperview()
}
item2View.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.top.equalTo(item1View.snp.bottom).offset(12)
make.bottom.equalToSuperview()
}
priceLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.centerY.equalTo(scrollView.snp.bottom).offset(22)
}
continueButton.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.left.equalToSuperview().offset(16)
make.top.equalTo(scrollView.snp.bottom).offset(44)
make.height.equalTo(48)
}
}
}

View File

@ -29,25 +29,24 @@ class SRCoinPackHeaderView: UIView {
}
}
private lazy var titleView: SRCoinPackTitleview = {
let view = SRCoinPackTitleview()
// view.dotColor = .C_5_DDF_5
private lazy var titleView: UILabel = {
let view = UILabel()
view.font = .font(ofSize: 14, weight: .regular)
// view.textColors = [UIColor.C_5_DDF_5.cgColor, UIColor.C_5_DDF_5.cgColor]
view.text = "fableon_rewards_overview".localized.uppercased()
view.textColor = .white
view.text = "synthreel_rewards_overview".localized.uppercased()
return view
}()
private lazy var coinsView1: CoinsView = {
let view = CoinsView()
view.title = "fableon_weekly_total".localized
view.title = "synthreel_weekly_total".localized
view.coins = 0
return view
}()
private lazy var coinsView2: CoinsView = {
let view = CoinsView()
view.title = "fableon_claimable_coins".localized
view.title = "synthreel_claimable_coins".localized
view.coins = 0
return view
}()
@ -66,7 +65,7 @@ class SRCoinPackHeaderView: UIView {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .regular)
label.textColor = .white.withAlphaComponent(0.5)
label.text = "fableon_active_refills".localized + ": "
label.text = "synthreel_active_refills".localized + ": "
return label
}()
@ -81,52 +80,111 @@ class SRCoinPackHeaderView: UIView {
}()
private lazy var claimButton: UIButton = {
let config = UIButton.Configuration.plain()
let button = SRGradientbutton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in
self?.clickClaimButton?()
}))
button.isEnabled = false
button.layer.cornerRadius = 24
button.layer.masksToBounds = true
button.fa_locations = [0, 1]
button.fa_startPoint = .init(x: 0, y: 0.5)
button.fa_endPoint = .init(x: 1, y: 0.5)
button.configurationUpdateHandler = { [weak self] button in
let button = UIButton(type: .custom)
// 使
let updateHandler: UIButton.ConfigurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
guard let button = button as? SRGradientbutton else { return }
var config = UIButton.Configuration.plain()
if button.isEnabled {
// button.fa_colors = [UIColor._53_A_2_F_1.cgColor, UIColor.C_5_DDF_5.cgColor]
button.setBackgroundImage(.claimAllHiglit, for: .normal)
let coinImage = UIImage(named: "coins_icon_03")!
//
config.background.image = .claimAllHiglit
config.background.imageContentMode = .scaleToFill
//
let coinImage = UIImage(named: "smallCoins")!
let coinText = NSTextAttachment(image: coinImage)
coinText.bounds = .init(x: 0, y: -2.5, width: coinImage.size.width, height: coinImage.size.height)
coinText.bounds = CGRect(x: 0, y: -2.5, width: coinImage.size.width, height: coinImage.size.height)
let coinAtt = AttributedString(NSAttributedString(attachment: coinText))
let textAtt = AttributedString("fableon_claim_all".localized + " ", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor.white
let textAtt = AttributedString("synthreel_claim_all".localized + " ", attributes: AttributeContainer([
.font: UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor: UIColor.white
]))
let countAtt = AttributedString(" \(self.model?.receive_coins ?? 0)".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor.white
let countAtt = AttributedString(" \(self.model?.receive_coins ?? 0)", attributes: AttributeContainer([
.font: UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor: UIColor.white
]))
button.configuration?.attributedTitle = textAtt + coinAtt + countAtt
config.attributedTitle = textAtt + coinAtt + countAtt
} else {
// button.fa_colors = [UIColor._6_D_6_D_6_D.cgColor, UIColor._6_D_6_D_6_D.cgColor]
button.setBackgroundImage(.claimAllNomarl, for: .normal)
button.configuration?.attributedTitle = AttributedString("fableon_get_refill_button_text".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor.white.withAlphaComponent(0.5)
//
config.background.image = .claimAllNomarl
config.background.imageContentMode = .scaleToFill
config.attributedTitle = AttributedString("synthreel_get_refill_button_text".localized, attributes: AttributeContainer([
.font: UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor: UIColor.white.withAlphaComponent(0.5)
]))
}
//
config.contentInsets = NSDirectionalEdgeInsets(top: 12, leading: 20, bottom: 12, trailing: 20)
//
button.configuration = config
}
//
button.configurationUpdateHandler = updateHandler
//
button.addAction(UIAction(handler: { [weak self] _ in
self?.clickClaimButton?()
}), for: .touchUpInside)
//
button.isEnabled = false
button.configurationUpdateHandler?(button) //
return button
}()
// private lazy var claimButton: UIButton = {
// let config = UIButton.Configuration.plain()
// let button = UIButton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in
// self?.clickClaimButton?()
// }))
// button.isEnabled = false
// button.configurationUpdateHandler = { [weak self] button in
// guard let self = self else { return }
// guard let button = button as? UIButton else { return }
// if button.isEnabled {
//// button.fa_colors = [UIColor._53_A_2_F_1.cgColor, UIColor.C_5_DDF_5.cgColor]
// button.setBackgroundImage(.claimAllHiglit, for: .normal)
// let coinImage = UIImage(named: "smallCoins")!
// let coinText = NSTextAttachment(image: coinImage)
// coinText.bounds = .init(x: 0, y: -2.5, width: coinImage.size.width, height: coinImage.size.height)
// let coinAtt = AttributedString(NSAttributedString(attachment: coinText))
//
// let textAtt = AttributedString("synthreel_claim_all".localized + " ", attributes: AttributeContainer([
// .font : UIFont.font(ofSize: 14, weight: .bold),
// .foregroundColor : UIColor.white
// ]))
//
// let countAtt = AttributedString(" \(self.model?.receive_coins ?? 0)".localized, attributes: AttributeContainer([
// .font : UIFont.font(ofSize: 14, weight: .bold),
// .foregroundColor : UIColor.white
// ]))
//
// button.configuration?.attributedTitle = textAtt + coinAtt + countAtt
//
//
// } else {
//// button.fa_colors = [UIColor._6_D_6_D_6_D.cgColor, UIColor._6_D_6_D_6_D.cgColor]
// button.setBackgroundImage(.claimAllNomarl, for: .normal)
// button.configuration?.attributedTitle = AttributedString("synthreel_get_refill_button_text".localized, attributes: AttributeContainer([
// .font : UIFont.font(ofSize: 14, weight: .bold),
// .foregroundColor : UIColor.white.withAlphaComponent(0.5)
// ]))
// }
//
// }
// return button
// }()
override init(frame: CGRect) {
super.init(frame: frame)
@ -222,7 +280,7 @@ extension SRCoinPackHeaderView {
}()
private lazy var iconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "coins_icon_09"))
let imageView = UIImageView(image: UIImage(named: "smallCoins"))
return imageView
}()

View File

@ -1,92 +0,0 @@
//
// SRCoinPackTitleview.swift
// SynthReel
//
// Created by CSGY on 2025/11/28.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinPackTitleview: UIView {
var text: String? {
didSet {
label.text = text
}
}
var font: UIFont? {
didSet {
label.font = font
}
}
var textColors: [CGColor]? {
didSet {
label.textColors = textColors
}
}
var dotColor: UIColor? {
didSet {
dotView1.backgroundColor = dotColor
dotView2.backgroundColor = dotColor
}
}
private lazy var dotView1: UIView = {
let view = UIView()
view.layer.cornerRadius = 3
view.layer.masksToBounds = true
return view
}()
private lazy var dotView2: UIView = {
let view = UIView()
view.layer.cornerRadius = 3
view.layer.masksToBounds = true
return view
}()
private lazy var label: SRLabel = {
let label = SRLabel()
label.textStartPoint = .init(x: 0, y: 0.5)
label.textEndPoint = .init(x: 1, y: 0.5)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(dotView1)
addSubview(dotView2)
addSubview(label)
dotView1.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.left.equalToSuperview()
make.width.height.equalTo(6)
make.bottom.lessThanOrEqualToSuperview()
}
label.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.bottom.lessThanOrEqualToSuperview()
make.left.equalTo(dotView1.snp.right).offset(6)
}
dotView2.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.bottom.lessThanOrEqualToSuperview()
make.right.equalToSuperview()
make.width.height.equalTo(dotView1)
make.left.equalTo(label.snp.right).offset(6)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,114 @@
//
// SRCoinsPackBuyView.swift
// SynthReel
//
// Created by CSGY on 2025/12/4.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinsPackBuyView: UIView {
var buyFinishHandle: (() -> Void)?
var dataArr: [SRPayItem] = []
private lazy var titleView: UILabel = {
let view = UILabel()
view.textColor = .white
view.font = .font(ofSize: 14, weight: .regular)
view.text = "synthreel_weekly_refill".localized.uppercased()
return view
}()
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.itemSize = .init(width: UIScreen.width - 32, height: 84)
layout.minimumLineSpacing = 12
return layout
}()
private lazy var collectionView: SRCollectionView = {
let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.addObserver(self, forKeyPath: "contentSize", context: nil)
collectionView.register(SRPackCoinViewCell.self, forCellWithReuseIdentifier: "cell")
return collectionView
}()
deinit {
collectionView.removeObserver(self, forKeyPath: "contentSize")
}
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "contentSize" {
let height = self.collectionView.contentSize.height + 1
collectionView.snp.updateConstraints { make in
make.height.equalTo(height)
}
}
}
}
extension SRCoinsPackBuyView {
private func fa_setupLayout() {
addSubview(titleView)
addSubview(collectionView)
titleView.snp.makeConstraints { make in
make.top.equalToSuperview()
make.centerX.equalToSuperview()
}
collectionView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(30)
make.height.equalTo(1)
make.bottom.equalToSuperview()
}
}
}
//MARK: UICollectionViewDelegate UICollectionViewDataSource
extension SRCoinsPackBuyView: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRPackCoinViewCell
cell.model = self.dataArr[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataArr.count
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let model = self.dataArr[indexPath.row]
let view = SRCoinPackConfirmView()
view.model = model
view.buyFinishHandle = { [weak self] in
guard let self = self else { return }
Task {
await SRAccountManager.manager.updateUserInfo()
self.buyFinishHandle?()
}
}
view.present(in: nil)
}
}

View File

@ -0,0 +1,266 @@
//
// SRCoinsPackClaimListCell.swift
// SynthReel
//
// Created by CSGY on 2025/12/4.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinsPackClaimListCell: UICollectionViewCell {
var clickClaimButton: ((_ id: String?) -> Void)?
var model: SRCoinsPackReceiveModel? {
didSet {
coinsView1.coins = model?.week_max_total
coinsView2.coins = model?.week_remaining_total
claimButton.isEnabled = (model?.receive_coins ?? 0) > 0
claimButton.setNeedsUpdateConfiguration()
let titleAtt = NSMutableAttributedString(string: "\(model?.title ?? "")")
titleAtt.yy_color = .white
titleAtt.yy_font = .font(ofSize: 14, weight: .bold)
let day = "synthreel_d".localized
let dayAtt = NSMutableAttributedString(string: " (\(day) \(model?.day_text ?? ""))")
dayAtt.yy_color = .white
dayAtt.yy_font = .font(ofSize: 14, weight: .regular)
titleAtt.append(dayAtt)
titleLabel.attributedText = titleAtt
}
}
private lazy var bgView: UIImageView = {
let view = UIImageView()
view.image = .claimCellbg
view.contentMode = .scaleToFill
view.isUserInteractionEnabled = true
return view
}()
private lazy var bgIconImageView1 = UIImageView(image: UIImage(named: "coin_attachment_01"))
private lazy var bgIconImageView2 = UIImageView(image: UIImage(named: "coin_attachment_03"))
private lazy var bgIconImageView4 = UIImageView(image: UIImage(named: "coin_attachment_04"))
private lazy var bgIconImageView5 = UIImageView(image: UIImage(named: "coin_attachment_05"))
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
return label
}()
private lazy var lineView1 = UIImageView(image: UIImage(named: "横间隔虚线"))
private lazy var lineView2 = UIImageView(image: UIImage(named: "间隔线"))
private lazy var coinsView1: CoinsView = {
let view = CoinsView()
view.title = "synthreel_total_reward".localized
return view
}()
private lazy var coinsView2: CoinsView = {
let view = CoinsView()
view.title = "synthreel_remaining".localized
return view
}()
private lazy var claimButton: UIButton = {
var config = UIButton.Configuration.plain()
config.titleAlignment = .center
config.background.image = .coinStackBg
let button = UIButton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
self.clickClaimButton?(self.model?.id)
}))
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
if button.isEnabled {
button.configuration?.background.image = UIImage(named: "coinStackBg")
let coinImage = UIImage(named: "bigCoins")!
let coinText = NSTextAttachment(image: coinImage)
coinText.bounds = .init(x: 0, y: -2.5, width: coinImage.size.width, height: coinImage.size.height)
let coinAtt = AttributedString(NSAttributedString(attachment: coinText))
let countAtt = AttributedString(" \(self.model?.receive_coins ?? 0)", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 12, weight: .bold),
.foregroundColor : UIColor._000000.withAlphaComponent(0.5)
]))
button.configuration?.attributedTitle = AttributedString("fableon_claim".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor.white
]))
button.configuration?.attributedSubtitle = coinAtt + countAtt
} else {
button.configuration?.background.image = UIImage(named: "claimd_button_bg")
button.configuration?.attributedTitle = AttributedString("synthreel_claimd".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor.FFFFFF_40
]))
button.configuration?.attributedSubtitle = nil
}
}
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SRCoinsPackClaimListCell {
private func fa_setupLayout() {
contentView.addSubview(bgView)
bgView.addSubview(bgIconImageView5)
bgView.addSubview(bgIconImageView4)
bgView.addSubview(bgIconImageView1)
bgView.addSubview(bgIconImageView2)
bgView.addSubview(titleLabel)
bgView.addSubview(lineView1)
bgView.addSubview(lineView2)
bgView.addSubview(coinsView1)
bgView.addSubview(coinsView2)
bgView.addSubview(claimButton)
bgView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
bgIconImageView1.snp.makeConstraints { make in
make.left.top.equalToSuperview()
}
bgIconImageView2.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.right.equalToSuperview().offset(-8)
}
bgIconImageView4.snp.makeConstraints { make in
make.top.equalToSuperview()
make.centerX.equalTo(bgIconImageView2)
}
bgIconImageView5.snp.makeConstraints { make in
make.bottom.equalToSuperview()
make.left.equalToSuperview().offset(0)
}
titleLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.centerY.equalTo(self.bgView.snp.top).offset(25)
make.right.lessThanOrEqualToSuperview().offset(-12)
}
lineView1.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(48)
}
coinsView1.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.bottom.equalToSuperview().offset(-18)
}
lineView2.snp.makeConstraints { make in
make.centerY.equalTo(coinsView1)
make.left.equalTo(coinsView1.snp.right).offset(12)
}
coinsView2.snp.makeConstraints { make in
make.centerY.equalTo(coinsView1)
make.left.equalTo(lineView2.snp.right).offset(12)
}
claimButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-12)
make.centerY.equalTo(coinsView1)
make.height.equalTo(48)
make.width.equalTo(120)
}
}
}
extension SRCoinsPackClaimListCell {
class CoinsView: UIView {
var title: String? {
didSet {
titleLabel.text = title
}
}
var coins: Int? {
didSet {
coinsLabel.text = "\(coins ?? 0)"
}
}
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .bold)
label.textColor = .white
return label
}()
private lazy var iconImageView = UIImageView(image: UIImage(named: "coins_icon_03"))
private lazy var coinsLabel: UILabel = {
let label = SRLabel()
label.font = .font(ofSize: 14, weight: .bold)
label.textColors = [UIColor._4_CFFD_4.cgColor, UIColor._51_D_4_FF.cgColor]
label.textStartPoint = .init(x: 0.5, y: 0)
label.textEndPoint = .init(x: 0.5, y: 1)
label.text = "0"
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(titleLabel)
addSubview(iconImageView)
addSubview(coinsLabel)
titleLabel.snp.makeConstraints { make in
make.left.equalToSuperview()
make.top.equalToSuperview()
make.right.lessThanOrEqualToSuperview()
}
iconImageView.snp.makeConstraints { make in
make.left.equalToSuperview()
make.bottom.equalToSuperview()
make.top.equalTo(titleLabel.snp.bottom).offset(8)
}
coinsLabel.snp.makeConstraints { make in
make.centerY.equalTo(iconImageView)
make.left.equalTo(iconImageView.snp.right).offset(4)
make.right.lessThanOrEqualToSuperview()
}
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}

View File

@ -0,0 +1,106 @@
//
// SRCoinsPackClaimListView.swift
// SynthReel
//
// Created by CSGY on 2025/12/4.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinsPackClaimListView: UIView {
var clickClaimButton: ((_ id: String?) -> Void)?
var dataArr: [SRCoinsPackReceiveModel] = [] {
didSet {
collectionView.reloadData()
}
}
private lazy var titleView: UILabel = {
let view = UILabel()
view.textColor = .white
view.font = .font(ofSize: 14, weight: .regular)
view.text = "synthreel_refills".localized.uppercased()
return view
}()
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.itemSize = .init(width: UIScreen.width - 32, height: 122)
layout.minimumLineSpacing = 12
return layout
}()
private lazy var collectionView: SRCollectionView = {
let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.addObserver(self, forKeyPath: "contentSize", context: nil)
collectionView.register(SRCoinsPackClaimListCell.self, forCellWithReuseIdentifier: "cell")
return collectionView
}()
deinit {
collectionView.removeObserver(self, forKeyPath: "contentSize")
}
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "contentSize" {
let height = self.collectionView.contentSize.height + 1
collectionView.snp.updateConstraints { make in
make.height.equalTo(height)
}
}
}
}
extension SRCoinsPackClaimListView {
private func fa_setupLayout() {
addSubview(titleView)
addSubview(collectionView)
titleView.snp.makeConstraints { make in
make.top.equalToSuperview()
make.centerX.equalToSuperview()
}
collectionView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(30)
make.height.equalTo(1)
make.bottom.equalToSuperview()
}
}
}
//MARK: UICollectionViewDelegate UICollectionViewDataSource
extension SRCoinsPackClaimListView: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRCoinsPackClaimListCell
cell.model = self.dataArr[indexPath.row]
cell.clickClaimButton = { [weak self] id in
self?.clickClaimButton?(id)
}
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataArr.count
}
}

View File

@ -81,8 +81,6 @@ class SREpSelectorCell: UICollectionViewCell {
make.right.top.equalToSuperview().inset(4)
make.width.height.equalTo(12)
}
}

View File

@ -36,6 +36,12 @@ class SRShortDetailPlayerCell: JXPlayerListCell {
await self?.sr_viewModel?.handleUnlockVideo()
}
}
view.adUnlockButton = { [weak self] in
Task {
await self?.sr_viewModel?.handleAdUnlockVideo()
}
}
return view
}()

View File

@ -0,0 +1,320 @@
//
// SRVideoRechargeView.swift
// SynthReel
//
// Created by CSGY on 2025/12/5.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRVideoRechargeView: SRPanModalContentView {
var buyFinishHandle: (() -> Void)?
var didDismissHandle: (() -> Void)?
var model: SRPayDateModel? {
didSet {
self.stackView.sr_removeAllArrangedSubview()
self.vipView.dataArr = model?.list_sub_vip ?? []
self.coinsView.setDataArr(model?.list_coins ?? [])
if let sort = model?.sort, sort.count > 0 {
sort.forEach {
if $0 == .vip, model?.list_sub_vip?.isEmpty == false {
self.stackView.addArrangedSubview(self.vipView)
} else if $0 == .coin, model?.list_coins?.isEmpty == false {
self.stackView.addArrangedSubview(self.coinsView)
}
}
} else {
if model?.list_sub_vip?.isEmpty == false {
self.stackView.addArrangedSubview(self.vipView)
}
if model?.list_coins?.isEmpty == false {
self.stackView.addArrangedSubview(self.coinsView)
}
}
self.stackView.addArrangedSubview(self.tipView)
self.setNeedsLayoutUpdate()
}
}
var videoInfo: SRVideoInfoModel? {
didSet {
self.coinsView.videoId = videoInfo?.short_play_video_id
self.coinsView.shortPlayId = videoInfo?.short_play_id
self.vipView.videoId = videoInfo?.short_play_video_id
self.vipView.shortPlayId = videoInfo?.short_play_id
videoCoinsView.coins = videoInfo?.coins ?? 0
self.requestRestore()
}
}
private lazy var closeButton: UIButton = {
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
// FAStatAPI.requestEventStat(orderCode: nil, shortPlayId: self.videoInfo?.short_play_id, videoId: self.videoInfo?.short_play_video_id, eventKey: .payTemplateDialog, errorMsg: nil, otherParamenters: [
// "event_name" : "pay cancel"
// ])
Task {
await self.dismiss(animated: true)
}
self.didDismissHandle?()
}))
button.setImage(UIImage(named: "Close"), for: .normal)
return button
}()
private lazy var scrollView: SRScrollView = {
let scrollView = SRScrollView()
// scrollView.clipsToBounds = false
return scrollView
}()
private lazy var stackView: UIStackView = {
let view = UIStackView()
view.axis = .vertical
view.spacing = 18
return view
}()
private lazy var coinsView: SRStoreCoinsView = {
let view = SRStoreCoinsView()
view.buyFinishHandle = { [weak self] in
self?.buyFinishHandle?()
Task {
await self?.dismiss(animated: true)
}
}
return view
}()
private lazy var vipView: SRStoreVipView = {
let view = SRStoreVipView()
view.buyFinishHandle = { [weak self] in
self?.buyFinishHandle?()
Task {
await self?.dismiss(animated: true)
}
}
return view
}()
private lazy var videoCoinsView: SRVideoRechargeView.CoinsView = {
let view = SRVideoRechargeView.CoinsView()
view.title = "synthreel_price".localized + ":"
return view
}()
private lazy var totalCoinsView: SRVideoRechargeView.CoinsView = {
let view = SRVideoRechargeView.CoinsView()
view.title = "synthreel_balance".localized + ":"
view.coins = SRLogin.manager.userInfo?.totalCoins ?? 0
return view
}()
private lazy var tipView: UIView = {
let view = UIView()
view.addSubview(tipTitleLabel)
view.addSubview(tipTextLabel)
tipTitleLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(7)
make.left.equalToSuperview().offset(16)
}
tipTextLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.right.lessThanOrEqualToSuperview().offset(-16)
make.top.equalTo(tipTitleLabel.snp.bottom).offset(4)
make.bottom.equalToSuperview()
}
return view
}()
private lazy var tipTitleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .medium)
label.textColor = .white
label.text = "synthreel_tips".localized
return label
}()
private lazy var tipTextLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 10, weight: .regular)
label.textColor = .white
label.text = "store_tips".localized
label.numberOfLines = 0
return label
}()
deinit {
NotificationCenter.default.removeObserver(self)
}
override init(frame: CGRect) {
super.init(frame: frame)
NotificationCenter.default.addObserver(self, selector: #selector(userInfoUpdateNotification), name: SRLogin.userInfoUpdateNotification, object: nil)
self.contentHeight = UIScreen.height - UIScreen.safeTop
// self.backgroundColor = ._000000.withAlphaComponent(0.6)
self.backgroundColor = .clear
self.mainScrollView = self.scrollView
fa_setupLayout()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func userInfoUpdateNotification() {
totalCoinsView.coins = SRLogin.manager.userInfo?.totalCoins ?? 0
}
override func present(in view: UIView?) {
super.present(in: view)
// self.hw_contentView.sr_addEffectView(style: .dark)
// self.hw_contentView.sr_setRoundedCorner(topLeft: 24, topRight: 24, bottomLeft: 0, bottomRight: 0)
}
override func allowsTapBackgroundToDismiss() -> Bool {
return false
}
}
extension SRVideoRechargeView {
private func fa_setupLayout() {
addSubview(closeButton)
addSubview(videoCoinsView)
addSubview(totalCoinsView)
// addSubview(titleLabel)
addSubview(scrollView)
scrollView.addSubview(stackView)
closeButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-16)
make.top.equalToSuperview().offset(12)
}
videoCoinsView.snp.makeConstraints { make in
make.centerY.equalTo(closeButton)
make.left.equalToSuperview().offset(16)
}
totalCoinsView.snp.makeConstraints { make in
make.centerY.equalTo(closeButton)
make.left.equalTo(videoCoinsView.snp.right).offset(15)
}
// titleLabel.snp.makeConstraints { make in
// make.left.equalToSuperview().offset(16)
// make.top.equalToSuperview().offset(39)
// }
scrollView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalToSuperview().offset(74)
make.bottom.equalToSuperview()
}
stackView.snp.makeConstraints { make in
make.left.centerX.equalToSuperview()
make.top.equalToSuperview()
make.bottom.equalToSuperview().offset(-(UIScreen.safeBottom + 10))
}
}
}
extension SRVideoRechargeView {
@objc private func requestRestore() {
guard let shortPlayId = self.videoInfo?.short_play_id, let videoId = self.videoInfo?.short_play_video_id else { return }
SRIapManager.manager.restore(isLoding: false, shortPlayId: shortPlayId, videoId: videoId) { [weak self] isFinish, buyType in
if isFinish {
Task {
await SRLogin.manager.requestUserInfo(completer: nil)
self?.buyFinishHandle?()
await self?.dismiss(animated: true)
}
}
}
}
}
extension SRVideoRechargeView {
class CoinsView: UIView {
var title: String? {
didSet {
titleLabel.text = title
}
}
var coins: Int = 0 {
didSet {
coinsLabel.text = "\(coins)"
}
}
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 14, weight: .regular)
label.textColor = .white
return label
}()
private lazy var iconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "bigCoins"))
return imageView
}()
private lazy var coinsLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 14, weight: .bold)
label.textColor = .white
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(titleLabel)
addSubview(iconImageView)
addSubview(coinsLabel)
titleLabel.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.bottom.lessThanOrEqualToSuperview()
make.left.equalToSuperview()
}
iconImageView.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.bottom.lessThanOrEqualToSuperview()
make.left.equalTo(titleLabel.snp.right).offset(6)
}
coinsLabel.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.bottom.lessThanOrEqualToSuperview()
make.left.equalTo(iconImageView.snp.right).offset(4)
make.right.equalToSuperview()
}
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}

View File

@ -19,6 +19,8 @@ class SRShortPlayerViewModel: JXPlayerListViewModel {
var recommandList: [SRShortModel] = []
private var payDataRequest: SRPayDataRequest?
///
private(set) var isShowRecommand = false
private var recommandTimer: Timer?
@ -87,9 +89,9 @@ class SRShortPlayerViewModel: JXPlayerListViewModel {
case .noPlay:
SRToast.show(text: "buy_fail_toast_01".localized)
case .notEnough:
break
//广
// self.openRechargeView()
self.openRechargeView()
break
default: break
}
@ -139,7 +141,71 @@ extension SRShortPlayerViewModel {
}
}
}
@MainActor
func handleAdUnlockVideo() async {
}
func openRechargeView() {
guard let videoInfo = self.currentCell?.model as? SRVideoInfoModel else { return }
guard self.popView == nil else { return }
self.payDataRequest = SRPayDataRequest()
if let model = SRIapManager.manager.payDateModel {
self._openRechargeView(model, videoInfo)
self.payDataRequest?.requestProducts(isLoding: false) { [weak self] model in
guard let self = self else { return }
guard let model = model else { return }
if let view = self.popView as? SRVideoRechargeView {
view.model = model
}
}
} else {
self.payDataRequest?.requestProducts(isLoding: true) { [weak self] model in
guard let self = self else { return }
guard let model = model else { return }
self._openRechargeView(model, videoInfo)
}
}
}
private func _openRechargeView(_ model: SRPayDateModel, _ videoInfo: SRVideoInfoModel) {
guard self.popView == nil else { return }
Task {
await SRStatAPI.requestEventStat(orderCode: nil, shortPlayId: videoInfo.short_play_id, videoId: videoInfo.short_play_video_id, eventKey: .payTemplateDialog, errorMsg: nil, otherParamenters: [
"event_name" : "pay open"
])
}
// if model.pay_mode == 1 {
// _openNewRechargeView(model, videoInfo)
// } else {
_openOldRechargeView(model, videoInfo)
// }
}
private func _openOldRechargeView(_ model: SRPayDateModel, _ videoInfo: SRVideoInfoModel) {
Task {
await SRStatAPI.requestEventStat(orderCode: nil, shortPlayId: videoInfo.short_play_id, videoId: videoInfo.short_play_video_id, eventKey: .payTemplateDialog, errorMsg: nil, otherParamenters: [
"event_name" : "pay open"
])
}
let view = SRVideoRechargeView()
view.model = model
view.videoInfo = videoInfo
view.buyFinishHandle = { [weak self] in
guard let self = self else { return }
// self.requestDetailData(indexPath: self.currentIndexPath, completer: nil)
}
view.didDismissHandle = { [weak self] in
guard let self = self else { return }
// self._showVipRetainAlert(videoInfo)
}
view.present(in: nil)
self.popView = view
}
@objc private func handleRecommandTimer() {
self.isShowRecommand = true

View File

@ -0,0 +1,18 @@
//
// SRIapVerifyModel.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import SmartCodable
struct SRIapVerifyModel: SmartCodable {
var money: String?
var status: String?
var is_backhaul: String?
var code: String?
}

View File

@ -0,0 +1,25 @@
//
// SRPayAlertModel.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import SmartCodable
class SRPayAlertModel: NSObject, SmartCodable {
required override init() {
super.init()
}
var coins_modal_easy_close: Bool?
var info: SRPayItem?
var forced_recharge: Bool?
var close_label: String?
}

View File

@ -0,0 +1,168 @@
//
// SRPayDateModel.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import SmartCodable
import StoreKit
class SRPayDateModel: NSObject, SmartCodable {
required override init() {
super.init()
}
enum SortName: String, SmartCaseDefaultable {
case coin = "list_coins"
case vip = "list_sub_vip"
}
var list_coins: [SRPayItem]?
var list_sub_vip: [SRPayItem]?
var list_sub_coins: [SRPayItem]?
var sort: [SortName]?
///0: 1
var pay_mode: Int?
///0: 1
var show_type: Int?
}
class SRPayItem: NSObject, SmartCodable {
required override init() {
super.init()
}
enum VipTypeKey: String, SmartCaseDefaultable {
case week = "week"
case month = "month"
case quarter = "quarter"
case year = "year"
}
enum SizeType: String, SmartCaseDefaultable {
case big = "big"
case small = "small"
}
var id: String?
var backhaul_price: String?
var coins: Int?
var status: String?
var price: String?
var send_coins: Int?
var buy_type: SRStoreAPI.BuyType?
var origin_price: String?
var vip_type: String?
var vip_type_key: VipTypeKey?
var sort: String?
var fa_description: String?
var brief: String?
var title: String?
var auto_sub: String?
var send_coin_ttl: Int?
var size: SizeType?
var ios_template_id: String?
///
var corner_marker: String?
///
var platform: String?
///
var currency: String?
var ext_info: SRPayExtInfo?
///0 1 2
var discount_type: Int?
@IgnoredKey
var product: SKProduct?
///
var introductionaryOffer: SKProductDiscount? {
return product?.introductoryPrice
}
///
var promotionalOffers: [SKProductDiscount]? {
return product?.discounts
}
static func mappingForKey() -> [SmartKeyTransformer]? {
return [
CodingKeys.fa_description <--- ["description"]
]
}
func getTimeString() -> String? {
switch self.vip_type_key {
case .week:
return "w_complex".localized
case .month:
return "m_complex".localized
case .quarter:
return "q_complex".localized
case .year:
return "Y_complex".localized
default:
return nil
}
}
func getVipTitle() -> String? {
switch self.vip_type_key {
case .week:
return "synthreel_weekly_vip".localized
case .month:
return "synthreel_monthly_vip".localized
case .quarter:
return "synthreel_quarterly_vip".localized
case .year:
return "synthreel_yearly_vip".localized
default:
return nil
}
}
}
class SRPayExtInfo: NSObject, SmartCodable {
required override init() {
super.init()
}
var extra_day_coins: Int?
var receive_coins_rate: String?
var max_total_coins: Int?
var sub_coins_txt_list: [String]?
}

View File

@ -0,0 +1,8 @@
//
// Untitled.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//

View File

@ -0,0 +1,222 @@
//
// SRStoreController.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRStoreController: SRViewController {
private var payRequest: SRPayDataRequest?
///
private var payDataModel = SRIapManager.manager.payDateModel
private lazy var scrollView: SRScrollView = {
let scrollView = SRScrollView()
return scrollView
}()
private lazy var stackView: UIStackView = {
let view = UIStackView()
view.axis = .vertical
view.spacing = 18
return view
}()
private lazy var coinsView: SRStoreCoinsView = {
let view = SRStoreCoinsView()
// view.buyFinishHandle = { [weak self] in
// self?.buyFinish()
// }
return view
}()
private lazy var vipView: SRStoreVipView = {
let view = SRStoreVipView()
// view.buyFinishHandle = { [weak self] in
// self?.buyFinish()
// }
return view
}()
private lazy var tipView: UIView = {
let view = UIView()
view.addSubview(tipTitleLabel)
view.addSubview(tipTextLabel)
tipTitleLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(7)
make.left.equalToSuperview().offset(16)
}
tipTextLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.right.lessThanOrEqualToSuperview().offset(-16)
make.top.equalTo(tipTitleLabel.snp.bottom).offset(4)
make.bottom.equalToSuperview()
}
return view
}()
private lazy var tipTitleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .medium)
label.textColor = .white
label.text = "synthreel_tips".localized
return label
}()
private lazy var tipTextLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 10, weight: .regular)
label.textColor = .white
label.text = "store_tips".localized
label.numberOfLines = 0
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "synthreel_store".localized
let barButtonItem = UIBarButtonItem(title: "synthreel_restore".localized, style: .plain, target: self, action: #selector(handleRestore))
barButtonItem.tintColor = .white
self.navigationItem.rightBarButtonItem = barButtonItem
fa_setupLayout()
updateLayout()
requestPayData()
SRIapManager.manager.restore(isLoding: false) { [weak self] isFinish, buyType in
if isFinish {
Task{
await SRAccountManager.manager.updateUserInfo()
self?.buyFinish()
}
}
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
// fa_setNavigationStyle()
}
private func buyFinish() {
self.requestPayData()
}
@objc private func handleRestore() {
SRIapManager.manager.restore { [weak self] isFinish, buyType in
if isFinish {
Task{
await SRAccountManager.manager.updateUserInfo()
self?.buyFinish()
}
}
}
}
func updateLayout() {
self.stackView.sr_removeAllArrangedSubview()
guard let model = self.payDataModel else { return }
// if model.pay_mode == 1 {
// self.addCoinsView()
// } else {
// }
if let sort = model.sort, sort.count > 0 {
sort.forEach {
if $0 == .vip {
self.addVipView()
} else if $0 == .coin {
self.addCoinsView()
}
}
} else {
self.addVipView()
self.addCoinsView()
}
self.stackView.addArrangedSubview(tipView)
}
private func addCoinsView() {
guard let model = self.payDataModel else { return }
var newList: [SRPayItem] = []
// if model.show_type == 1, model.pay_mode == 1 {
// if let list = model.list_coins, list.count > 0 {
// list.forEach {
// if $0.buy_type == .subCoins {
// newList.append($0)
// }
// }
// }
// } else {
// }
if let list = model.list_coins, list.count > 0 {
newList = list
}
if newList.count > 0 {
coinsView.setDataArr(newList)
self.stackView.addArrangedSubview(coinsView)
}
}
private func addVipView() {
guard let list = payDataModel?.list_sub_vip else { return }
guard list.count > 0 else { return }
self.vipView.dataArr = list
self.stackView.addArrangedSubview(self.vipView)
}
}
extension SRStoreController {
private func fa_setupLayout() {
view.addSubview(scrollView)
// scrollView.addSubview(titleLabel)
scrollView.addSubview(stackView)
scrollView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
}
stackView.snp.makeConstraints { make in
make.left.centerX.equalToSuperview()
make.top.equalToSuperview().offset(29)
make.bottom.equalToSuperview().offset(-(UIScreen.safeBottom + 10))
}
}
}
extension SRStoreController {
private func requestPayData() {
payRequest = SRPayDataRequest()
payRequest?.requestProducts { [weak self] model in
guard let self = self else { return }
guard let model = model else { return }
self.payDataModel = model
self.updateLayout()
}
}
}

View File

@ -0,0 +1,183 @@
//
// SRBigCoinViewCell.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRBigCoinViewCell: SRStoreCoinsCell {
// MARK: - UI Elements
private let centerContainer = UIView() //
private lazy var bgImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "hot_cell_back")
imageView.contentMode = .scaleToFill
imageView.clipsToBounds = true
imageView.isUserInteractionEnabled = true
return imageView
}()
private lazy var badgeImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "hotBadge")
imageView.contentMode = .scaleToFill
return imageView
}()
private lazy var badgeLabel: UILabel = {
let label = UILabel()
label.font = UIFont.boldSystemFont(ofSize: 10)
label.textColor = UIColor(hexString:"#FFAF1A")
label.textAlignment = .center
return label
}()
private lazy var coinImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "bigCoins")
imageView.contentMode = .scaleAspectFit
return imageView
}()
private lazy var coinLabel: UILabel = {
let label = UILabel()
label.font = UIFont.boldSystemFont(ofSize: 20)
label.textColor = UIColor(hexString: "#FF9F05")
label.textAlignment = .center
return label
}()
private lazy var bonusLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 12)
label.textColor = UIColor(hexString: "#FFD400")
label.textAlignment = .center
return label
}()
private lazy var priceLabel: UILabel = {
let label = UILabel()
label.font = UIFont.boldSystemFont(ofSize: 17)
label.textColor = .white
label.textAlignment = .center
return label
}()
// MARK: - Properties
override var model: SRPayItem? {
didSet {
updateUI()
}
}
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
setupConstraints()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupUI()
setupConstraints()
}
// MARK: - Setup
private func setupUI() {
contentView.addSubview(bgImageView)
contentView.addSubview(badgeImageView)
badgeImageView.addSubview(badgeLabel)
// + +
contentView.addSubview(centerContainer)
centerContainer.addSubview(coinImageView)
centerContainer.addSubview(coinLabel)
centerContainer.addSubview(bonusLabel)
contentView.addSubview(priceLabel)
}
private func setupConstraints() {
//
bgImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
//
badgeImageView.snp.makeConstraints { make in
make.top.right.equalToSuperview().inset(3)
}
badgeLabel.snp.makeConstraints { make in
make.center.equalToSuperview()
}
//
centerContainer.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(19)
}
//
coinImageView.snp.makeConstraints { make in
make.left.top.bottom.equalToSuperview()
make.size.equalTo(CGSize(width: 32, height: 25))
}
//
coinLabel.snp.makeConstraints { make in
make.centerY.equalTo(coinImageView)
make.left.equalTo(coinImageView.snp.right).offset(0)
}
//
bonusLabel.snp.makeConstraints { make in
make.centerY.equalTo(coinImageView)
make.left.equalTo(coinLabel.snp.right).offset(0)
make.right.equalToSuperview()
}
//
priceLabel.snp.makeConstraints { make in
make.bottom.equalToSuperview().offset(-8)
make.centerX.equalToSuperview()
}
}
// MARK: - Update UI
private func updateUI() {
let coins = model?.coins ?? 0
let sendCoins = model?.send_coins ?? 0
coinLabel.text = "\(coins)"
priceLabel.text = (model?.currency ?? "") + " " + (model?.price ?? "")
if sendCoins > 0 {
bonusLabel.isHidden = false
badgeImageView.isHidden = false
bonusLabel.text = "+\(sendCoins) "
let ratio = String(format: "%.0f", CGFloat(sendCoins) / CGFloat(coins) * 100)
badgeLabel.text = "+\(ratio)%"
} else {
bonusLabel.isHidden = true
badgeImageView.isHidden = true
}
}
}

View File

@ -0,0 +1,202 @@
//
// SRPackCoinViewCell.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRPackCoinViewCell: SRStoreCoinsCell {
override var model: SRPayItem? {
didSet {
coinsLabel.text = "\(model?.ext_info?.max_total_coins ?? 0)"
if let text = model?.ext_info?.receive_coins_rate, !text.isEmpty {
ratioLabel.text = text
ratioBgView.isHidden = false
} else {
ratioBgView.isHidden = true
}
priceView.setNeedsUpdateConfiguration()
}
}
private lazy var bgImageview: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "coinPackCell"))
return imageView
}()
private lazy var moreCoinsImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "moreCoins"))
return imageView
}()
private lazy var iconImageview: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "bigCoins"))
return imageView
}()
private lazy var stackImageview: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "coinStackBg"))
return imageView
}()
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 14, weight: .init(700))
label.textColor = .white
label.text = "synthreel_weekly_refill".localized
return label
}()
private lazy var coinsLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 18, weight: .init(900))
label.textColor = .white
return label
}()
private lazy var ratioBgView: UIView = {
let view = UIView()
view.backgroundColor = .white.withAlphaComponent(0.15)
view.layer.cornerRadius = 4
view.layer.masksToBounds = true
return view
}()
private lazy var ratioLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .regular)
label.textColor = .white
return label
}()
private lazy var priceView: UIButton = {
var config = UIButton.Configuration.plain()
config.titleAlignment = .center
config.titlePadding = 0
config.contentInsets = .init(top: 0, leading: 10, bottom: 0, trailing: 10)
config.background.image = .coinStackBg
let button = UIButton(configuration: config)
button.isUserInteractionEnabled = false
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
let currency = self.model?.currency ?? ""
let timeText = model?.getTimeString() ?? ""
let oldPrice = self.model?.price ?? ""
var discountPrice: String? = nil
if self.model?.discount_type == 1, let introductoryPrice = self.model?.introductionaryOffer {
discountPrice = introductoryPrice.price.stringValue
} else if self.model?.discount_type == 2, let discount = self.model?.promotionalOffers?.first {
discountPrice = discount.price.stringValue
}
if let discountPrice = discountPrice {
let priceString = AttributedString("\(currency)\(discountPrice)", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 18, weight: .bold),
.foregroundColor : UIColor(hexString: "#FFE600")
]))
button.configuration?.attributedTitle = priceString
var subtitle = AttributedString("\(currency)\(oldPrice)", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 12, weight: .regular),
.foregroundColor : UIColor.white.withAlphaComponent(0.05),
.strikethroughStyle: NSUnderlineStyle.double.rawValue,
.strikethroughColor: UIColor.white.withAlphaComponent(0.05)
]))
button.configuration?.attributedSubtitle = subtitle
} else {
button.configuration?.attributedTitle = AttributedString("\(currency)\(oldPrice)", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 18, weight: .bold),
.foregroundColor : UIColor(hexString: "#FFE600")
]))
button.configuration?.attributedSubtitle = AttributedString("/\(timeText)", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 12, weight: .regular),
.foregroundColor : UIColor.black.withAlphaComponent(0.5)
]))
}
}
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SRPackCoinViewCell {
private func fa_setupLayout() {
contentView.addSubview(bgImageview)
bgImageview.addSubview(moreCoinsImageView)
bgImageview.addSubview(iconImageview)
bgImageview.addSubview(titleLabel)
bgImageview.addSubview(coinsLabel)
bgImageview.addSubview(ratioBgView)
ratioBgView.addSubview(ratioLabel)
bgImageview.addSubview(priceView)
bgImageview.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
titleLabel.snp.makeConstraints { make in
make.top.equalTo(6)
make.left.equalTo(14)
}
moreCoinsImageView.snp.makeConstraints { make in
make.top.equalTo(titleLabel.snp.bottom).offset(9)
make.left.equalTo(11)
make.size.equalTo(CGSizeMake(54, 31))
}
iconImageview.snp.makeConstraints { make in
make.left.equalTo(moreCoinsImageView.snp.right)
make.centerY.equalTo(moreCoinsImageView)
}
coinsLabel.snp.makeConstraints { make in
make.centerY.equalTo(iconImageview)
make.left.equalTo(iconImageview.snp.right)
}
ratioBgView.snp.makeConstraints { make in
make.left.equalTo(coinsLabel.snp.right).offset(4)
make.height.equalTo(18)
make.centerY.equalTo(moreCoinsImageView)
}
ratioLabel.snp.makeConstraints { make in
make.center.equalToSuperview()
make.left.equalToSuperview().offset(8)
}
priceView.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-12)
make.bottom.equalTo(-8)
make.height.equalTo(48)
make.width.greaterThanOrEqualTo(88)
}
}
}

View File

@ -0,0 +1,182 @@
//
// SRSmallCoinViewCell.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRSmallCoinViewCell: SRStoreCoinsCell {
// MARK: - UI Elements
private let centerContainer = UIView() //
private lazy var bgImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "samllCellBg")
imageView.contentMode = .scaleToFill
imageView.clipsToBounds = true
imageView.isUserInteractionEnabled = true
return imageView
}()
private lazy var badgeImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "samllBadge")
imageView.contentMode = .scaleToFill
return imageView
}()
private lazy var badgeLabel: UILabel = {
let label = UILabel()
label.font = UIFont.boldSystemFont(ofSize: 10)
label.textColor = UIColor(hexString:"#FFAF1A")
label.textAlignment = .center
return label
}()
private lazy var coinImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "smallCoins")
imageView.contentMode = .scaleAspectFit
return imageView
}()
private lazy var coinLabel: UILabel = {
let label = UILabel()
label.font = UIFont.boldSystemFont(ofSize: 20)
label.textColor = UIColor(hexString: "#FF9F05")
label.textAlignment = .center
return label
}()
private lazy var bonusLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 12)
label.textColor = UIColor(hexString: "#FFD400")
label.textAlignment = .center
return label
}()
private lazy var priceLabel: UILabel = {
let label = UILabel()
label.font = UIFont.boldSystemFont(ofSize: 17)
label.textColor = .white
label.textAlignment = .center
return label
}()
// MARK: - Properties
override var model: SRPayItem? {
didSet {
updateUI()
}
}
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
setupConstraints()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupUI()
setupConstraints()
}
// MARK: - Setup
private func setupUI() {
contentView.addSubview(bgImageView)
contentView.addSubview(badgeImageView)
badgeImageView.addSubview(badgeLabel)
// + +
contentView.addSubview(centerContainer)
centerContainer.addSubview(coinImageView)
centerContainer.addSubview(coinLabel)
centerContainer.addSubview(bonusLabel)
contentView.addSubview(priceLabel)
}
private func setupConstraints() {
//
bgImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
//
badgeImageView.snp.makeConstraints { make in
make.top.right.equalToSuperview().inset(3)
}
badgeLabel.snp.makeConstraints { make in
make.center.equalToSuperview()
}
//
centerContainer.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(19)
}
//
coinImageView.snp.makeConstraints { make in
make.left.top.bottom.equalToSuperview()
make.size.equalTo(CGSize(width: 32, height: 25))
}
//
coinLabel.snp.makeConstraints { make in
make.centerY.equalTo(coinImageView)
make.left.equalTo(coinImageView.snp.right).offset(0)
}
//
bonusLabel.snp.makeConstraints { make in
make.centerY.equalTo(coinImageView)
make.left.equalTo(coinLabel.snp.right).offset(0)
make.right.equalToSuperview()
}
//
priceLabel.snp.makeConstraints { make in
make.bottom.equalToSuperview().offset(-8)
make.centerX.equalToSuperview()
}
}
// MARK: - Update UI
private func updateUI() {
let coins = model?.coins ?? 0
let sendCoins = model?.send_coins ?? 0
coinLabel.text = "\(coins)"
priceLabel.text = (model?.currency ?? "") + " " + (model?.price ?? "")
if sendCoins > 0 {
bonusLabel.isHidden = false
badgeImageView.isHidden = false
bonusLabel.text = "+\(sendCoins) "
let ratio = String(format: "%.0f", CGFloat(sendCoins) / CGFloat(coins) * 100)
badgeLabel.text = "+\(ratio)%"
} else {
bonusLabel.isHidden = true
badgeImageView.isHidden = true
}
}
}

View File

@ -0,0 +1,24 @@
//
// SRStoreCoinsCell.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRStoreCoinsCell: UICollectionViewCell {
var model: SRPayItem?
var sr_isSelected = false
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,233 @@
//
// SRStoreCoinsView.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRStoreCoinsView: UIView {
var shortPlayId: String?
var videoId: String?
var buyFinishHandle: (() -> Void)?
private lazy var dataArr: [[SRPayItem]] = []
private var selectedIndexPath: IndexPath?
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .medium)
label.textColor = .white
label.text = "store_title_1".localized
return label
}()
private lazy var collectionViewLayout: UICollectionViewCompositionalLayout = {
let config = UICollectionViewCompositionalLayoutConfiguration()
config.interSectionSpacing = 10
let layout = UICollectionViewCompositionalLayout { [weak self] section, _ in
guard let self = self else { return nil}
guard let model = dataArr[section].first else { return nil }
if model.buy_type == .subCoins {
return self.coinsBigLayoutSection()
} else if model.size == .big {
return self.bigLayoutSection()
} else {
return self.smallLayoutSection()
}
}
layout.configuration = config
return layout
}()
private lazy var collectionView: SRCollectionView = {
let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.clipsToBounds = false
collectionView.isScrollEnabled = false
collectionView.register(SRBigCoinViewCell.self, forCellWithReuseIdentifier: "SRBigCoinViewCell")
collectionView.register(SRSmallCoinViewCell.self, forCellWithReuseIdentifier: "SRSmallCoinViewCell")
collectionView.register(SRPackCoinViewCell.self, forCellWithReuseIdentifier: "SRPackCoinViewCell")
collectionView.addObserver(self, forKeyPath: "contentSize", context: nil)
return collectionView
}()
deinit {
collectionView.removeObserver(self, forKeyPath: "contentSize")
}
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "contentSize" {
let height = self.collectionView.contentSize.height + 1
self.collectionView.snp.updateConstraints { make in
make.height.equalTo(height)
}
}
}
func setDataArr(_ arr: [SRPayItem]) {
self.dataArr.removeAll()
var bigArr: [SRPayItem] = []
var smallArr: [SRPayItem] = []
var coinPackArr: [SRPayItem] = []
arr.forEach {
if $0.buy_type == .subCoins {
coinPackArr.append($0)
} else if $0.size == .big {
bigArr.append($0)
} else {
smallArr.append($0)
}
}
if bigArr.count > 0 {
self.dataArr.append(bigArr)
}
if coinPackArr.count > 0 {
self.dataArr.append(coinPackArr)
}
if smallArr.count > 0 {
self.dataArr.append(smallArr)
}
self.collectionView.reloadData()
}
}
extension SRStoreCoinsView {
private func fa_setupLayout() {
addSubview(collectionView)
addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(0)
make.left.equalTo(12)
}
collectionView.snp.makeConstraints { make in
make.top.equalTo(titleLabel.snp.bottom).offset(10)
make.left.right.bottom.equalToSuperview()
make.height.equalTo(1)
}
}
}
extension SRStoreCoinsView {
private func bigLayoutSection() -> NSCollectionLayoutSection {
let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(1)))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(80)), subitems: [item])
group.interItemSpacing = .fixed(12)
let layoutSection = NSCollectionLayoutSection(group: group)
layoutSection.interGroupSpacing = 12
layoutSection.contentInsets = .init(top: 0, leading: 15, bottom: 0, trailing: 15)
return layoutSection
}
private func smallLayoutSection() -> NSCollectionLayoutSection {
let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1 / 3), heightDimension: .fractionalHeight(1)))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(121)), subitems: [item])
group.interItemSpacing = .fixed(8)
let layoutSection = NSCollectionLayoutSection(group: group)
layoutSection.interGroupSpacing = 10
layoutSection.contentInsets = .init(top: 0, leading: 16, bottom: 0, trailing: 16)
return layoutSection
}
private func coinsBigLayoutSection() -> NSCollectionLayoutSection {
let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(84)), subitems: [item])
let layoutSection = NSCollectionLayoutSection(group: group)
layoutSection.interGroupSpacing = 10
layoutSection.contentInsets = .init(top: 0, leading: 16, bottom: 0, trailing: 16)
return layoutSection
}
}
//MARK: UICollectionViewDelegate UICollectionViewDataSource
extension SRStoreCoinsView: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let model = dataArr[indexPath.section][indexPath.row]
var identifier = "SRBigCoinViewCell"
if model.buy_type == .subCoins {
identifier = "SRPackCoinViewCell"
} else if model.size == .small {
identifier = "SRSmallCoinViewCell"
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! SRStoreCoinsCell
cell.model = model
cell.sr_isSelected = selectedIndexPath == indexPath
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataArr[section].count
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return dataArr.count
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let model = dataArr[indexPath.section][indexPath.row]
self.selectedIndexPath = indexPath
collectionView.reloadData()
if model.buy_type == .subCoins {
let view = SRCoinPackConfirmView()
view.shortPlayId = self.shortPlayId
view.videoId = self.videoId
view.model = model
view.buyFinishHandle = { [weak self] in
guard let self = self else { return }
Task {
await SRAccountManager.manager.updateUserInfo()
self.buyFinishHandle?()
}
}
view.present(in: nil)
} else {
SRIapManager.manager.start(model: model, shortPlayId: self.shortPlayId, videoId: self.videoId) { [weak self] finish in
guard let self = self else { return }
if finish {
Task {
await SRAccountManager.manager.updateUserInfo()
self.buyFinishHandle?()
}
}
}
}
}
}

View File

@ -0,0 +1,206 @@
//
// SRStoreVipCell.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRStoreVipCell: UICollectionViewCell {
var item:SRPayItem? {
didSet {
nameLabel.text = item?.brief
desLabel.text = item?.fa_description
// coinTimeLabel.text = item?.auto_sub
if let coins = item?.send_coins, coins > 0 {
sendCoinLabel.text = "+\("synthreel_extra".localized) \(coins)"
sendCoinLabel.isHidden = false
coinImageView.isHidden = false
} else {
sendCoinLabel.isHidden = true
coinImageView.isHidden = true
}
let currency = item?.currency ?? ""
let oldPrice = item?.price ?? ""
let time = "/\(item?.getTimeString() ?? "")"
var offerPrice: String?
if item?.discount_type == 1, let offer = item?.introductionaryOffer {
offerPrice = offer.price.stringValue
} else if item?.discount_type == 2, let offer = item?.promotionalOffers?.first {
offerPrice = offer.price.stringValue
}
if let price = offerPrice {
let priceString = NSMutableAttributedString(string: currency + price)
priceString.yy_font = .font(ofSize: 28, weight: .init(800))
priceLabel.attributedText = priceString
let oldPriceStr = NSMutableAttributedString(string: oldPrice)
oldPriceStr.yy_strikethroughColor = discountLabel.textColor
oldPriceStr.yy_strikethroughStyle = .double
discountLabel.attributedText = oldPriceStr
discountLabel.isHidden = false
} else {
let priceString = NSMutableAttributedString(string: currency + oldPrice)
priceString.yy_font = .font(ofSize: 28, weight: .init(800))
let timeString = NSMutableAttributedString(string: time)
timeString.yy_font = .font(ofSize: 14, weight: .medium)
timeString.yy_baselineOffset = 1
priceString.append(timeString)
priceLabel.attributedText = priceString
discountLabel.isHidden = true
}
switch item?.vip_type_key {
case .week:
bgView.image = UIImage(named: "vip_bg_image_week")
priceLabel.textColor = UIColor(hexString: "#4CFFD4")
case .month:
bgView.image = UIImage(named: "vip_bg_image_month")
priceLabel.textColor = UIColor(hexString: "#00EEFF")
case .quarter:
bgView.image = UIImage(named: "vip_bg_image_quarter")
priceLabel.textColor = UIColor(hexString: "#FFC21B")
case .year:
bgView.image = UIImage(named: "vip_bg_image_year")
priceLabel.textColor = UIColor(hexString: "#FF721B")
default:
break
}
coinTimeLabel.textColor = sendCoinLabel.textColor
}
}
private lazy var bgView: UIImageView = {
let view = UIImageView()
return view
}()
private lazy var nameLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 14, weight: .init(800))
label.textColor = .white
return label
}()
private lazy var priceLabel: SRLabel = {
let label = SRLabel()
label.textStartPoint = .init(x: 0, y: 0.5)
label.textEndPoint = .init(x: 1, y: 0.5)
return label
}()
private lazy var discountLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 16, weight: .medium)
label.textColor = .white.withAlphaComponent(0.03)
return label
}()
private lazy var desLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .regular)
label.textColor = .white.withAlphaComponent(0.4)
return label
}()
private lazy var sendCoinLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .medium)
return label
}()
private lazy var coinImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "coins_icon_08"))
return imageView
}()
private lazy var coinTimeLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 10, weight: .regular)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SRStoreVipCell {
private func fa_setupLayout() {
contentView.addSubview(bgView)
contentView.addSubview(nameLabel)
bgView.addSubview(priceLabel)
bgView.addSubview(desLabel)
bgView.addSubview(discountLabel)
// subBgView.addSubview(sendCoinLabel)
// subBgView.addSubview(coinImageView)
// subBgView.addSubview(coinTimeLabel)
bgView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
}
nameLabel.snp.makeConstraints { make in
make.top.equalTo(11)
make.left.equalToSuperview().offset(17)
}
priceLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(17)
make.top.equalTo(nameLabel.snp.bottom).offset(5)
}
desLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(17)
make.bottom.equalToSuperview().offset(-10)
}
discountLabel.snp.makeConstraints { make in
make.left.equalTo(priceLabel.snp.right).offset(4)
make.centerY.equalTo(priceLabel).offset(3)
}
// sendCoinLabel.snp.makeConstraints { make in
// make.centerY.equalToSuperview()
// make.left.equalToSuperview().offset(12)
// }
//
// coinImageView.snp.makeConstraints { make in
// make.centerY.equalToSuperview()
// make.left.equalTo(sendCoinLabel.snp.right).offset(3)
// }
//
// coinTimeLabel.snp.makeConstraints { make in
// make.centerY.equalToSuperview()
// make.right.equalToSuperview().offset(-11)
// }
}
}

View File

@ -0,0 +1,133 @@
//
// SRStoreVipView.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRStoreVipView: UIView {
var dataArr: [SRPayItem] = [] {
didSet {
collectionView.reloadData()
}
}
var shortPlayId: String?
var videoId: String?
var buyFinishHandle: (() -> Void)?
private lazy var viptitleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .medium)
label.textColor = .white
label.text = "synthreel_viptitle".localized
return label
}()
private lazy var collectionViewLayout: UICollectionViewCompositionalLayout = {
let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(90)), subitems: [item])
let layoutSection = NSCollectionLayoutSection(group: group)
layoutSection.interGroupSpacing = 10
layoutSection.contentInsets = .init(top: 0, leading: 16, bottom: 0, trailing: 16)
let config = UICollectionViewCompositionalLayoutConfiguration()
let layout = UICollectionViewCompositionalLayout(section: layoutSection)
layout.configuration = config
return layout
}()
private lazy var collectionView: SRCollectionView = {
let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.isScrollEnabled = false
collectionView.addObserver(self, forKeyPath: "contentSize", context: nil)
collectionView.register(SRStoreVipCell.self, forCellWithReuseIdentifier: "cell")
return collectionView
}()
deinit {
collectionView.removeObserver(self, forKeyPath: "contentSize")
}
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "contentSize" {
let height = self.collectionView.contentSize.height + 1
self.collectionView.snp.updateConstraints { make in
make.height.equalTo(height)
}
}
}
}
extension SRStoreVipView {
private func fa_setupLayout() {
addSubview(viptitleLabel)
addSubview(collectionView)
viptitleLabel.snp.makeConstraints { make in
make.top.equalToSuperview()
make.left.equalTo(12)
}
collectionView.snp.makeConstraints { make in
make.top.equalTo(viptitleLabel.snp.bottom).offset(10)
make.left.right.bottom.equalToSuperview()
make.height.equalTo(1)
}
}
}
//MARK: UICollectionViewDelegate UICollectionViewDataSource
extension SRStoreVipView: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRStoreVipCell
cell.item = self.dataArr[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataArr.count
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let model = self.dataArr[indexPath.row]
SRIapManager.manager.start(model: model, shortPlayId: self.shortPlayId, videoId: self.videoId) { [weak self] finish in
guard let self = self else { return }
if finish {
Task {
await SRAccountManager.manager.updateUserInfo()
self.buyFinishHandle?()
}
}
}
}
}

View File

@ -0,0 +1,127 @@
//
// SRUserListViewController.swift
// SynthReel
//
// Created by CSGY on 2025/12/02.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import JXPagingView
class SRUserListViewController: SRViewController {
private lazy var dataArr: [SRUserSettingModel] = []
private lazy var tableViewHeaderView: SRUserListHeaderView = {
let view = SRUserListHeaderView(frame: .init(x: 0, y: 0, width: UIScreen.width, height: 44))
return view
}()
private lazy var tableView: SRTableView = {
let tableView = SRTableView(frame: .zero, style: .plain)
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = 60
tableView.separatorStyle = .none
tableView.backgroundColor = .clear
tableView.register(SRUserSettingCell.self, forCellReuseIdentifier: "cell")
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
sr_setupLayout()
self.backgroundImageView.image = UIImage(named: "settingback")
self.backgroundImageView.snp.remakeConstraints { make in
make.bottom.top.equalTo(self.tableView)
make.left.right.equalToSuperview().inset(15)
}
setDataArr()
}
// override func listScrollView() -> UIScrollView {
// return self.tableView
// }
//
// override func listViewDidScrollCallback(callback: @escaping (UIScrollView) -> ()) {
// self.didScrollCallback = callback
// }
}
extension SRUserListViewController {
private func sr_setupLayout() {
tableView.tableHeaderView = self.tableViewHeaderView
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.top.bottom.equalToSuperview()
make.left.right.equalToSuperview().inset(15)
}
}
private func setDataArr() {
let arr = [
SRUserSettingModel(type: .feedback, name: "synthreel_feedback".localized, icon: UIImage(named: "icon_feedback")),
SRUserSettingModel(type: .login, name: "synthreel_login".localized, icon: UIImage(named: "icon_login")),
SRUserSettingModel(type: .deleteAccount, name: "synthreel_delet_account".localized, icon: UIImage(named: "icon_delet")),
SRUserSettingModel(type: .about, name: "synthreel_about_us".localized, icon: UIImage(named: "icon_about")),
SRUserSettingModel(type: .privacyPolicy, name: "synthreel_privacy_policy".localized, icon: UIImage(named: "icon_privacy")),
SRUserSettingModel(type: .userAgreement, name: "synthreel_user_agreement".localized, icon: UIImage(named: "icon_user")),
]
self.dataArr = arr
self.tableView.reloadData()
}
}
//MARK: UITableViewDelegate, UITableViewDataSource
extension SRUserListViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SRUserSettingCell
cell.model = dataArr[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataArr.count
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.didScrollCallback?(scrollView)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let model = dataArr[indexPath.row]
switch model.type {
case .about:
let aboutvc = SRAboutUsController ();
self.navigationController?.pushViewController(aboutvc, animated: true)
break
case .login:
let loginview = SRUserLoginView()
loginview.present(in: nil)
break
case .feedback:
let vc = SRFeedBackController()
self.navigationController?.pushViewController(vc, animated: true)
break
case .privacyPolicy:
if let url = URL(string: SRWebBaseURL + "/private") {
UIApplication.shared.open(url)
}
break
case .userAgreement:
if let url = URL(string: SRWebBaseURL + "/user_policy") {
UIApplication.shared.open(url)
}
break
default: break
}
}
}

View File

@ -7,174 +7,109 @@
//
import UIKit
import JXPagingView
class SRUserViewController: SRViewController {
private lazy var dataArr: [SRUserSettingModel] = []
lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
return layout
private lazy var headerView: SRUserHeaderView = {
let view = SRUserHeaderView()
return view
}()
lazy var collectionView: SRCollectionView = {
let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.showsVerticalScrollIndicator = false
collectionView.showsHorizontalScrollIndicator = false
collectionView.register(SRUserSettingCell.self, forCellWithReuseIdentifier: "cell")
collectionView.register(SRUserTopCell.self, forCellWithReuseIdentifier: "topcell")
collectionView.register(SRCoinPackCell.self, forCellWithReuseIdentifier: "coincell")
collectionView.register(SRUserRewardCell.self, forCellWithReuseIdentifier: "rewardCell")
collectionView.sr_addRefreshHeader { [weak self] in
Task {
await SRAccountManager.manager.updateUserInfo()
self?.collectionView.reloadData()
}
private lazy var listView: SRUserListViewController = SRUserListViewController()
private lazy var pagingView: JXPagingView = {
let view = JXPagingView(delegate: self)
view.mainTableView.backgroundColor = .clear
view.listContainerView.backgroundColor = .clear
view.listContainerView.listCellBackgroundColor = .clear
view.mainTableView.sr_addRefreshHeader { [weak self] in
self?.handleHeaderRefresh()
}
return collectionView
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
set_ui()
setDataArr()
headerView.coinPackClickCallback = { [weak self] in
let vc = SRCoinPackController()
self?.navigationController?.pushViewController(vc, animated: true)
}
headerView.vipClickCallback = { [weak self] in
let vc = SRStoreController()
self?.navigationController?.pushViewController(vc, animated: true)
}
headerView.rewardClickCallback = { [weak self] in
let vc = SRRewardController ()
vc.theme = "theme_15"
self?.navigationController?.pushViewController(vc, animated: true)
}
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: true)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
Task {
await SRAccountManager.manager.updateUserInfo()
self.headerView.updateUserInfo()
}
}
func handleHeaderRefresh() {
Task {
await SRAccountManager.manager.updateUserInfo()
self.headerView.updateUserInfo()
self.pagingView.mainTableView.sr_endHeaderRefreshing()
}
}
}
extension SRUserViewController {
func set_ui (){
view.addSubview(collectionView)
view.addSubview(pagingView)
collectionView.snp.makeConstraints { make in
make.edges.equalToSuperview()
pagingView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview()
}
}
}
//MARK: UICollectionViewDelegate UICollectionViewDataSource
extension SRUserViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == 0{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "topcell", for: indexPath) as! SRUserTopCell
return cell
} else if indexPath.section == 1 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "coincell", for: indexPath) as! SRCoinPackCell
return cell
}else if indexPath.section == 2 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "rewardCell", for: indexPath) as! SRUserRewardCell
return cell
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRUserSettingCell
cell.model = self.dataArr[indexPath.row];
return cell
//MARK: JXPagingViewDelegate
extension SRUserViewController: JXPagingViewDelegate {
func pagingView(_ pagingView: JXPagingView, initListAtIndex index: Int) -> any JXPagingViewListViewDelegate {
return listView
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0 || section == 1{
return 1
}
return self.dataArr.count
func tableHeaderViewHeight(in pagingView: JXPagingView) -> Int {
return Int(ceil(headerView.contentHeight))
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
func tableHeaderView(in pagingView: JXPagingView) -> UIView {
return headerView
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.didScrollCallback?(scrollView)
func heightForPinSectionHeader(in pagingView: JXPagingView) -> Int {
return 0
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.section == 0 { return }
if indexPath.section == 1 {
let vc = SRCoinPackController()
self.navigationController?.pushViewController(vc, animated: true)
return
}
let model = dataArr[indexPath.row]
switch model.type {
case .about:
let aboutvc = SRAboutUsController ();
self.navigationController?.pushViewController(aboutvc, animated: true)
break
case .login:
let loginview = SRUserLoginView()
loginview.present(in: nil)
break
case .feedback:
// let vc = SRFeedBackController ();
// self.navigationController?.pushViewController(vc, animated: true)
let vc = SRRewardController ()
vc.theme = "theme_15"
self.navigationController?.pushViewController(vc, animated: true)
break
case .privacyPolicy:
if let url = URL(string: SRWebBaseURL + "/private") {
UIApplication.shared.open(url)
}
break
case .userAgreement:
if let url = URL(string: SRWebBaseURL + "/user_policy") {
UIApplication.shared.open(url)
}
break
default: break
}
func viewForPinSectionHeader(in pagingView: JXPagingView) -> UIView {
return UIView()
}
}
extension SRUserViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if indexPath.section == 0 { return CGSizeMake(UIScreen.width, 278) }
if indexPath.section == 1 { return CGSizeMake(UIScreen.width - 30, 70) }
return CGSizeMake(UIScreen.width, 60)
}
}
extension SRFavoritesViewController {
private func requestDataArr(page: Int) async {
if let dataArr = await SRHomeApi.requestFavoritesData(page: page) {
if page == 1 {
self.dataArr.removeAll()
}
self.dataArr += dataArr
self.page = page
self.collectionView.reloadData()
}
}
}
extension SRUserViewController {
private func setDataArr() {
let arr = [
SRUserSettingModel(type: .feedback, name: "synthreel_feedback".localized, icon: UIImage(named: "icon_feedback")),
SRUserSettingModel(type: .login, name: "synthreel_login".localized, icon: UIImage(named: "icon_login")),
SRUserSettingModel(type: .about, name: "synthreel_about_us".localized, icon: UIImage(named: "icon_about")),
SRUserSettingModel(type: .privacyPolicy, name: "synthreel_privacy_policy".localized, icon: UIImage(named: "icon_privacy")),
SRUserSettingModel(type: .userAgreement, name: "synthreel_user_agreement".localized, icon: UIImage(named: "icon_user")),
]
self.dataArr = arr
self.collectionView.reloadData()
func numberOfLists(in pagingView: JXPagingView) -> Int {
return 1
}
}

View File

@ -8,7 +8,7 @@
import UIKit
class SRCoinPackCell: UICollectionViewCell {
class SRCoinPackCell: UIView {
lazy var bgImageView = UIImageView.init(image: UIImage(named: "userCoinCell"))
@ -41,9 +41,9 @@ class SRCoinPackCell: UICollectionViewCell {
extension SRCoinPackCell {
func sr_setupUI() {
contentView.addSubview(bgImageView)
contentView.addSubview(titleLabel)
contentView.addSubview(calimLabel)
addSubview(bgImageView)
addSubview(titleLabel)
addSubview(calimLabel)
bgImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()

View File

@ -0,0 +1,339 @@
//
// SRUserHeaderView.swift
// SynthReel
//
// Created by CSGY on 2025/12/02.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRUserHeaderView: UIView {
lazy var topImageView : UIImageView = {
let image = UIImageView(image: .bg)
image.contentMode = .scaleToFill
return image
}()
lazy var iconBgImageView = UIImageView(image: UIImage(named: "头像框"))
lazy var titleLabel : UILabel = {
let label = UILabel()
label.text = "Visitor"
label.textColor = UIColor._51_D_4_FF
label.font = .font(ofSize: 18, weight: .regular)
label.backgroundColor = UIColor.init(patternImage: .userNameBg)
label.textAlignment = .center
return label
}()
lazy var userId : UILabel = {
let label = UILabel()
label.textColor = UIColor.AAEAFF_0_5
label.font = .font(ofSize: 13, weight: .regular)
label.text = SRAccountManager.manager.userInfo?.customer_id
return label
}()
lazy var iconImageView: SRImageView = {
let imageView = SRImageView()
imageView.isUserInteractionEnabled = true
imageView.image = .logo
return imageView
}()
lazy var coinImageView = UIImageView(image: UIImage(named: "coinBg"))
lazy var sendCoinImageView = UIImageView(image: UIImage(named: "sendCoinBg"))
lazy var coinTitleLabel: UILabel = {
let label = UILabel()
label.text = "Coins"
label.textColor = .white
label.font = .font(ofSize: 12, weight: .regular)
return label
}()
lazy var coinValueLabel: UILabel = {
let label = UILabel()
label.text = "0"
label.textColor = UIColor.srGreen
label.font = .font(ofSize: 16, weight: .bold)
return label
}()
lazy var donateTitleLabel: UILabel = {
let label = UILabel()
label.text = "Donate"
label.textColor = .white
label.font = .font(ofSize: 12, weight: .regular)
return label
}()
lazy var donateValueLabel: UILabel = {
let label = UILabel()
label.text = "0"
label.textColor = UIColor.srBlue
label.font = .font(ofSize: 16, weight: .bold)
return label
}()
lazy var coinPackView = SRCoinPackCell()
lazy var vipView = SRUserVIPView()
lazy var rewardView = SRRewardView()
var contentHeight: CGFloat = 580
var clickCallback: (() -> Void)? // Kept for backward compatibility or remove if unused
var coinPackClickCallback: (() -> Void)?
var vipClickCallback: (() -> Void)?
var rewardClickCallback: (() -> Void)?
override init(frame: CGRect) {
super.init(frame: frame)
sr_setupUI()
let coinPackTap = UITapGestureRecognizer(target: self, action: #selector(coinPackTapAction))
coinPackView.isUserInteractionEnabled = true
coinPackView.addGestureRecognizer(coinPackTap)
let vipTap = UITapGestureRecognizer(target: self, action: #selector(vipTapAction))
vipView.isUserInteractionEnabled = true
vipView.addGestureRecognizer(vipTap)
let rewardTap = UITapGestureRecognizer(target: self, action: #selector(reardTapAction))
rewardView.isUserInteractionEnabled = true
rewardView.addGestureRecognizer(rewardTap)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func coinPackTapAction() {
coinPackClickCallback?()
}
@objc func vipTapAction() {
vipClickCallback?()
}
@objc func reardTapAction() {
rewardClickCallback?()
}
override func layoutSubviews() {
super.layoutSubviews()
DispatchQueue.main.async {
self.iconBgImageView.applyHexagonMask(cornerRadius: 2, rotationAngle: .pi / 6)
}
}
func updateUserInfo() {
self.userId.text = SRAccountManager.manager.userInfo?.customer_id
self.coinValueLabel.text = "\(SRAccountManager.manager.userInfo?.totalCoins ?? 0)"
self.donateValueLabel.text = "\(SRAccountManager.manager.userInfo?.totalCoins ?? 0)" // Using same value for now as placeholder
}
}
extension SRUserHeaderView {
func sr_setupUI (){
addSubview(topImageView)
addSubview(iconBgImageView)
iconBgImageView.addSubview(iconImageView)
addSubview(titleLabel)
addSubview(userId)
addSubview(coinImageView)
addSubview(sendCoinImageView)
coinImageView.addSubview(coinTitleLabel)
coinImageView.addSubview(coinValueLabel)
sendCoinImageView.addSubview(donateTitleLabel)
sendCoinImageView.addSubview(donateValueLabel)
addSubview(vipView)
addSubview(coinPackView)
addSubview(rewardView)
topImageView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
make.height.equalTo(278)
}
iconBgImageView.snp.makeConstraints { make in
make.top.equalTo(UIScreen.safeTop + 12);
make.centerX.equalToSuperview()
make.size.equalTo(CGSizeMake(86 , 96))
}
iconImageView.snp.makeConstraints { make in
make.edges.equalTo(UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))
}
titleLabel.snp.makeConstraints { make in
make.top.equalTo(204);
make.centerX.equalToSuperview()
make.size.equalTo(CGSizeMake(136, 26))
}
userId.snp.makeConstraints { make in
make.top.equalTo(titleLabel.snp_bottomMargin).offset(7)
make.centerX.equalToSuperview()
make.height.equalTo(26)
}
coinImageView.snp.makeConstraints { make in
make.left.equalTo(15)
make.top.equalTo(topImageView.snp.bottom).offset(0)
make.height.equalTo(40)
make.right.equalTo(self.snp.centerX).offset(-5)
}
sendCoinImageView.snp.makeConstraints { make in
make.right.equalTo(-15)
make.top.equalTo(coinImageView)
make.height.equalTo(40)
make.left.equalTo(self.snp.centerX).offset(5)
}
coinTitleLabel.snp.makeConstraints { make in
make.left.equalTo(coinImageView.snp.left).offset(52)
make.centerY.equalToSuperview()
}
coinValueLabel.snp.makeConstraints { make in
make.left.equalTo(coinTitleLabel.snp.right).offset(24)
make.centerY.equalToSuperview()
}
donateTitleLabel.snp.makeConstraints { make in
make.left.equalTo(sendCoinImageView.snp.left).offset(52)
make.centerY.equalToSuperview()
}
donateValueLabel.snp.makeConstraints { make in
make.left.equalTo(donateTitleLabel.snp.right).offset(24)
make.centerY.equalToSuperview()
}
coinPackView.snp.makeConstraints { make in
make.left.right.equalToSuperview().inset(15)
make.height.equalTo(70)
make.top.equalTo(vipView.snp.bottom).offset(10)
}
vipView.snp.makeConstraints { make in
make.left.right.equalToSuperview().inset(15)
make.height.equalTo(48)
make.top.equalTo(sendCoinImageView.snp.bottom).offset(20)
}
rewardView.snp.makeConstraints { make in
make.left.right.equalToSuperview().inset(15)
make.height.equalTo(72)
make.top.equalTo(coinPackView.snp.bottom).offset(20)
}
}
}
class SRUserVIPView: UIView {
lazy var bgImageView = UIImageView(image: UIImage(named: "userStoreBg"))
lazy var iconImageView = UIImageView(image: UIImage(named: "vicon"))
lazy var arrowImageView = UIImageView(image: UIImage(named: "storeArrow"))
lazy var titleLabel: UILabel = {
let label = UILabel()
label.text = "Top-Up Coins & VIP"
label.textColor = .white
label.font = .font(ofSize: 16, weight: .bold)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupUI() {
addSubview(bgImageView)
addSubview(iconImageView)
addSubview(titleLabel)
addSubview(arrowImageView)
bgImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
iconImageView.snp.makeConstraints { make in
make.left.equalTo(15)
make.centerY.equalToSuperview()
make.size.equalTo(CGSize(width: 24, height: 24))
}
titleLabel.snp.makeConstraints { make in
make.left.equalTo(iconImageView.snp.right).offset(10)
make.centerY.equalToSuperview()
}
arrowImageView.snp.makeConstraints { make in
make.right.equalTo(-15)
make.centerY.equalToSuperview()
}
}
}
class SRRewardView: UIView {
lazy var bgImageView = UIImageView(image: UIImage(named: "rewardsbg"))
lazy var arrowImageView = UIImageView(image: UIImage(named: "rewardArrow"))
lazy var titleLabel: UILabel = {
let label = UILabel()
label.text = "Premium Rewards for Tasks"
label.textColor = .white
label.numberOfLines = 2;
label.font = .font(ofSize: 16, weight: .bold)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupUI() {
addSubview(bgImageView)
addSubview(titleLabel)
addSubview(arrowImageView)
bgImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
titleLabel.snp.makeConstraints { make in
make.left.equalTo(110)
make.centerY.equalToSuperview()
make.right.equalTo(arrowImageView.snp.left).offset(-20)
}
arrowImageView.snp.makeConstraints { make in
make.right.equalTo(-12)
make.size.equalTo(CGSizeMake(40, 40))
make.centerY.equalToSuperview()
}
}
}

View File

@ -0,0 +1,49 @@
//
// SRUserListHeaderView.swift
// SynthReel
//
// Created by CSGY on 2025/12/02.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRUserListHeaderView: UIView {
lazy var iconImageView = UIImageView.init(image: UIImage(named: "Union"))
lazy var titleLabel : UILabel = {
let label = UILabel()
label.textColor = UIColor.white
label.font = .font(ofSize: 16, weight: .init(500))
label.text = "Settings & More".localized
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
sr_setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SRUserListHeaderView {
func sr_setupUI() {
addSubview(iconImageView)
addSubview(titleLabel)
iconImageView.snp.makeConstraints { make in
make.left.equalTo(15)
make.centerY.equalToSuperview()
make.size.equalTo(CGSizeMake(33, 16))
}
titleLabel.snp.makeConstraints { make in
make.left.equalTo(iconImageView.snp.left).offset(15)
make.centerY.equalToSuperview()
}
}
}

View File

@ -8,7 +8,7 @@
import UIKit
class SRUserSettingCell: UICollectionViewCell {
class SRUserSettingCell: SRTableViewCell {
var model : SRUserSettingModel? {
didSet {
@ -28,9 +28,8 @@ class SRUserSettingCell: UICollectionViewCell {
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
sr_setupUI()
}

View File

@ -0,0 +1,292 @@
//
// SRIapManager.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import StoreKit
class SRIapManager {
typealias CompletionHandler = ((_ finish: Bool) -> Void)
///
static let IAPPrefix = "univico"
static let manager = SRIapManager()
///
private var completionHandler: CompletionHandler?
private var shortPlayId: String?
private var videoId: String?
private lazy var iapManager: SRPayManger = {
let manager = SRPayManger()
manager.delegate = self
return manager
}()
private var orderCode: String?
private var payId: String?
///
private var payRequest: SRPayDataRequest?
var payDateModel: SRPayDateModel?
///使
///
private var waitRestoreModel: SRWaitRestoreModel? = UserDefaults.sr_object(forKey: kSRWaitRestoreIAPDefaultsKey, as: SRWaitRestoreModel.self)
///
func start(model: SRPayItem, shortPlayId: String? = nil, videoId: String? = nil, hudShowView: UIView? = nil, handler: CompletionHandler? = nil) {
if let _ = self.waitRestoreModel {
SRToast.show(text: "pay_error_6".localized)
handler?(false)
return
}
guard let payId = model.id else {
handler?(false)
return
}
self.shortPlayId = shortPlayId
self.videoId = videoId
self.completionHandler = handler
self.waitRestoreModel = SRWaitRestoreModel()
self.waitRestoreModel?.buyType = model.buy_type
let productId = getProductId(templateId: model.ios_template_id) ?? ""
var isDiscount = false
var identifierDiscount: String? = nil
if model.discount_type == 1, let _ = model.introductionaryOffer {
isDiscount = true
} else if model.discount_type == 2, let discount = model.promotionalOffers?.first {
isDiscount = true
identifierDiscount = discount.identifier
}
SRHud.show(containerView: hudShowView)
Task {
let orderModel = await SynthReel.SRStoreAPI.requestCreateOrder(payId: payId, shortPlayId: shortPlayId ?? "0", videoId: videoId ?? "0", isDiscount: isDiscount, identifierDiscount: identifierDiscount)
guard let orderModel = orderModel else {
SRHud.dismiss()
self.waitRestoreModel = nil
self.completionHandler?(false)
self.clean()
return
}
self.orderCode = orderModel.order_code
self.payId = payId
self.waitRestoreModel?.payId = payId
self.waitRestoreModel?.orderCode = orderModel.order_code
var discount: SKPaymentDiscount? = nil
if let identifierDiscount = identifierDiscount,
let signData = orderModel.discount?.sign_data,
let keyIdentifier = signData.keyIdentifier,
let nonce = UUID(uuidString: signData.nonce ?? ""),
let signature = signData.signature,
let timestamp = signData.timestamp
{
discount = SKPaymentDiscount(identifier: identifierDiscount,
keyIdentifier: keyIdentifier,
nonce: nonce,
signature: signature,
timestamp: NSNumber(value: timestamp))
}
self.iapManager.start(productId: productId, orderId: self.orderCode ?? "", applicationUsername: orderModel.discount?.sign_data?.applicationUsername, discount: discount)
}
}
func restore(isLoding: Bool = true, shortPlayId: String? = nil, videoId: String? = nil, completer: ((_ isFinish: Bool, _ buyType: SRStoreAPI.BuyType?) -> Void)?) {
let buyType = self.waitRestoreModel?.buyType
guard let waitRestoreModel = self.waitRestoreModel,
let orderCode = waitRestoreModel.orderCode,
let payId = waitRestoreModel.payId,
let receipt = waitRestoreModel.receipt,
let transactionId = waitRestoreModel.transactionId
else {
if isLoding {
SRToast.show(text: "pay_error_5".localized)
}
completer?(false, buyType)
return
}
if isLoding {
SRHud.show()
}
let verifyData = self.getVerifyOrderParameters(orderCode: orderCode, payId: payId, transactionId: transactionId, purchaseToken: receipt)
let statParamenters: [String : Any] = [
"type" : isLoding ? "manual" : "auto",
"pay_data" : verifyData.toJsonString() ?? ""
]
Task {
await SRStatAPI.requestEventStat(orderCode: orderCode, shortPlayId: shortPlayId, videoId: videoId, eventKey: .payRestore, errorMsg: "restore", otherParamenters: statParamenters)
let model = await SRStoreAPI.requestVerifyOrder(parameters: verifyData)
if model == nil {
completer?(false, buyType)
return
}
self.waitRestoreModel = nil
UserDefaults.sr_setObject(nil, forKey: kSRWaitRestoreIAPDefaultsKey)
if model?.status == "success" {
if buyType == .subVip {
SRLogin.manager.userInfo?.is_vip = true
}
if isLoding {
SRToast.show(text: "SRbleo_success".localized)
}
completer?(true, buyType)
if buyType == .subVip {
NotificationCenter.default.post(name: SRIapManager.buyVipFinishNotification, object: nil)
}
} else {
completer?(false, buyType)
}
}
}
func getProductId(templateId: String?) -> String? {
guard let templateId = templateId else { return nil }
return SRIapManager.IAPPrefix + "." + templateId
}
func clean() {
self.orderCode = nil
self.payId = nil
self.shortPlayId = nil
self.videoId = nil
self.completionHandler = nil
}
}
//MARK: JXIAPManagerDelegate
extension SRIapManager: SRPayMangerDelegate {
func jx_iapPaySuccess(productId: String, receipt: String, transactionIdentifier: String) {
guard let orderCode = self.orderCode, let payId = self.payId else {
self.waitRestoreModel = nil
self.completionHandler?(false)
self.clean()
SRHud.dismiss()
return
}
self.waitRestoreModel?.productId = productId
self.waitRestoreModel?.receipt = receipt
self.waitRestoreModel?.transactionId = transactionIdentifier
UserDefaults.sr_setObject(self.waitRestoreModel, forKey: kSRWaitRestoreIAPDefaultsKey)
#if DEBUG
let verifyData = self.getVerifyOrderParameters(orderCode: orderCode, payId: payId, transactionId: transactionIdentifier, purchaseToken: receipt)
#else
let verifyData = self.getVerifyOrderParameters(orderCode: orderCode, payId: payId, transactionId: transactionIdentifier, purchaseToken: receipt)
#endif
Task {
let model = await SRStoreAPI.requestVerifyOrder(parameters: verifyData)
SRHud.dismiss()
if model == nil {
await SRStatAPI.requestEventStat(orderCode: self.orderCode, shortPlayId: self.shortPlayId, videoId: self.videoId, eventKey: .payCallback, errorMsg: verifyData.toJsonString())
self.completionHandler?(false)
self.clean()
return
}
let buyType = self.waitRestoreModel?.buyType
self.waitRestoreModel = nil
UserDefaults.sr_setObject(nil, forKey: kSRWaitRestoreIAPDefaultsKey)
if model?.status == "success" {
if buyType == .subVip {
SRLogin.manager.userInfo?.is_vip = true
}
SRToast.show(text: "SRbleo_success".localized)
self.completionHandler?(true)
if buyType == .subVip {
NotificationCenter.default.post(name: SRIapManager.buyVipFinishNotification, object: nil)
}
} else {
SRToast.show(text: "pay_error_4".localized)
await SRStatAPI.requestEventStat(orderCode: self.orderCode, shortPlayId: self.shortPlayId, videoId: self.videoId, eventKey: .payCallback, errorMsg: verifyData.toJsonString())
self.completionHandler?(false)
}
self.clean()
}
}
func jx_iapPayFailed(productId: String, code: SRPayMangerCode, msg: String?) {
SRHud.dismiss()
if code == .noProduct {
SRToast.show(text: "pay_error_2".localized)
} else if code == .cancelled {
SRToast.show(text: "pay_error_3".localized)
}
if code == .cancelled {
Task {
await SRStatAPI.requestEventStat(orderCode: self.orderCode, shortPlayId: self.shortPlayId, videoId: self.videoId, eventKey: .payCancel, errorMsg: "user cancel")
}
} else {
Task {
await SRStatAPI.requestEventStat(orderCode: self.orderCode, shortPlayId: self.shortPlayId, videoId: self.videoId, eventKey: .payError, errorMsg: msg)
}
}
self.completionHandler?(false)
self.waitRestoreModel = nil
self.clean()
}
}
extension SRIapManager {
func getVerifyOrderParameters(orderCode: String, payId: String, transactionId: String, purchaseToken: String) -> [String : Any] {
let parameters: [String : Any] = [
"order_code" : orderCode,
"pay_setting_id" : payId,
"pkg_name" : kSRAPPBundleName,
"transaction_id": transactionId,
"purchases_token" : purchaseToken
]
return parameters
}
///
func preloadingProducts() {
SRPayManger.manager.fetchReceipt { _ in
self.payRequest = SRPayDataRequest()
self.payRequest?.requestProducts(isLoding: false, isToast: false) { model in
}
}
}
}
extension SRIapManager {
///
@objc static let buyVipFinishNotification = NSNotification.Name(rawValue: "SRIapManager.buyVipFinishNotification")
}

View File

@ -0,0 +1,37 @@
//
// SRIapOrderModel.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import SmartCodable
struct SRIapOrderModel: SmartCodable {
var code: Int?
var message: String?
var order_code: String?
var is_backhaul: String?
var money: String?
var discount: SRIapOrderDiscountModel?
}
struct SRIapOrderDiscountModel: SmartCodable {
var discount_code: String?
var is_discount: Bool?
var sign_data: SRIapOrderDiscountSign?
}
struct SRIapOrderDiscountSign: SmartCodable {
var keyIdentifier: String?
var nonce: String?
var timestamp: TimeInterval?
var applicationUsername: String?
var signature: String?
}

View File

@ -0,0 +1,133 @@
//
// SRPayDataRequest.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import StoreKit
class SRPayDataRequest: NSObject {
private var oldTemplateModel: SRPayDateModel?
private(set) var newTemplateModel: SRPayDateModel?
private var payAlertModel: SRPayAlertModel?
private var completerBlock: ((_ model: SRPayDateModel?) -> Void)?
private var payAlertBlock: ((_ model: SRPayAlertModel?) -> Void)?
private var isLoding = false
private var isToast = false
func requestProducts(isLoding: Bool = false, isToast: Bool = true, completer: @escaping ((_ model: SRPayDateModel?) -> Void)) {
self.payAlertBlock = nil
self.completerBlock = completer
self.isLoding = isLoding
self.isToast = isToast
if isLoding {
SRHud.show()
}
Task {
let model = await SRStoreAPI.requestPayTemplate()
guard let model = model else {
if isLoding {
SRHud.dismiss()
}
self.completerBlock?(nil)
return
}
self.oldTemplateModel = model
var productIdArr: [String] = []
model.list_sub_vip?.forEach { item in
productIdArr.append(SRIapManager.manager.getProductId(templateId: item.ios_template_id) ?? "")
}
model.list_coins?.forEach { item in
productIdArr.append(SRIapManager.manager.getProductId(templateId: item.ios_template_id) ?? "")
}
let set = Set(productIdArr)
let productsRequest = SKProductsRequest(productIdentifiers: set)
productsRequest.delegate = self
productsRequest.start()
}
}
}
//MARK: SKProductsRequestDelegate
extension SRPayDataRequest: SKProductsRequestDelegate {
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
if isLoding {
SRHud.dismiss()
}
let products = response.products
if let block = self.completerBlock {
guard let templateModel = self.oldTemplateModel else { return }
var newCoinList: [SRPayItem] = []
var newVipList: [SRPayItem] = []
templateModel.list_coins?.forEach { item in
let productId = SRIapManager.manager.getProductId(templateId: item.ios_template_id) ?? ""
for product in products {
if productId == product.productIdentifier {
item.price = product.price.stringValue
item.currency = product.priceLocale.currencySymbol
item.product = product
newCoinList.append(item)
break
}
}
}
templateModel.list_sub_vip?.forEach { item in
let productId = SRIapManager.manager.getProductId(templateId: item.ios_template_id) ?? ""
for product in products {
if productId == product.productIdentifier {
item.price = product.price.stringValue
item.currency = product.priceLocale.currencySymbol
item.product = product
newVipList.append(item)
break
}
}
}
templateModel.list_coins = newCoinList
templateModel.list_sub_vip = newVipList
self.newTemplateModel = templateModel
SRIapManager.manager.payDateModel = templateModel
DispatchQueue.main.async {
block(templateModel)
}
} else if let block = self.payAlertBlock {
guard let coinalertModel = self.payAlertModel else { return }
let productId = SRIapManager.manager.getProductId(templateId: coinalertModel.info?.ios_template_id) ?? ""
for product in products {
if productId == product.productIdentifier {
coinalertModel.info?.price = product.price.stringValue
coinalertModel.info?.currency = product.priceLocale.currencySymbol
coinalertModel.info?.product = product
break
}
}
DispatchQueue.main.async {
block(coinalertModel)
}
}
}
}

View File

@ -0,0 +1,50 @@
//
// SRWaitRestoreModel.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRWaitRestoreModel: NSObject, NSSecureCoding {
var orderCode: String?
var payId: String?
var productId: String?
var receipt: String?
var buyType: SRStoreAPI.BuyType?
var transactionId: String?
required override init() { }
static var supportsSecureCoding: Bool {
get {
return true
}
}
func encode(with coder: NSCoder) {
coder.encode(orderCode, forKey: "orderCode")
coder.encode(payId, forKey: "payId")
coder.encode(productId, forKey: "productId")
coder.encode(receipt, forKey: "receipt")
coder.encode(buyType?.rawValue, forKey: "buyType")
coder.encode(transactionId, forKey: "transactionId")
}
required init?(coder: NSCoder) {
super.init()
orderCode = coder.decodeObject(of: NSString.self, forKey: "orderCode") as? String
payId = coder.decodeObject(of: NSString.self, forKey: "payId") as? String
productId = coder.decodeObject(of: NSString.self, forKey: "productId") as? String
receipt = coder.decodeObject(of: NSString.self, forKey: "receipt") as? String
transactionId = coder.decodeObject(of: NSString.self, forKey: "transactionId") as? String
if let type = coder.decodeObject(of: NSString.self, forKey: "buyType") as? String {
buyType = SRStoreAPI.BuyType(rawValue: type)
}
}
}

View File

@ -0,0 +1,216 @@
//
// SRPayManger.swift
// SynthReel
//
// Created by CSGY on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import StoreKit
@objc protocol SRPayMangerDelegate {
///
@objc optional func jx_iapPaySuccess(productId: String, receipt: String, transactionIdentifier: String)
///
@objc optional func jx_iapPayFailed(productId: String, code: SRPayMangerCode, msg: String?)
///
@objc optional func iapPayRestore(productIds: [String], transactionIds: [String])
}
@objc enum SRPayMangerCode: Int {
///
case unknown
///
case cancelled
///
case noProduct
}
class SRPayManger: NSObject {
static let manager: SRPayManger = SRPayManger()
weak var delegate: SRPayMangerDelegate?
private var payment: SKPayment?
private var product: SKProduct?
private var productId: String?
private var discount: SKPaymentDiscount?
private var orderId: String?
private var applicationUsername: String?
var receiptCompletion: ((URL?) -> Void)?
override init() {
super.init()
SKPaymentQueue.default().add(self)
}
func start(productId: String, orderId: String, applicationUsername: String?, discount: SKPaymentDiscount? = nil) {
self.product = nil
self.productId = productId
self.orderId = orderId
self.discount = discount
self.applicationUsername = applicationUsername
let set = Set([productId])
let productsRequest = SKProductsRequest(productIdentifiers: set)
productsRequest.delegate = self
productsRequest.start()
}
///
private func buyProduct() {
guard let product = self.product else { return }
//
let payment = SKMutablePayment(product: product)
payment.applicationUsername = self.applicationUsername
if let discount = self.discount {
payment.paymentDiscount = discount
self.discount = nil
}
self.payment = payment
//
SKPaymentQueue.default().add(payment)
}
}
//MARK: -------------- SKProductsRequestDelegate --------------
extension SRPayManger: SKProductsRequestDelegate {
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
guard let product = response.products.first else {
DispatchQueue.main.async {
if let productId = self.productId {
self.productId = nil
self.delegate?.jx_iapPayFailed?(productId: productId, code: .noProduct, msg: nil)
}
}
return
}
self.product = product
self.buyProduct()
}
}
//MARK: -------------- SKPaymentTransactionObserver --------------
extension SRPayManger: SKPaymentTransactionObserver {
///
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .purchased:
DispatchQueue.main.async {
self.completeTransaction(transaction: transaction)
}
SKPaymentQueue.default().finishTransaction(transaction)
case .failed:
DispatchQueue.main.async {
self.failedTransaction(transaction: transaction)
}
SKPaymentQueue.default().finishTransaction(transaction)
// case .restored:
// self.restoreTransaction(transaction: transaction)
case .purchasing:
break
default:
SKPaymentQueue.default().finishTransaction(transaction)
break
}
}
}
// func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool {
// return true
// }
///
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
}
}
extension SRPayManger {
private func completeTransaction(transaction: SKPaymentTransaction) {
guard let encodeStr = getAppStoreReceipt() else { return }
guard let transactionIdentifier = transaction.transactionIdentifier else { return }
guard let productId = self.productId, productId == transaction.payment.productIdentifier else { return }
self.productId = nil
self.delegate?.jx_iapPaySuccess?(productId: productId, receipt: encodeStr, transactionIdentifier: transactionIdentifier)
}
private func failedTransaction(transaction: SKPaymentTransaction) {
let error = transaction.error as? SKError
guard let productId = self.productId else { return }
self.productId = nil
switch error?.code {
case SKError.paymentCancelled:
self.delegate?.jx_iapPayFailed?(productId: productId, code: .cancelled, msg: error?.localizedDescription)
default:
self.delegate?.jx_iapPayFailed?(productId: productId, code: .unknown, msg: error?.localizedDescription)
}
}
}
extension SRPayManger: SKRequestDelegate {
func getAppStoreReceipt() -> String? {
guard let receiptURL = Bundle.main.appStoreReceiptURL else { return nil }
let receiptData = NSData(contentsOf: receiptURL)
return receiptData?.base64EncodedString(options: .endLineWithLineFeed)
}
///
func fetchReceipt(completion: @escaping (URL?) -> Void) {
let receiptURL = Bundle.main.appStoreReceiptURL
if let url = receiptURL, FileManager.default.fileExists(atPath: url.path) {
completion(url)
return
}
let request = SKReceiptRefreshRequest()
request.delegate = self
request.start()
// delegate
self.receiptCompletion = completion
}
func requestDidFinish(_ request: SKRequest) {
let receiptURL = Bundle.main.appStoreReceiptURL
if let url = receiptURL, FileManager.default.fileExists(atPath: url.path) {
receiptCompletion?(url)
} else {
receiptCompletion?(nil)
}
}
func request(_ request: SKRequest, didFailWithError error: Error) {
print("Receipt request failed: \(error)")
receiptCompletion?(nil)
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.400",
"blue" : "0xFF",
"green" : "0xFF",
"red" : "0xFF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Union@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Union@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "dailyIocn@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "dailyIocn@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "getBg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "getBg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "itemBg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "itemBg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "receiveBg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "receiveBg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "rewardicon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "rewardicon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "coinBg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "coinBg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon_delet@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon_delet@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "moreCoins@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "moreCoins@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "rewardArrow@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "rewardArrow@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "rewardsbg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "rewardsbg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "sendCoinBg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "sendCoinBg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "settingback@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "settingback@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "storeArrow@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "storeArrow@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "userStoreBg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "userStoreBg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "vicon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "vicon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "bigCoins@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "bigCoins@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "claimCellbg@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "claimCellbg@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Some files were not shown because too many files have changed in this diff Show More