diff --git a/Podfile b/Podfile index 70aad42..ec5312e 100644 --- a/Podfile +++ b/Podfile @@ -24,6 +24,7 @@ target 'XSeri' do pod 'MJRefresh' pod 'HWPanModal' pod 'ZLPhotoBrowser' + pod 'JXIAPManager', :git => 'https://git.qinjiu8.com/zengjx/JXIAPManager.git', :tag => '0.0.3' post_install do |installer| installer.pods_project.targets.each do |target| diff --git a/XSeri.xcodeproj/project.pbxproj b/XSeri.xcodeproj/project.pbxproj index f0eb651..8c48c38 100644 --- a/XSeri.xcodeproj/project.pbxproj +++ b/XSeri.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 50DCF656E93DB15465B55F09 /* Pods_XSeri.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 161AA91CBF35A7C2C85D6649 /* Pods_XSeri.framework */; }; + F304E6102F67A9B600E9B0A6 /* XSShortDetailLockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F304E60F2F67A9B600E9B0A6 /* XSShortDetailLockView.swift */; }; + F304E6122F67D74A00E9B0A6 /* XSVideoUnlockResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = F304E6112F67D74A00E9B0A6 /* XSVideoUnlockResult.swift */; }; F347D28D2F03709200786648 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F347D2832F03709200786648 /* AppDelegate.swift */; }; F347D28E2F03709200786648 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F347D28A2F03709200786648 /* SceneDelegate.swift */; }; F347D2902F03709200786648 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F347D2842F03709200786648 /* Assets.xcassets */; }; @@ -135,6 +137,33 @@ F3B312BD2F30B0A10093B180 /* XSSearchSuggestionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3B312B82F30B0A10093B180 /* XSSearchSuggestionCell.swift */; }; F3B312BE2F30B0A10093B180 /* XSSearchResultCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3B312B92F30B0A10093B180 /* XSSearchResultCell.swift */; }; F3B312BF2F30B2000093B180 /* XSSearchHistoryHotView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3B312B52F30B2000093B180 /* XSSearchHistoryHotView.swift */; }; + F3F388D52F67DDBC001B0E15 /* XSMineWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388D42F67DDBC001B0E15 /* XSMineWalletView.swift */; }; + F3F388D72F67EDEC001B0E15 /* XSMineCoinsPackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388D62F67EDEC001B0E15 /* XSMineCoinsPackView.swift */; }; + F3F388D92F67F9E1001B0E15 /* XSMineVipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388D82F67F9E1001B0E15 /* XSMineVipView.swift */; }; + F3F388DF2F67FE6D001B0E15 /* XSStoreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388DE2F67FE6D001B0E15 /* XSStoreViewController.swift */; }; + F3F388E12F680090001B0E15 /* XSScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388E02F68008F001B0E15 /* XSScrollView.swift */; }; + F3F388E32F680316001B0E15 /* XSStoreCoinsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388E22F680316001B0E15 /* XSStoreCoinsView.swift */; }; + F3F388E52F68032A001B0E15 /* XSStoreVipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388E42F68032A001B0E15 /* XSStoreVipView.swift */; }; + F3F388E72F6805DE001B0E15 /* XSPayItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388E62F6805DE001B0E15 /* XSPayItem.swift */; }; + F3F388E92F68062F001B0E15 /* XSPayDateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388E82F68062F001B0E15 /* XSPayDateModel.swift */; }; + F3F388EC2F680CEE001B0E15 /* XSIapManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388EB2F680CEE001B0E15 /* XSIapManager.swift */; }; + F3F388EE2F68E6EB001B0E15 /* XSWaitRestoreModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388ED2F68E6EB001B0E15 /* XSWaitRestoreModel.swift */; }; + F3F388F02F68E737001B0E15 /* XSStoreAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388EF2F68E737001B0E15 /* XSStoreAPI.swift */; }; + F3F388F22F68E9BE001B0E15 /* XSIapVerifyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388F12F68E9BE001B0E15 /* XSIapVerifyModel.swift */; }; + F3F388F42F68F2CD001B0E15 /* XSIapOrderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388F32F68F2CD001B0E15 /* XSIapOrderModel.swift */; }; + F3F388F62F68F746001B0E15 /* XSPayDataRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388F52F68F746001B0E15 /* XSPayDataRequest.swift */; }; + F3F388F82F692031001B0E15 /* XSWalletViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388F72F692031001B0E15 /* XSWalletViewController.swift */; }; + F3F388FA2F69240E001B0E15 /* XSWalletHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388F92F69240E001B0E15 /* XSWalletHeaderView.swift */; }; + F3F388FC2F692CEF001B0E15 /* XSWalletCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388FB2F692CEF001B0E15 /* XSWalletCell.swift */; }; + F3F388FE2F6931D1001B0E15 /* XSConsumptionRecordsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388FD2F6931D1001B0E15 /* XSConsumptionRecordsViewController.swift */; }; + F3F389002F693293001B0E15 /* XSConsumptionRecordsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F388FF2F693293001B0E15 /* XSConsumptionRecordsCell.swift */; }; + F3F389022F693785001B0E15 /* XSBuyRecordsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389012F693785001B0E15 /* XSBuyRecordsModel.swift */; }; + F3F389042F6939C7001B0E15 /* XSOrderRecordsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389032F6939C7001B0E15 /* XSOrderRecordsViewController.swift */; }; + F3F389062F6939EE001B0E15 /* XSRewardCoinsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389052F6939EE001B0E15 /* XSRewardCoinsViewController.swift */; }; + F3F389082F6940BB001B0E15 /* XSRewardCoinsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389072F6940BB001B0E15 /* XSRewardCoinsCell.swift */; }; + F3F3890A2F694109001B0E15 /* XSSendCoinRecordModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389092F694109001B0E15 /* XSSendCoinRecordModel.swift */; }; + F3F3890C2F695055001B0E15 /* FACoinsRecordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3890B2F695055001B0E15 /* FACoinsRecordViewController.swift */; }; + F3F3890E2F695068001B0E15 /* FAVipRecordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3890D2F695068001B0E15 /* FAVipRecordViewController.swift */; }; F3F683ED2F56C380008AF250 /* XSHomeHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F683EC2F56C380008AF250 /* XSHomeHistoryView.swift */; }; /* End PBXBuildFile section */ @@ -142,6 +171,8 @@ 161AA91CBF35A7C2C85D6649 /* Pods_XSeri.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_XSeri.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3711C91B5F8043B3213F7116 /* Pods-XSeri.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-XSeri.debug.xcconfig"; path = "Target Support Files/Pods-XSeri/Pods-XSeri.debug.xcconfig"; sourceTree = ""; }; A9C8E3A3362E04C75537A56F /* Pods-XSeri.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-XSeri.release.xcconfig"; path = "Target Support Files/Pods-XSeri/Pods-XSeri.release.xcconfig"; sourceTree = ""; }; + F304E60F2F67A9B600E9B0A6 /* XSShortDetailLockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSShortDetailLockView.swift; sourceTree = ""; }; + F304E6112F67D74A00E9B0A6 /* XSVideoUnlockResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSVideoUnlockResult.swift; sourceTree = ""; }; F347D26B2F03708600786648 /* XSeri.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XSeri.app; sourceTree = BUILT_PRODUCTS_DIR; }; F347D2832F03709200786648 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; F347D2842F03709200786648 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -271,6 +302,33 @@ F3B312B62F319CBE0093B180 /* XSEmpty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSEmpty.swift; sourceTree = ""; }; F3B312B82F30B0A10093B180 /* XSSearchSuggestionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSSearchSuggestionCell.swift; sourceTree = ""; }; F3B312B92F30B0A10093B180 /* XSSearchResultCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSSearchResultCell.swift; sourceTree = ""; }; + F3F388D42F67DDBC001B0E15 /* XSMineWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSMineWalletView.swift; sourceTree = ""; }; + F3F388D62F67EDEC001B0E15 /* XSMineCoinsPackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSMineCoinsPackView.swift; sourceTree = ""; }; + F3F388D82F67F9E1001B0E15 /* XSMineVipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSMineVipView.swift; sourceTree = ""; }; + F3F388DE2F67FE6D001B0E15 /* XSStoreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSStoreViewController.swift; sourceTree = ""; }; + F3F388E02F68008F001B0E15 /* XSScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSScrollView.swift; sourceTree = ""; }; + F3F388E22F680316001B0E15 /* XSStoreCoinsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSStoreCoinsView.swift; sourceTree = ""; }; + F3F388E42F68032A001B0E15 /* XSStoreVipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSStoreVipView.swift; sourceTree = ""; }; + F3F388E62F6805DE001B0E15 /* XSPayItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSPayItem.swift; sourceTree = ""; }; + F3F388E82F68062F001B0E15 /* XSPayDateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSPayDateModel.swift; sourceTree = ""; }; + F3F388EB2F680CEE001B0E15 /* XSIapManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSIapManager.swift; sourceTree = ""; }; + F3F388ED2F68E6EB001B0E15 /* XSWaitRestoreModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSWaitRestoreModel.swift; sourceTree = ""; }; + F3F388EF2F68E737001B0E15 /* XSStoreAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSStoreAPI.swift; sourceTree = ""; }; + F3F388F12F68E9BE001B0E15 /* XSIapVerifyModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSIapVerifyModel.swift; sourceTree = ""; }; + F3F388F32F68F2CD001B0E15 /* XSIapOrderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSIapOrderModel.swift; sourceTree = ""; }; + F3F388F52F68F746001B0E15 /* XSPayDataRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSPayDataRequest.swift; sourceTree = ""; }; + F3F388F72F692031001B0E15 /* XSWalletViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSWalletViewController.swift; sourceTree = ""; }; + F3F388F92F69240E001B0E15 /* XSWalletHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSWalletHeaderView.swift; sourceTree = ""; }; + F3F388FB2F692CEF001B0E15 /* XSWalletCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSWalletCell.swift; sourceTree = ""; }; + F3F388FD2F6931D1001B0E15 /* XSConsumptionRecordsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSConsumptionRecordsViewController.swift; sourceTree = ""; }; + F3F388FF2F693293001B0E15 /* XSConsumptionRecordsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSConsumptionRecordsCell.swift; sourceTree = ""; }; + F3F389012F693785001B0E15 /* XSBuyRecordsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSBuyRecordsModel.swift; sourceTree = ""; }; + F3F389032F6939C7001B0E15 /* XSOrderRecordsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSOrderRecordsViewController.swift; sourceTree = ""; }; + F3F389052F6939EE001B0E15 /* XSRewardCoinsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSRewardCoinsViewController.swift; sourceTree = ""; }; + F3F389072F6940BB001B0E15 /* XSRewardCoinsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSRewardCoinsCell.swift; sourceTree = ""; }; + F3F389092F694109001B0E15 /* XSSendCoinRecordModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSSendCoinRecordModel.swift; sourceTree = ""; }; + F3F3890B2F695055001B0E15 /* FACoinsRecordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinsRecordViewController.swift; sourceTree = ""; }; + F3F3890D2F695068001B0E15 /* FAVipRecordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAVipRecordViewController.swift; sourceTree = ""; }; F3F683EC2F56C380008AF250 /* XSHomeHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSHomeHistoryView.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -375,6 +433,7 @@ F347D2AD2F03AE3E00786648 /* Home */, F347D3172F0A53FE00786648 /* Discover */, F347D3112F0A41FC00786648 /* Player */, + F3F388DA2F67FDFF001B0E15 /* Store */, ); path = Class; sourceTree = ""; @@ -404,6 +463,7 @@ F35547F92F4E9F0A006F28CD /* XSView.swift */, F35547FB2F4EA069006F28CD /* XSButton.swift */, F355480D2F50485E006F28CD /* XSPanModalContentView.swift */, + F3F388E02F68008F001B0E15 /* XSScrollView.swift */, ); path = View; sourceTree = ""; @@ -422,6 +482,7 @@ F347D29C2F03A5A100786648 /* Libs */ = { isa = PBXGroup; children = ( + F3F388EA2F680CC7001B0E15 /* IapManager */, F3B312B52F319CA50093B180 /* Empty */, F347D3042F0A12DB00786648 /* HUD */, F347D29E2F03A6B100786648 /* XSTool.swift */, @@ -563,6 +624,7 @@ F347D3212F0A58BE00786648 /* XSVideoAPI.swift */, F347D30D2F0A39D500786648 /* XSHomeAPI.swift */, F347D3002F0A0D7E00786648 /* XSUserAPI.swift */, + F3F388EF2F68E737001B0E15 /* XSStoreAPI.swift */, ); path = API; sourceTree = ""; @@ -593,6 +655,7 @@ F347D3132F0A429C00786648 /* XSShortModel.swift */, F347D3152F0A430300786648 /* XSVideoInfoModel.swift */, F3585C462F14EAE700EEC469 /* XSShortDetailModel.swift */, + F304E6112F67D74A00E9B0A6 /* XSVideoUnlockResult.swift */, ); path = Model; sourceTree = ""; @@ -652,6 +715,9 @@ F35548072F4FD703006F28CD /* XSMineUserInfoView.swift */, F35548052F4FD6DA006F28CD /* XSMinePlayHistoryView.swift */, F35548092F4FE99F006F28CD /* XSMinePlayHistoryCell.swift */, + F3F388D42F67DDBC001B0E15 /* XSMineWalletView.swift */, + F3F388D62F67EDEC001B0E15 /* XSMineCoinsPackView.swift */, + F3F388D82F67F9E1001B0E15 /* XSMineVipView.swift */, ); path = View; sourceTree = ""; @@ -720,6 +786,7 @@ children = ( F3585C482F14EE8D00EEC469 /* XSShortDetailPlayerCell.swift */, F3585C4A2F14FD1000EEC469 /* XSShortDetailPlayerControlView.swift */, + F304E60F2F67A9B600E9B0A6 /* XSShortDetailLockView.swift */, F3585C3E2F14C83700EEC469 /* XSPlayerEpButton.swift */, F355480F2F504D74006F28CD /* XSEpSelectorView.swift */, F35548112F5129F2006F28CD /* XSEpSelectorCell.swift */, @@ -761,6 +828,66 @@ path = Empty; sourceTree = ""; }; + F3F388DA2F67FDFF001B0E15 /* Store */ = { + isa = PBXGroup; + children = ( + F3F388DB2F67FE13001B0E15 /* Controller */, + F3F388DC2F67FE2E001B0E15 /* View */, + F3F388DD2F67FE36001B0E15 /* Model */, + ); + path = Store; + sourceTree = ""; + }; + F3F388DB2F67FE13001B0E15 /* Controller */ = { + isa = PBXGroup; + children = ( + F3F388DE2F67FE6D001B0E15 /* XSStoreViewController.swift */, + F3F388F72F692031001B0E15 /* XSWalletViewController.swift */, + F3F388FD2F6931D1001B0E15 /* XSConsumptionRecordsViewController.swift */, + F3F389052F6939EE001B0E15 /* XSRewardCoinsViewController.swift */, + F3F389032F6939C7001B0E15 /* XSOrderRecordsViewController.swift */, + F3F3890B2F695055001B0E15 /* FACoinsRecordViewController.swift */, + F3F3890D2F695068001B0E15 /* FAVipRecordViewController.swift */, + ); + path = Controller; + sourceTree = ""; + }; + F3F388DC2F67FE2E001B0E15 /* View */ = { + isa = PBXGroup; + children = ( + F3F388E22F680316001B0E15 /* XSStoreCoinsView.swift */, + F3F388E42F68032A001B0E15 /* XSStoreVipView.swift */, + F3F388F92F69240E001B0E15 /* XSWalletHeaderView.swift */, + F3F388FB2F692CEF001B0E15 /* XSWalletCell.swift */, + F3F388FF2F693293001B0E15 /* XSConsumptionRecordsCell.swift */, + F3F389072F6940BB001B0E15 /* XSRewardCoinsCell.swift */, + ); + path = View; + sourceTree = ""; + }; + F3F388DD2F67FE36001B0E15 /* Model */ = { + isa = PBXGroup; + children = ( + F3F388E82F68062F001B0E15 /* XSPayDateModel.swift */, + F3F388E62F6805DE001B0E15 /* XSPayItem.swift */, + F3F389012F693785001B0E15 /* XSBuyRecordsModel.swift */, + F3F389092F694109001B0E15 /* XSSendCoinRecordModel.swift */, + ); + path = Model; + sourceTree = ""; + }; + F3F388EA2F680CC7001B0E15 /* IapManager */ = { + isa = PBXGroup; + children = ( + F3F388EB2F680CEE001B0E15 /* XSIapManager.swift */, + F3F388F52F68F746001B0E15 /* XSPayDataRequest.swift */, + F3F388ED2F68E6EB001B0E15 /* XSWaitRestoreModel.swift */, + F3F388F12F68E9BE001B0E15 /* XSIapVerifyModel.swift */, + F3F388F32F68F2CD001B0E15 /* XSIapOrderModel.swift */, + ); + path = IapManager; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -898,6 +1025,7 @@ F347D2F32F0A083500786648 /* XSDeviceId.swift in Sources */, F35547E22F3DCB82006F28CD /* UINavigationBar+XS.swift in Sources */, F347D30A2F0A162800786648 /* XSNetworkPlugin.swift in Sources */, + F3F388EC2F680CEE001B0E15 /* XSIapManager.swift in Sources */, F347D3082F0A134500786648 /* XSHud.swift in Sources */, F347D29E2F03750000786648 /* XSTabBarItemContentView.swift in Sources */, F347D2FE2F0A0D0700786648 /* XSUserDefaultsKey.swift in Sources */, @@ -912,16 +1040,25 @@ F347D30C2F0A36E200786648 /* XSNetworkMonitorManager.swift in Sources */, F347D2D82F04CB0300786648 /* XSLabel.swift in Sources */, F35548082F4FD703006F28CD /* XSMineUserInfoView.swift in Sources */, + F3F388FC2F692CEF001B0E15 /* XSWalletCell.swift in Sources */, F347D2E82F0A03AB00786648 /* XSNetworkModel.swift in Sources */, F35547FE2F4EC450006F28CD /* XSMyCollectViewController.swift in Sources */, F347D29B2F03740000786648 /* XSConfig.swift in Sources */, + F3F388DF2F67FE6D001B0E15 /* XSStoreViewController.swift in Sources */, F35547F42F3EFC37006F28CD /* XSMyListCollectsCell.swift in Sources */, F347D2A72F03AAD700786648 /* XSViewController.swift in Sources */, F3585C342F148F0800EEC469 /* XSHomeViewModel.swift in Sources */, F347D2B02F03AE6700786648 /* XSHomeViewController.swift in Sources */, + F304E6102F67A9B600E9B0A6 /* XSShortDetailLockView.swift in Sources */, F3585C3A2F14999700EEC469 /* XSProgressView.swift in Sources */, + F3F389062F6939EE001B0E15 /* XSRewardCoinsViewController.swift in Sources */, F3585C382F1497AF00EEC469 /* XSDiscoverControlView.swift in Sources */, + F3F388D52F67DDBC001B0E15 /* XSMineWalletView.swift in Sources */, + F3F388D92F67F9E1001B0E15 /* XSMineVipView.swift in Sources */, F347D2D42F04BF3D00786648 /* XSHomeNewTitleView.swift in Sources */, + F304E6122F67D74A00E9B0A6 /* XSVideoUnlockResult.swift in Sources */, + F3F388FE2F6931D1001B0E15 /* XSConsumptionRecordsViewController.swift in Sources */, + F3F3890C2F695055001B0E15 /* FACoinsRecordViewController.swift in Sources */, F347D28D2F03709200786648 /* AppDelegate.swift in Sources */, F3585C492F14EE8D00EEC469 /* XSShortDetailPlayerCell.swift in Sources */, F35548412F52BE6D006F28CD /* XSBaseWebViewController+Script.swift in Sources */, @@ -931,6 +1068,7 @@ F355480E2F50485E006F28CD /* XSPanModalContentView.swift in Sources */, F347D2C82F03DBD300786648 /* UIView+XS.swift in Sources */, F355483B2F52BA4A006F28CD /* XSFeedbackViewController.swift in Sources */, + F3F388F22F68E9BE001B0E15 /* XSIapVerifyModel.swift in Sources */, F347D2FA2F0A0C5600786648 /* XSLoginManager.swift in Sources */, F35547DC2F3DB33C006F28CD /* XSMineHeaderView.swift in Sources */, F347D2B52F03B2B500786648 /* UIFont+XS.swift in Sources */, @@ -939,18 +1077,26 @@ F347D2EA2F0A047A00786648 /* XSNetwork.swift in Sources */, F347D2A32F03A93200786648 /* XSDefine.swift in Sources */, F3585C3C2F14BFAB00EEC469 /* XSCustomTabBar.swift in Sources */, + F3F388EE2F68E6EB001B0E15 /* XSWaitRestoreModel.swift in Sources */, F3585C422F14E99C00EEC469 /* XSShortDetailViewController.swift in Sources */, F347D2DE2F04F54400786648 /* XSHomeCategoriesViewController.swift in Sources */, F347D3012F0A0D8200786648 /* XSUserAPI.swift in Sources */, + F3F388F02F68E737001B0E15 /* XSStoreAPI.swift in Sources */, F3585C452F14EA0A00EEC469 /* XSShortDetailViewModel.swift in Sources */, F35547D02F3DA6CA006F28CD /* XSMineViewController.swift in Sources */, F347D2D22F04B97600786648 /* XSHomeNewBigCell.swift in Sources */, + F3F388F42F68F2CD001B0E15 /* XSIapOrderModel.swift in Sources */, + F3F389022F693785001B0E15 /* XSBuyRecordsModel.swift in Sources */, F35548102F504D74006F28CD /* XSEpSelectorView.swift in Sources */, + F3F388FA2F69240E001B0E15 /* XSWalletHeaderView.swift in Sources */, F35548122F5129F2006F28CD /* XSEpSelectorCell.swift in Sources */, + F3F388E32F680316001B0E15 /* XSStoreCoinsView.swift in Sources */, F355480C2F50398B006F28CD /* NSNumber+XS.swift in Sources */, F347D2E02F04F7ED00786648 /* XSHomeCategoriesCell.swift in Sources */, + F3F3890A2F694109001B0E15 /* XSSendCoinRecordModel.swift in Sources */, F347D3032F0A10B600786648 /* XSCryptorService.swift in Sources */, F35547D42F3DA7A8006F28CD /* XSTableViewCell.swift in Sources */, + F3F388F62F68F746001B0E15 /* XSPayDataRequest.swift in Sources */, F347D30E2F0A39DE00786648 /* XSHomeAPI.swift in Sources */, F3B312B72F319CBE0093B180 /* XSEmpty.swift in Sources */, F347D2F62F0A0B0B00786648 /* XSLoginToken.swift in Sources */, @@ -958,11 +1104,14 @@ F347D2BE2F03C24B00786648 /* XSCollectionView.swift in Sources */, F35547DA2F3DAACB006F28CD /* XSMineItem.swift in Sources */, F347D2D62F04C7D500786648 /* XSHomeNewCell.swift in Sources */, + F3F388E92F68062F001B0E15 /* XSPayDateModel.swift in Sources */, F347D2CC2F03E04400786648 /* AppDelegate+Config.swift in Sources */, F347D3202F0A57A300786648 /* XSDiscoverPlayerCell.swift in Sources */, F35548022F4ECD5C006F28CD /* UIScrollView+Refresh.swift in Sources */, F347D2EC2F0A060E00786648 /* XSNetworkTarget.swift in Sources */, + F3F388E12F680090001B0E15 /* XSScrollView.swift in Sources */, F35547E92F3DDBDD006F28CD /* XSWebView.swift in Sources */, + F3F389042F6939C7001B0E15 /* XSOrderRecordsViewController.swift in Sources */, F347D31A2F0A545000786648 /* XSDiscoverViewController.swift in Sources */, F355483D2F52BC81006F28CD /* XSSettingAPI.swift in Sources */, F3B312A42F30AC9B0093B180 /* XSSearchData.swift in Sources */, @@ -973,11 +1122,14 @@ F3F683ED2F56C380008AF250 /* XSHomeHistoryView.swift in Sources */, F347D2A12F03A84300786648 /* XSScreen.swift in Sources */, F35548062F4FD6DA006F28CD /* XSMinePlayHistoryView.swift in Sources */, + F3F3890E2F695068001B0E15 /* FAVipRecordViewController.swift in Sources */, + F3F388E72F6805DE001B0E15 /* XSPayItem.swift in Sources */, F347D2992F03730E00786648 /* XSTabBarController.swift in Sources */, F35547D22F3DA757006F28CD /* XSTableView.swift in Sources */, F355480A2F4FE99F006F28CD /* XSMinePlayHistoryCell.swift in Sources */, F3B312AD2F30ACF60093B180 /* XSSearchHotSectionView.swift in Sources */, F3B312AE2F30ACF60093B180 /* XSSearchTagsView.swift in Sources */, + F3F389082F6940BB001B0E15 /* XSRewardCoinsCell.swift in Sources */, F3B312AF2F30ACF60093B180 /* XSSearchTagCell.swift in Sources */, F3B312B02F30ACF60093B180 /* XSSearchRecentView.swift in Sources */, F35548042F4EDF27006F28CD /* UIStackView+XS.swift in Sources */, @@ -995,17 +1147,21 @@ F35547EB2F3DDCCE006F28CD /* XSBaseWebViewController.swift in Sources */, F35547EF2F3EF606006F28CD /* XSMyListViewController.swift in Sources */, F347D2D02F04B5BB00786648 /* XSHomeNewViewController.swift in Sources */, + F3F389002F693293001B0E15 /* XSConsumptionRecordsCell.swift in Sources */, F3B312A22F30A7DA0093B180 /* XSSearchViewController.swift in Sources */, F347D3222F0A58C300786648 /* XSVideoAPI.swift in Sources */, F347D29F2F03A6B100786648 /* XSTool.swift in Sources */, F35548372F52B86C006F28CD /* XSAppWebViewController.swift in Sources */, F3585C472F14EAE700EEC469 /* XSShortDetailModel.swift in Sources */, F347D2B32F03AF7500786648 /* XSHomeSearchButton.swift in Sources */, + F3F388D72F67EDEC001B0E15 /* XSMineCoinsPackView.swift in Sources */, F35547E42F3DCCDE006F28CD /* XSAboutCell.swift in Sources */, + F3F388F82F692031001B0E15 /* XSWalletViewController.swift in Sources */, F347D2C22F03C59B00786648 /* XSWaterfallFlowLayout.swift in Sources */, F35547DE2F3DBAF6006F28CD /* XSAboutViewController.swift in Sources */, F35547D72F3DA8B5006F28CD /* XSMineCell.swift in Sources */, F347D2AC2F03ADC800786648 /* String+XS.swift in Sources */, + F3F388E52F68032A001B0E15 /* XSStoreVipView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/XSeri/AppScene/AppDelegate.swift b/XSeri/AppScene/AppDelegate.swift index f0844ef..2eca858 100644 --- a/XSeri/AppScene/AppDelegate.swift +++ b/XSeri/AppScene/AppDelegate.swift @@ -48,6 +48,7 @@ extension AppDelegate { Task { await XSLoginManager.manager.updateUserInfo() } + XSIapManager.manager.preloadingProducts() } } diff --git a/XSeri/Base/Constants/XSUserDefaultsKey.swift b/XSeri/Base/Constants/XSUserDefaultsKey.swift index 4e394cf..45ebf8d 100644 --- a/XSeri/Base/Constants/XSUserDefaultsKey.swift +++ b/XSeri/Base/Constants/XSUserDefaultsKey.swift @@ -9,3 +9,5 @@ let kXSLoginTokenDefaultsKey = "kXSLoginTokenDefaultsKey" ///用户信息 let kXSUserInfoDefaultsKey = "kXSUserInfoDefaultsKey" + +let kXSWaitRestoreIAPDefaultsKey = "kXSWaitRestoreIAPDefaultsKey" diff --git a/XSeri/Base/Controller/XSTabBarController.swift b/XSeri/Base/Controller/XSTabBarController.swift index 0d645dc..0f562b0 100644 --- a/XSeri/Base/Controller/XSTabBarController.swift +++ b/XSeri/Base/Controller/XSTabBarController.swift @@ -14,6 +14,9 @@ class XSTabBarController: ESTabBarController { super.viewDidLoad() self.view.backgroundColor = .black + ///预加载支付数据 + XSIapManager.manager.preloadingProducts() + setupCustomTabBar() setupTabBarAppearance() setupViewControllers() diff --git a/XSeri/Base/Networking/API/XSStoreAPI.swift b/XSeri/Base/Networking/API/XSStoreAPI.swift new file mode 100644 index 0000000..88d7795 --- /dev/null +++ b/XSeri/Base/Networking/API/XSStoreAPI.swift @@ -0,0 +1,131 @@ +// +// XSStoreAPI.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import SmartCodable +import JXIAPManager + +struct XSStoreAPI { + enum BuyType: String, SmartCaseDefaultable { + case coins = "coins" + case subVip = "sub_vip" + case subCoins = "sub_coins" + case vip = "vip" + } + + ///创建内购订单 + static func requestCreateOrder(payId: String, shortPlayId: String, videoId: String, isDiscount: Bool = false, identifierDiscount: String? = nil) async -> XSIapOrderModel? { + + let parameters: [String : Any] = [ + "payment_channel" : "apple", + "short_play_id" : shortPlayId, + "video_id" : videoId, + "pay_setting_id" : payId, + "is_discount" : isDiscount ? 1 : 0, + "product_discount" : identifierDiscount ?? "", + ] + + var param = XSNetwork.Parameters(path: "/createOrder") + param.method = .post + param.isLoding = false + param.isToast = false + param.parameters = parameters + let response: XSNetwork.Response = await XSNetwork.request(parameters: param) + guard let data = response.data else { + XSToast.show("network_error_2".localized) + return nil + } + if let message = data.message, message.count > 0 { + if response.data?.code == 30007 { + XSToast.show("pay_error_1".localized) + } else { + XSToast.show(message) + } + return nil + } else { + return response.data + } + + } + + ///校验订单 + static func requestVerifyOrder(_ parameters: [String : Any]) async -> XSNetwork.Response { + var param = XSNetwork.Parameters(path: "/applePaid") + param.method = .post + param.isLoding = false + param.parameters = parameters + let response: XSNetwork.Response = await XSNetwork.request(parameters: param) + + return response + } + + ///获取支付模版 + static func requestPayTemplate(isLoding: Bool = false, isToast: Bool = true) async -> XSPayDateModel? { + let parameters = [ + "discount" : "1", + "purchases_token" : JXIAPManager.manager.getAppStoreReceipt() ?? "", + ] + + var param = XSNetwork.Parameters(path: "/paySettingsV4") + param.method = .post + param.isLoding = isLoding + param.isToast = isToast + param.parameters = parameters + + let response: XSNetwork.Response = await XSNetwork.request(parameters: param) + return response.data + } + + ///挽留支付项 + static func requestPayRetainInfo() async -> XSPayDateModel? { + let parameters = [ + "purchases_token" : JXIAPManager.manager.getAppStoreReceipt() ?? "", + "discount" : "1", + ] + + var param = XSNetwork.Parameters(path: "/payRetrieveSettings") + param.method = .post + param.isLoding = false + param.isToast = false + param.parameters = parameters + + let response: XSNetwork.Response = await XSNetwork.request(parameters: param) + if (response.data?.list_coins?.count ?? 0) > 0 || (response.data?.list_sub_vip?.count ?? 0) > 0 { + return response.data + } else { + return nil + } + } + + static func requestBuyRecords(page: Int) async -> XSNetwork.List? { + let parameters = [ + "page_size" : 20, + "current_page" : page, + ] + + var param = XSNetwork.Parameters(path: "/getCustomerBuyRecords") + param.method = .get + param.parameters = parameters + + let response: XSNetwork.Response> = await XSNetwork.request(parameters: param) + return response.data + } + + static func reuqestSendCoinRecord(page: Int) async -> XSNetwork.List? { + let parameters = [ + "page_size" : 20, + "current_page" : page, + ] + + var param = XSNetwork.Parameters(path: "/sendCoinList") + param.method = .post + param.parameters = parameters + + let response: XSNetwork.Response> = await XSNetwork.request(parameters: param) + return response.data + } +} diff --git a/XSeri/Base/Networking/API/XSVideoAPI.swift b/XSeri/Base/Networking/API/XSVideoAPI.swift index e5fc9f1..99b1481 100644 --- a/XSeri/Base/Networking/API/XSVideoAPI.swift +++ b/XSeri/Base/Networking/API/XSVideoAPI.swift @@ -105,6 +105,23 @@ struct XSVideoAPI { return nil } } + + static func requestCoinUnlockVideo(shortPlayId: String, videoId: String) async -> XSVideoUnlockResult? { + let parameters: [String : Any] = [ + "short_play_id" : shortPlayId, + "video_id" : videoId, + ] + var param = XSNetwork.Parameters(path: "/buy_video") + param.method = .post + param.isLoding = true + param.parameters = parameters + let response: XSNetwork.Response = await XSNetwork.request(parameters: param) + if response.isSuccess { + return response.data + } else { + return nil + } + } } extension XSVideoAPI { diff --git a/XSeri/Base/View/XSScrollView.swift b/XSeri/Base/View/XSScrollView.swift new file mode 100644 index 0000000..1042ae6 --- /dev/null +++ b/XSeri/Base/View/XSScrollView.swift @@ -0,0 +1,21 @@ +// +// XSScrollView.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/16. +// + +import UIKit + +class XSScrollView: UIScrollView { + + override init(frame: CGRect) { + super.init(frame: frame) + self.contentInsetAdjustmentBehavior = .never + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/XSeri/Class/Mine/Controller/XSMineViewController.swift b/XSeri/Class/Mine/Controller/XSMineViewController.swift index 622202b..c9c267f 100644 --- a/XSeri/Class/Mine/Controller/XSMineViewController.swift +++ b/XSeri/Class/Mine/Controller/XSMineViewController.swift @@ -25,6 +25,7 @@ class XSMineViewController: XSCommonViewController { tableView.dataSource = self tableView.separatorStyle = .none tableView.rowHeight = 44 + tableView.contentInset = .init(top: 0, left: 0, bottom: XSScreen.customTabBarHeight, right: 0) tableView.xs_addRefreshHeader { [weak self] in guard let self = self else { return } self.handleHeaderRefresh(nil) diff --git a/XSeri/Class/Mine/Model/XSMineItem.swift b/XSeri/Class/Mine/Model/XSMineItem.swift index 299898b..792d9fe 100644 --- a/XSeri/Class/Mine/Model/XSMineItem.swift +++ b/XSeri/Class/Mine/Model/XSMineItem.swift @@ -15,6 +15,12 @@ struct XSMineItem { case feedback case web case safari + ///消费记录 + case consumptionRecords + ///购买记录 + case purchaseRecords + ///金币奖励 + case rewardCoins } diff --git a/XSeri/Class/Mine/View/XSMineCoinsPackView.swift b/XSeri/Class/Mine/View/XSMineCoinsPackView.swift new file mode 100644 index 0000000..ae737ad --- /dev/null +++ b/XSeri/Class/Mine/View/XSMineCoinsPackView.swift @@ -0,0 +1,93 @@ +// +// XSMineCoinsPackView.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/16. +// + +import UIKit +import SnapKit +import YYCategories + +class XSMineCoinsPackView: UIView { + + + private lazy var bgView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "coins_pack_bg_image_01")) + imageView.isUserInteractionEnabled = true + imageView.layer.cornerRadius = 12 + imageView.layer.masksToBounds = true + return imageView + }() + + private lazy var giftsIconImageView = UIImageView(image: UIImage(named: "gifts_icon_01")) + + private lazy var titleLabel: XSLabel = { + let label = XSLabel() + label.textColors = [UIColor.F_6564_A.cgColor, UIColor.F_98_C_36.cgColor] + label.textStartPoint = .init(x: 0, y: 0.5) + label.textEndPoint = .init(x: 1, y: 0.5) + label.font = .font(ofSize: 16, weight: .bold).withBoldItalic() + label.text = "me_daily_1".localized + return label + }() + + private lazy var subtitleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .regular).withBoldItalic() + label.textColor = ._523927 + label.text = "me_daily_2".localized + return label + }() + + private lazy var indicatorImageView = UIImageView(image: UIImage(named: "arrow_right_icon_05")) + + override init(frame: CGRect) { + super.init(frame: frame) + xs_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension XSMineCoinsPackView { + + private func xs_setupUI() { + addSubview(bgView) + bgView.addSubview(giftsIconImageView) + bgView.addSubview(titleLabel) + bgView.addSubview(subtitleLabel) + bgView.addSubview(indicatorImageView) + + bgView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.centerX.equalToSuperview() + make.height.equalTo(60) + make.top.bottom.equalToSuperview() + } + + giftsIconImageView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.centerY.equalToSuperview() + } + + titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview().offset(12) + make.left.equalTo(giftsIconImageView.snp.right).offset(8) + } + + subtitleLabel.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.bottom.equalToSuperview().offset(-12) + } + + indicatorImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().offset(-12) + } + } + +} diff --git a/XSeri/Class/Mine/View/XSMineHeaderView.swift b/XSeri/Class/Mine/View/XSMineHeaderView.swift index 35978a7..b676988 100644 --- a/XSeri/Class/Mine/View/XSMineHeaderView.swift +++ b/XSeri/Class/Mine/View/XSMineHeaderView.swift @@ -13,6 +13,8 @@ class XSMineHeaderView: UITableViewHeaderFooterView { var userInfo: XSUserInfo? { didSet { userInfoView.userInfo = userInfo + walletView.userInfo = userInfo + vipView.userInfo = userInfo } } @@ -41,6 +43,21 @@ class XSMineHeaderView: UITableViewHeaderFooterView { return view }() + private lazy var walletView: XSMineWalletView = { + let view = XSMineWalletView() + return view + }() + + private lazy var coinsPackView: XSMineCoinsPackView = { + let view = XSMineCoinsPackView() + return view + }() + + private lazy var vipView: XSMineVipView = { + let view = XSMineVipView() + return view + }() + override init(reuseIdentifier: String?) { super.init(reuseIdentifier: reuseIdentifier) @@ -55,6 +72,10 @@ class XSMineHeaderView: UITableViewHeaderFooterView { stackView.xs_removeAllArrangedSubview() stackView.addArrangedSubview(userInfoView) + stackView.addArrangedSubview(walletView) + stackView.addArrangedSubview(coinsPackView) + + stackView.addArrangedSubview(vipView) if !historyDataArr.isEmpty { stackView.addArrangedSubview(historyView) diff --git a/XSeri/Class/Mine/View/XSMineVipView.swift b/XSeri/Class/Mine/View/XSMineVipView.swift new file mode 100644 index 0000000..557d32f --- /dev/null +++ b/XSeri/Class/Mine/View/XSMineVipView.swift @@ -0,0 +1,55 @@ +// +// XSMineVipView.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/16. +// + +import UIKit +import SnapKit + +class XSMineVipView: UIView { + + + override var intrinsicContentSize: CGSize { + return .init(width: XSScreen.width, height: 15) + } + + var userInfo: XSUserInfo? { + didSet { + titleLabel.text = "VIP Expiration Time:2023-11-23" + } + } + + private lazy var iconImageView = UIImageView(image: UIImage(named: "time_icon_01")) + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .regular) + label.textColor = .FFDAA_4 + return label + }() + + + override init(frame: CGRect) { + super.init(frame: frame) + addSubview(iconImageView) + addSubview(titleLabel) + + iconImageView.snp.makeConstraints { make in + make.centerY.equalTo(titleLabel) + make.left.equalToSuperview().offset(16) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalTo(iconImageView.snp.right).offset(5) + make.top.equalToSuperview() + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + +} diff --git a/XSeri/Class/Mine/View/XSMineWalletView.swift b/XSeri/Class/Mine/View/XSMineWalletView.swift new file mode 100644 index 0000000..2989540 --- /dev/null +++ b/XSeri/Class/Mine/View/XSMineWalletView.swift @@ -0,0 +1,248 @@ +// +// XSMineWalletView.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/16. +// + +import UIKit +import SnapKit + +class XSMineWalletView: UIView { + + + override var intrinsicContentSize: CGSize { + return .init(width: XSScreen.width, height: 36 + 87) + } + + var userInfo: XSUserInfo? { + didSet { + coinsView.coins = userInfo?.coin_left_total + sendCoinsView.coins = userInfo?.send_coin_left_total + } + } + + private lazy var contentView: UIView = { + let view = UIView() + view.layer.cornerRadius = 12 + view.layer.masksToBounds = true + view.layer.borderWidth = 1 + view.layer.borderColor = UIColor.white.withAlphaComponent(0.25).cgColor + return view + }() + + private lazy var titleBgView: UIView = { + let view = UIImageView(image: UIImage(named: "wallet_bg_image_01")) + view.isUserInteractionEnabled = true + view.layer.cornerRadius = 12 + view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + view.layer.masksToBounds = true + view.layer.borderColor = UIColor.FFDAA_4.cgColor + view.layer.borderWidth = 1 + return view + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .bold) + label.textColor = .white + label.text = "My Wallet".localized + return label + }() + + private lazy var walletButton: UIButton = { + var configuration = UIButton.Configuration.plain() + configuration.contentInsets = .zero + configuration.imagePadding = 4 + configuration.imagePlacement = .trailing + configuration.image = UIImage(named: "arrow_right_icon_04") + configuration.attributedTitle = AttributedString("Details".localized, attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 12, weight: .regular), + .foregroundColor : UIColor.white.withAlphaComponent(0.5) + ])) + + let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + let vc = XSWalletViewController() + self.viewController?.navigationController?.pushViewController(vc, animated: true) + })) + return button + }() + + private lazy var storeButton: UIButton = { + var configuration = UIButton.Configuration.plain() + configuration.background.image = UIImage(named: "gradient_color_image_01") + configuration.background.cornerRadius = 18 + configuration.attributedTitle = AttributedString("Top Up".localized, attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 14, weight: .semibold), + .foregroundColor : UIColor._482_B_00 + ])) + + let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + let vc = XSStoreViewController() + self.viewController?.navigationController?.pushViewController(vc, animated: true) + })) + return button + }() + + private lazy var coinsView: CoinsView = { + let view = CoinsView() + view.title = "Coins".localized + return view + }() + + private lazy var coinsLineView: UIView = { + let view = UIView() + view.backgroundColor = .white.withAlphaComponent(0.1) + return view + }() + + private lazy var sendCoinsView: CoinsView = { + let view = CoinsView() + view.title = "Bonus".localized + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + xs_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension XSMineWalletView { + + private func xs_setupUI() { + addSubview(contentView) + addSubview(titleBgView) + titleBgView.addSubview(titleLabel) + titleBgView.addSubview(walletButton) + contentView.addSubview(storeButton) + contentView.addSubview(coinsView) + contentView.addSubview(coinsLineView) + contentView.addSubview(sendCoinsView) + + contentView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.centerX.equalToSuperview() + make.top.bottom.equalToSuperview() + } + + titleBgView.snp.makeConstraints { make in + make.left.right.top.equalTo(contentView) + make.height.equalTo(36) + } + + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(12) + } + + walletButton.snp.makeConstraints { make in + make.top.bottom.equalToSuperview() + make.right.equalToSuperview().offset(-12) + } + + storeButton.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-12) + make.width.equalTo(108) + make.height.equalTo(36) + make.bottom.equalToSuperview().offset(-26) + } + + coinsView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.centerY.equalTo(storeButton) + } + + coinsLineView.snp.makeConstraints { make in + make.centerY.equalTo(storeButton) + make.left.equalTo(coinsView.snp.right).offset(25) + make.width.equalTo(1) + make.height.equalTo(36) + } + + sendCoinsView.snp.makeConstraints { make in + make.left.equalTo(coinsLineView.snp.right).offset(25) + make.centerY.equalTo(storeButton) + } + } + +} + + +extension XSMineWalletView { + + private class CoinsView: UIView { + + var title: String? { + set { + titleLabel.text = newValue + } + get { + return titleLabel.text + } + } + + var coins: Int? { + didSet { + coinsLabel.text = "\(coins ?? 0)" + } + } + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .bold) + label.textColor = .white.withAlphaComponent(0.5) + return label + }() + + private lazy var coinsImageView = UIImageView(image: UIImage(named: "coins_icon_01")) + + private lazy var coinsLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 18, weight: .regular) + label.textColor = .FFDAA_4 + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + addSubview(titleLabel) + addSubview(coinsImageView) + addSubview(coinsLabel) + + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview() + make.top.equalToSuperview() + make.right.lessThanOrEqualToSuperview() + } + + coinsImageView.snp.makeConstraints { make in + make.left.equalToSuperview() + make.bottom.equalToSuperview() + make.top.equalTo(titleLabel.snp.bottom).offset(6) + } + + coinsLabel.snp.makeConstraints { make in + make.centerY.equalTo(coinsImageView) + make.left.equalTo(coinsImageView.snp.right).offset(6) + make.right.lessThanOrEqualToSuperview() + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + } + +} + diff --git a/XSeri/Class/Player/Controller/XSShortDetailViewController.swift b/XSeri/Class/Player/Controller/XSShortDetailViewController.swift index c4a4e3f..9e16323 100644 --- a/XSeri/Class/Player/Controller/XSShortDetailViewController.swift +++ b/XSeri/Class/Player/Controller/XSShortDetailViewController.swift @@ -72,12 +72,32 @@ class XSShortDetailViewController: JXPlayerListViewController { } override func play() { - super.play() let videoInfo = self.xs_viewModel.currentCell?.model as? XSVideoInfoModel - Task { - await XSVideoAPI.requestAddPlayHistory(shortPlayId: videoInfo?.short_play_id, videoId: videoInfo?.short_play_video_id) + if videoInfo?.is_lock != true { + super.play() + Task { + await XSVideoAPI.requestAddPlayHistory(shortPlayId: videoInfo?.short_play_id, videoId: videoInfo?.short_play_video_id) + } + return } + + self.pause() + + let myCoins = XSLoginManager.manager.userInfo?.totalCoins ?? 0 + let lockCoins = videoInfo?.coins ?? 0 + + if myCoins < lockCoins, (self.xs_viewModel.currentCell as? XSShortDetailPlayerCell)?.hasLastEpisodeUnlocked != true { + self.xs_viewModel.openRechargeView() + } else { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in + guard let self = self else { return } + if (self.xs_viewModel.currentCell as? XSShortDetailPlayerCell)?.hasLastEpisodeUnlocked != true { + self.xs_viewModel.handleUnlockVideo() + } + } + } + } } diff --git a/XSeri/Class/Player/Model/XSVideoUnlockResult.swift b/XSeri/Class/Player/Model/XSVideoUnlockResult.swift new file mode 100644 index 0000000..56b036b --- /dev/null +++ b/XSeri/Class/Player/Model/XSVideoUnlockResult.swift @@ -0,0 +1,25 @@ +// +// XSVideoUnlockResult.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/16. +// + +import UIKit +import SmartCodable + +struct XSVideoUnlockResult: SmartCodable { + + enum Status: String, SmartCaseDefaultable { + ///前面还有没购买的剧 + case jump = "jump" + ///没找到视频 + case noPlay = "no_play" + ///金币不足跳充值 + case notEnough = "not_enough" + ///购买成功 + case success = "success" + } + + var status: Status? +} diff --git a/XSeri/Class/Player/View/XSEpSelectorCell.swift b/XSeri/Class/Player/View/XSEpSelectorCell.swift index 7c90240..557af5f 100644 --- a/XSeri/Class/Player/View/XSEpSelectorCell.swift +++ b/XSeri/Class/Player/View/XSEpSelectorCell.swift @@ -14,6 +14,7 @@ class XSEpSelectorCell: UICollectionViewCell { var model: XSVideoInfoModel? { didSet { epLabel.text = model?.episode + lockImageView.isHidden = !(model?.is_lock ?? false) } } @@ -51,6 +52,11 @@ class XSEpSelectorCell: UICollectionViewCell { private lazy var playImageView = UIImageView(image: UIImage(named: "play_icon_02")) + private lazy var lockImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "lock_icon_01")) + return imageView + }() + override init(frame: CGRect) { super.init(frame: frame) xs_setupUI() @@ -66,8 +72,9 @@ extension XSEpSelectorCell { private func xs_setupUI() { contentView.addSubview(bgView) - contentView.addSubview(epLabel) - contentView.addSubview(playImageView) + bgView.addSubview(epLabel) + bgView.addSubview(playImageView) + bgView.addSubview(lockImageView) bgView.snp.makeConstraints { make in make.edges.equalToSuperview() @@ -81,6 +88,10 @@ extension XSEpSelectorCell { make.left.equalToSuperview().offset(6) make.bottom.equalToSuperview().offset(-5) } + + lockImageView.snp.makeConstraints { make in + make.right.top.equalToSuperview() + } } } diff --git a/XSeri/Class/Player/View/XSShortDetailLockView.swift b/XSeri/Class/Player/View/XSShortDetailLockView.swift new file mode 100644 index 0000000..f2d3a82 --- /dev/null +++ b/XSeri/Class/Player/View/XSShortDetailLockView.swift @@ -0,0 +1,84 @@ +// +// XSShortDetailLockView.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/16. +// + +import UIKit +import SnapKit + +class XSShortDetailLockView: UIView { + + var clickUnlockButton: (() -> Void)? + + var videoInfo: XSVideoInfoModel? { + didSet { + unlockButton.setNeedsUpdateConfiguration() + } + } + + var hasLastEpisodeUnlocked = false { + didSet { + unlockButton.setNeedsUpdateConfiguration() + } + } + + private lazy var lockIconImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "lock_icon_03")) + return imageView + }() + + private lazy var unlockButton: UIButton = { + var config = UIButton.Configuration.plain() + config.background.image = UIImage(named: "button_bg_image_01") + config.image = UIImage(named: "lock_icon_02") + config.imagePadding = 8 + + let button = UIButton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + self.clickUnlockButton?() + })) + button.configurationUpdateHandler = { [weak self] button in + guard let self = self else { return } + let attributeContainer = AttributeContainer([ + .font : UIFont.font(ofSize: 16, weight: .medium), + .foregroundColor : UIColor._712_A_03 + ]) + if hasLastEpisodeUnlocked { + button.configuration?.attributedTitle = .init("video_lock_tip_text".localized, attributes: attributeContainer) + } else { + button.configuration?.attributedTitle = .init("unlocking_coins_notice".localizedReplace(text: "\(videoInfo?.coins ?? 0)"), attributes: attributeContainer) + } + } + button.layer.cornerRadius = 27 + button.layer.masksToBounds = true + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + backgroundColor = .black.withAlphaComponent(0.6) + + addSubview(unlockButton) + addSubview(lockIconImageView) + + unlockButton.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.left.equalToSuperview().offset(18) + make.height.equalTo(54) + make.centerY.equalToSuperview().offset(30) + } + + lockIconImageView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalTo(unlockButton.snp.top).offset(-34) + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/XSeri/Class/Player/View/XSShortDetailPlayerCell.swift b/XSeri/Class/Player/View/XSShortDetailPlayerCell.swift index 773e34a..41c4388 100644 --- a/XSeri/Class/Player/View/XSShortDetailPlayerCell.swift +++ b/XSeri/Class/Player/View/XSShortDetailPlayerCell.swift @@ -7,6 +7,7 @@ import UIKit import JXPlayer +import SnapKit class XSShortDetailPlayerCell: JXPlayerListCell { @@ -20,8 +21,8 @@ class XSShortDetailPlayerCell: JXPlayerListCell { let model = self.model as? XSVideoInfoModel self.player.setPlayUrl(url: model?.video_url ?? "") -// self.lockView.isHidden = !(model?.is_lock ?? true) -// lockView.videoInfo = model + self.lockView.isHidden = !(model?.is_lock ?? true) + lockView.videoInfo = model } } @@ -35,11 +36,32 @@ class XSShortDetailPlayerCell: JXPlayerListCell { var hasLastEpisodeUnlocked: Bool = false { didSet { -// self.lockView.hasLastEpisodeUnlocked = hasLastEpisodeUnlocked + self.lockView.hasLastEpisodeUnlocked = hasLastEpisodeUnlocked } } var xs_controlView: XSShortDetailPlayerControlView { return self.controlView as! XSShortDetailPlayerControlView } + + + private lazy var lockView: XSShortDetailLockView = { + let view = XSShortDetailLockView() + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + addSubview(lockView) + + lockView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } } diff --git a/XSeri/Class/Player/ViewModel/XSShortDetailViewModel.swift b/XSeri/Class/Player/ViewModel/XSShortDetailViewModel.swift index 6b1132a..bd40309 100644 --- a/XSeri/Class/Player/ViewModel/XSShortDetailViewModel.swift +++ b/XSeri/Class/Player/ViewModel/XSShortDetailViewModel.swift @@ -94,4 +94,53 @@ extension XSShortDetailViewModel { self.popView = view } + ///解锁视频 + func handleUnlockVideo() { + Task { + if await self.unlockVideo() { + await self.playerListVC?.reloadData { [weak self] in + self?.playerListVC?.play() + } + } + } + } + + ///打开充值页面 + func openRechargeView() { + + } + + +} + +extension XSShortDetailViewModel { + + private func unlockVideo() async -> Bool { + guard let videoInfo = self.currentCell?.model as? XSVideoInfoModel else { return false } + guard let shortPlayId = videoInfo.short_play_id else { return false } + guard let videoId = videoInfo.short_play_video_id else { return false } + + guard let model = await XSVideoAPI.requestCoinUnlockVideo(shortPlayId: shortPlayId, videoId: videoId) else { return false } + + + switch model.status { + case .jump: + XSToast.show("buy_fail_toast_02".localized) + case .noPlay: + XSToast.show("buy_fail_toast_01".localized) + case .notEnough: + self.openRechargeView() + default: break + } + + if model.status == .success { + await XSLoginManager.manager.updateUserInfo() + videoInfo.is_lock = false + XSToast.show("Success".localized) + return true + } else { + return false + } + } + } diff --git a/XSeri/Class/Store/Controller/FACoinsRecordViewController.swift b/XSeri/Class/Store/Controller/FACoinsRecordViewController.swift new file mode 100644 index 0000000..e6ea51f --- /dev/null +++ b/XSeri/Class/Store/Controller/FACoinsRecordViewController.swift @@ -0,0 +1,29 @@ +// +// FACoinsRecordViewController.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit + +class FACoinsRecordViewController: XSViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + // Get the new view controller using segue.destination. + // Pass the selected object to the new view controller. + } + */ + +} diff --git a/XSeri/Class/Store/Controller/FAVipRecordViewController.swift b/XSeri/Class/Store/Controller/FAVipRecordViewController.swift new file mode 100644 index 0000000..dec6421 --- /dev/null +++ b/XSeri/Class/Store/Controller/FAVipRecordViewController.swift @@ -0,0 +1,29 @@ +// +// FAVipRecordViewController.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit + +class FAVipRecordViewController: XSViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + // Get the new view controller using segue.destination. + // Pass the selected object to the new view controller. + } + */ + +} diff --git a/XSeri/Class/Store/Controller/XSConsumptionRecordsViewController.swift b/XSeri/Class/Store/Controller/XSConsumptionRecordsViewController.swift new file mode 100644 index 0000000..9b26402 --- /dev/null +++ b/XSeri/Class/Store/Controller/XSConsumptionRecordsViewController.swift @@ -0,0 +1,123 @@ +// +// XSConsumptionRecordsViewController.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import SnapKit + +class XSConsumptionRecordsViewController: XSViewController { + + private lazy var dataArr: [XSBuyRecordsModel] = [] + private lazy var page = 1 + + private lazy var bgImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "bg_image_01")) + return imageView + }() + + private lazy var tableView: XSTableView = { + let tableView = XSTableView(frame: .zero, style: .plain) + tableView.delegate = self + tableView.dataSource = self + tableView.rowHeight = 71 + tableView.separatorColor = .white.withAlphaComponent(0.1) + tableView.separatorInset = .init(top: 0, left: 32, bottom: 0, right: 32) + tableView.contentInset = .init(top: 10, left: 0, bottom: XSScreen.safeBottom + 10, right: 0) + tableView.register(XSConsumptionRecordsCell.self, forCellReuseIdentifier: "cell") + tableView.ly_emptyView = XSEmpty.xs_emptyView() + + tableView.xs_addRefreshHeader(insetTop: tableView.contentInset.top) { [weak self] in + self?.handleHeaderRefresh(nil) + } + tableView.xs_addRefreshFooter(insetBottom: tableView.contentInset.bottom) { [weak self] in + self?.handleFooterRefresh(nil) + } + return tableView + }() + + override func viewDidLoad() { + super.viewDidLoad() + self.title = "Consumption Records".localized + self.view.backgroundColor = .black + + xs_setupUI() + + Task { + await requestDataArr(page: 1) + } + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.setNavigationBarHidden(false, animated: true) + xs_setNavigationStyle() + } + + + override func handleHeaderRefresh(_ completer: (() -> Void)?) { + Task { + await requestDataArr(page: 1) + self.tableView.xs_endHeaderRefreshing() + } + } + + override func handleFooterRefresh(_ completer: (() -> Void)?) { + Task { + await requestDataArr(page: self.page + 1) + self.tableView.xs_endFooterRefreshing() + } + } + +} + +extension XSConsumptionRecordsViewController { + + private func xs_setupUI() { + view.addSubview(bgImageView) + view.addSubview(tableView) + + bgImageView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + } + + tableView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalTo(self.view.safeAreaLayoutGuide) + } + } + +} + +//MARK: UITableViewDelegate UITableViewDataSource +extension XSConsumptionRecordsViewController: UITableViewDelegate, UITableViewDataSource { + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! XSConsumptionRecordsCell + cell.model = dataArr[indexPath.row] + return cell + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return self.dataArr.count + } +} + +extension XSConsumptionRecordsViewController { + + private func requestDataArr(page: Int) async { + + guard let listModel = await XSStoreAPI.requestBuyRecords(page: page) else { return } + guard let list = listModel.list else { return } + + if page == 1 { + self.dataArr.removeAll() + } + self.dataArr += list + self.page = page + self.tableView.reloadData() + } + +} diff --git a/XSeri/Class/Store/Controller/XSOrderRecordsViewController.swift b/XSeri/Class/Store/Controller/XSOrderRecordsViewController.swift new file mode 100644 index 0000000..b477d7b --- /dev/null +++ b/XSeri/Class/Store/Controller/XSOrderRecordsViewController.swift @@ -0,0 +1,104 @@ +// +// XSOrderRecordsViewController.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import SnapKit +import JXSegmentedView + +class XSOrderRecordsViewController: XSViewController { + + + private lazy var viewControllers: [XSViewController] = { + return [FACoinsRecordViewController(), FAVipRecordViewController()] + }() + + private lazy var bgImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "bg_image_01")) + return imageView + }() + + /// Segmented 数据源 + private lazy var segmentedDataSource: JXSegmentedTitleDataSource = { + let dataSource = JXSegmentedTitleDataSource() + dataSource.titles = ["Coin Record".localized, "VIP Record".localized] + dataSource.titleNormalColor = UIColor.white.withAlphaComponent(0.55) + dataSource.titleSelectedColor = .white + dataSource.titleNormalFont = .font(ofSize: 16, weight: .medium) + dataSource.titleSelectedFont = .font(ofSize: 16, weight: .semibold) + dataSource.isTitleColorGradientEnabled = true + dataSource.itemSpacing = 24 + return dataSource + }() + + /// Segmented 视图 (Tab 栏) + private lazy var segmentedView: JXSegmentedView = { + let view = JXSegmentedView() + view.dataSource = segmentedDataSource + view.delegate = self + view.listContainer = listContainerView + view.contentEdgeInsetLeft = 16 + view.contentEdgeInsetRight = 16 + view.backgroundColor = .clear + return view + }() + + /// 列表容器 (内容展示) + private lazy var listContainerView: JXSegmentedListContainerView = { + return JXSegmentedListContainerView(dataSource: self) + }() + + override func viewDidLoad() { + super.viewDidLoad() + + xs_setupUI() + } + + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.setNavigationBarHidden(false, animated: true) + xs_setNavigationStyle() + } + + +} + +extension XSOrderRecordsViewController { + + private func xs_setupUI() { + view.addSubview(bgImageView) + view.addSubview(listContainerView) + + bgImageView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + } + + listContainerView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalTo(self.view.safeAreaLayoutGuide) + } + } + +} + +// MARK: - JXSegmentedViewDelegate +extension XSOrderRecordsViewController: JXSegmentedViewDelegate { + func segmentedView(_ segmentedView: JXSegmentedView, didSelectedItemAt index: Int) { + + } +} + +// MARK: - JXSegmentedListContainerViewDataSource +extension XSOrderRecordsViewController: JXSegmentedListContainerViewDataSource { + func numberOfLists(in listContainerView: JXSegmentedListContainerView) -> Int { + return segmentedDataSource.titles.count + } + + func listContainerView(_ listContainerView: JXSegmentedListContainerView, initListAt index: Int) -> JXSegmentedListContainerViewListDelegate { + return viewControllers[index] + } +} diff --git a/XSeri/Class/Store/Controller/XSRewardCoinsViewController.swift b/XSeri/Class/Store/Controller/XSRewardCoinsViewController.swift new file mode 100644 index 0000000..565e2c9 --- /dev/null +++ b/XSeri/Class/Store/Controller/XSRewardCoinsViewController.swift @@ -0,0 +1,124 @@ +// +// XSRewardCoinsViewController.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import SnapKit + +class XSRewardCoinsViewController: XSViewController { + + private lazy var dataArr: [XSSendCoinRecordModel] = [] + private lazy var page = 1 + + private lazy var bgImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "bg_image_01")) + return imageView + }() + + private lazy var tableView: XSTableView = { + let tableView = XSTableView(frame: .zero, style: .plain) + tableView.delegate = self + tableView.dataSource = self + tableView.rowHeight = 96 + tableView.separatorColor = .white.withAlphaComponent(0.1) + tableView.separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16) + tableView.contentInset = .init(top: 10, left: 0, bottom: XSScreen.safeBottom + 10, right: 0) + tableView.register(XSRewardCoinsCell.self, forCellReuseIdentifier: "cell") + tableView.ly_emptyView = XSEmpty.xs_emptyView() + + tableView.xs_addRefreshHeader(insetTop: tableView.contentInset.top) { [weak self] in + self?.handleHeaderRefresh(nil) + } + tableView.xs_addRefreshFooter(insetBottom: tableView.contentInset.bottom) { [weak self] in + self?.handleFooterRefresh(nil) + } + return tableView + }() + + override func viewDidLoad() { + super.viewDidLoad() + self.title = "Reward Coins".localized + self.view.backgroundColor = .black + xs_setupUI() + + Task { + await requestDataArr(page: 1) + } + } + + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.setNavigationBarHidden(false, animated: true) + xs_setNavigationStyle() + } + + override func handleHeaderRefresh(_ completer: (() -> Void)?) { + Task { + await requestDataArr(page: 1) + self.tableView.xs_endHeaderRefreshing() + } + } + + override func handleFooterRefresh(_ completer: (() -> Void)?) { + Task { + await requestDataArr(page: self.page + 1) + self.tableView.xs_endFooterRefreshing() + } + } +} + + +extension XSRewardCoinsViewController { + + private func xs_setupUI() { + view.addSubview(bgImageView) + view.addSubview(tableView) + + bgImageView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + } + + tableView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalTo(self.view.safeAreaLayoutGuide).offset(10) + } + } + +} + +//MARK: UITableViewDelegate UITableViewDataSource +extension XSRewardCoinsViewController: UITableViewDelegate, UITableViewDataSource { + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! XSRewardCoinsCell + cell.model = dataArr[indexPath.row] + return cell + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return self.dataArr.count + } + +} + +extension XSRewardCoinsViewController { + + private func requestDataArr(page: Int) async { + + guard let listModel = await XSStoreAPI.reuqestSendCoinRecord(page: page) else { return } + guard let list = listModel.list else { return } + + if page == 1 { + self.dataArr.removeAll() + } + self.dataArr += list + self.page = page + self.tableView.reloadData() + } + +} + diff --git a/XSeri/Class/Store/Controller/XSStoreViewController.swift b/XSeri/Class/Store/Controller/XSStoreViewController.swift new file mode 100644 index 0000000..3fc6384 --- /dev/null +++ b/XSeri/Class/Store/Controller/XSStoreViewController.swift @@ -0,0 +1,60 @@ +// +// XSStoreViewController.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/16. +// + +import UIKit + +class XSStoreViewController: XSViewController { + + + private lazy var scrollView: XSScrollView = { + let scrollView = XSScrollView() + return scrollView + }() + + private lazy var stackView: UIStackView = { + let view = UIStackView() + view.axis = .vertical + view.spacing = 12 + return view + }() + + override func viewDidLoad() { + super.viewDidLoad() + self.title = "Store".localized + + + xs_setupUI() + } + + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.setNavigationBarHidden(false, animated: true) + xs_setNavigationStyle() + } + +} + +extension XSStoreViewController { + + private func xs_setupUI() { + view.addSubview(scrollView) + scrollView.addSubview(stackView) + + scrollView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().offset(XSScreen.navBarHeight) + } + + stackView.snp.makeConstraints { make in + make.left.centerX.equalToSuperview() + make.top.equalToSuperview().offset(12) + make.bottom.equalToSuperview().offset(-(XSScreen.safeBottom + 10)) + } + } + +} diff --git a/XSeri/Class/Store/Controller/XSWalletViewController.swift b/XSeri/Class/Store/Controller/XSWalletViewController.swift new file mode 100644 index 0000000..de70479 --- /dev/null +++ b/XSeri/Class/Store/Controller/XSWalletViewController.swift @@ -0,0 +1,135 @@ +// +// XSWalletViewController.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import SnapKit + +class XSWalletViewController: XSViewController { + + private lazy var dataArr: [XSMineItem] = [ + XSMineItem(type: .consumptionRecords, iconImage: UIImage(named: "order_icon_01"), title: "Consumption records".localized), + XSMineItem(type: .purchaseRecords, iconImage: UIImage(named: "order_icon_02"), title: "Purchase records".localized), + XSMineItem(type: .rewardCoins, iconImage: UIImage(named: "order_icon_03"), title: "Reward Coins".localized), + XSMineItem(type: .feedback, iconImage: UIImage(named: "feedback_icon_03"), title: "feedback".localized), + ] + + private lazy var bgImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "bg_image_01")) + return imageView + }() + + private lazy var headerView: XSWalletHeaderView = { + let view = XSWalletHeaderView() + view.userInfo = XSLoginManager.manager.userInfo + return view + }() + + private lazy var tableView: XSTableView = { + let tableView = XSTableView(frame: .zero, style: .plain) + tableView.delegate = self + tableView.dataSource = self + tableView.rowHeight = 52 + tableView.separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16) + tableView.contentInset = .init(top: 0, left: 0, bottom: XSScreen.safeBottom, right: 0) + tableView.register(XSWalletCell.self, forCellReuseIdentifier: "cell") + return tableView + }() + + deinit { + NotificationCenter.default.removeObserver(self) + } + + override func viewDidLoad() { + super.viewDidLoad() + self.title = "Details".localized + NotificationCenter.default.addObserver(self, selector: #selector(loginStateDidChangeNotification), name: XSLoginManager.loginStateDidChangeNotification, object: nil) + + xs_setupUI() + } + + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.setNavigationBarHidden(false, animated: true) + xs_setNavigationStyle() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + Task { + await XSLoginManager.manager.updateUserInfo() + } + } + + @objc private func loginStateDidChangeNotification() { + headerView.userInfo = XSLoginManager.manager.userInfo + } + +} + +extension XSWalletViewController { + + private func xs_setupUI() { + view.addSubview(bgImageView) + view.addSubview(headerView) + view.addSubview(tableView) + + bgImageView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + } + + headerView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalTo(self.view.safeAreaLayoutGuide).offset(10) + } + + tableView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalTo(headerView.snp.bottom).offset(18) + } + } + +} + +//MARK: UITableViewDelegate & UITableViewDataSource +extension XSWalletViewController: UITableViewDelegate, UITableViewDataSource { + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let item = self.dataArr[indexPath.row] + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! XSWalletCell + cell.item = item + return cell + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return dataArr.count + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let item = self.dataArr[indexPath.row] + + var vc: UIViewController? + + switch item.type { + case .consumptionRecords: + vc = XSConsumptionRecordsViewController() + + case .purchaseRecords: + vc = XSOrderRecordsViewController() + + case .rewardCoins: + vc = XSRewardCoinsViewController() + + default: + break + } + + if let vc = vc { + self.navigationController?.pushViewController(vc, animated: true) + } + } +} diff --git a/XSeri/Class/Store/Model/XSBuyRecordsModel.swift b/XSeri/Class/Store/Model/XSBuyRecordsModel.swift new file mode 100644 index 0000000..3806adf --- /dev/null +++ b/XSeri/Class/Store/Model/XSBuyRecordsModel.swift @@ -0,0 +1,21 @@ +// +// XSBuyRecordsModel.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import SmartCodable + +struct XSBuyRecordsModel: SmartCodable { + + var short_play_video_id: String? + var created_at: String? + var short_play_id: String? + var coins: Int? + var image_url: String? + var name: String? + var episode: String? + var coin_type: Int? +} diff --git a/XSeri/Class/Store/Model/XSPayDateModel.swift b/XSeri/Class/Store/Model/XSPayDateModel.swift new file mode 100644 index 0000000..9318dc0 --- /dev/null +++ b/XSeri/Class/Store/Model/XSPayDateModel.swift @@ -0,0 +1,46 @@ +// +// XSPayDateModel.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/16. +// + +import UIKit +import SmartCodable + +class XSPayDateModel: NSObject, SmartCodable { + + required override init() { + super.init() + } + + enum SortName: String, SmartCaseDefaultable { + case coin = "list_coins" + case vip = "list_sub_vip" + } + + var list_coins: [XSPayItem]? + var list_sub_vip: [XSPayItem]? + var list_sub_coins: [XSPayItem]? + var sort: [SortName]? + + ///0: 老版支付 1:新版支付 + var pay_mode: Int? + ///0: 普通金币 1:金币包模式 + var show_type: Int? + + var retrieve_lang: XSPayRetrieveLang? +} + +class XSPayRetrieveLang: NSObject, SmartCodable { + required override init() { + super.init() + } + + var title: String? + var remaining_time: String? + var miss_out: String? + var subtitle: String? + var claim_reward: String? +} + diff --git a/XSeri/Class/Store/Model/XSPayItem.swift b/XSeri/Class/Store/Model/XSPayItem.swift new file mode 100644 index 0000000..3095d6c --- /dev/null +++ b/XSeri/Class/Store/Model/XSPayItem.swift @@ -0,0 +1,112 @@ +// +// XSPayItem.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/16. +// + +import UIKit +import SmartCodable +import StoreKit + +class XSPayItem: 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" + case spread = "spread" + } + + + var id: String? + var backhaul_price: String? + var coins: Int? + var status: String? + var price: String? + var send_coins: Int? + var buy_type: XSStoreAPI.BuyType? + var origin_price: String? + + var brief: String? + var title: String? + var auto_sub: String? + var vip_type: String? + var vip_type_key: VipTypeKey? + var sort: String? + var xs_description: 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: XSPayExtInfo? + + ///0 无优惠 1 首次购买优惠 2 二次购买优惠 + var discount_type: Int? + + @IgnoredKey + var product: SKProduct? + + + ///首冲优惠数据 + var introductionaryOffer: SKProductDiscount? { + return product?.introductoryPrice + } + + ///促销优惠数据 + var promotionalOffers: [SKProductDiscount]? { + return product?.discounts + } + + var discount_price: String? { + var price: String? = nil + if self.discount_type == 1, let introductoryPrice = self.introductionaryOffer { + price = introductoryPrice.price.stringValue + } else if self.discount_type == 2, let discount = self.promotionalOffers?.first { + price = discount.price.stringValue + } + return price + } + + + static func mappingForKey() -> [SmartKeyTransformer]? { + return [ + CodingKeys.xs_description <--- ["description"] + ] + } +} + +class XSPayExtInfo: NSObject, SmartCodable { + + required override init() { + super.init() + } + + var extra_day_coins: Int? + var receive_coins_rate: String? + var max_total_coins: Int? + var max_total_coins_pop: Int? + var sub_coins_txt_list: [String]? + +} diff --git a/XSeri/Class/Store/Model/XSSendCoinRecordModel.swift b/XSeri/Class/Store/Model/XSSendCoinRecordModel.swift new file mode 100644 index 0000000..08ea521 --- /dev/null +++ b/XSeri/Class/Store/Model/XSSendCoinRecordModel.swift @@ -0,0 +1,21 @@ +// +// XSSendCoinRecordModel.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import SmartCodable + +struct XSSendCoinRecordModel: SmartCodable { + + var id: String? + var diff_datetime: String? + var expired_time: TimeInterval? + var coins: Int? + var type: String? + var left_coins: String? + var created_at: String? + var is_effective: Int? +} diff --git a/XSeri/Class/Store/View/XSConsumptionRecordsCell.swift b/XSeri/Class/Store/View/XSConsumptionRecordsCell.swift new file mode 100644 index 0000000..c276f83 --- /dev/null +++ b/XSeri/Class/Store/View/XSConsumptionRecordsCell.swift @@ -0,0 +1,96 @@ +// +// XSConsumptionRecordsCell.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import SnapKit + +class XSConsumptionRecordsCell: XSTableViewCell { + + var model: XSBuyRecordsModel? { + didSet { + subtitleLabel.text = "EP.##".localizedReplace(text: "\(model?.episode ?? "")") + " " + "\(model?.name ?? "")" + dateLabel.text = model?.created_at + coinsLabel.text = "-\(model?.coins ?? 0)" + "Coins".localized + } + } + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .medium) + label.textColor = .FFDAA_4 + label.text = "Purchase Single Episode".localized + return label + }() + + private lazy var subtitleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .regular) + label.textColor = .white + label.setContentHuggingPriority(.required, for: .horizontal) + label.setContentCompressionResistancePriority(.required, for: .horizontal) + return label + }() + + private lazy var dateLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .medium) + label.textColor = .white + return label + }() + + private lazy var coinsLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .medium) + label.textColor = .FFDAA_4 + label.setContentHuggingPriority(.required, for: .horizontal) + label.setContentCompressionResistancePriority(.required, for: .horizontal) + return label + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + xs_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension XSConsumptionRecordsCell { + + private func xs_setupUI() { + contentView.addSubview(titleLabel) + contentView.addSubview(subtitleLabel) + contentView.addSubview(dateLabel) + contentView.addSubview(coinsLabel) + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(32) + make.top.equalToSuperview().offset(16) + make.right.lessThanOrEqualTo(dateLabel.snp.left).offset(-10) + } + + subtitleLabel.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.bottom.equalToSuperview().offset(-16) + make.right.lessThanOrEqualTo(coinsLabel.snp.left).offset(-20) + } + + dateLabel.snp.makeConstraints { make in + make.centerY.equalTo(titleLabel) + make.right.equalToSuperview().offset(-36) + } + + coinsLabel.snp.makeConstraints { make in + make.right.equalTo(dateLabel) + make.centerY.equalTo(subtitleLabel) + } + } + +} diff --git a/XSeri/Class/Store/View/XSRewardCoinsCell.swift b/XSeri/Class/Store/View/XSRewardCoinsCell.swift new file mode 100644 index 0000000..19061b1 --- /dev/null +++ b/XSeri/Class/Store/View/XSRewardCoinsCell.swift @@ -0,0 +1,142 @@ +// +// XSRewardCoinsCell.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import SnapKit + +class XSRewardCoinsCell: XSTableViewCell { + + var model: XSSendCoinRecordModel? { + didSet { + nameLabel.text = model?.type + dateLabel.text = model?.created_at + countLabel.text = "+\(model?.coins ?? 0)" + remainingLabel.text = model?.left_coins + + if model?.is_effective == 1 { + expiresIconImageView.isHidden = false + expiresLabel.isHidden = false + expiredLabel.isHidden = true + expiresLabel.text = "Expires in ## days".localizedReplace(text: model?.diff_datetime ?? "") + } else { + expiresIconImageView.isHidden = true + expiresLabel.isHidden = true + expiredLabel.isHidden = false + } + } + } + + private lazy var dateLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .medium) + label.textColor = .white + return label + }() + + private lazy var nameLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .semibold) + label.textColor = .white + return label + }() + + private lazy var expiresIconImageView = UIImageView(image: UIImage(named: "expires_icon_01")) + + private lazy var expiresLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .regular) + label.textColor = .FFDAA_4 + return label + }() + + private lazy var expiredLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .medium) + label.textColor = .white + label.text = "Expired".localized + return label + }() + + private lazy var countLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .semibold) + label.textColor = .FFDAA_4 + return label + }() + + private lazy var remainingLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .medium) + label.textColor = .white + return label + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + self.dateLabel.text = "2024-6-10 23:41:18" + self.nameLabel.text = "Check in" + self.countLabel.text = "+50" + self.remainingLabel.text = "Remaining:0" + + xs_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension XSRewardCoinsCell { + + private func xs_setupUI() { + contentView.addSubview(dateLabel) + contentView.addSubview(nameLabel) + contentView.addSubview(expiresIconImageView) + contentView.addSubview(expiresLabel) + contentView.addSubview(expiredLabel) + contentView.addSubview(countLabel) + contentView.addSubview(remainingLabel) + + dateLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(32) + make.top.equalTo(17) + } + + nameLabel.snp.makeConstraints { make in + make.left.equalTo(dateLabel) + make.top.equalTo(dateLabel.snp.bottom).offset(6) + } + + expiresIconImageView.snp.makeConstraints { make in + make.left.equalTo(dateLabel) + make.bottom.equalToSuperview().offset(-17) + } + + expiresLabel.snp.makeConstraints { make in + make.centerY.equalTo(expiresIconImageView) + make.left.equalTo(expiresIconImageView.snp.right).offset(4) + } + + expiredLabel.snp.makeConstraints { make in + make.left.equalTo(expiresIconImageView) + make.centerY.equalTo(expiresIconImageView) + } + + countLabel.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-32) + make.top.equalToSuperview().offset(28) + } + + remainingLabel.snp.makeConstraints { make in + make.right.equalTo(countLabel) + make.centerY.equalTo(expiresIconImageView) + } + + } + +} diff --git a/XSeri/Class/Store/View/XSStoreCoinsView.swift b/XSeri/Class/Store/View/XSStoreCoinsView.swift new file mode 100644 index 0000000..bc14eaa --- /dev/null +++ b/XSeri/Class/Store/View/XSStoreCoinsView.swift @@ -0,0 +1,77 @@ +// +// XSStoreCoinsView.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/16. +// + +import UIKit +import SnapKit + +class XSStoreCoinsView: UIView { + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .bold) + label.textColor = .white + label.text = "Coins Purchase".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: FACollectionView = { +// let collectionView = FACollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) +// collectionView.delegate = self +// collectionView.dataSource = self +// collectionView.clipsToBounds = false +// collectionView.isScrollEnabled = false +// collectionView.register(FAStoreCoinsBigCell.self, forCellWithReuseIdentifier: "FAStoreCoinsBigCell") +// collectionView.register(FAStoreCoinsSmallCell.self, forCellWithReuseIdentifier: "FAStoreCoinsSmallCell") +// collectionView.register(FAStoreCoinsPackCell.self, forCellWithReuseIdentifier: "FAStoreCoinsPackCell") +// collectionView.addObserver(self, forKeyPath: "contentSize", context: nil) +// return collectionView +// }() + + override init(frame: CGRect) { + super.init(frame: frame) + xs_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension XSStoreCoinsView { + + private func xs_setupUI() { + addSubview(titleLabel) + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.top.equalToSuperview() + } + } + +} diff --git a/XSeri/Class/Store/View/XSStoreVipView.swift b/XSeri/Class/Store/View/XSStoreVipView.swift new file mode 100644 index 0000000..57e9861 --- /dev/null +++ b/XSeri/Class/Store/View/XSStoreVipView.swift @@ -0,0 +1,14 @@ +// +// XSStoreVipView.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/16. +// + +import UIKit + +class XSStoreVipView: UIView { + + + +} diff --git a/XSeri/Class/Store/View/XSWalletCell.swift b/XSeri/Class/Store/View/XSWalletCell.swift new file mode 100644 index 0000000..3c0119c --- /dev/null +++ b/XSeri/Class/Store/View/XSWalletCell.swift @@ -0,0 +1,69 @@ +// +// XSWalletCell.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import SnapKit + +class XSWalletCell: XSTableViewCell { + + var item: XSMineItem? { + didSet { + iconImageView.image = item?.iconImage + titleLabel.text = item?.title + } + } + + private lazy var iconImageView: UIImageView = { + let imageView = UIImageView() + return imageView + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .medium) + label.textColor = .white + return label + }() + + private lazy var indicatorImageView = UIImageView(image: UIImage(named: "arrow_right_icon_06")) + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + xs_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension XSWalletCell { + + private func xs_setupUI() { + contentView.addSubview(iconImageView) + contentView.addSubview(titleLabel) + contentView.addSubview(indicatorImageView) + + iconImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(16) + } + + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(42) + } + + indicatorImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().offset(-14) + } + + } + +} diff --git a/XSeri/Class/Store/View/XSWalletHeaderView.swift b/XSeri/Class/Store/View/XSWalletHeaderView.swift new file mode 100644 index 0000000..853d797 --- /dev/null +++ b/XSeri/Class/Store/View/XSWalletHeaderView.swift @@ -0,0 +1,204 @@ +// +// XSWalletHeaderView.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import SnapKit + +class XSWalletHeaderView: UIView { + + + override var intrinsicContentSize: CGSize { + return .init(width: XSScreen.width, height: 119) + } + + var userInfo: XSUserInfo? { + didSet { + coinsView.coins = userInfo?.coin_left_total + sendCoinsView.coins = userInfo?.send_coin_left_total + } + } + + + private lazy var bgView: UIView = { + let view = UIView() + view.backgroundColor = .white.withAlphaComponent(0.1) + view.layer.cornerRadius = 8 + view.layer.masksToBounds = true + view.layer.borderWidth = 1 + view.layer.borderColor = UIColor.white.withAlphaComponent(0.14).cgColor + return view + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .semibold) + label.textColor = .white + label.text = "My Wallet".localized + return label + }() + + private lazy var lineView: UIView = { + let view = UIView() + view.backgroundColor = .white.withAlphaComponent(0.12) + return view + }() + + private lazy var storeButton: UIButton = { + var configuration = UIButton.Configuration.plain() + configuration.background.image = UIImage(named: "store_button_bg_image") + configuration.attributedTitle = AttributedString("Store".localized, attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 16, weight: .bold), + .foregroundColor : UIColor._482_B_00 + ])) + + let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + let vc = XSStoreViewController() + self.viewController?.navigationController?.pushViewController(vc, animated: true) + })) + return button + }() + + private lazy var coinsView: CoinsView = { + let view = CoinsView() + view.title = "Coins".localized + return view + }() + + private lazy var sendCoinsView: CoinsView = { + let view = CoinsView() + view.title = "Bonus".localized + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + xs_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension XSWalletHeaderView { + + private func xs_setupUI() { + addSubview(bgView) + bgView.addSubview(titleLabel) + bgView.addSubview(lineView) + bgView.addSubview(storeButton) + bgView.addSubview(coinsView) + bgView.addSubview(sendCoinsView) + + bgView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.centerX.equalToSuperview() + make.top.bottom.equalToSuperview() + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(10) + make.height.equalTo(36) + make.top.equalToSuperview() + } + + lineView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.centerX.equalToSuperview() + make.top.equalTo(titleLabel.snp.bottom) + make.height.equalTo(1) + } + + storeButton.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-12) + make.bottom.equalToSuperview().offset(-22) + make.width.equalTo(102) + make.height.equalTo(36) + } + + coinsView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(8) + make.bottom.equalToSuperview().offset(-18) + } + + sendCoinsView.snp.makeConstraints { make in + make.centerY.equalTo(coinsView) + make.left.equalToSuperview().offset(119) + } + } + +} + + +extension XSWalletHeaderView { + + 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: 14, weight: .semibold) + label.textColor = .white.withAlphaComponent(0.52) + return label + }() + + private lazy var coinsIconView = UIImageView(image: UIImage(named: "coins_icon_02")) + + private lazy var coinsLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 18, weight: .bold) + label.textColor = .white + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + addSubview(titleLabel) + addSubview(coinsIconView) + addSubview(coinsLabel) + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(3) + make.top.equalToSuperview() + make.right.lessThanOrEqualToSuperview() + } + + coinsIconView.snp.makeConstraints { make in + make.bottom.left.equalToSuperview() + make.top.equalTo(titleLabel.snp.bottom).offset(11) + } + + coinsLabel.snp.makeConstraints { make in + make.centerY.equalTo(coinsIconView) + make.left.equalTo(coinsIconView.snp.right).offset(2) + make.right.lessThanOrEqualToSuperview() + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + } + +} diff --git a/XSeri/Libs/IapManager/XSIapManager.swift b/XSeri/Libs/IapManager/XSIapManager.swift new file mode 100644 index 0000000..82bd4f2 --- /dev/null +++ b/XSeri/Libs/IapManager/XSIapManager.swift @@ -0,0 +1,297 @@ +// +// XSIapManager.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/16. +// + +import UIKit +import StoreKit +import JXIAPManager + +class XSIapManager { + + typealias CompletionHandler = ((_ finish: Bool) -> Void) + ///内购模版前缀 + static let IAPPrefix = "xseri" + + static let manager = XSIapManager() + + private lazy var iapManager: JXIAPManager = { + let manager = JXIAPManager.manager + manager.delegate = self + return manager + }() + + ///成功回调 + private var completionHandler: CompletionHandler? + + private var shortPlayId: String? + private var videoId: String? + + private var orderCode: String? + private var payId: String? + + ///恢复购买使用 + ///等待恢复的数据 + private var waitRestoreModel: XSWaitRestoreModel? = UserDefaults.xs_object(forKey: kXSWaitRestoreIAPDefaultsKey, as: XSWaitRestoreModel.self) + + ///预加载数据 + private var payRequest: XSPayDataRequest? + var payDateModel: XSPayDateModel? + + + ///开始内购 + @MainActor + func start(model: XSPayItem, shortPlayId: String? = nil, videoId: String? = nil, hudShowView: UIView? = nil, handler: CompletionHandler? = nil) { + + if let _ = self.waitRestoreModel { + XSToast.show("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 = XSWaitRestoreModel() + 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 + } + + XSHud.show(containerView: hudShowView) + + Task { + guard let orderModel = await XSStoreAPI.requestCreateOrder(payId: payId, shortPlayId: shortPlayId ?? "0", videoId: videoId ?? "0", isDiscount: isDiscount, identifierDiscount: identifierDiscount) else { + XSHud.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) + } + } + + @MainActor + func restore(isLoding: Bool = true, shortPlayId: String? = nil, videoId: String? = nil, completer: ((_ isFinish: Bool, _ buyType: XSStoreAPI.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 { + XSToast.show("pay_error_5".localized) + } + completer?(false, buyType) + return + } + + if isLoding { + XSHud.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() ?? "" + ] +// FAStatAPI.requestEventStat(orderCode: orderCode, shortPlayId: shortPlayId, videoId: videoId, eventKey: .payRestore, errorMsg: "restore", otherParamenters: statParamenters) + + Task { + let response = await XSStoreAPI.requestVerifyOrder(verifyData) + if isLoding { + XSHud.dismiss() + } + guard let model = response.data else { + completer?(false, buyType) + return + } + + self.waitRestoreModel = nil + UserDefaults.xs_setObject(nil, forKey: kXSWaitRestoreIAPDefaultsKey) + + if model.status == "success" { + if buyType == .subVip { + XSLoginManager.manager.userInfo?.is_vip = true + } + + if isLoding { + XSToast.show("Success".localized) + } + completer?(true, buyType) + if buyType == .subVip { + NotificationCenter.default.post(name: XSIapManager.buyVipFinishNotification, object: nil) + } + } else { + completer?(false, buyType) + } + } + + } + + + + func getProductId(templateId: String?) -> String? { + guard let templateId = templateId else { return nil } + return XSIapManager.IAPPrefix + "." + templateId + } + + func clean() { + self.orderCode = nil + self.payId = nil + self.shortPlayId = nil + self.videoId = nil + self.completionHandler = nil + } +} + +//MARK: JXIAPManagerDelegate +extension XSIapManager: JXIAPManagerDelegate { + + 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() + XSHud.dismiss() + return + } + + self.waitRestoreModel?.productId = productId + self.waitRestoreModel?.receipt = receipt + self.waitRestoreModel?.transactionId = transactionIdentifier + + UserDefaults.xs_setObject(self.waitRestoreModel, forKey: kXSWaitRestoreIAPDefaultsKey) + +#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 response = await XSStoreAPI.requestVerifyOrder(verifyData) + XSHud.dismiss() + + guard let model = response.data else { +// FAStatAPI.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.xs_setObject(nil, forKey: kXSWaitRestoreIAPDefaultsKey) + + if model.status == "success" { + if buyType == .subVip { + XSLoginManager.manager.userInfo?.is_vip = true + } + + XSToast.show("Success".localized) + self.completionHandler?(true) + if buyType == .subVip { + NotificationCenter.default.post(name: XSIapManager.buyVipFinishNotification, object: nil) + } + } else { + XSToast.show("pay_error_4".localized) +// FAStatAPI.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: JXIAPManager.ErrorCode, msg: String?) { + XSHud.dismiss() + + if code == .noProduct { + XSToast.show("pay_error_2".localized) + } else if code == .cancelled { + XSToast.show("pay_error_3".localized) + } + +// if code == .cancelled { +// FAStatAPI.requestEventStat(orderCode: self.orderCode, shortPlayId: self.shortPlayId, videoId: self.videoId, eventKey: .payCancel, errorMsg: "user cancel") +// } else { +// FAStatAPI.requestEventStat(orderCode: self.orderCode, shortPlayId: self.shortPlayId, videoId: self.videoId, eventKey: .payError, errorMsg: msg) +// } + + self.completionHandler?(false) + + self.waitRestoreModel = nil + self.clean() + } + +} + +extension XSIapManager { + + 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" : kXSBundleIdentifier, + "transaction_id": transactionId, + "purchases_token" : purchaseToken + ] + return parameters + } + + ///预加载支付项 + func preloadingProducts() { + JXIAPManager.manager.fetchReceipt { _ in + self.payRequest = XSPayDataRequest() + self.payRequest?.requestProducts(isToast: false) { model in + + } + } + } + +} + +extension XSIapManager { + ///成功购买会员 + @objc static let buyVipFinishNotification = Notification.Name(rawValue: "XSIapManager.buyVipFinishNotification") + +} diff --git a/XSeri/Libs/IapManager/XSIapOrderModel.swift b/XSeri/Libs/IapManager/XSIapOrderModel.swift new file mode 100644 index 0000000..c7910b6 --- /dev/null +++ b/XSeri/Libs/IapManager/XSIapOrderModel.swift @@ -0,0 +1,37 @@ +// +// XSIapOrderModel.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import SmartCodable + +struct XSIapOrderModel: SmartCodable { + + var code: Int? + var message: String? + var order_code: String? + var is_backhaul: String? + var money: String? + var discount: XSIapOrderDiscountModel? + +} + +struct XSIapOrderDiscountModel: SmartCodable { + + var discount_code: String? + var is_discount: Bool? + var sign_data: XSIapOrderDiscountSign? +} + +struct XSIapOrderDiscountSign: SmartCodable { + + var keyIdentifier: String? + var nonce: String? + var timestamp: TimeInterval? + var applicationUsername: String? + var signature: String? +} + diff --git a/XSeri/Libs/IapManager/XSIapVerifyModel.swift b/XSeri/Libs/IapManager/XSIapVerifyModel.swift new file mode 100644 index 0000000..9ab0d75 --- /dev/null +++ b/XSeri/Libs/IapManager/XSIapVerifyModel.swift @@ -0,0 +1,16 @@ +// +// XSIapVerifyModel.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import SmartCodable + +struct XSIapVerifyModel: SmartCodable { + var money: String? + var status: String? + var is_backhaul: String? + var code: String? +} diff --git a/XSeri/Libs/IapManager/XSPayDataRequest.swift b/XSeri/Libs/IapManager/XSPayDataRequest.swift new file mode 100644 index 0000000..c00ea1a --- /dev/null +++ b/XSeri/Libs/IapManager/XSPayDataRequest.swift @@ -0,0 +1,165 @@ +// +// XSPayDataRequest.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import StoreKit + +class XSPayDataRequest: NSObject { + + private var oldTemplateModel: XSPayDateModel? + + private var completerBlock: ((_ model: XSPayDateModel?) -> Void)? + private var payRetainBlock: ((_ model: XSPayDateModel?) -> Void)? + + private var isLoding = false + private var isToast = false + + func requestProducts(isLoding: Bool = false, isToast: Bool = true, completer: @escaping ((_ model: XSPayDateModel?) -> Void)) { + self.completerBlock = completer + self.isLoding = isLoding + self.isToast = isToast + + if isLoding { + XSHud.show() + } + + Task { + guard let model = await XSStoreAPI.requestPayTemplate(isLoding: false, isToast: isToast) else { + if isLoding { + XSHud.dismiss() + } + return + } + self.oldTemplateModel = model + + var productIdArr: [String] = [] + model.list_sub_vip?.forEach { item in + productIdArr.append(XSIapManager.manager.getProductId(templateId: item.ios_template_id) ?? "") + } + model.list_coins?.forEach { item in + productIdArr.append(XSIapManager.manager.getProductId(templateId: item.ios_template_id) ?? "") + } + + let set = Set(productIdArr) + let productsRequest = SKProductsRequest(productIdentifiers: set) + productsRequest.delegate = self + productsRequest.start() + } + } + + func requestPayRetainInfo(completer: ((_ model: XSPayDateModel?) -> Void)?) { + self.payRetainBlock = completer + self.isLoding = true + XSHud.show() + + Task { + guard let model = await XSStoreAPI.requestPayRetainInfo() else { + XSHud.dismiss() + self.payRetainBlock?(nil) + return + } + + self.oldTemplateModel = model + + var productIdArr: [String] = [] + model.list_coins?.forEach { item in + productIdArr.append(XSIapManager.manager.getProductId(templateId: item.ios_template_id) ?? "") + } + model.list_sub_vip?.forEach { item in + productIdArr.append(XSIapManager.manager.getProductId(templateId: item.ios_template_id) ?? "") + } + + let set = Set(productIdArr) + let productsRequest = SKProductsRequest(productIdentifiers: set) + productsRequest.delegate = self + productsRequest.start() + + } + } +} + +//MARK: SKProductsRequestDelegate +extension XSPayDataRequest: SKProductsRequestDelegate { + + func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { + if isLoding { + XSHud.dismiss() + } + let products = response.products + + if self.completerBlock != nil || self.payRetainBlock != nil { + guard let templateModel = self.oldTemplateModel else { return } + + var newCoinList: [XSPayItem] = [] + var newVipList: [XSPayItem] = [] + + templateModel.list_coins?.forEach { item in + let productId = XSIapManager.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 = XSIapManager.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 + + + if let block = self.completerBlock { + XSIapManager.manager.payDateModel = templateModel + DispatchQueue.main.async { + block(templateModel) + } + } else if let block = self.payRetainBlock { + DispatchQueue.main.async { + block(templateModel) + } + } + + } +// else if let block = self.payAlertBlock { +// guard let coinalertModel = self.payAlertModel else { return } +// let productId = FAIapManager.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) +// } +// } + + } + + +} diff --git a/XSeri/Libs/IapManager/XSWaitRestoreModel.swift b/XSeri/Libs/IapManager/XSWaitRestoreModel.swift new file mode 100644 index 0000000..e51e95b --- /dev/null +++ b/XSeri/Libs/IapManager/XSWaitRestoreModel.swift @@ -0,0 +1,48 @@ +// +// XSWaitRestoreModel.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit + +class XSWaitRestoreModel: NSObject, NSSecureCoding { + + var orderCode: String? + var payId: String? + var productId: String? + var receipt: String? + var buyType: XSStoreAPI.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 = XSStoreAPI.BuyType(rawValue: type) + } + } +} diff --git a/XSeri/Source/Assets.xcassets/Color/#482B00.colorset/Contents.json b/XSeri/Source/Assets.xcassets/Color/#482B00.colorset/Contents.json new file mode 100644 index 0000000..7a85470 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Color/#482B00.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x00", + "green" : "0x2B", + "red" : "0x48" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Color/#523927.colorset/Contents.json b/XSeri/Source/Assets.xcassets/Color/#523927.colorset/Contents.json new file mode 100644 index 0000000..d244566 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Color/#523927.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x27", + "green" : "0x39", + "red" : "0x52" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Color/#712A03.colorset/Contents.json b/XSeri/Source/Assets.xcassets/Color/#712A03.colorset/Contents.json new file mode 100644 index 0000000..de5a209 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Color/#712A03.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x03", + "green" : "0x2A", + "red" : "0x71" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Color/#F6564A.colorset/Contents.json b/XSeri/Source/Assets.xcassets/Color/#F6564A.colorset/Contents.json new file mode 100644 index 0000000..8d5404e --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Color/#F6564A.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x4A", + "green" : "0x56", + "red" : "0xF6" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Color/#F98C36.colorset/Contents.json b/XSeri/Source/Assets.xcassets/Color/#F98C36.colorset/Contents.json new file mode 100644 index 0000000..323ded7 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Color/#F98C36.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x36", + "green" : "0x8C", + "red" : "0xF9" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_04.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_04.imageset/Contents.json new file mode 100644 index 0000000..397060f --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_04.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "arrow@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "arrow@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_04.imageset/arrow@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_04.imageset/arrow@2x.png new file mode 100644 index 0000000..23a3edc Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_04.imageset/arrow@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_04.imageset/arrow@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_04.imageset/arrow@3x.png new file mode 100644 index 0000000..25dbbe5 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_04.imageset/arrow@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_05.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_05.imageset/Contents.json new file mode 100644 index 0000000..397060f --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_05.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "arrow@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "arrow@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_05.imageset/arrow@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_05.imageset/arrow@2x.png new file mode 100644 index 0000000..66ca4b7 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_05.imageset/arrow@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_05.imageset/arrow@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_05.imageset/arrow@3x.png new file mode 100644 index 0000000..ae11da3 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_05.imageset/arrow@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_06.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_06.imageset/Contents.json new file mode 100644 index 0000000..5c4d3b1 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_06.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_06.imageset/Frame@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_06.imageset/Frame@2x.png new file mode 100644 index 0000000..ca81e53 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_06.imageset/Frame@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_06.imageset/Frame@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_06.imageset/Frame@3x.png new file mode 100644 index 0000000..d431aaf Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/arrow_right_icon_06.imageset/Frame@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_01.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_01.imageset/Contents.json new file mode 100644 index 0000000..0a36bb4 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "渐变@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "渐变@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_01.imageset/渐变@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_01.imageset/渐变@2x.png new file mode 100644 index 0000000..3e17080 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_01.imageset/渐变@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_01.imageset/渐变@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_01.imageset/渐变@3x.png new file mode 100644 index 0000000..f21b2e2 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_01.imageset/渐变@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_01.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_01.imageset/Contents.json new file mode 100644 index 0000000..a385738 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "查看图片 24@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "查看图片 24@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_01.imageset/查看图片 24@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_01.imageset/查看图片 24@2x.png new file mode 100644 index 0000000..4b6e341 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_01.imageset/查看图片 24@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_01.imageset/查看图片 24@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_01.imageset/查看图片 24@3x.png new file mode 100644 index 0000000..ca1ac88 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_01.imageset/查看图片 24@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_02.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_02.imageset/Contents.json new file mode 100644 index 0000000..6974814 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "金币@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "金币@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_02.imageset/金币@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_02.imageset/金币@2x.png new file mode 100644 index 0000000..91afdfe Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_02.imageset/金币@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_02.imageset/金币@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_02.imageset/金币@3x.png new file mode 100644 index 0000000..64cb811 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_02.imageset/金币@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_01.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_01.imageset/Contents.json new file mode 100644 index 0000000..d3cf364 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "内容@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "内容@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_01.imageset/内容@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_01.imageset/内容@2x.png new file mode 100644 index 0000000..0866848 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_01.imageset/内容@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_01.imageset/内容@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_01.imageset/内容@3x.png new file mode 100644 index 0000000..f4795a7 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/coins_pack_bg_image_01.imageset/内容@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/expires_icon_01.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/expires_icon_01.imageset/Contents.json new file mode 100644 index 0000000..6efec3a --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/expires_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "time icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "time icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/expires_icon_01.imageset/time icon@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/expires_icon_01.imageset/time icon@2x.png new file mode 100644 index 0000000..628699c Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/expires_icon_01.imageset/time icon@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/expires_icon_01.imageset/time icon@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/expires_icon_01.imageset/time icon@3x.png new file mode 100644 index 0000000..bca2221 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/expires_icon_01.imageset/time icon@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/feedback_icon_03.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/feedback_icon_03.imageset/Contents.json new file mode 100644 index 0000000..5c4d3b1 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/feedback_icon_03.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/feedback_icon_03.imageset/Frame@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/feedback_icon_03.imageset/Frame@2x.png new file mode 100644 index 0000000..af73d7e Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/feedback_icon_03.imageset/Frame@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/feedback_icon_03.imageset/Frame@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/feedback_icon_03.imageset/Frame@3x.png new file mode 100644 index 0000000..fc547c1 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/feedback_icon_03.imageset/Frame@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/gifts_icon_01.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/gifts_icon_01.imageset/Contents.json new file mode 100644 index 0000000..880e9d8 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/gifts_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "礼物盒-icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "礼物盒-icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/gifts_icon_01.imageset/礼物盒-icon@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/gifts_icon_01.imageset/礼物盒-icon@2x.png new file mode 100644 index 0000000..c9b7bca Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/gifts_icon_01.imageset/礼物盒-icon@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/gifts_icon_01.imageset/礼物盒-icon@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/gifts_icon_01.imageset/礼物盒-icon@3x.png new file mode 100644 index 0000000..ecb7f48 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/gifts_icon_01.imageset/礼物盒-icon@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_01.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_01.imageset/Contents.json new file mode 100644 index 0000000..bc617eb --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group 2108@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group 2108@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_01.imageset/Group 2108@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_01.imageset/Group 2108@2x.png new file mode 100644 index 0000000..67db52f Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_01.imageset/Group 2108@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_01.imageset/Group 2108@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_01.imageset/Group 2108@3x.png new file mode 100644 index 0000000..6c0ffb1 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_01.imageset/Group 2108@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_02.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_02.imageset/Contents.json new file mode 100644 index 0000000..65fe77e --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Lock@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Lock@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_02.imageset/Lock@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_02.imageset/Lock@2x.png new file mode 100644 index 0000000..ed6f996 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_02.imageset/Lock@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_02.imageset/Lock@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_02.imageset/Lock@3x.png new file mode 100644 index 0000000..1005c5f Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_02.imageset/Lock@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_03.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_03.imageset/Contents.json new file mode 100644 index 0000000..972637a --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_03.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "锁-@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "锁-@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_03.imageset/锁-@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_03.imageset/锁-@2x.png new file mode 100644 index 0000000..988540f Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_03.imageset/锁-@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_03.imageset/锁-@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_03.imageset/锁-@3x.png new file mode 100644 index 0000000..8e26bb3 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/lock_icon_03.imageset/锁-@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/order_icon_01.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_01.imageset/Contents.json new file mode 100644 index 0000000..1ac8225 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group 315@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group 315@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/order_icon_01.imageset/Group 315@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_01.imageset/Group 315@2x.png new file mode 100644 index 0000000..0582846 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_01.imageset/Group 315@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/order_icon_01.imageset/Group 315@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_01.imageset/Group 315@3x.png new file mode 100644 index 0000000..289c77e Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_01.imageset/Group 315@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/order_icon_02.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_02.imageset/Contents.json new file mode 100644 index 0000000..3eb7831 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Purchase records@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Purchase records@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/order_icon_02.imageset/Purchase records@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_02.imageset/Purchase records@2x.png new file mode 100644 index 0000000..4d3ca6c Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_02.imageset/Purchase records@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/order_icon_02.imageset/Purchase records@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_02.imageset/Purchase records@3x.png new file mode 100644 index 0000000..d35b763 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_02.imageset/Purchase records@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/order_icon_03.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_03.imageset/Contents.json new file mode 100644 index 0000000..f07fde9 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_03.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group 614@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group 614@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/order_icon_03.imageset/Group 614@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_03.imageset/Group 614@2x.png new file mode 100644 index 0000000..64a0ceb Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_03.imageset/Group 614@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/order_icon_03.imageset/Group 614@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_03.imageset/Group 614@3x.png new file mode 100644 index 0000000..0d53867 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/order_icon_03.imageset/Group 614@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/store_button_bg_image.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/store_button_bg_image.imageset/Contents.json new file mode 100644 index 0000000..b7b3656 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/store_button_bg_image.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame 2085662672@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame 2085662672@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/store_button_bg_image.imageset/Frame 2085662672@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/store_button_bg_image.imageset/Frame 2085662672@2x.png new file mode 100644 index 0000000..c12b3aa Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/store_button_bg_image.imageset/Frame 2085662672@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/store_button_bg_image.imageset/Frame 2085662672@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/store_button_bg_image.imageset/Frame 2085662672@3x.png new file mode 100644 index 0000000..8a68191 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/store_button_bg_image.imageset/Frame 2085662672@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/time_icon_01.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/time_icon_01.imageset/Contents.json new file mode 100644 index 0000000..8df15d2 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/time_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame 2085663363@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame 2085663363@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/time_icon_01.imageset/Frame 2085663363@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/time_icon_01.imageset/Frame 2085663363@2x.png new file mode 100644 index 0000000..2cd73dd Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/time_icon_01.imageset/Frame 2085663363@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/time_icon_01.imageset/Frame 2085663363@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/time_icon_01.imageset/Frame 2085663363@3x.png new file mode 100644 index 0000000..bcbdd19 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/time_icon_01.imageset/Frame 2085663363@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/wallet_bg_image_01.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/wallet_bg_image_01.imageset/Contents.json new file mode 100644 index 0000000..f0437f6 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/wallet_bg_image_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "卡片 bg@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "卡片 bg@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/wallet_bg_image_01.imageset/卡片 bg@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/wallet_bg_image_01.imageset/卡片 bg@2x.png new file mode 100644 index 0000000..1b1d6cf Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/wallet_bg_image_01.imageset/卡片 bg@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/wallet_bg_image_01.imageset/卡片 bg@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/wallet_bg_image_01.imageset/卡片 bg@3x.png new file mode 100644 index 0000000..de79c8e Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/wallet_bg_image_01.imageset/卡片 bg@3x.png differ diff --git a/XSeri/Source/en.lproj/Localizable.strings b/XSeri/Source/en.lproj/Localizable.strings index c52efc7..82fb18a 100644 --- a/XSeri/Source/en.lproj/Localizable.strings +++ b/XSeri/Source/en.lproj/Localizable.strings @@ -40,12 +40,40 @@ "account_deletion" = "Account Deletion"; "Continue" = "Continue"; "Last Watched:Ep.##" = "Last Watched:Ep.##"; +"video_lock_tip_text" = "Pre.locked"; +"unlocking_coins_notice" = "Unlocking costs ## coins"; +"Success" = "Success"; +"My Wallet" = "My Wallet"; +"Details" = "Details"; +"Top Up" = "Top Up"; +"Coins" = "Coins"; +"Store" = "Store"; +"Coins Purchase" = "Coins Purchase"; +"Details" = "Details"; +"Bonus" = "Bonus"; +"Consumption records" = "Consumption records"; +"Purchase records" = "Purchase records"; +"Reward Coins" = "Reward Coins"; +"Purchase Single Episode" = "Purchase Single Episode"; +"Expired" = "Expired"; +"Expires in ## days" = "Expires in ## days"; +"Coin Record" = "Coin Record"; +"VIP Record" = "VIP Record"; + +"me_daily_1" = "Daily reward ready!"; +"me_daily_2" = "Claim your rewards now."; "empty_title_01" = "We couldn’t find any dramas"; - "buy_fail_toast_01" = "Purchase failed, please try again later!"; "buy_fail_toast_02" = "The prequel to this series is not unlocked. Please unlock the prequel before unlocking this series"; "network_error_1" = "Your account is already logged in on another device~"; "network_error_2" = "The service is abnormal. Check the network."; + +"pay_error_1" = "You are already a member!"; +"pay_error_2" = "Invalid in-app purchase"; +"pay_error_3" = "Payment has been cancelled"; +"pay_error_4" = "Purchase Failed"; +"pay_error_5" = "There are no recoverable in-app purchases."; +"pay_error_6" = "You have unfinished in-app purchases, please restore them first.";