diff --git a/XSeri.xcodeproj/project.pbxproj b/XSeri.xcodeproj/project.pbxproj index 8c48c38..1fbd819 100644 --- a/XSeri.xcodeproj/project.pbxproj +++ b/XSeri.xcodeproj/project.pbxproj @@ -162,8 +162,23 @@ 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 */; }; + F3F3890C2F695055001B0E15 /* XSCoinsOrderRecordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3890B2F695055001B0E15 /* XSCoinsOrderRecordViewController.swift */; }; + F3F3890E2F695068001B0E15 /* XSVipOrderRecordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3890D2F695068001B0E15 /* XSVipOrderRecordViewController.swift */; }; + F3F389102F695C5A001B0E15 /* XSSegmentedGradientIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3890F2F695C5A001B0E15 /* XSSegmentedGradientIndicatorView.swift */; }; + F3F389122F6A37E3001B0E15 /* XSCoinsOrderRecordCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389112F6A37E3001B0E15 /* XSCoinsOrderRecordCell.swift */; }; + F3F389142F6A3812001B0E15 /* XSOrderRecordModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389132F6A3812001B0E15 /* XSOrderRecordModel.swift */; }; + F3F389162F6A3AD3001B0E15 /* XSVipOrderRecordCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389152F6A3AD3001B0E15 /* XSVipOrderRecordCell.swift */; }; + F3F389182F6A4407001B0E15 /* XSStoreCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389172F6A4407001B0E15 /* XSStoreCell.swift */; }; + F3F3891A2F6A444D001B0E15 /* XSStoreCoinsBigCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389192F6A444D001B0E15 /* XSStoreCoinsBigCell.swift */; }; + F3F3891C2F6A4465001B0E15 /* XSStoreCoinsSmallCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3891B2F6A4465001B0E15 /* XSStoreCoinsSmallCell.swift */; }; + F3F3891E2F6A4484001B0E15 /* XSStoreCoinsSpreadCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3891D2F6A4484001B0E15 /* XSStoreCoinsSpreadCell.swift */; }; + F3F389202F6A487B001B0E15 /* XSStoreVipCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3891F2F6A487B001B0E15 /* XSStoreVipCell.swift */; }; + F3F389222F6A4B8F001B0E15 /* XSCoinsPackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389212F6A4B8F001B0E15 /* XSCoinsPackViewController.swift */; }; + F3F389242F6A8B89001B0E15 /* XSHomeCoinsPackButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389232F6A8B89001B0E15 /* XSHomeCoinsPackButton.swift */; }; + F3F389272F6A9597001B0E15 /* XSBaseAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389262F6A9597001B0E15 /* XSBaseAlert.swift */; }; + F3F389292F6A98C9001B0E15 /* XSAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F389282F6A98C9001B0E15 /* XSAlert.swift */; }; + F3F3892B2F6AAC25001B0E15 /* XSVersionUpdateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3892A2F6AAC25001B0E15 /* XSVersionUpdateModel.swift */; }; + F3F3892D2F6B86F7001B0E15 /* XSLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F3892C2F6B86F7001B0E15 /* XSLoginView.swift */; }; F3F683ED2F56C380008AF250 /* XSHomeHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3F683EC2F56C380008AF250 /* XSHomeHistoryView.swift */; }; /* End PBXBuildFile section */ @@ -327,8 +342,23 @@ 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 = ""; }; + F3F3890B2F695055001B0E15 /* XSCoinsOrderRecordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSCoinsOrderRecordViewController.swift; sourceTree = ""; }; + F3F3890D2F695068001B0E15 /* XSVipOrderRecordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSVipOrderRecordViewController.swift; sourceTree = ""; }; + F3F3890F2F695C5A001B0E15 /* XSSegmentedGradientIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSSegmentedGradientIndicatorView.swift; sourceTree = ""; }; + F3F389112F6A37E3001B0E15 /* XSCoinsOrderRecordCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSCoinsOrderRecordCell.swift; sourceTree = ""; }; + F3F389132F6A3812001B0E15 /* XSOrderRecordModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSOrderRecordModel.swift; sourceTree = ""; }; + F3F389152F6A3AD3001B0E15 /* XSVipOrderRecordCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSVipOrderRecordCell.swift; sourceTree = ""; }; + F3F389172F6A4407001B0E15 /* XSStoreCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSStoreCell.swift; sourceTree = ""; }; + F3F389192F6A444D001B0E15 /* XSStoreCoinsBigCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSStoreCoinsBigCell.swift; sourceTree = ""; }; + F3F3891B2F6A4465001B0E15 /* XSStoreCoinsSmallCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSStoreCoinsSmallCell.swift; sourceTree = ""; }; + F3F3891D2F6A4484001B0E15 /* XSStoreCoinsSpreadCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSStoreCoinsSpreadCell.swift; sourceTree = ""; }; + F3F3891F2F6A487B001B0E15 /* XSStoreVipCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSStoreVipCell.swift; sourceTree = ""; }; + F3F389212F6A4B8F001B0E15 /* XSCoinsPackViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSCoinsPackViewController.swift; sourceTree = ""; }; + F3F389232F6A8B89001B0E15 /* XSHomeCoinsPackButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSHomeCoinsPackButton.swift; sourceTree = ""; }; + F3F389262F6A9597001B0E15 /* XSBaseAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSBaseAlert.swift; sourceTree = ""; }; + F3F389282F6A98C9001B0E15 /* XSAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSAlert.swift; sourceTree = ""; }; + F3F3892A2F6AAC25001B0E15 /* XSVersionUpdateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSVersionUpdateModel.swift; sourceTree = ""; }; + F3F3892C2F6B86F7001B0E15 /* XSLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSLoginView.swift; sourceTree = ""; }; F3F683EC2F56C380008AF250 /* XSHomeHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XSHomeHistoryView.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -482,6 +512,7 @@ F347D29C2F03A5A100786648 /* Libs */ = { isa = PBXGroup; children = ( + F3F389252F6A955D001B0E15 /* Alert */, F3F388EA2F680CC7001B0E15 /* IapManager */, F3B312B52F319CA50093B180 /* Empty */, F347D3042F0A12DB00786648 /* HUD */, @@ -560,6 +591,7 @@ F3B312B82F30B0A10093B180 /* XSSearchSuggestionCell.swift */, F3B312B92F30B0A10093B180 /* XSSearchResultCell.swift */, F3F683EC2F56C380008AF250 /* XSHomeHistoryView.swift */, + F3F389232F6A8B89001B0E15 /* XSHomeCoinsPackButton.swift */, ); path = View; sourceTree = ""; @@ -718,6 +750,7 @@ F3F388D42F67DDBC001B0E15 /* XSMineWalletView.swift */, F3F388D62F67EDEC001B0E15 /* XSMineCoinsPackView.swift */, F3F388D82F67F9E1001B0E15 /* XSMineVipView.swift */, + F3F3892C2F6B86F7001B0E15 /* XSLoginView.swift */, ); path = View; sourceTree = ""; @@ -727,6 +760,7 @@ children = ( F35547D92F3DAACB006F28CD /* XSMineItem.swift */, F355483E2F52BD07006F28CD /* XSFeedbackCountModel.swift */, + F3F3892A2F6AAC25001B0E15 /* XSVersionUpdateModel.swift */, ); path = Model; sourceTree = ""; @@ -846,8 +880,9 @@ F3F388FD2F6931D1001B0E15 /* XSConsumptionRecordsViewController.swift */, F3F389052F6939EE001B0E15 /* XSRewardCoinsViewController.swift */, F3F389032F6939C7001B0E15 /* XSOrderRecordsViewController.swift */, - F3F3890B2F695055001B0E15 /* FACoinsRecordViewController.swift */, - F3F3890D2F695068001B0E15 /* FAVipRecordViewController.swift */, + F3F3890B2F695055001B0E15 /* XSCoinsOrderRecordViewController.swift */, + F3F3890D2F695068001B0E15 /* XSVipOrderRecordViewController.swift */, + F3F389212F6A4B8F001B0E15 /* XSCoinsPackViewController.swift */, ); path = Controller; sourceTree = ""; @@ -857,10 +892,18 @@ children = ( F3F388E22F680316001B0E15 /* XSStoreCoinsView.swift */, F3F388E42F68032A001B0E15 /* XSStoreVipView.swift */, + F3F389172F6A4407001B0E15 /* XSStoreCell.swift */, + F3F389192F6A444D001B0E15 /* XSStoreCoinsBigCell.swift */, + F3F3891B2F6A4465001B0E15 /* XSStoreCoinsSmallCell.swift */, + F3F3891D2F6A4484001B0E15 /* XSStoreCoinsSpreadCell.swift */, + F3F3891F2F6A487B001B0E15 /* XSStoreVipCell.swift */, F3F388F92F69240E001B0E15 /* XSWalletHeaderView.swift */, F3F388FB2F692CEF001B0E15 /* XSWalletCell.swift */, F3F388FF2F693293001B0E15 /* XSConsumptionRecordsCell.swift */, F3F389072F6940BB001B0E15 /* XSRewardCoinsCell.swift */, + F3F3890F2F695C5A001B0E15 /* XSSegmentedGradientIndicatorView.swift */, + F3F389112F6A37E3001B0E15 /* XSCoinsOrderRecordCell.swift */, + F3F389152F6A3AD3001B0E15 /* XSVipOrderRecordCell.swift */, ); path = View; sourceTree = ""; @@ -872,6 +915,7 @@ F3F388E62F6805DE001B0E15 /* XSPayItem.swift */, F3F389012F693785001B0E15 /* XSBuyRecordsModel.swift */, F3F389092F694109001B0E15 /* XSSendCoinRecordModel.swift */, + F3F389132F6A3812001B0E15 /* XSOrderRecordModel.swift */, ); path = Model; sourceTree = ""; @@ -888,6 +932,15 @@ path = IapManager; sourceTree = ""; }; + F3F389252F6A955D001B0E15 /* Alert */ = { + isa = PBXGroup; + children = ( + F3F389262F6A9597001B0E15 /* XSBaseAlert.swift */, + F3F389282F6A98C9001B0E15 /* XSAlert.swift */, + ); + path = Alert; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -1028,6 +1081,7 @@ F3F388EC2F680CEE001B0E15 /* XSIapManager.swift in Sources */, F347D3082F0A134500786648 /* XSHud.swift in Sources */, F347D29E2F03750000786648 /* XSTabBarItemContentView.swift in Sources */, + F3F3891E2F6A4484001B0E15 /* XSStoreCoinsSpreadCell.swift in Sources */, F347D2FE2F0A0D0700786648 /* XSUserDefaultsKey.swift in Sources */, F347D2EE2F0A06FB00786648 /* XSURLPath.swift in Sources */, F347D2DC2F04EE5F00786648 /* XSHomeRankingsCell.swift in Sources */, @@ -1044,21 +1098,24 @@ F347D2E82F0A03AB00786648 /* XSNetworkModel.swift in Sources */, F35547FE2F4EC450006F28CD /* XSMyCollectViewController.swift in Sources */, F347D29B2F03740000786648 /* XSConfig.swift in Sources */, + F3F3892D2F6B86F7001B0E15 /* XSLoginView.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 */, + F3F389242F6A8B89001B0E15 /* XSHomeCoinsPackButton.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 */, + F3F389272F6A9597001B0E15 /* XSBaseAlert.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 */, + F3F3890C2F695055001B0E15 /* XSCoinsOrderRecordViewController.swift in Sources */, F347D28D2F03709200786648 /* AppDelegate.swift in Sources */, F3585C492F14EE8D00EEC469 /* XSShortDetailPlayerCell.swift in Sources */, F35548412F52BE6D006F28CD /* XSBaseWebViewController+Script.swift in Sources */, @@ -1095,19 +1152,23 @@ F347D2E02F04F7ED00786648 /* XSHomeCategoriesCell.swift in Sources */, F3F3890A2F694109001B0E15 /* XSSendCoinRecordModel.swift in Sources */, F347D3032F0A10B600786648 /* XSCryptorService.swift in Sources */, + F3F3891C2F6A4465001B0E15 /* XSStoreCoinsSmallCell.swift in Sources */, F35547D42F3DA7A8006F28CD /* XSTableViewCell.swift in Sources */, F3F388F62F68F746001B0E15 /* XSPayDataRequest.swift in Sources */, F347D30E2F0A39DE00786648 /* XSHomeAPI.swift in Sources */, + F3F3891A2F6A444D001B0E15 /* XSStoreCoinsBigCell.swift in Sources */, F3B312B72F319CBE0093B180 /* XSEmpty.swift in Sources */, F347D2F62F0A0B0B00786648 /* XSLoginToken.swift in Sources */, F347D2CA2F03DC9200786648 /* CGMutablePath+XS.swift in Sources */, F347D2BE2F03C24B00786648 /* XSCollectionView.swift in Sources */, F35547DA2F3DAACB006F28CD /* XSMineItem.swift in Sources */, + F3F389182F6A4407001B0E15 /* XSStoreCell.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 */, + F3F3892B2F6AAC25001B0E15 /* XSVersionUpdateModel.swift in Sources */, F347D2EC2F0A060E00786648 /* XSNetworkTarget.swift in Sources */, F3F388E12F680090001B0E15 /* XSScrollView.swift in Sources */, F35547E92F3DDBDD006F28CD /* XSWebView.swift in Sources */, @@ -1121,24 +1182,31 @@ F3585C4B2F14FD1000EEC469 /* XSShortDetailPlayerControlView.swift in Sources */, F3F683ED2F56C380008AF250 /* XSHomeHistoryView.swift in Sources */, F347D2A12F03A84300786648 /* XSScreen.swift in Sources */, + F3F389202F6A487B001B0E15 /* XSStoreVipCell.swift in Sources */, F35548062F4FD6DA006F28CD /* XSMinePlayHistoryView.swift in Sources */, - F3F3890E2F695068001B0E15 /* FAVipRecordViewController.swift in Sources */, + F3F3890E2F695068001B0E15 /* XSVipOrderRecordViewController.swift in Sources */, F3F388E72F6805DE001B0E15 /* XSPayItem.swift in Sources */, + F3F389292F6A98C9001B0E15 /* XSAlert.swift in Sources */, F347D2992F03730E00786648 /* XSTabBarController.swift in Sources */, + F3F389122F6A37E3001B0E15 /* XSCoinsOrderRecordCell.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 */, + F3F389142F6A3812001B0E15 /* XSOrderRecordModel.swift in Sources */, F3B312B02F30ACF60093B180 /* XSSearchRecentView.swift in Sources */, F35548042F4EDF27006F28CD /* UIStackView+XS.swift in Sources */, F3B312B12F30ACF60093B180 /* XSSearchHeaderView.swift in Sources */, + F3F389162F6A3AD3001B0E15 /* XSVipOrderRecordCell.swift in Sources */, F3B312B22F30ACF60093B180 /* XSSearchHotListItemView.swift in Sources */, F3B312B32F30ACF60093B180 /* XSSearchGradientButton.swift in Sources */, F3B312B42F30ACF60093B180 /* XSSearchHotListCardView.swift in Sources */, F35547F62F3F0407006F28CD /* XSMyListHistoryView.swift in Sources */, F3B312BF2F30B2000093B180 /* XSSearchHistoryHotView.swift in Sources */, + F3F389222F6A4B8F001B0E15 /* XSCoinsPackViewController.swift in Sources */, + F3F389102F695C5A001B0E15 /* XSSegmentedGradientIndicatorView.swift in Sources */, F35548432F52BFB8006F28CD /* XSWebMessageModel.swift in Sources */, F3B312BD2F30B0A10093B180 /* XSSearchSuggestionCell.swift in Sources */, F3B312BE2F30B0A10093B180 /* XSSearchResultCell.swift in Sources */, diff --git a/XSeri/Base/Controller/XSTabBarController.swift b/XSeri/Base/Controller/XSTabBarController.swift index 0f562b0..a745366 100644 --- a/XSeri/Base/Controller/XSTabBarController.swift +++ b/XSeri/Base/Controller/XSTabBarController.swift @@ -102,3 +102,22 @@ class XSTabBarController: ESTabBarController { return nav } } + +extension XSTabBarController { + + func checkUpdates() { + + Task { + guard let model = await XSSettingAPI.requestVersionUpdateData() else { return } + + self.showUpdatesAlert(model) + } + + } + + private func showUpdatesAlert(_ model: XSVersionUpdateModel) { + + + + } +} diff --git a/XSeri/Base/Extension/String+XS.swift b/XSeri/Base/Extension/String+XS.swift index 1eded32..272db59 100644 --- a/XSeri/Base/Extension/String+XS.swift +++ b/XSeri/Base/Extension/String+XS.swift @@ -19,6 +19,15 @@ extension String: SmartCodable { func localizedReplace(text: String?) -> String { return self.localized.replacingOccurrences(of: "##", with: text ?? "") } + + func localizedReplace(text1: String, text2: String, text3: String? = nil) -> String { + var string = self.localized.replacingOccurrences(of: "#1#", with: text1) + string = string.replacingOccurrences(of: "#2#", with: text2) + if let text = text3 { + string = string.replacingOccurrences(of: "#3#", with: text) + } + return string + } } extension String { diff --git a/XSeri/Base/Networking/API/XSSettingAPI.swift b/XSeri/Base/Networking/API/XSSettingAPI.swift index 0f4168e..37d9b59 100644 --- a/XSeri/Base/Networking/API/XSSettingAPI.swift +++ b/XSeri/Base/Networking/API/XSSettingAPI.swift @@ -17,4 +17,14 @@ struct XSSettingAPI { let response: XSNetwork.Response = await XSNetwork.request(parameters: param) return response.data } + + static func requestVersionUpdateData() async -> XSVersionUpdateModel? { + + var param = XSNetwork.Parameters(path: "/customer/versionControl") + param.method = .get + param.isLoding = false + param.isToast = false + let response: XSNetwork.Response = await XSNetwork.request(parameters: param) + return response.data + } } diff --git a/XSeri/Base/Networking/API/XSStoreAPI.swift b/XSeri/Base/Networking/API/XSStoreAPI.swift index 88d7795..46b88aa 100644 --- a/XSeri/Base/Networking/API/XSStoreAPI.swift +++ b/XSeri/Base/Networking/API/XSStoreAPI.swift @@ -128,4 +128,19 @@ struct XSStoreAPI { let response: XSNetwork.Response> = await XSNetwork.request(parameters: param) return response.data } + + static func requestRechargeRecord(page: Int, buyType: BuyType) async -> XSNetwork.List? { + let parameters: [String : Any] = [ + "page_size" : 20, + "current_page" : page, + "buy_type" : buyType.rawValue + ] + + var param = XSNetwork.Parameters(path: "/getCustomerOrder") + param.method = .get + param.parameters = parameters + + let response: XSNetwork.Response> = await XSNetwork.request(parameters: param) + return response.data + } } diff --git a/XSeri/Base/View/XSScrollView.swift b/XSeri/Base/View/XSScrollView.swift index 1042ae6..f0d0da3 100644 --- a/XSeri/Base/View/XSScrollView.swift +++ b/XSeri/Base/View/XSScrollView.swift @@ -19,3 +19,16 @@ class XSScrollView: UIScrollView { } } + +class XSPagerScrollView: XSScrollView { + + override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + let result = super.gestureRecognizerShouldBegin(gestureRecognizer) + guard let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer else { return result } + let translation = panGestureRecognizer.translation(in: self) + if translation.x > 0, self.contentOffset.x == 0 { + return false + } + return result + } +} diff --git a/XSeri/Class/Home/Controller/XSHomeViewController.swift b/XSeri/Class/Home/Controller/XSHomeViewController.swift index 4abc69c..83eebf9 100644 --- a/XSeri/Class/Home/Controller/XSHomeViewController.swift +++ b/XSeri/Class/Home/Controller/XSHomeViewController.swift @@ -73,6 +73,16 @@ class XSHomeViewController: XSViewController { return view }() + private lazy var coinsPackButton: XSHomeCoinsPackButton = { + let view = XSHomeCoinsPackButton() + view.addAction(UIAction(handler: { [weak self] _ in + guard let self = self else { return } + let vc = XSCoinsPackViewController() + self.navigationController?.pushViewController(vc, animated: true) + }), for: .touchUpInside) + return view + }() + deinit { NotificationCenter.default.removeObserver(self) } @@ -121,6 +131,7 @@ extension XSHomeViewController { view.addSubview(searchButton) view.addSubview(segmentedView) view.addSubview(historyView) + view.addSubview(coinsPackButton) searchButton.snp.makeConstraints { make in make.left.equalToSuperview().offset(16) @@ -150,6 +161,11 @@ extension XSHomeViewController { make.bottom.equalToSuperview().offset(-(XSScreen.customTabBarHeight + 5)) } + coinsPackButton.snp.makeConstraints { make in + make.right.equalToSuperview().inset(10) + make.bottom.equalTo(historyView.snp.top).offset(-20) + } + } } diff --git a/XSeri/Class/Home/View/XSHomeCoinsPackButton.swift b/XSeri/Class/Home/View/XSHomeCoinsPackButton.swift new file mode 100644 index 0000000..e3a2279 --- /dev/null +++ b/XSeri/Class/Home/View/XSHomeCoinsPackButton.swift @@ -0,0 +1,51 @@ +// +// XSHomeCoinsPackButton.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/18. +// + +import UIKit +import SnapKit + +class XSHomeCoinsPackButton: UIControl { + + private lazy var bgImageView = UIImageView(image: UIImage(named: "calendar_icon_01")) + private lazy var textBgImageView = UIImageView(image: UIImage(named: "button_bg_image_02")) + + private lazy var textLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 10, weight: .black).withBoldItalic() + label.textColor = ._783902 + label.text = "Daily Coins".localized + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + addSubview(bgImageView) + addSubview(textBgImageView) + textBgImageView.addSubview(textLabel) + + bgImageView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + } + + textBgImageView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalToSuperview() + make.top.equalToSuperview().offset(50) + } + + textLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.centerX.equalToSuperview() + } + } + + 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 c9c267f..96b9764 100644 --- a/XSeri/Class/Mine/Controller/XSMineViewController.swift +++ b/XSeri/Class/Mine/Controller/XSMineViewController.swift @@ -135,6 +135,10 @@ extension XSMineViewController: UITableViewDelegate, UITableViewDataSource { let vc = XSFeedbackViewController() self.navigationController?.pushViewController(vc, animated: true) + case .wallet: + let vc = XSWalletViewController() + self.navigationController?.pushViewController(vc, animated: true) + default: break } @@ -145,6 +149,7 @@ extension XSMineViewController { private func createDataArr() { let arr = [ + XSMineItem(type: .wallet, iconImage: UIImage(named: "wallet_icon_01"), title: "My Wallet".localized), // XSMineItem(type: .setting, iconImage: UIImage(named: "setting_icon_01"), title: "Setting".localized), XSMineItem(type: .about, iconImage: UIImage(named: "about_icon_01"), title: "About".localized), XSMineItem(type: .feedback, iconImage: UIImage(named: "feedback_icon_01"), title: "Feedback".localized) diff --git a/XSeri/Class/Mine/Model/XSMineItem.swift b/XSeri/Class/Mine/Model/XSMineItem.swift index 792d9fe..b8b0a7a 100644 --- a/XSeri/Class/Mine/Model/XSMineItem.swift +++ b/XSeri/Class/Mine/Model/XSMineItem.swift @@ -15,6 +15,7 @@ struct XSMineItem { case feedback case web case safari + case wallet ///消费记录 case consumptionRecords ///购买记录 diff --git a/XSeri/Class/Mine/Model/XSVersionUpdateModel.swift b/XSeri/Class/Mine/Model/XSVersionUpdateModel.swift new file mode 100644 index 0000000..53969c8 --- /dev/null +++ b/XSeri/Class/Mine/Model/XSVersionUpdateModel.swift @@ -0,0 +1,36 @@ +// +// XSVersionUpdateModel.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/18. +// + +import UIKit +import SmartCodable + +struct XSVersionUpdateModel: SmartCodable { + + var version_code: String? + var des: String? + var version_name: String? + var force: Bool? + + + func canUpdate() -> Bool { + guard let version = version_code else { return false } + + let result = kXSVersion.compare(version, options: .numeric) + if result == .orderedAscending { + return true + } else { + return false + } + } + + static func mappingForKey() -> [SmartKeyTransformer]? { + return [ + CodingKeys.des <--- ["description"] + ] + } + +} diff --git a/XSeri/Class/Mine/View/XSLoginView.swift b/XSeri/Class/Mine/View/XSLoginView.swift new file mode 100644 index 0000000..08fdab4 --- /dev/null +++ b/XSeri/Class/Mine/View/XSLoginView.swift @@ -0,0 +1,175 @@ +// +// XSLoginView.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/19. +// + +import UIKit +import SnapKit +import YYText + +class XSLoginView: XSPanModalContentView { + + + private lazy var bgView: UIImageView = { + let view = UIImageView(image: UIImage(named: "bg_image_01")) + return view + }() + + private lazy var closeButton: UIButton = { + let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + self.dismiss(animated: true) { } + })) + button.setImage(UIImage(named: "close_icon_02"), for: .normal) + return button + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 18, weight: .semibold) + label.textColor = .white + label.text = "Welcome to XSeri" + return label + }() + + private lazy var stackView: UIStackView = { + let view = UIStackView(arrangedSubviews: [facebookButton, appleButton]) + view.axis = .vertical + view.spacing = 18 + return view + }() + + private lazy var facebookButton: UIButton = { + var configuration = UIButton.Configuration.plain() + configuration.background.cornerRadius = 8 + configuration.background.backgroundColor = ._0866_FF + configuration.imagePadding = 12 + configuration.image = UIImage(named: "facebook_icon_01") + configuration.attributedTitle = AttributedString("Login with Facebook".localized, attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 14, weight: .semibold), + .foregroundColor : UIColor.white + ])) + + let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + + })) + button.snp.makeConstraints { make in + make.height.equalTo(48) + } + + return button + }() + + private lazy var appleButton: UIButton = { + var configuration = UIButton.Configuration.plain() + configuration.background.cornerRadius = 8 + configuration.background.backgroundColor = .white + configuration.imagePadding = 12 + configuration.image = UIImage(named: "apple_icon_01") + configuration.attributedTitle = AttributedString("Login with Apple".localized, attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 14, weight: .semibold), + .foregroundColor : UIColor._333333 + ])) + + let button = UIButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + + })) + button.snp.makeConstraints { make in + make.height.equalTo(48) + } + + return button + }() + + private lazy var agreementLabel: YYLabel = { + let userAgreementText = "User Agreement".localized + let privacyPolicyText = "Privacy Policy".localized + let text = "By continuing, you agree to the\n#1# and #2#".localizedReplace(text1: userAgreementText, text2: privacyPolicyText) + + let userAgreementRange = (text as NSString).range(of: userAgreementText) + let privacyPolicyRange = (text as NSString).range(of: privacyPolicyText) + + let string = NSMutableAttributedString(string: text, attributes: [ + .font : UIFont.font(ofSize: 10, weight: .semibold), + .foregroundColor : UIColor.FFDAA_4 + ]) + + string.yy_setTextHighlight(userAgreementRange, color: .white, backgroundColor: nil) { [weak self] _, _, _, _ in + guard let self = self else { return } + let vc = XSBaseWebViewController() + vc.webUrl = kXSUserAgreementWebUrl + XSTool.topViewController?.navigationController?.pushViewController(vc, animated: true) + self.dismiss(animated: true) { + + } + } + + string.yy_setTextHighlight(privacyPolicyRange, color: .white, backgroundColor: nil) { [weak self] _, _, _, _ in + guard let self = self else { return } + let vc = XSBaseWebViewController() + vc.webUrl = kXSPrivacyPolicyWebUrl + XSTool.topViewController?.navigationController?.pushViewController(vc, animated: true) + self.dismiss(animated: true) { + + } + } + + let label = YYLabel() + label.attributedText = string + label.textAlignment = .center + label.numberOfLines = 0 + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + self.contentHeight = XSScreen.safeBottom + 320 + + xs_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + + +extension XSLoginView { + private func xs_setupUI() { + addSubview(bgView) + addSubview(closeButton) + addSubview(titleLabel) + addSubview(stackView) + addSubview(agreementLabel) + + bgView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + } + + closeButton.snp.makeConstraints { make in + make.right.equalToSuperview().inset(23) + make.top.equalToSuperview().offset(20) + } + + titleLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(closeButton.snp.bottom).offset(12) + } + + stackView.snp.makeConstraints { make in + make.width.equalTo(300) + make.centerX.equalToSuperview() + make.top.equalTo(closeButton.snp.bottom).offset(75) + } + + agreementLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalToSuperview().inset(XSScreen.safeBottom + 20) + } + } +} diff --git a/XSeri/Class/Mine/View/XSMineCoinsPackView.swift b/XSeri/Class/Mine/View/XSMineCoinsPackView.swift index ae737ad..b1539c6 100644 --- a/XSeri/Class/Mine/View/XSMineCoinsPackView.swift +++ b/XSeri/Class/Mine/View/XSMineCoinsPackView.swift @@ -44,6 +44,14 @@ class XSMineCoinsPackView: UIView { override init(frame: CGRect) { super.init(frame: frame) + + let tap = UITapGestureRecognizer { [weak self] _ in + guard let self = self else { return } + let vc = XSCoinsPackViewController() + self.viewController?.navigationController?.pushViewController(vc, animated: true) + } + bgView.addGestureRecognizer(tap) + xs_setupUI() } diff --git a/XSeri/Class/Mine/View/XSMineUserInfoView.swift b/XSeri/Class/Mine/View/XSMineUserInfoView.swift index f37e714..ffc7fdc 100644 --- a/XSeri/Class/Mine/View/XSMineUserInfoView.swift +++ b/XSeri/Class/Mine/View/XSMineUserInfoView.swift @@ -15,6 +15,7 @@ class XSMineUserInfoView: UIView { avatarImageView.xs_setImage(userInfo?.avator, placeholder: UIImage(named: "avatar_placeholder_icon_01")) nickNameLabel.text = userInfo?.getNickName() idLabel.text = "ID: \(userInfo?.customer_id ?? "")" + loginButton.isHidden = !(userInfo?.is_tourist ?? true) } } @@ -38,6 +39,22 @@ class XSMineUserInfoView: UIView { return label }() + private lazy var loginButton: UIButton = { + let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + let view = XSLoginView() + view.present(in: nil) + })) + button.layer.cornerRadius = 16 + button.layer.masksToBounds = true + button.layer.borderWidth = 1 + button.layer.borderColor = UIColor.white.withAlphaComponent(0.14).cgColor + button.setTitle("Login".localized, for: .normal) + button.setTitleColor(.white.withAlphaComponent(0.7), for: .normal) + button.titleLabel?.font = .font(ofSize: 14, weight: .semibold) + return button + }() + override init(frame: CGRect) { super.init(frame: frame) @@ -56,6 +73,7 @@ extension XSMineUserInfoView { addSubview(avatarImageView) addSubview(nickNameLabel) addSubview(idLabel) + addSubview(loginButton) avatarImageView.snp.makeConstraints { make in make.left.equalToSuperview().offset(16) @@ -74,6 +92,13 @@ extension XSMineUserInfoView { make.bottom.equalTo(avatarImageView).offset(-6) } + loginButton.snp.makeConstraints { make in + make.centerY.equalTo(avatarImageView) + make.right.equalToSuperview().inset(16) + make.width.equalTo(86) + make.height.equalTo(32) + } + } } diff --git a/XSeri/Class/Store/Controller/FACoinsRecordViewController.swift b/XSeri/Class/Store/Controller/FACoinsRecordViewController.swift deleted file mode 100644 index e6ea51f..0000000 --- a/XSeri/Class/Store/Controller/FACoinsRecordViewController.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// 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 deleted file mode 100644 index dec6421..0000000 --- a/XSeri/Class/Store/Controller/FAVipRecordViewController.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// 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/XSCoinsOrderRecordViewController.swift b/XSeri/Class/Store/Controller/XSCoinsOrderRecordViewController.swift new file mode 100644 index 0000000..092c792 --- /dev/null +++ b/XSeri/Class/Store/Controller/XSCoinsOrderRecordViewController.swift @@ -0,0 +1,108 @@ +// +// XSCoinsOrderRecordViewController.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import SnapKit + +class XSCoinsOrderRecordViewController: XSViewController { + + private lazy var dataArr: [XSOrderRecordModel] = [] + private lazy var page = 1 + + private lazy var tableView: XSTableView = { + let tableView = XSTableView(frame: .zero, style: .plain) + tableView.delegate = self + tableView.dataSource = self + tableView.rowHeight = 70 + 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(XSCoinsOrderRecordCell.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.view.backgroundColor = .clear + + xs_setupUI() + + Task { + await requestDataArr(page: 1) + } + } + + 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 XSCoinsOrderRecordViewController { + + private func xs_setupUI() { + view.addSubview(tableView) + + tableView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().offset(1) + } + } + +} + + +//MARK: UITableViewDelegate UITableViewDataSource +extension XSCoinsOrderRecordViewController: UITableViewDelegate, UITableViewDataSource { + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! XSCoinsOrderRecordCell + cell.model = dataArr[indexPath.row] + return cell + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return self.dataArr.count + } + +} + +extension XSCoinsOrderRecordViewController { + + private func requestDataArr(page: Int) async { + + guard let listModel = await XSStoreAPI.requestRechargeRecord(page: page, buyType: .coins) 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/XSCoinsPackViewController.swift b/XSeri/Class/Store/Controller/XSCoinsPackViewController.swift new file mode 100644 index 0000000..53db6ef --- /dev/null +++ b/XSeri/Class/Store/Controller/XSCoinsPackViewController.swift @@ -0,0 +1,25 @@ +// +// XSCoinsPackViewController.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/18. +// + +import UIKit + +class XSCoinsPackViewController: XSViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + + } + + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.setNavigationBarHidden(false, animated: true) + xs_setNavigationStyle() + } + +} diff --git a/XSeri/Class/Store/Controller/XSOrderRecordsViewController.swift b/XSeri/Class/Store/Controller/XSOrderRecordsViewController.swift index b477d7b..01a9146 100644 --- a/XSeri/Class/Store/Controller/XSOrderRecordsViewController.swift +++ b/XSeri/Class/Store/Controller/XSOrderRecordsViewController.swift @@ -10,38 +10,65 @@ import SnapKit import JXSegmentedView class XSOrderRecordsViewController: XSViewController { - - + + private var segmentedItemWidth: CGFloat = 0 + private lazy var viewControllers: [XSViewController] = { - return [FACoinsRecordViewController(), FAVipRecordViewController()] + return [XSCoinsOrderRecordViewController(), XSVipOrderRecordViewController()] }() - + private lazy var bgImageView: UIImageView = { let imageView = UIImageView(image: UIImage(named: "bg_image_01")) return imageView }() - + + private lazy var segmentedContainerView: UIView = { + let view = UIView() + view.backgroundColor = .white.withAlphaComponent(0.16) + view.layer.cornerRadius = 16 + view.layer.masksToBounds = true + return view + }() + /// 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.titleSelectedColor = ._00181_A + dataSource.titleNormalFont = .font(ofSize: 16, weight: .semibold) dataSource.titleSelectedFont = .font(ofSize: 16, weight: .semibold) dataSource.isTitleColorGradientEnabled = true - dataSource.itemSpacing = 24 + dataSource.isItemSpacingAverageEnabled = false + dataSource.itemSpacing = 0 return dataSource }() - + + private lazy var segmentedIndicator: XSSegmentedGradientIndicatorView = { + let indicator = XSSegmentedGradientIndicatorView() + indicator.gradientColors = [ + UIColor.F_5_BD_7_E.cgColor, + UIColor.FFEABC.cgColor, + UIColor.FFCF_99.cgColor + ] + indicator.gradientLocations = [0, 0.5, 1] + indicator.startPoint = CGPoint(x: 0, y: 0.5) + indicator.endPoint = CGPoint(x: 1, y: 0.5) + indicator.indicatorHeight = 36 + indicator.indicatorCornerRadius = 18 + indicator.indicatorWidthIncrement = 0 + return indicator + }() + /// 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.contentEdgeInsetLeft = 6 + view.contentEdgeInsetRight = 6 + view.indicators = [segmentedIndicator] view.backgroundColor = .clear return view }() @@ -53,42 +80,67 @@ class XSOrderRecordsViewController: XSViewController { override func viewDidLoad() { super.viewDidLoad() - + self.title = "Order Records".localized + self.view.backgroundColor = .black xs_setupUI() } - - + + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.navigationController?.setNavigationBarHidden(false, animated: true) xs_setNavigationStyle() } + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + let horizontalInset = segmentedView.contentEdgeInsetLeft + segmentedView.contentEdgeInsetRight + let availableWidth = segmentedContainerView.bounds.width - horizontalInset + let itemWidth = floor(availableWidth / CGFloat(max(segmentedDataSource.titles.count, 1))) + guard itemWidth > 0, segmentedItemWidth != itemWidth else { return } + segmentedItemWidth = itemWidth + segmentedDataSource.itemWidth = itemWidth + segmentedView.reloadDataWithoutListContainer() + } + } extension XSOrderRecordsViewController { - + private func xs_setupUI() { view.addSubview(bgImageView) + view.addSubview(segmentedContainerView) + segmentedContainerView.addSubview(segmentedView) view.addSubview(listContainerView) - + bgImageView.snp.makeConstraints { make in make.left.right.top.equalToSuperview() } - + + segmentedContainerView.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(16) + make.top.equalTo(self.view.safeAreaLayoutGuide).offset(10) + make.height.equalTo(48) + } + + segmentedView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + listContainerView.snp.makeConstraints { make in make.left.right.bottom.equalToSuperview() - make.top.equalTo(self.view.safeAreaLayoutGuide) + make.top.equalTo(segmentedContainerView.snp.bottom).offset(8) } } - + } // MARK: - JXSegmentedViewDelegate extension XSOrderRecordsViewController: JXSegmentedViewDelegate { func segmentedView(_ segmentedView: JXSegmentedView, didSelectedItemAt index: Int) { - + } } @@ -101,4 +153,9 @@ extension XSOrderRecordsViewController: JXSegmentedListContainerViewDataSource { func listContainerView(_ listContainerView: JXSegmentedListContainerView, initListAt index: Int) -> JXSegmentedListContainerViewListDelegate { return viewControllers[index] } + + func scrollViewClass(in listContainerView: JXSegmentedListContainerView) -> AnyClass { + return XSPagerScrollView.self + } } + diff --git a/XSeri/Class/Store/Controller/XSStoreViewController.swift b/XSeri/Class/Store/Controller/XSStoreViewController.swift index 3fc6384..dfe8631 100644 --- a/XSeri/Class/Store/Controller/XSStoreViewController.swift +++ b/XSeri/Class/Store/Controller/XSStoreViewController.swift @@ -9,6 +9,9 @@ import UIKit class XSStoreViewController: XSViewController { + private var payRequest: XSPayDataRequest? + + private var payDataModel: XSPayDateModel? = XSIapManager.manager.payDateModel private lazy var scrollView: XSScrollView = { let scrollView = XSScrollView() @@ -22,12 +25,32 @@ class XSStoreViewController: XSViewController { return view }() + private lazy var coinsView: XSStoreCoinsView = { + let view = XSStoreCoinsView() + view.buyFinishHandle = { [weak self] in + self?.buyFinish() + } + return view + }() + + private lazy var vipView: XSStoreVipView = { + let view = XSStoreVipView() + view.buyFinishHandle = { [weak self] in + self?.buyFinish() + } + return view + }() + override func viewDidLoad() { super.viewDidLoad() self.title = "Store".localized xs_setupUI() + + reloadData() + + requestPayData() } @@ -36,6 +59,50 @@ class XSStoreViewController: XSViewController { self.navigationController?.setNavigationBarHidden(false, animated: true) xs_setNavigationStyle() } + + func reloadData() { + self.stackView.xs_removeAllArrangedSubview() + guard let model = self.payDataModel else { return } + + if let sort = model.sort, sort.count > 0 { + sort.forEach { + if $0 == .vip { + self.addVipView() + } else if $0 == .coin { + self.addCoinsView() + } + } + } else { + self.addVipView() + self.addCoinsView() + } +// self.stackView.addArrangedSubview(tipView) + } + + private func addCoinsView() { + guard let model = self.payDataModel else { return } + + var newList: [XSPayItem] = [] + if let list = model.list_coins, list.count > 0 { + newList = list + } + if newList.count > 0 { + coinsView.setDataArr(newList) + self.stackView.addArrangedSubview(coinsView) + } + } + + private func addVipView() { + guard let list = payDataModel?.list_sub_vip else { return } + guard list.count > 0 else { return } + + self.vipView.dataArr = list + self.stackView.addArrangedSubview(self.vipView) + } + + private func buyFinish() { + self.requestPayData() + } } @@ -58,3 +125,21 @@ extension XSStoreViewController { } } + + +extension XSStoreViewController { + + private func requestPayData() { + payRequest = XSPayDataRequest() + + payRequest?.requestProducts { [weak self] model in + guard let self = self else { return } + guard let model = model else { return } + + self.payDataModel = model + + self.reloadData() + } + + } +} diff --git a/XSeri/Class/Store/Controller/XSVipOrderRecordViewController.swift b/XSeri/Class/Store/Controller/XSVipOrderRecordViewController.swift new file mode 100644 index 0000000..96b65ba --- /dev/null +++ b/XSeri/Class/Store/Controller/XSVipOrderRecordViewController.swift @@ -0,0 +1,107 @@ +// +// XSVipOrderRecordViewController.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit + +class XSVipOrderRecordViewController: XSViewController { + + private lazy var dataArr: [XSOrderRecordModel] = [] + private lazy var page = 1 + + private lazy var tableView: XSTableView = { + let tableView = XSTableView(frame: .zero, style: .plain) + tableView.delegate = self + tableView.dataSource = self + tableView.rowHeight = 70 + 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(XSVipOrderRecordCell.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.view.backgroundColor = .clear + + xs_setupUI() + + Task { + await requestDataArr(page: 1) + } + } + + 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 XSVipOrderRecordViewController { + + private func xs_setupUI() { + view.addSubview(tableView) + + tableView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().offset(1) + } + } + +} + + +//MARK: UITableViewDelegate UITableViewDataSource +extension XSVipOrderRecordViewController: UITableViewDelegate, UITableViewDataSource { + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! XSVipOrderRecordCell + cell.model = dataArr[indexPath.row] + return cell + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return self.dataArr.count + } + +} + +extension XSVipOrderRecordViewController { + + private func requestDataArr(page: Int) async { + + guard let listModel = await XSStoreAPI.requestRechargeRecord(page: page, buyType: .vip) 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/XSWalletViewController.swift b/XSeri/Class/Store/Controller/XSWalletViewController.swift index de70479..84202b4 100644 --- a/XSeri/Class/Store/Controller/XSWalletViewController.swift +++ b/XSeri/Class/Store/Controller/XSWalletViewController.swift @@ -46,6 +46,7 @@ class XSWalletViewController: XSViewController { override func viewDidLoad() { super.viewDidLoad() self.title = "Details".localized + self.view.backgroundColor = .black NotificationCenter.default.addObserver(self, selector: #selector(loginStateDidChangeNotification), name: XSLoginManager.loginStateDidChangeNotification, object: nil) xs_setupUI() @@ -124,6 +125,9 @@ extension XSWalletViewController: UITableViewDelegate, UITableViewDataSource { case .rewardCoins: vc = XSRewardCoinsViewController() + case .feedback: + vc = XSFeedbackViewController() + default: break } diff --git a/XSeri/Class/Store/Model/XSOrderRecordModel.swift b/XSeri/Class/Store/Model/XSOrderRecordModel.swift new file mode 100644 index 0000000..0ae7a53 --- /dev/null +++ b/XSeri/Class/Store/Model/XSOrderRecordModel.swift @@ -0,0 +1,16 @@ +// +// XSOrderRecordModel.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/18. +// + +import UIKit +import SmartCodable + +struct XSOrderRecordModel: SmartCodable { + + var type: String? + var value: String? + var created_at: String? +} diff --git a/XSeri/Class/Store/View/XSCoinsOrderRecordCell.swift b/XSeri/Class/Store/View/XSCoinsOrderRecordCell.swift new file mode 100644 index 0000000..08ebba8 --- /dev/null +++ b/XSeri/Class/Store/View/XSCoinsOrderRecordCell.swift @@ -0,0 +1,80 @@ +// +// XSCoinsOrderRecordCell.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/18. +// + +import UIKit +import SnapKit + +class XSCoinsOrderRecordCell: XSTableViewCell { + + var model: XSOrderRecordModel? { + didSet { + titleLabel.text = model?.type + dateLabel.text = model?.created_at + coinsLabel.text = "+\(model?.value ?? "0")" + } + } + + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .semibold) + label.textColor = .white + return label + }() + + private lazy var dateLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .regular) + label.textColor = .FFDAA_4 + return label + }() + + private lazy var coinsIconImageView = UIImageView(image: UIImage(named: "coins_icon_03")) + + private lazy var coinsLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .semibold) + label.textColor = .white + return label + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + + contentView.addSubview(titleLabel) + contentView.addSubview(dateLabel) + contentView.addSubview(coinsIconImageView) + contentView.addSubview(coinsLabel) + + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(32) + make.top.equalToSuperview().offset(15) + } + + dateLabel.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.bottom.equalToSuperview().offset(-15) + } + + coinsLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().offset(-25) + } + + coinsIconImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalTo(coinsLabel.snp.left).offset(-4) + } + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/XSeri/Class/Store/View/XSSegmentedGradientIndicatorView.swift b/XSeri/Class/Store/View/XSSegmentedGradientIndicatorView.swift new file mode 100644 index 0000000..0e32060 --- /dev/null +++ b/XSeri/Class/Store/View/XSSegmentedGradientIndicatorView.swift @@ -0,0 +1,57 @@ +// +// XSSegmentedGradientIndicatorView.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/17. +// + +import UIKit +import JXSegmentedView + +class XSSegmentedGradientIndicatorView: JXSegmentedIndicatorBackgroundView { + + var gradientColors: [CGColor] = [] { + didSet { + gradientLayer.colors = gradientColors + } + } + + var gradientLocations: [NSNumber]? { + didSet { + gradientLayer.locations = gradientLocations + } + } + + var startPoint: CGPoint = CGPoint(x: 0, y: 0.5) { + didSet { + gradientLayer.startPoint = startPoint + } + } + + var endPoint: CGPoint = CGPoint(x: 1, y: 0.5) { + didSet { + gradientLayer.endPoint = endPoint + } + } + + private let gradientLayer = CAGradientLayer() + + override func commonInit() { + super.commonInit() + indicatorColor = .clear + backgroundColor = .clear + layer.insertSublayer(gradientLayer, at: 0) + } + + override func layoutSubviews() { + super.layoutSubviews() + backgroundColor = .clear + gradientLayer.frame = bounds + gradientLayer.cornerRadius = layer.cornerRadius + gradientLayer.colors = gradientColors + gradientLayer.locations = gradientLocations + gradientLayer.startPoint = startPoint + gradientLayer.endPoint = endPoint + } + +} diff --git a/XSeri/Class/Store/View/XSStoreCell.swift b/XSeri/Class/Store/View/XSStoreCell.swift new file mode 100644 index 0000000..b7462ba --- /dev/null +++ b/XSeri/Class/Store/View/XSStoreCell.swift @@ -0,0 +1,15 @@ +// +// XSStoreCell.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/18. +// + +import UIKit + +class XSStoreCell: UICollectionViewCell { + + var item: XSPayItem? + + var xs_isSelected: Bool? +} diff --git a/XSeri/Class/Store/View/XSStoreCoinsBigCell.swift b/XSeri/Class/Store/View/XSStoreCoinsBigCell.swift new file mode 100644 index 0000000..a64e29a --- /dev/null +++ b/XSeri/Class/Store/View/XSStoreCoinsBigCell.swift @@ -0,0 +1,29 @@ +// +// XSStoreCoinsBigCell.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/18. +// + +import UIKit + +class XSStoreCoinsBigCell: XSStoreCell { + + + private lazy var bgView: XSView = { + let view = XSView() + view.xs_colors = [] + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/XSeri/Class/Store/View/XSStoreCoinsSmallCell.swift b/XSeri/Class/Store/View/XSStoreCoinsSmallCell.swift new file mode 100644 index 0000000..26fea94 --- /dev/null +++ b/XSeri/Class/Store/View/XSStoreCoinsSmallCell.swift @@ -0,0 +1,12 @@ +// +// XSStoreCoinsSmallCell.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/18. +// + +import UIKit + +class XSStoreCoinsSmallCell: XSStoreCell { + +} diff --git a/XSeri/Class/Store/View/XSStoreCoinsSpreadCell.swift b/XSeri/Class/Store/View/XSStoreCoinsSpreadCell.swift new file mode 100644 index 0000000..2b5941c --- /dev/null +++ b/XSeri/Class/Store/View/XSStoreCoinsSpreadCell.swift @@ -0,0 +1,12 @@ +// +// XSStoreCoinsSpreadCell.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/18. +// + +import UIKit + +class XSStoreCoinsSpreadCell: XSStoreCell { + +} diff --git a/XSeri/Class/Store/View/XSStoreCoinsView.swift b/XSeri/Class/Store/View/XSStoreCoinsView.swift index bc14eaa..0ec3dc3 100644 --- a/XSeri/Class/Store/View/XSStoreCoinsView.swift +++ b/XSeri/Class/Store/View/XSStoreCoinsView.swift @@ -10,6 +10,14 @@ import SnapKit class XSStoreCoinsView: UIView { + var buyFinishHandle: (() -> Void)? + + var shortPlayId: String? + var videoId: String? + + private lazy var dataArr: [[XSPayItem]] = [] + private var selectedIndexPath: IndexPath? + private lazy var titleLabel: UILabel = { let label = UILabel() label.font = .font(ofSize: 16, weight: .bold) @@ -18,39 +26,43 @@ class XSStoreCoinsView: UIView { 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 -// }() + private lazy var collectionViewLayout: UICollectionViewCompositionalLayout = { + let config = UICollectionViewCompositionalLayoutConfiguration() + config.interSectionSpacing = 12 + + let layout = UICollectionViewCompositionalLayout { [weak self] section, environment in + guard let self = self else { return nil} + guard let model = dataArr[section].first else { return nil } + + if model.size == .spread { + return self.spreadLayoutSection(environment) + } else if model.size == .big { + return self.bigLayoutSection(environment) + } else { + return self.smallLayoutSection(environment) + } + } + layout.configuration = config + + return layout + }() + + private lazy var collectionView: XSCollectionView = { + let collectionView = XSCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.clipsToBounds = false + collectionView.isScrollEnabled = false + collectionView.register(XSStoreCoinsBigCell.self, forCellWithReuseIdentifier: XSPayItem.SizeType.big.rawValue) + collectionView.register(XSStoreCoinsSmallCell.self, forCellWithReuseIdentifier: XSPayItem.SizeType.small.rawValue) + collectionView.register(XSStoreCoinsSpreadCell.self, forCellWithReuseIdentifier: XSPayItem.SizeType.spread.rawValue) + collectionView.addObserver(self, forKeyPath: "contentSize", context: nil) + return collectionView + }() + + deinit { + collectionView.removeObserver(self, forKeyPath: "contentSize") + } override init(frame: CGRect) { super.init(frame: frame) @@ -61,17 +73,153 @@ class XSStoreCoinsView: UIView { fatalError("init(coder:) has not been implemented") } + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "contentSize" { + let height = self.collectionView.contentSize.height + 1 + self.collectionView.snp.updateConstraints { make in + make.height.equalTo(height) + } + } + } + + func setDataArr(_ arr: [XSPayItem]) { + self.dataArr.removeAll() + var bigArr: [XSPayItem] = [] + var smallArr: [XSPayItem] = [] + var spreadArr: [XSPayItem] = [] + + arr.forEach { + if $0.size == .spread { + spreadArr.append($0) + } else if $0.size == .big { + bigArr.append($0) + } else { + smallArr.append($0) + } + } + + if bigArr.count > 0 { + self.dataArr.append(bigArr) + } + if spreadArr.count > 0 { + self.dataArr.append(spreadArr) + } + if smallArr.count > 0 { + self.dataArr.append(smallArr) + } + self.collectionView.reloadData() + } + } extension XSStoreCoinsView { private func xs_setupUI() { addSubview(titleLabel) + addSubview(collectionView) titleLabel.snp.makeConstraints { make in make.left.equalToSuperview().offset(16) make.top.equalToSuperview() } + + collectionView.snp.makeConstraints { make in + make.top.equalToSuperview().offset(36) + make.left.right.equalToSuperview().inset(16) + make.height.equalTo(1) + } } } + +extension XSStoreCoinsView { + + private func bigLayoutSection(_ environment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { + let contentWidth = environment.container.effectiveContentSize.width + let itemWidth = floor((contentWidth - 15) / 2) + + let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .absolute(itemWidth), heightDimension: .fractionalHeight(1))) + + let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(84)), subitems: [item]) + group.interItemSpacing = .fixed(15) + + let layoutSection = NSCollectionLayoutSection(group: group) + layoutSection.interGroupSpacing = 12 + layoutSection.contentInsets = .zero + return layoutSection + } + + private func smallLayoutSection(_ environment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { + let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1 / 3), heightDimension: .fractionalHeight(1))) + + let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(121)), subitems: [item]) + group.interItemSpacing = .fixed(8) + + let layoutSection = NSCollectionLayoutSection(group: group) + layoutSection.interGroupSpacing = 10 + layoutSection.contentInsets = .zero + return layoutSection + } + + private func spreadLayoutSection(_ environment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { + let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))) + + let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(84)), subitems: [item]) + + + let layoutSection = NSCollectionLayoutSection(group: group) + layoutSection.interGroupSpacing = 10 + layoutSection.contentInsets = .zero + return layoutSection + } +} + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension XSStoreCoinsView: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let item = dataArr[indexPath.section][indexPath.row] + var identifier = item.size?.rawValue ?? "" + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! XSStoreCell + cell.item = item + cell.xs_isSelected = selectedIndexPath == indexPath + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return dataArr[section].count + } + + func numberOfSections(in collectionView: UICollectionView) -> Int { + return dataArr.count + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let model = dataArr[indexPath.section][indexPath.row] + self.selectedIndexPath = indexPath + collectionView.reloadData() + +// if model.buy_type == .subCoins { +// let view = FACoinPackConfirmView() +// view.shortPlayId = self.shortPlayId +// view.videoId = self.videoId +// view.model = model +// view.buyFinishHandle = { [weak self] in +// guard let self = self else { return } +// FALogin.manager.requestUserInfo(completer: nil) +// self.buyFinishHandle?() +// } +// view.present(in: nil) +// } else { +// FAIapManager.manager.start(model: model, shortPlayId: self.shortPlayId, videoId: self.videoId) { [weak self] finish in +// guard let self = self else { return } +// if finish { +// FALogin.manager.requestUserInfo(completer: nil) +// self.buyFinishHandle?() +// } +// } +// } + + + } +} diff --git a/XSeri/Class/Store/View/XSStoreVipCell.swift b/XSeri/Class/Store/View/XSStoreVipCell.swift new file mode 100644 index 0000000..57b3a35 --- /dev/null +++ b/XSeri/Class/Store/View/XSStoreVipCell.swift @@ -0,0 +1,12 @@ +// +// XSStoreVipCell.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/18. +// + +import UIKit + +class XSStoreVipCell: XSStoreCell { + +} diff --git a/XSeri/Class/Store/View/XSStoreVipView.swift b/XSeri/Class/Store/View/XSStoreVipView.swift index 57e9861..2921c44 100644 --- a/XSeri/Class/Store/View/XSStoreVipView.swift +++ b/XSeri/Class/Store/View/XSStoreVipView.swift @@ -6,9 +6,140 @@ // import UIKit +import SnapKit class XSStoreVipView: UIView { - + var buyFinishHandle: (() -> Void)? + var shortPlayId: String? + var videoId: String? + + var dataArr: [XSPayItem] = [] { + didSet { + collectionView.reloadData() + } + } + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .bold) + label.textColor = .white + label.text = "VIP Membership".localized + return label + }() + + private lazy var subtitleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .regular) + label.textColor = .white.withAlphaComponent(0.5) + label.text = "Auto renew,cancel anytime".localized + return label + }() + + private lazy var collectionViewLayout: UICollectionViewCompositionalLayout = { + + let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))) + + let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(110)), subitems: [item]) + + let layoutSection = NSCollectionLayoutSection(group: group) + layoutSection.interGroupSpacing = 14 + layoutSection.contentInsets = .init(top: 0, leading: 16, bottom: 0, trailing: 16) + + + let config = UICollectionViewCompositionalLayoutConfiguration() + + let layout = UICollectionViewCompositionalLayout(section: layoutSection) + layout.configuration = config + + return layout + }() + + private lazy var collectionView: XSCollectionView = { + let collectionView = XSCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.isScrollEnabled = false + collectionView.addObserver(self, forKeyPath: "contentSize", context: nil) + collectionView.register(XSStoreVipCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + deinit { + collectionView.removeObserver(self, forKeyPath: "contentSize") + } + + + override init(frame: CGRect) { + super.init(frame: frame) + xs_setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "contentSize" { + let height = self.collectionView.contentSize.height + 1 + self.collectionView.snp.updateConstraints { make in + make.height.equalTo(height) + } + } + } } + +extension XSStoreVipView { + + private func xs_setupUI() { + addSubview(titleLabel) + addSubview(subtitleLabel) + addSubview(collectionView) + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.top.equalToSuperview() + } + + subtitleLabel.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.top.equalTo(titleLabel.snp.bottom).offset(0) + } + + collectionView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().offset(54) + make.height.equalTo(1) + } + } + +} + + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension XSStoreVipView: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! XSStoreVipCell + cell.item = self.dataArr[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return dataArr.count + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let model = self.dataArr[indexPath.row] + +// FAIapManager.manager.start(model: model, shortPlayId: self.shortPlayId, videoId: self.videoId) { [weak self] finish in +// guard let self = self else { return } +// if finish { +// FALogin.manager.requestUserInfo(completer: nil) +// self.buyFinishHandle?() +// } +// } + } +} + diff --git a/XSeri/Class/Store/View/XSVipOrderRecordCell.swift b/XSeri/Class/Store/View/XSVipOrderRecordCell.swift new file mode 100644 index 0000000..ec2f142 --- /dev/null +++ b/XSeri/Class/Store/View/XSVipOrderRecordCell.swift @@ -0,0 +1,71 @@ +// +// XSVipOrderRecordCell.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/18. +// + +import UIKit + +class XSVipOrderRecordCell: XSTableViewCell { + + var model: XSOrderRecordModel? { + didSet { + titleLabel.text = model?.type + dateLabel.text = model?.created_at + countLabel.text = "+\(model?.value ?? "0")" + } + } + + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .semibold) + label.textColor = .white + return label + }() + + private lazy var dateLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .regular) + label.textColor = .FFDAA_4 + return label + }() + + private lazy var countLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .semibold) + label.textColor = .white + return label + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + + contentView.addSubview(titleLabel) + contentView.addSubview(dateLabel) + contentView.addSubview(countLabel) + + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(32) + make.top.equalToSuperview().offset(15) + } + + dateLabel.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.bottom.equalToSuperview().offset(-15) + } + + countLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().offset(-25) + } + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/XSeri/Libs/Alert/XSAlert.swift b/XSeri/Libs/Alert/XSAlert.swift new file mode 100644 index 0000000..4cbf266 --- /dev/null +++ b/XSeri/Libs/Alert/XSAlert.swift @@ -0,0 +1,159 @@ +// +// XSAlert.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/18. +// + +import UIKit +import SnapKit + +class XSAlert: XSBaseAlert { + + private var contentBorderImage = UIImage(named: "alert_bg_border_color_image") + + private var buttonText: String? { + didSet { + self.highlightButton.updateConfiguration() + } + } + + private lazy var titleBgView: UIView = { + let view = XSView() + view.xs_colors = [UIColor.black.withAlphaComponent(0).cgColor, UIColor.FFDAA_4.withAlphaComponent(0.34).cgColor, UIColor.black.withAlphaComponent(0).cgColor] + view.xs_startPoint = .init(x: 0, y: 0.5) + view.xs_endPoint = .init(x: 1, y: 0.5) + return view + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 18, weight: .semibold) + label.textColor = .FFDAA_4 + return label + }() + + private lazy var imageView: UIImageView = { + let imageView = UIImageView() + return imageView + }() + + private lazy var textLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .regular) + label.textColor = .white + label.numberOfLines = 0 + label.textAlignment = .center + return label + }() + + private lazy var highlightButton: UIButton = { + var configuration = UIButton.Configuration.plain() + + let button = XSButton(configuration: configuration, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + self.handleHighlightButton() + })) + button.xs_colors = [UIColor._0_C_0701.withAlphaComponent(0).cgColor, UIColor._16110_E.cgColor, UIColor._574537.cgColor] + button.xs_startPoint = .init(x: 0.5, y: 0) + button.xs_endPoint = .init(x: 0.5, y: 1) + button.layer.cornerRadius = 24 + button.layer.masksToBounds = true + button.layer.borderWidth = 1 + button.layer.borderColor = UIColor.FFDAA_4.cgColor + button.configurationUpdateHandler = { [weak self] button in + guard let self = self else { return } + var configuration = button.configuration + + configuration?.attributedTitle = AttributedString(self.buttonText ?? "", attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 14, weight: .semibold), + .foregroundColor : UIColor.FFDAA_4 + ])) + + button.configuration = configuration + + } + return button + }() + + deinit { + self.contentView.removeObserver(self, forKeyPath: "bounds") + } + + override init(frame: CGRect) { + super.init(frame: frame) + self.contentView.backgroundColor = .black + self.contentView.layer.cornerRadius = 36 + self.contentView.layer.masksToBounds = true + self.contentView.layer.borderWidth = 1 + self.contentView.addObserver(self, forKeyPath: "bounds", context: nil) + + xs_setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "bounds" { + self.contentBorderImage = self.contentBorderImage?.xs_resized(to: self.contentView.bounds.size) + self.contentView.layer.borderColor = UIColor(patternImage: self.contentBorderImage!).cgColor + } + } + + func show(title: String, text: String, image: UIImage?, buttonText: String) -> Self { + self.titleLabel.text = title + self.textLabel.text = text + self.imageView.image = image + self.buttonText = buttonText + return self + } + +} + +extension XSAlert { + + private func xs_setupUI() { + contentView.addSubview(titleBgView) + titleBgView.addSubview(titleLabel) + contentView.addSubview(imageView) + contentView.addSubview(textLabel) + contentView.addSubview(highlightButton) + + titleBgView.snp.makeConstraints { make in + make.top.equalToSuperview() + make.left.right.equalToSuperview().inset(23) + make.height.equalTo(48) + make.width.equalTo(256) + } + + titleLabel.snp.makeConstraints { make in + make.center.equalToSuperview() + } + + imageView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.centerY.equalTo(self.contentView.snp.top).inset(131) + } + + textLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.right.lessThanOrEqualToSuperview().offset(-45) + make.top.equalTo(titleBgView.snp.bottom).offset(152) + make.height.greaterThanOrEqualTo(36) + } + + highlightButton.snp.makeConstraints { make in + make.right.left.equalToSuperview().inset(40) + make.top.equalTo(textLabel.snp.bottom).offset(18) + make.bottom.equalToSuperview().offset(-17) + make.height.equalTo(48) + } + } + +} diff --git a/XSeri/Libs/Alert/XSBaseAlert.swift b/XSeri/Libs/Alert/XSBaseAlert.swift new file mode 100644 index 0000000..86e4d41 --- /dev/null +++ b/XSeri/Libs/Alert/XSBaseAlert.swift @@ -0,0 +1,143 @@ +// +// XSBaseAlert.swift +// XSeri +// +// Created by 长沙鸿瑶 on 2026/3/18. +// + +import UIKit +import SnapKit + +class XSBaseAlert: UIView { + + var clickHighlightButton: (() -> Void)? + + private(set) var containerView: UIView = { + let view = UIView() + return view + }() + + private(set) var contentView: UIView = { + let view = UIView() + return view + }() + + private(set) lazy var closeButton: UIButton = { + let button = UIButton(type: .custom) + button.setImage(UIImage(named: "close_icon_02"), for: .normal) + button.addTarget(self, action: #selector(dismiss), for: .touchUpInside) + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = .black.withAlphaComponent(0.5) + + + addSubview(containerView) + containerView.addSubview(contentView) + containerView.addSubview(closeButton) + + containerView.snp.makeConstraints { make in + make.center.equalToSuperview() + } + + contentView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + make.bottom.equalToSuperview().offset(-76) + } + + closeButton.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalToSuperview() + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @discardableResult + @objc func show(in view: UIView? = nil) -> Self { + guard self.superview == nil else { return self } + + var inView: UIView + if let view = view { + inView = view + } else { + inView = XSBaseAlert.Window.manager.createWindow() + } + + inView.addSubview(self) + self.frame = inView.bounds + showAnimation() + + return self + } + @objc func dismiss() { + dismissAnimation() + } + + @objc func handleHighlightButton() { + self.dismissAnimation() + self.clickHighlightButton?() + } +} + +extension XSBaseAlert { + private func showAnimation() { + containerView.transform = CGAffineTransform(translationX: 0, y: 200) + + UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0) { + self.containerView.transform = CGAffineTransform.identity + } + } + + private func dismissAnimation() { + + UIView.animate(withDuration: 0.3) { + self.alpha = 0 + self.containerView.transform = CGAffineTransform(translationX: 0, y: 500) + } completion: { _ in + self.removeFromSuperview() + XSBaseAlert.Window.manager.dismissWindow() + } + } +} + +extension XSBaseAlert { + class Window { + static let manager = Window() + + private(set) var window: UIWindow? + + func createWindow() -> UIWindow { + guard let window = window else { + let window = UIWindow(windowScene: XSTool.windowScene!) + window.backgroundColor = .clear + window.windowLevel = .alert + window.isHidden = false + self.window = window + return window + } + return window + } + + func dismissWindow() { + guard let window = self.window else { return } + + var isHidden = true + + window.subviews.forEach { + if $0.isKind(of: XSBaseAlert.self) { + isHidden = false + } + } + if isHidden { + window.isHidden = true + self.window = nil + } + } + } +} + diff --git a/XSeri/Libs/DeviceId/XSDeviceId.swift b/XSeri/Libs/DeviceId/XSDeviceId.swift index 9376b9e..3a8b9a4 100644 --- a/XSeri/Libs/DeviceId/XSDeviceId.swift +++ b/XSeri/Libs/DeviceId/XSDeviceId.swift @@ -8,18 +8,20 @@ import UIKit class XSDeviceId: NSObject { - + static let shared = XSDeviceId() private let key = "com.xseri.deviceid" - - - lazy var id: String = { + + let id: String + + private override init() { if let savedID = XSKeychain.shared.read(key: key) { - return savedID + self.id = savedID } else { let newID = UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString XSKeychain.shared.save(key: key, value: newID) - return newID + self.id = newID } - }() + super.init() + } } diff --git a/XSeri/Source/Assets.xcassets/Color/#00181A.colorset/Contents.json b/XSeri/Source/Assets.xcassets/Color/#00181A.colorset/Contents.json new file mode 100644 index 0000000..3d03f3b --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Color/#00181A.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x1A", + "green" : "0x18", + "red" : "0x00" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Color/#0866FF.colorset/Contents.json b/XSeri/Source/Assets.xcassets/Color/#0866FF.colorset/Contents.json new file mode 100644 index 0000000..08df283 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Color/#0866FF.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0x66", + "red" : "0x08" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Color/#0C0701.colorset/Contents.json b/XSeri/Source/Assets.xcassets/Color/#0C0701.colorset/Contents.json new file mode 100644 index 0000000..c175283 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Color/#0C0701.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x01", + "green" : "0x07", + "red" : "0x0C" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Color/#16110E.colorset/Contents.json b/XSeri/Source/Assets.xcassets/Color/#16110E.colorset/Contents.json new file mode 100644 index 0000000..0a28d62 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Color/#16110E.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x0E", + "green" : "0x11", + "red" : "0x16" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Color/#333333.colorset/Contents.json b/XSeri/Source/Assets.xcassets/Color/#333333.colorset/Contents.json new file mode 100644 index 0000000..be3a0c9 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Color/#333333.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x33", + "green" : "0x33", + "red" : "0x33" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Color/#574537.colorset/Contents.json b/XSeri/Source/Assets.xcassets/Color/#574537.colorset/Contents.json new file mode 100644 index 0000000..4b6c343 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Color/#574537.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x37", + "green" : "0x45", + "red" : "0x57" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/alert_bg_border_color_image.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/alert_bg_border_color_image.imageset/Contents.json new file mode 100644 index 0000000..9e6ce69 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/alert_bg_border_color_image.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 346272052@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Rectangle 346272052@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/alert_bg_border_color_image.imageset/Rectangle 346272052@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/alert_bg_border_color_image.imageset/Rectangle 346272052@2x.png new file mode 100644 index 0000000..7b615d2 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/alert_bg_border_color_image.imageset/Rectangle 346272052@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/alert_bg_border_color_image.imageset/Rectangle 346272052@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/alert_bg_border_color_image.imageset/Rectangle 346272052@3x.png new file mode 100644 index 0000000..5f0b63e Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/alert_bg_border_color_image.imageset/Rectangle 346272052@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/apple_icon_01.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/apple_icon_01.imageset/Contents.json new file mode 100644 index 0000000..6a4d508 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/apple_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Vector@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Vector@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/apple_icon_01.imageset/Vector@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/apple_icon_01.imageset/Vector@2x.png new file mode 100644 index 0000000..9b922c9 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/apple_icon_01.imageset/Vector@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/apple_icon_01.imageset/Vector@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/apple_icon_01.imageset/Vector@3x.png new file mode 100644 index 0000000..6b1304e Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/apple_icon_01.imageset/Vector@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_02.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_02.imageset/Contents.json new file mode 100644 index 0000000..379ba42 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_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/button_bg_image_02.imageset/按钮@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_02.imageset/按钮@2x.png new file mode 100644 index 0000000..4c15246 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_02.imageset/按钮@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_02.imageset/按钮@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_02.imageset/按钮@3x.png new file mode 100644 index 0000000..3164e19 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/button_bg_image_02.imageset/按钮@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/calendar_icon_01.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/calendar_icon_01.imageset/Contents.json new file mode 100644 index 0000000..16d0592 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/calendar_icon_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/calendar_icon_01.imageset/签到板+飞舞的金币@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/calendar_icon_01.imageset/签到板+飞舞的金币@2x.png new file mode 100644 index 0000000..d7e9c2a Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/calendar_icon_01.imageset/签到板+飞舞的金币@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/calendar_icon_01.imageset/签到板+飞舞的金币@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/calendar_icon_01.imageset/签到板+飞舞的金币@3x.png new file mode 100644 index 0000000..a3829a6 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/calendar_icon_01.imageset/签到板+飞舞的金币@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/close_icon_02.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/close_icon_02.imageset/Contents.json new file mode 100644 index 0000000..fc17345 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/close_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame 126@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame 126@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/close_icon_02.imageset/Frame 126@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/close_icon_02.imageset/Frame 126@2x.png new file mode 100644 index 0000000..442e7b6 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/close_icon_02.imageset/Frame 126@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/close_icon_02.imageset/Frame 126@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/close_icon_02.imageset/Frame 126@3x.png new file mode 100644 index 0000000..ab34252 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/close_icon_02.imageset/Frame 126@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_03.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_03.imageset/Contents.json new file mode 100644 index 0000000..014d81b --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_03.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "新APP金币@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "新APP金币@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_03.imageset/新APP金币@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_03.imageset/新APP金币@2x.png new file mode 100644 index 0000000..3dd03b6 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_03.imageset/新APP金币@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_03.imageset/新APP金币@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_03.imageset/新APP金币@3x.png new file mode 100644 index 0000000..e7c40b4 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/coins_icon_03.imageset/新APP金币@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/facebook_icon_01.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/facebook_icon_01.imageset/Contents.json new file mode 100644 index 0000000..6a4d508 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/facebook_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Vector@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Vector@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/facebook_icon_01.imageset/Vector@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/facebook_icon_01.imageset/Vector@2x.png new file mode 100644 index 0000000..e824b5e Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/facebook_icon_01.imageset/Vector@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/facebook_icon_01.imageset/Vector@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/facebook_icon_01.imageset/Vector@3x.png new file mode 100644 index 0000000..fd67bc3 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/facebook_icon_01.imageset/Vector@3x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/wallet_icon_01.imageset/Contents.json b/XSeri/Source/Assets.xcassets/Image/icon/wallet_icon_01.imageset/Contents.json new file mode 100644 index 0000000..f841473 --- /dev/null +++ b/XSeri/Source/Assets.xcassets/Image/icon/wallet_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Wallet-three (钱包3)@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Wallet-three (钱包3)@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/XSeri/Source/Assets.xcassets/Image/icon/wallet_icon_01.imageset/Wallet-three (钱包3)@2x.png b/XSeri/Source/Assets.xcassets/Image/icon/wallet_icon_01.imageset/Wallet-three (钱包3)@2x.png new file mode 100644 index 0000000..fd68dc2 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/wallet_icon_01.imageset/Wallet-three (钱包3)@2x.png differ diff --git a/XSeri/Source/Assets.xcassets/Image/icon/wallet_icon_01.imageset/Wallet-three (钱包3)@3x.png b/XSeri/Source/Assets.xcassets/Image/icon/wallet_icon_01.imageset/Wallet-three (钱包3)@3x.png new file mode 100644 index 0000000..f025bd4 Binary files /dev/null and b/XSeri/Source/Assets.xcassets/Image/icon/wallet_icon_01.imageset/Wallet-three (钱包3)@3x.png differ diff --git a/XSeri/Source/en.lproj/Localizable.strings b/XSeri/Source/en.lproj/Localizable.strings index 82fb18a..2cf1419 100644 --- a/XSeri/Source/en.lproj/Localizable.strings +++ b/XSeri/Source/en.lproj/Localizable.strings @@ -59,6 +59,10 @@ "Expires in ## days" = "Expires in ## days"; "Coin Record" = "Coin Record"; "VIP Record" = "VIP Record"; +"Order Records" = "Order Records"; +"VIP Membership" = "VIP Membership"; +"Auto renew,cancel anytime" = "Auto renew,cancel anytime"; +"Daily Coins" = "Daily Coins"; "me_daily_1" = "Daily reward ready!"; "me_daily_2" = "Claim your rewards now.";