diff --git a/Fableon.xcodeproj/project.pbxproj b/Fableon.xcodeproj/project.pbxproj index 81a6686..73b94e6 100644 --- a/Fableon.xcodeproj/project.pbxproj +++ b/Fableon.xcodeproj/project.pbxproj @@ -7,6 +7,37 @@ objects = { /* Begin PBXBuildFile section */ + 031FDEAC2EAF05FB00F4CAC7 /* FAStoreVipCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEAB2EAF05FB00F4CAC7 /* FAStoreVipCell.swift */; }; + 031FDEAE2EB093B100F4CAC7 /* FABuyRecordsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEAD2EB093B100F4CAC7 /* FABuyRecordsModel.swift */; }; + 031FDEB02EB09AB300F4CAC7 /* FARechargeRecordModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEAF2EB09AB300F4CAC7 /* FARechargeRecordModel.swift */; }; + 031FDEB22EB0A5AF00F4CAC7 /* FASendCoinRecordModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEB12EB0A5AF00F4CAC7 /* FASendCoinRecordModel.swift */; }; + 031FDEB42EB0AD7D00F4CAC7 /* FACoinPackConfirmView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEB32EB0AD7D00F4CAC7 /* FACoinPackConfirmView.swift */; }; + 031FDEB62EB0B77F00F4CAC7 /* FACoinPackConfirmItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEB52EB0B77F00F4CAC7 /* FACoinPackConfirmItemView.swift */; }; + 031FDEB82EB0B80400F4CAC7 /* FACoinPackConfirmItem1View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEB72EB0B80400F4CAC7 /* FACoinPackConfirmItem1View.swift */; }; + 031FDEBA2EB0B82600F4CAC7 /* FACoinPackConfirmItem2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEB92EB0B82600F4CAC7 /* FACoinPackConfirmItem2View.swift */; }; + 031FDEBC2EB0C97A00F4CAC7 /* FAOldVideoRechargeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEBB2EB0C97A00F4CAC7 /* FAOldVideoRechargeView.swift */; }; + 031FDEBE2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEBD2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift */; }; + 039CE6042EAA2621007B5EED /* AppDelegate+FAAdjust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6032EAA2612007B5EED /* AppDelegate+FAAdjust.swift */; }; + 039CE6092EAA2F71007B5EED /* FAAdjustStateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6082EAA2F62007B5EED /* FAAdjustStateManager.swift */; }; + 039CE60B2EAA31CB007B5EED /* FAStatAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE60A2EAA31CB007B5EED /* FAStatAPI.swift */; }; + 039CE60E2EAA32A8007B5EED /* FAOpenAppModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE60D2EAA32A8007B5EED /* FAOpenAppModel.swift */; }; + 039CE6112EAB0D49007B5EED /* JXIAPManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6102EAB0D47007B5EED /* JXIAPManager.swift */; }; + 039CE6142EAB0E0D007B5EED /* FAIapManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6132EAB0E0D007B5EED /* FAIapManager.swift */; }; + 039CE6162EAB0E30007B5EED /* FAWaitRestoreModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6152EAB0E30007B5EED /* FAWaitRestoreModel.swift */; }; + 039CE6182EAB0E7E007B5EED /* FAStoreAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6172EAB0E7E007B5EED /* FAStoreAPI.swift */; }; + 039CE61A2EAB0EE0007B5EED /* FAIapVerifyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6192EAB0EE0007B5EED /* FAIapVerifyModel.swift */; }; + 039CE61C2EAB0F29007B5EED /* FAIapOrderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE61B2EAB0F29007B5EED /* FAIapOrderModel.swift */; }; + 039CE6202EAB114B007B5EED /* FAPayDateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE61F2EAB114B007B5EED /* FAPayDateModel.swift */; }; + 039CE6222EAB1340007B5EED /* FAUserDefaultsKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6212EAB1335007B5EED /* FAUserDefaultsKey.swift */; }; + 039CE6242EAB29D1007B5EED /* FAPayDataRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6232EAB29D1007B5EED /* FAPayDataRequest.swift */; }; + 039CE6262EAB2A72007B5EED /* FAPayAlertModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6252EAB2A72007B5EED /* FAPayAlertModel.swift */; }; + 039CE6282EAB50A9007B5EED /* FAStoreCoinsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6272EAB50A9007B5EED /* FAStoreCoinsView.swift */; }; + 039CE62A2EAB5A8C007B5EED /* FAStoreCoinsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6292EAB5A8C007B5EED /* FAStoreCoinsCell.swift */; }; + 039CE62C2EAB5B07007B5EED /* FAStoreCoinsBigCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE62B2EAB5B07007B5EED /* FAStoreCoinsBigCell.swift */; }; + 039CE62E2EAB5B18007B5EED /* FAStoreCoinsSmallCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE62D2EAB5B18007B5EED /* FAStoreCoinsSmallCell.swift */; }; + 039CE6302EAB5B26007B5EED /* FAStoreCoinsPackCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE62F2EAB5B26007B5EED /* FAStoreCoinsPackCell.swift */; }; + 039CE6322EAB796F007B5EED /* FALabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6312EAB796F007B5EED /* FALabel.swift */; }; + 03DBD8512EAEFF5F00DD04B8 /* FAStoreVipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03DBD8502EAEFF5F00DD04B8 /* FAStoreVipView.swift */; }; 03E239632EAA1945004A8CEC /* AppDelegate+FAConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E239612EAA1945004A8CEC /* AppDelegate+FAConfig.swift */; }; 03E239642EAA1945004A8CEC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E239602EAA1945004A8CEC /* AppDelegate.swift */; }; 03E239652EAA1945004A8CEC /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E239622EAA1945004A8CEC /* SceneDelegate.swift */; }; @@ -265,6 +296,38 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 031FDEAB2EAF05FB00F4CAC7 /* FAStoreVipCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreVipCell.swift; sourceTree = ""; }; + 031FDEAD2EB093B100F4CAC7 /* FABuyRecordsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FABuyRecordsModel.swift; sourceTree = ""; }; + 031FDEAF2EB09AB300F4CAC7 /* FARechargeRecordModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FARechargeRecordModel.swift; sourceTree = ""; }; + 031FDEB12EB0A5AF00F4CAC7 /* FASendCoinRecordModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FASendCoinRecordModel.swift; sourceTree = ""; }; + 031FDEB32EB0AD7D00F4CAC7 /* FACoinPackConfirmView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinPackConfirmView.swift; sourceTree = ""; }; + 031FDEB52EB0B77F00F4CAC7 /* FACoinPackConfirmItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinPackConfirmItemView.swift; sourceTree = ""; }; + 031FDEB72EB0B80400F4CAC7 /* FACoinPackConfirmItem1View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinPackConfirmItem1View.swift; sourceTree = ""; }; + 031FDEB92EB0B82600F4CAC7 /* FACoinPackConfirmItem2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinPackConfirmItem2View.swift; sourceTree = ""; }; + 031FDEBB2EB0C97A00F4CAC7 /* FAOldVideoRechargeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAOldVideoRechargeView.swift; sourceTree = ""; }; + 031FDEBD2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FANewVideoRechargeView.swift; sourceTree = ""; }; + 039CE6032EAA2612007B5EED /* AppDelegate+FAAdjust.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+FAAdjust.swift"; sourceTree = ""; }; + 039CE6082EAA2F62007B5EED /* FAAdjustStateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAAdjustStateManager.swift; sourceTree = ""; }; + 039CE60A2EAA31CB007B5EED /* FAStatAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStatAPI.swift; sourceTree = ""; }; + 039CE60D2EAA32A8007B5EED /* FAOpenAppModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAOpenAppModel.swift; sourceTree = ""; }; + 039CE6102EAB0D47007B5EED /* JXIAPManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JXIAPManager.swift; sourceTree = ""; }; + 039CE6132EAB0E0D007B5EED /* FAIapManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAIapManager.swift; sourceTree = ""; }; + 039CE6152EAB0E30007B5EED /* FAWaitRestoreModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAWaitRestoreModel.swift; sourceTree = ""; }; + 039CE6172EAB0E7E007B5EED /* FAStoreAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreAPI.swift; sourceTree = ""; }; + 039CE6192EAB0EE0007B5EED /* FAIapVerifyModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAIapVerifyModel.swift; sourceTree = ""; }; + 039CE61B2EAB0F29007B5EED /* FAIapOrderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAIapOrderModel.swift; sourceTree = ""; }; + 039CE61D2EAB0FE4007B5EED /* Fableon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Fableon.entitlements; sourceTree = ""; }; + 039CE61F2EAB114B007B5EED /* FAPayDateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAPayDateModel.swift; sourceTree = ""; }; + 039CE6212EAB1335007B5EED /* FAUserDefaultsKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAUserDefaultsKey.swift; sourceTree = ""; }; + 039CE6232EAB29D1007B5EED /* FAPayDataRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAPayDataRequest.swift; sourceTree = ""; }; + 039CE6252EAB2A72007B5EED /* FAPayAlertModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAPayAlertModel.swift; sourceTree = ""; }; + 039CE6272EAB50A9007B5EED /* FAStoreCoinsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreCoinsView.swift; sourceTree = ""; }; + 039CE6292EAB5A8C007B5EED /* FAStoreCoinsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreCoinsCell.swift; sourceTree = ""; }; + 039CE62B2EAB5B07007B5EED /* FAStoreCoinsBigCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreCoinsBigCell.swift; sourceTree = ""; }; + 039CE62D2EAB5B18007B5EED /* FAStoreCoinsSmallCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreCoinsSmallCell.swift; sourceTree = ""; }; + 039CE62F2EAB5B26007B5EED /* FAStoreCoinsPackCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreCoinsPackCell.swift; sourceTree = ""; }; + 039CE6312EAB796F007B5EED /* FALabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FALabel.swift; sourceTree = ""; }; + 03DBD8502EAEFF5F00DD04B8 /* FAStoreVipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreVipView.swift; sourceTree = ""; }; 03E239602EAA1945004A8CEC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 03E239612EAA1945004A8CEC /* AppDelegate+FAConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+FAConfig.swift"; sourceTree = ""; }; 03E239622EAA1945004A8CEC /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -538,6 +601,54 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 039CE6072EAA2F37007B5EED /* AdjustStateManager */ = { + isa = PBXGroup; + children = ( + 039CE6082EAA2F62007B5EED /* FAAdjustStateManager.swift */, + ); + path = AdjustStateManager; + sourceTree = ""; + }; + 039CE60C2EAA328A007B5EED /* Model */ = { + isa = PBXGroup; + children = ( + 039CE60D2EAA32A8007B5EED /* FAOpenAppModel.swift */, + ); + path = Model; + sourceTree = ""; + }; + 039CE60F2EAB0D2D007B5EED /* JXIAPManager */ = { + isa = PBXGroup; + children = ( + 039CE6102EAB0D47007B5EED /* JXIAPManager.swift */, + ); + path = JXIAPManager; + sourceTree = ""; + }; + 039CE6122EAB0DE1007B5EED /* FAIap */ = { + isa = PBXGroup; + children = ( + 039CE6132EAB0E0D007B5EED /* FAIapManager.swift */, + 039CE6232EAB29D1007B5EED /* FAPayDataRequest.swift */, + 039CE6192EAB0EE0007B5EED /* FAIapVerifyModel.swift */, + 039CE61B2EAB0F29007B5EED /* FAIapOrderModel.swift */, + 039CE6152EAB0E30007B5EED /* FAWaitRestoreModel.swift */, + ); + path = FAIap; + sourceTree = ""; + }; + 039CE61E2EAB10EC007B5EED /* M */ = { + isa = PBXGroup; + children = ( + 039CE61F2EAB114B007B5EED /* FAPayDateModel.swift */, + 039CE6252EAB2A72007B5EED /* FAPayAlertModel.swift */, + 031FDEAD2EB093B100F4CAC7 /* FABuyRecordsModel.swift */, + 031FDEAF2EB09AB300F4CAC7 /* FARechargeRecordModel.swift */, + 031FDEB12EB0A5AF00F4CAC7 /* FASendCoinRecordModel.swift */, + ); + path = M; + sourceTree = ""; + }; 03E2395F2EAA1900004A8CEC /* Object */ = { isa = PBXGroup; children = ( @@ -552,6 +663,7 @@ isa = PBXGroup; children = ( 03E2396A2EAA1A29004A8CEC /* Controller */, + 039CE60C2EAA328A007B5EED /* Model */, 03E2396C2EAA1A29004A8CEC /* Define */, 03E2397A2EAA1A29004A8CEC /* Extension */, 03E239812EAA1A29004A8CEC /* Request */, @@ -575,6 +687,7 @@ isa = PBXGroup; children = ( 03E2396B2EAA1A29004A8CEC /* FADefine.swift */, + 039CE6212EAB1335007B5EED /* FAUserDefaultsKey.swift */, ); path = Define; sourceTree = ""; @@ -610,6 +723,8 @@ isa = PBXGroup; children = ( 03E2397B2EAA1A29004A8CEC /* FAAPI.swift */, + 039CE60A2EAA31CB007B5EED /* FAStatAPI.swift */, + 039CE6172EAB0E7E007B5EED /* FAStoreAPI.swift */, ); path = FAAPI; sourceTree = ""; @@ -637,6 +752,7 @@ 03E239872EAA1A29004A8CEC /* FAScrollView.swift */, 03E239882EAA1A29004A8CEC /* FATableView.swift */, 03E239892EAA1A29004A8CEC /* FATableViewCell.swift */, + 039CE6312EAB796F007B5EED /* FALabel.swift */, ); path = View; sourceTree = ""; @@ -862,6 +978,8 @@ 03E23A0E2EAA1A4E004A8CEC /* FAPlayerDetailControlView.swift */, 03E23A0F2EAA1A4E004A8CEC /* FAPlayerProgressView.swift */, 03E23A102EAA1A4E004A8CEC /* FAVideoLockView.swift */, + 031FDEBB2EB0C97A00F4CAC7 /* FAOldVideoRechargeView.swift */, + 031FDEBD2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift */, ); path = V; sourceTree = ""; @@ -885,10 +1003,10 @@ 03E23A162EAA1A4E004A8CEC /* Player */ = { isa = PBXGroup; children = ( - 03E23A062EAA1A4E004A8CEC /* M */, 03E23A082EAA1A4E004A8CEC /* UI */, - 03E23A112EAA1A4E004A8CEC /* V */, 03E23A132EAA1A4E004A8CEC /* VC */, + 03E23A062EAA1A4E004A8CEC /* M */, + 03E23A112EAA1A4E004A8CEC /* V */, 03E23A152EAA1A4E004A8CEC /* VM */, ); path = Player; @@ -932,13 +1050,13 @@ 03E23A262EAA1A4E004A8CEC /* C */ = { isa = PBXGroup; children = ( + 03E23A232EAA1A4E004A8CEC /* FAStoreViewController.swift */, + 03E23A252EAA1A4E004A8CEC /* FAWalletViewController.swift */, 03E23A1F2EAA1A4E004A8CEC /* FACoinRecordViewController.swift */, 03E23A202EAA1A4E004A8CEC /* FAConsumptionRecordsViewController.swift */, 03E23A212EAA1A4E004A8CEC /* FAOrderRecordsViewController.swift */, 03E23A222EAA1A4E004A8CEC /* FARewardCoinsViewController.swift */, - 03E23A232EAA1A4E004A8CEC /* FAStoreViewController.swift */, 03E23A242EAA1A4E004A8CEC /* FAVipRecordViewController.swift */, - 03E23A252EAA1A4E004A8CEC /* FAWalletViewController.swift */, ); path = C; sourceTree = ""; @@ -955,6 +1073,17 @@ 03E23A2D2EAA1A4E004A8CEC /* FAWalletCell.swift */, 03E23A2E2EAA1A4E004A8CEC /* FAWalletCell.xib */, 03E23A2F2EAA1A4E004A8CEC /* FAWalletHeaderView.swift */, + 039CE6272EAB50A9007B5EED /* FAStoreCoinsView.swift */, + 03DBD8502EAEFF5F00DD04B8 /* FAStoreVipView.swift */, + 039CE6292EAB5A8C007B5EED /* FAStoreCoinsCell.swift */, + 039CE62B2EAB5B07007B5EED /* FAStoreCoinsBigCell.swift */, + 039CE62D2EAB5B18007B5EED /* FAStoreCoinsSmallCell.swift */, + 039CE62F2EAB5B26007B5EED /* FAStoreCoinsPackCell.swift */, + 031FDEAB2EAF05FB00F4CAC7 /* FAStoreVipCell.swift */, + 031FDEB32EB0AD7D00F4CAC7 /* FACoinPackConfirmView.swift */, + 031FDEB52EB0B77F00F4CAC7 /* FACoinPackConfirmItemView.swift */, + 031FDEB72EB0B80400F4CAC7 /* FACoinPackConfirmItem1View.swift */, + 031FDEB92EB0B82600F4CAC7 /* FACoinPackConfirmItem2View.swift */, ); path = V; sourceTree = ""; @@ -964,6 +1093,7 @@ children = ( 03E23A262EAA1A4E004A8CEC /* C */, 03E23A302EAA1A4E004A8CEC /* V */, + 039CE61E2EAB10EC007B5EED /* M */, ); path = Store; sourceTree = ""; @@ -971,6 +1101,9 @@ 03E23A962EAA1A65004A8CEC /* Libs */ = { isa = PBXGroup; children = ( + 039CE6122EAB0DE1007B5EED /* FAIap */, + 039CE60F2EAB0D2D007B5EED /* JXIAPManager */, + 039CE6072EAA2F37007B5EED /* AdjustStateManager */, 03E23A982EAA1A7F004A8CEC /* Empty */, 03E23A9B2EAA1A7F004A8CEC /* FADeviceId */, 03E23A9D2EAA1A7F004A8CEC /* FALocalized */, @@ -1468,6 +1601,7 @@ children = ( 03E239602EAA1945004A8CEC /* AppDelegate.swift */, 03E239612EAA1945004A8CEC /* AppDelegate+FAConfig.swift */, + 039CE6032EAA2612007B5EED /* AppDelegate+FAAdjust.swift */, 03E239622EAA1945004A8CEC /* SceneDelegate.swift */, ); path = App; @@ -1505,6 +1639,7 @@ F3YR32SS1P8731297KD665X5 /* Fableon */ = { isa = PBXGroup; children = ( + 039CE61D2EAB0FE4007B5EED /* Fableon.entitlements */, F3U31ND27BB4R9G072AN9BM3 /* App */, 03E2395F2EAA1900004A8CEC /* Object */, F3IF9S30I6U4301G5521663T /* Source */, @@ -1687,11 +1822,16 @@ F3Q1B8O8164TYQ9724T34V29 /* OACAbleView.swift in Sources */, F36MZ39RQ295569BV1915908 /* FOMptyView.swift in Sources */, F323CZU4466303487TY7K12U /* YHRegister.swift in Sources */, + 031FDEB02EB09AB300F4CAC7 /* FARechargeRecordModel.swift in Sources */, F35T0LSS4O974ZP1IJ27TB30 /* BEedbackCenterController.swift in Sources */, F39356S815VILHH18O4HK577 /* BHSSwiftRefresh.swift in Sources */, F33O83184592105998E35JY1 /* VRServiceCenterItem.swift in Sources */, F347N086226Y53261B0285I3 /* QLoginRoundedView.swift in Sources */, + 031FDEB22EB0A5AF00F4CAC7 /* FASendCoinRecordModel.swift in Sources */, + 03DBD8512EAEFF5F00DD04B8 /* FAStoreVipView.swift in Sources */, + 031FDEB62EB0B77F00F4CAC7 /* FACoinPackConfirmItemView.swift in Sources */, F35S8138Z362EG3435ME1455 /* CSGleeView.swift in Sources */, + 031FDEB42EB0AD7D00F4CAC7 /* FACoinPackConfirmView.swift in Sources */, F31451S3X15B941342J922Q3 /* TConfigCell.swift in Sources */, F3670DZ2KI89TBO3L9L36P57 /* XLoginView.swift in Sources */, F3V24670CK2EGR4595CDQ261 /* AWUModalMageCell.swift in Sources */, @@ -1702,6 +1842,7 @@ F381909BQ5JWXYI119A49469 /* DREychainCell.swift in Sources */, F39R66F983517825QO331145 /* QCenterEfineView.swift in Sources */, F3II3AF834346F516W51V693 /* GAMainRecommendedView.swift in Sources */, + 031FDEAE2EB093B100F4CAC7 /* FABuyRecordsModel.swift in Sources */, F377Y22Z3J44H22J23963544 /* CDNewsController.swift in Sources */, F358686093028548K00WQIO1 /* NGleeVionView.swift in Sources */, F3019606DA7P36H41G408X13 /* ZStreamCell.swift in Sources */, @@ -1716,11 +1857,13 @@ 03E239632EAA1945004A8CEC /* AppDelegate+FAConfig.swift in Sources */, 03E239642EAA1945004A8CEC /* AppDelegate.swift in Sources */, 03E239652EAA1945004A8CEC /* SceneDelegate.swift in Sources */, + 039CE6092EAA2F71007B5EED /* FAAdjustStateManager.swift in Sources */, F333U95746V7VK13QI9275B3 /* UMenuTransformerCell.swift in Sources */, F3Q234J5M18F1Q5G2948P056 /* NPVBoutModalController.swift in Sources */, F38Y7NBX3M0RE4O142264411 /* EZEFlow.swift in Sources */, F363P024H4W1T8LN2546W883 /* YBanner.swift in Sources */, F3P4D170962A2JAF14W0520R /* ADCheckMageView.swift in Sources */, + 031FDEBA2EB0B82600F4CAC7 /* FACoinPackConfirmItem2View.swift in Sources */, F3206101P0DVK224N85S1W62 /* XIPathEviceController.swift in Sources */, F31N0S2575E3YC5AOG80WE90 /* TYElyon.swift in Sources */, F3JX29E3JQ40V23245TS8Z36 /* YRegisterController.swift in Sources */, @@ -1729,6 +1872,7 @@ F39EH05VP4YX3F00P5162031 /* YFddebcdbeeffcebdfCenterView.swift in Sources */, F32T95836171611R26I33D4D /* AJMEychainView.swift in Sources */, F3242J387RV5714007E92OH3 /* BEOConfigController.swift in Sources */, + 031FDEAC2EAF05FB00F4CAC7 /* FAStoreVipCell.swift in Sources */, F3N346893L1U6DP8228XK227 /* UOLaunch.swift in Sources */, F39S0H806843N21RM0O95488 /* VARResultController.swift in Sources */, F3D03219Y8NV50N2S66014LX /* GPOOllectionFableon.swift in Sources */, @@ -1738,20 +1882,25 @@ 03E23AAC2EAA1A7F004A8CEC /* FALogin.swift in Sources */, 03E23AAD2EAA1A7F004A8CEC /* FAKeychainHelper.swift in Sources */, 03E23AAE2EAA1A7F004A8CEC /* FATool.swift in Sources */, + 039CE6262EAB2A72007B5EED /* FAPayAlertModel.swift in Sources */, 03E23AAF2EAA1A7F004A8CEC /* FSPagerSwiftUIView.swift in Sources */, 03E23AB02EAA1A7F004A8CEC /* FADeviceIDManager.swift in Sources */, 03E23AB12EAA1A7F004A8CEC /* FATokenModel.swift in Sources */, 03E23AB22EAA1A7F004A8CEC /* FAEmpty.swift in Sources */, 03E23AB32EAA1A7F004A8CEC /* FAHUD.swift in Sources */, 03E23AB42EAA1A7F004A8CEC /* FAPagerViewTransformer.swift in Sources */, + 039CE62A2EAB5A8C007B5EED /* FAStoreCoinsCell.swift in Sources */, + 039CE6242EAB29D1007B5EED /* FAPayDataRequest.swift in Sources */, 03E23AB52EAA1A7F004A8CEC /* FAToast.swift in Sources */, 03E23AB62EAA1A7F004A8CEC /* FAUserInfo.swift in Sources */, 03E23AB72EAA1A7F004A8CEC /* FALocalized.swift in Sources */, + 039CE6222EAB1340007B5EED /* FAUserDefaultsKey.swift in Sources */, 03E23AB82EAA1A7F004A8CEC /* FAWaterfallFlowLayout.swift in Sources */, F3K238643L913I6RK4G7S006 /* CSceneRegister.swift in Sources */, F3ZT3I4VAGB5405FWL36UW12 /* ZFGEtworkCell.swift in Sources */, F35O71332554S53191121042 /* UOModalCell.swift in Sources */, F3L30772U87770116598U282 /* NLaunchDelegate.swift in Sources */, + 039CE62C2EAB5B07007B5EED /* FAStoreCoinsBigCell.swift in Sources */, F38L64O0092TC8W47P271967 /* COcalizedController.swift in Sources */, 03E23A322EAA1A4E004A8CEC /* FAFeedbackViewController.swift in Sources */, 03E23A332EAA1A4E004A8CEC /* FAHomeRecommendedItemView.swift in Sources */, @@ -1777,6 +1926,7 @@ 03E23A472EAA1A4E004A8CEC /* FAPlayerDetailControlView.swift in Sources */, 03E23A482EAA1A4E004A8CEC /* FARewardCoinsCell.swift in Sources */, 03E23A492EAA1A4E004A8CEC /* FAVideoInfoModel.swift in Sources */, + 039CE6202EAB114B007B5EED /* FAPayDateModel.swift in Sources */, 03E23A4A2EAA1A4E004A8CEC /* FAConsumptionRecordsViewController.swift in Sources */, 03E23A4B2EAA1A4E004A8CEC /* FAMeCoinsView.swift in Sources */, 03E23A4C2EAA1A4E004A8CEC /* FAHomeViewModel.swift in Sources */, @@ -1789,6 +1939,7 @@ 03E23A532EAA1A4E004A8CEC /* FAVideoLockView.swift in Sources */, 03E23A542EAA1A4E004A8CEC /* FAHomeViewController.swift in Sources */, 03E23A552EAA1A4E004A8CEC /* FAPopularListViewController.swift in Sources */, + 039CE6042EAA2621007B5EED /* AppDelegate+FAAdjust.swift in Sources */, 03E23A562EAA1A4E004A8CEC /* FAPlayerEpUIButton.swift in Sources */, 03E23A572EAA1A4E004A8CEC /* FAHistoryCell.swift in Sources */, 03E23A582EAA1A4E004A8CEC /* FASearchViewController.swift in Sources */, @@ -1821,6 +1972,8 @@ 03E23A732EAA1A4E004A8CEC /* FASearchRecordCell.swift in Sources */, 03E23A742EAA1A4E004A8CEC /* FAShortPlayModel.swift in Sources */, 03E23A752EAA1A4E004A8CEC /* FASearchRecommendCell.swift in Sources */, + 039CE6162EAB0E30007B5EED /* FAWaitRestoreModel.swift in Sources */, + 039CE6302EAB5B26007B5EED /* FAStoreCoinsPackCell.swift in Sources */, 03E23A762EAA1A4E004A8CEC /* FASearchHomeView.swift in Sources */, 03E23A772EAA1A4E004A8CEC /* FAHomeNewContentCell.swift in Sources */, 03E23A782EAA1A4E004A8CEC /* FAOrderRecordsViewController.swift in Sources */, @@ -1830,11 +1983,13 @@ 03E23A7C2EAA1A4E004A8CEC /* FAMeCell.swift in Sources */, 03E23A7D2EAA1A4E004A8CEC /* FAWalletHeaderView.swift in Sources */, 03E23A7E2EAA1A4E004A8CEC /* FAPlayerProgressView.swift in Sources */, + 031FDEB82EB0B80400F4CAC7 /* FACoinPackConfirmItem1View.swift in Sources */, 03E23A7F2EAA1A4E004A8CEC /* FAMeItemModel.swift in Sources */, 03E23A802EAA1A4E004A8CEC /* FAGenresListCell.swift in Sources */, 03E23A812EAA1A4E004A8CEC /* FACollectCell.swift in Sources */, 03E23A822EAA1A4E004A8CEC /* FASearchRecommendView.swift in Sources */, 03E23A832EAA1A4E004A8CEC /* FAHomeMustSeeShortView.swift in Sources */, + 039CE60B2EAA31CB007B5EED /* FAStatAPI.swift in Sources */, 03E23A842EAA1A4E004A8CEC /* FACoinRecordViewController.swift in Sources */, F3511I08934IV2C80B9Z6X2T /* QDVBoutLayout.swift in Sources */, F311250SJ7I3DZ74X009990N /* XJSelectorCell.swift in Sources */, @@ -1842,22 +1997,27 @@ F3O215L13X5155316V412904 /* AOkenView.swift in Sources */, F3G0PR235Z2X602JE530X692 /* EQThimraPathController.swift in Sources */, F3563UZ4T03I36J2R93Y31N6 /* IMRefreshManagerController.swift in Sources */, + 039CE6112EAB0D49007B5EED /* JXIAPManager.swift in Sources */, F3981047B9214665100286YG /* BZOEarchView.swift in Sources */, 03E23ABA2EAA1D85004A8CEC /* MNNItemController.swift in Sources */, F3817W6E02182D9A62U02849 /* WHAOastMainCell.swift in Sources */, F32C0941C670UO0IU4114086 /* XHDelegate.swift in Sources */, + 039CE61A2EAB0EE0007B5EED /* FAIapVerifyModel.swift in Sources */, F3XWY0D08020883935X46FG7 /* FHeaderController.swift in Sources */, F3223B1O80A345NB22134220 /* LAgerShortController.swift in Sources */, F3Q51496018LRJ1691985D08 /* PYMOastCell.swift in Sources */, F33Q5651983154V88746Y001 /* ODPlayerCell.swift in Sources */, F3774560U03XA8151BTQ2Z61 /* HYLLayoutView.swift in Sources */, F3N85463XSRG9VFK665G9028 /* XKRefreshCell.swift in Sources */, + 039CE6282EAB50A9007B5EED /* FAStoreCoinsView.swift in Sources */, F35K41930D96476I83FHZ0D9 /* YLZFableonAlignmentCell.swift in Sources */, F3M8439X72Q55JY9G8U502D9 /* DXYFire.swift in Sources */, 03E239912EAA1A29004A8CEC /* Dictionary+FAAdd.swift in Sources */, 03E239922EAA1A29004A8CEC /* FAImageView.swift in Sources */, + 031FDEBE2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift in Sources */, 03E239932EAA1A29004A8CEC /* Date+FAAdd.swift in Sources */, 03E239942EAA1A29004A8CEC /* UserDefaults+FAAdd.swift in Sources */, + 039CE6322EAB796F007B5EED /* FALabel.swift in Sources */, 03E239952EAA1A29004A8CEC /* Font+FAAdd.swift in Sources */, 03E239962EAA1A29004A8CEC /* FACryptorService.swift in Sources */, 03E239972EAA1A29004A8CEC /* FANetworkManager.swift in Sources */, @@ -1866,6 +2026,7 @@ 03E2399A2EAA1A29004A8CEC /* FAViewController.swift in Sources */, 03E2399B2EAA1A29004A8CEC /* FAWebMessageModel.swift in Sources */, 03E2399C2EAA1A29004A8CEC /* FAGradientButton.swift in Sources */, + 039CE6142EAB0E0D007B5EED /* FAIapManager.swift in Sources */, 03E2399D2EAA1A29004A8CEC /* FAGradientView.swift in Sources */, 03E2399E2EAA1A29004A8CEC /* FADefine.swift in Sources */, 03E2399F2EAA1A29004A8CEC /* FABaseWebViewController.swift in Sources */, @@ -1874,18 +2035,22 @@ 03E239A22EAA1A29004A8CEC /* FATabBarController.swift in Sources */, 03E239A32EAA1A29004A8CEC /* UIStackView+FAAdd.swift in Sources */, 03E239A42EAA1A29004A8CEC /* UIScreen+FAAdd.swift in Sources */, + 039CE6182EAB0E7E007B5EED /* FAStoreAPI.swift in Sources */, 03E239A52EAA1A29004A8CEC /* FACollectionView.swift in Sources */, 03E239A62EAA1A29004A8CEC /* UIView+FAAdd.swift in Sources */, 03E239A72EAA1A29004A8CEC /* FAScrollView.swift in Sources */, 03E239A82EAA1A29004A8CEC /* FATableView.swift in Sources */, 03E239A92EAA1A29004A8CEC /* CGMutablePath+FARoundedCorner.swift in Sources */, + 031FDEBC2EB0C97A00F4CAC7 /* FAOldVideoRechargeView.swift in Sources */, 03E239AA2EAA1A29004A8CEC /* FAPanModalContentView.swift in Sources */, 03E239AB2EAA1A29004A8CEC /* FATableViewCell.swift in Sources */, 03E239AC2EAA1A29004A8CEC /* FABaseWebViewController+Script.swift in Sources */, 03E239AD2EAA1A29004A8CEC /* FAAppWebViewController.swift in Sources */, 03E239AE2EAA1A29004A8CEC /* SwiftUIExtension.swift in Sources */, + 039CE60E2EAA32A8007B5EED /* FAOpenAppModel.swift in Sources */, 03E239AF2EAA1A29004A8CEC /* FAAPI.swift in Sources */, 03E239B02EAA1A29004A8CEC /* FAWebView.swift in Sources */, + 039CE61C2EAB0F29007B5EED /* FAIapOrderModel.swift in Sources */, 03E239B12EAA1A29004A8CEC /* FANetworkMonitor.swift in Sources */, 03E239B22EAA1A29004A8CEC /* UINavigationBar+FAAdd.swift in Sources */, F3EKB04Y9O2888900KQEO504 /* NHCornerController.swift in Sources */, @@ -1903,6 +2068,7 @@ F32G5ID338260A2W78SS4618 /* TCFableonCell.swift in Sources */, F31E84U5Y6YW6430Z54X1K40 /* UCHVion.swift in Sources */, F3554P018N62D11QM20Y695Q /* BCMonitorEcommendController.swift in Sources */, + 039CE62E2EAB5B18007B5EED /* FAStoreCoinsSmallCell.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2056,6 +2222,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Fableon/Fableon.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 6XALB8RSYF; @@ -2076,7 +2243,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.0.1; PRODUCT_BUNDLE_IDENTIFIER = com.hn.qinjiu.fableon; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -2096,6 +2263,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Fableon/Fableon.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 6XALB8RSYF; @@ -2115,7 +2283,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.0.1; PRODUCT_BUNDLE_IDENTIFIER = com.hn.qinjiu.fableon; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; diff --git a/Fableon/App/AppDelegate+FAAdjust.swift b/Fableon/App/AppDelegate+FAAdjust.swift new file mode 100644 index 0000000..7e37ac7 --- /dev/null +++ b/Fableon/App/AppDelegate+FAAdjust.swift @@ -0,0 +1,144 @@ +// +// AppDelegate+FAAdjust.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/23. +// +import UIKit +import AdjustSdk +import SmartCodable +#if canImport(FacebookCore) +import FacebookCore +#endif + +extension AppDelegate { + + func registerAdjust() { +#if DEBUG + let config = ADJConfig(appToken: "zyp26mbr3yf4", environment: ADJEnvironmentSandbox) + config?.logLevel = .verbose +#else + let config = ADJConfig(appToken: "zyp26mbr3yf4", environment: ADJEnvironmentProduction) +#endif + + config?.delegate = self + Adjust.initSdk(config) + } + +} + +//MARK: AdjustDelegate +extension AppDelegate: AdjustDelegate { + func adjustDeferredDeeplinkReceived(_ deeplink: URL?) -> Bool { + return true + } +} + + +extension SceneDelegate { + func handleOpenAppMessage(webpageURL: URL?) { + let manager = FAAdjustStateManager.manager + guard manager.allowOpenMessage else { return } + + guard FANetworkMonitor.manager.isReachable == true, + manager.isOpenApp, + manager.idfaAuthorizationFinish //idfa授权完成 + else { + if let webpageURL = webpageURL { + manager.webpageURL = webpageURL + } + manager.isNeedRetry = true + return + } + manager.isNeedRetry = false + manager.webpageURL = nil + + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self._handleOpenAppMessage(webpageURL: webpageURL) + } + } + + + private func _handleOpenAppMessage(webpageURL: URL?) { + + guard FAAdjustStateManager.manager.allowOpenMessage else { return } + FAAdjustStateManager.manager.allowOpenMessage = false + + //统计用URL + var statUrlStr: String? = webpageURL?.absoluteString + var data: [String : Any]? = webpageURL?.query?.urlQuryToDictionary() + + if statUrlStr == nil, let pasteStr = UIPasteboard.general.string, pasteStr.contains("movia") { + let tempArr = pasteStr.components(separatedBy: "?") + let query = tempArr.last + + let tempData = query?.urlQuryToDictionary() + if tempData?["short_play_id"] != nil { + data = tempData + statUrlStr = pasteStr + } + } + UIPasteboard.general.string = nil + + if let urlStr = statUrlStr {//上报结果 + FAStatAPI.requestStatW2a(data: urlStr) + } + + + + guard let data = data else { return } + guard let model = FAOpenAppModel.deserialize(from: data) else { return } + guard let shortPlayId = model.short_play_id, shortPlayId.count > 0 else { return } + + + let vc = FAPlayerDetailViewController() + vc.shortPlayId = shortPlayId + FATool.topViewController?.navigationController?.pushViewController(vc, animated: true) + } + + + ///重试 + func retryHandleOpenAppMessage() { + guard FAAdjustStateManager.manager.isNeedRetry else { return } + handleOpenAppMessage(webpageURL: FAAdjustStateManager.manager.webpageURL) + } +} + + +extension SceneDelegate { + + ///URL打开APP + func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { + guard let url = URLContexts.first?.url else { + return + } + var result = false + +#if canImport(FacebookCore) + result = ApplicationDelegate.shared.application(UIApplication.shared, open: url, sourceApplication: nil, annotation: [UIApplication.OpenURLOptionsKey.annotation]) +#endif + if !result { + if let link = ADJDeeplink(deeplink: url) { + Adjust.processDeeplink(link) + } + handleOpenAppMessage(webpageURL: url) + } + + } + + ///UniversalLink 打开app + func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { + guard let webpageURL = userActivity.webpageURL else { return } + var result = false + +#if canImport(FacebookCore) + let result = ApplicationDelegate.shared.application(UIApplication.shared, continue: userActivity) +#endif + + if !result { + handleOpenAppMessage(webpageURL: webpageURL) + } + } + +} diff --git a/Fableon/App/AppDelegate+FAConfig.swift b/Fableon/App/AppDelegate+FAConfig.swift index c980a93..df96a2e 100644 --- a/Fableon/App/AppDelegate+FAConfig.swift +++ b/Fableon/App/AppDelegate+FAConfig.swift @@ -28,6 +28,8 @@ extension AppDelegate { let appearance = UINavigationBarAppearance.defaultAppearance() UINavigationBar.appearance().scrollEdgeAppearance = appearance UINavigationBar.appearance().standardAppearance = appearance + + registerAdjust() } } diff --git a/Fableon/App/SceneDelegate.swift b/Fableon/App/SceneDelegate.swift index 9a17c9a..9aa1dd1 100644 --- a/Fableon/App/SceneDelegate.swift +++ b/Fableon/App/SceneDelegate.swift @@ -19,6 +19,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window = UIWindow(windowScene: windowScene) window?.rootViewController = FATabBarController() window?.makeKeyAndVisible() + FAAdjustStateManager.manager.isOpenApp = true } func sceneDidDisconnect(_ scene: UIScene) { @@ -39,14 +40,13 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func sceneWillEnterForeground(_ scene: UIScene) { - // Called as the scene transitions from the background to the foreground. - // Use this method to undo the changes made on entering the background. + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.handleOpenAppMessage(webpageURL: nil) + } } func sceneDidEnterBackground(_ scene: UIScene) { - // Called as the scene transitions from the foreground to the background. - // Use this method to save data, release shared resources, and store enough scene-specific state information - // to restore the scene back to its current state. + FAAdjustStateManager.manager.allowOpenMessage = true } diff --git a/Fableon/Fableon.entitlements b/Fableon/Fableon.entitlements new file mode 100644 index 0000000..1ba3f4d --- /dev/null +++ b/Fableon/Fableon.entitlements @@ -0,0 +1,11 @@ + + + + + com.apple.developer.associated-domains + + applinks:fableon.go.link + applinks:kuzt.adj.st + + + diff --git a/Fableon/Object/Base/Controller/FATabBarController.swift b/Fableon/Object/Base/Controller/FATabBarController.swift index f50c284..3b56e8b 100644 --- a/Fableon/Object/Base/Controller/FATabBarController.swift +++ b/Fableon/Object/Base/Controller/FATabBarController.swift @@ -39,6 +39,14 @@ class FATabBarController: UITabBarController { self.tabBar.standardAppearance = appearance self.tabBar.isTranslucent = false + ///预加载支付数据 + FAIapManager.manager.preloadingProducts() + + FAIapManager.manager.restore(isLoding: false) { isFinish, buyType in + if isFinish { + FALogin.manager.requestUserInfo(completer: nil) + } + } } override var childForStatusBarStyle: UIViewController? { diff --git a/Fableon/Object/Base/Define/FADefine.swift b/Fableon/Object/Base/Define/FADefine.swift index 8f5e64b..5c73a0d 100644 --- a/Fableon/Object/Base/Define/FADefine.swift +++ b/Fableon/Object/Base/Define/FADefine.swift @@ -10,7 +10,7 @@ import UIKit ///当前系统版本号 let kFAOsVersion: String = UIDevice.current.systemVersion -let kBRAPPBundleIdentifier: String = (Bundle.main.infoDictionary!["CFBundleIdentifier"] as? String) ?? "0" +let kFAAPPBundleIdentifier: String = (Bundle.main.infoDictionary!["CFBundleIdentifier"] as? String) ?? "0" ///app版本号 let kFAAPPVersion: String = (Bundle.main.infoDictionary!["CFBundleShortVersionString"] as? String) ?? "0" diff --git a/Fableon/Object/Base/Define/FAUserDefaultsKey.swift b/Fableon/Object/Base/Define/FAUserDefaultsKey.swift new file mode 100644 index 0000000..3216986 --- /dev/null +++ b/Fableon/Object/Base/Define/FAUserDefaultsKey.swift @@ -0,0 +1,9 @@ +// +// FAUserDefaultsKey.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + + +let kFAWaitRestoreIAPDefaultsKey = "kFAWaitRestoreIAPDefaultsKey" diff --git a/Fableon/Object/Base/Extension/String+FAAdd.swift b/Fableon/Object/Base/Extension/String+FAAdd.swift index 4c18c91..ccbd1ac 100644 --- a/Fableon/Object/Base/Extension/String+FAAdd.swift +++ b/Fableon/Object/Base/Extension/String+FAAdd.swift @@ -9,6 +9,7 @@ import Foundation import SmartCodable import YYCategories + extension String: SmartCodable { static func timeZone() -> String { @@ -39,3 +40,22 @@ extension String { static let color_333333 = "#333333" static let color_D9D9D9 = "#D9D9D9" } + +extension String { + ///将url中的参数转换成字典 + func urlQuryToDictionary() -> [String : Any] { + let array = self.components(separatedBy: "&") + var tempDic: [String : Any] = [:] + + array.forEach { + if let strRange = $0.range(of: "=") { + var key: String = String($0.prefix(upTo: strRange.upperBound)) + key.removeLast() + var value: String = String($0.suffix(from: strRange.upperBound)) + value = value.removingPercentEncoding ?? value + tempDic[key] = value + } + } + return tempDic + } +} diff --git a/Fableon/Object/Base/Model/FAOpenAppModel.swift b/Fableon/Object/Base/Model/FAOpenAppModel.swift new file mode 100644 index 0000000..345579a --- /dev/null +++ b/Fableon/Object/Base/Model/FAOpenAppModel.swift @@ -0,0 +1,25 @@ +// +// FAOpenAppModel.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/23. +// + +import UIKit +import SmartCodable + +struct FAOpenAppModel: SmartCodable { + + enum Path: String, SmartCaseDefaultable { + case videoDetail = "detail" + ///反馈列表 + case feedback = "feedback" + ///活动 + case promotion = "promotion" + } + + var id: String? + var message_id: String? + var short_play_id: String? + var path: Path? +} diff --git a/Fableon/Object/Base/Request/FAAPI/FAStatAPI.swift b/Fableon/Object/Base/Request/FAAPI/FAStatAPI.swift new file mode 100644 index 0000000..7efad73 --- /dev/null +++ b/Fableon/Object/Base/Request/FAAPI/FAStatAPI.swift @@ -0,0 +1,87 @@ +// +// FAStatAPI.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/23. +// + +import UIKit + +class FAStatAPI: NSObject { + enum EventKey: String { + case payError = "pay_error" //支付过程中的其它错误 + case payCallback = "pay_callback" //支付成功,但是后台接口失败 + case payRestore = "pay_restore" //restore 失败 + case forceUpdate = "force_update"//更新弹窗 + case payCancel = "pay_cancel" //用户取消支付 + case payTemplateDialog = "pay_template_dialog"//详情页面弹出支付模版事件 + } + + ///统计w2a点击 + static func requestStatW2a(data: String) { + + let parameters = [ + "data" : data + ] + + FANetworkManager.manager.request(FABaseURL + "/w2aSelfAttribution", + method: .post, + parameters: parameters, + isLoding: false, + isToast: false, + ) { (response: FANetworkManager.Response) in + + } + } + + ///事件统计 + static func requestEventStat(orderCode: String?, shortPlayId: String?, videoId: String?, eventKey: EventKey, errorMsg: String?, otherParamenters: [String : Any]? = nil) { + + var eventName = "" + switch eventKey { + case .payRestore: + eventName = "pay restore" + + case .payCallback: + eventName = "pay callback failed" + + case .forceUpdate: + eventName = "force update" + + case .payCancel: + eventName = "user pay canceled" + + default: + eventName = "platform pay failed" + } + + var parameters: [String : Any] = [ + "userId" : FALogin.manager.userInfo?.customer_id ?? "", + "short_play_video_id" : videoId ?? "0", + "short_play_id" : shortPlayId ?? "0", + "event_key" : eventKey.rawValue, + "order_code": orderCode ?? "", + "event_name" : eventName, + ] + + if let errorMsg = errorMsg { + parameters["error_msg"] = errorMsg + } + + + if let otherParamenters = otherParamenters { + otherParamenters.forEach { + parameters[$0] = $1 + } + } + + FANetworkManager.manager.request(FABaseURL + "/event/add", + method: .post, + parameters: parameters, + isLoding: false, + isToast: false, + ) { (response: FANetworkManager.Response) in + + } + } +} diff --git a/Fableon/Object/Base/Request/FAAPI/FAStoreAPI.swift b/Fableon/Object/Base/Request/FAAPI/FAStoreAPI.swift new file mode 100644 index 0000000..a902888 --- /dev/null +++ b/Fableon/Object/Base/Request/FAAPI/FAStoreAPI.swift @@ -0,0 +1,134 @@ +// +// FAStoreAPI.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit +import SmartCodable + +class FAStoreAPI: NSObject { + enum BuyType: String, SmartCaseDefaultable { + case coins = "coins" + case subVip = "sub_vip" + case subCoins = "sub_coins" + case vip = "vip" + } + + + ///创建内购订单 + static func requestCreateOrder(payId: String, shortPlayId: String, videoId: String, isDiscount: Bool = false, identifierDiscount: String? = nil, completer: ((_ orderModel: FAIapOrderModel?) -> Void)?) { + + let parameters: [String : Any] = [ + "payment_channel" : "apple", + "short_play_id" : shortPlayId, + "video_id" : videoId, + "pay_setting_id" : payId, + "is_discount" : isDiscount ? 1 : 0, + "product_discount" : identifierDiscount ?? "", + ] + + FANetworkManager.manager.request(FABaseURL + "/createOrder", + method: .post, + parameters: parameters, + isLoding: false, + isToast: false, + ) { (response: FANetworkManager.Response) in + guard let data = response.data else { + FAToast.show(text: "network_error_01".localized) + completer?(nil) + return + } + if let message = data.message, message.count > 0 { + if response.data?.code == 30007 { + FAToast.show(text: "pay_error_1".localized) + } else { + FAToast.show(text: message) + } + completer?(nil) + } else { + completer?(data) + } + } + } + + ///校验内购 + static func requestVerifyOrder(parameters: [String : Any], completer: ((_ response: FANetworkManager.Response) -> Void)?) { + + FANetworkManager.manager.request(FABaseURL + "/applePaid", + method: .post, + parameters: parameters, + isLoding: false, + isToast: true, + ) { (response: FANetworkManager.Response) in + completer?(response) + } + } + + ///获取支付模版 + static func requestPayTemplate(isLoding: Bool = false, isToast: Bool = true, completer: ((_ model: FAPayDateModel?) -> Void)?) { + let parameters = [ + "discount" : "1", + "purchases_token" : JXIAPManager.manager.getAppStoreReceipt() ?? "", + ] + + FANetworkManager.manager.request(FABaseURL + "/paySettingsV4", + method: .post, + parameters: parameters, + isLoding: isLoding, + isToast: isToast, + ) { (response: FANetworkManager.Response) in + completer?(response.data) + } + } + + static func requestBuyRecords(page: Int, completer: ((_ listModel: FANetworkManager.List?) -> Void)?) { + let parameters = [ + "page_size" : 20, + "current_page" : page, + ] + + FANetworkManager.manager.request(FABaseURL + "/getCustomerBuyRecords", + method: .get, + parameters: parameters, + isLoding: false, + isToast: true, + ) { (response: FANetworkManager.Response>) in + completer?(response.data) + } + } + + static func requestRechargeRecord(page: Int, buyType: BuyType, completer: ((_ listModel: FANetworkManager.List?) -> Void)?) { + let parameters: [String : Any] = [ + "page_size" : 20, + "current_page" : page, + "buy_type" : buyType.rawValue + ] + + FANetworkManager.manager.request(FABaseURL + "/getCustomerOrder", + method: .get, + parameters: parameters, + isLoding: false, + isToast: true, + ) { (response: FANetworkManager.Response>) in + completer?(response.data) + } + } + + static func reuqestSendCoinRecord(page: Int, completer: ((_ listModel: FANetworkManager.List?) -> Void)?) { + let parameters: [String : Any] = [ + "page_size" : 20, + "current_page" : page, + ] + + FANetworkManager.manager.request(FABaseURL + "/sendCoinList", + method: .post, + parameters: parameters, + isLoding: false, + isToast: true, + ) { (response: FANetworkManager.Response>) in + completer?(response.data) + } + } +} diff --git a/Fableon/Object/Base/View/FALabel.swift b/Fableon/Object/Base/View/FALabel.swift new file mode 100644 index 0000000..599c883 --- /dev/null +++ b/Fableon/Object/Base/View/FALabel.swift @@ -0,0 +1,116 @@ +// +// FALabel.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit + +class FALabel: UILabel { + + var textColorImage: UIImage? + var fa_textColor: UIColor? + var textColors: [CGColor]? + var textStartPoint: CGPoint? + var textEndPoint: CGPoint? + + var borderLineWidth: CGFloat = 0 + var borderImage: UIImage? + var borderColor: UIColor? + var borderColors: [CGColor]? + var borderStartPoint: CGPoint? + var borderEndPoint: CGPoint? + + + + + override var intrinsicContentSize: CGSize { + let size = super.intrinsicContentSize + return .init(width: size.width + borderLineWidth / 2, height: size.height) + } + + override func drawText(in rect: CGRect) { + guard let context = UIGraphicsGetCurrentContext(), + let text = self.text else { + super.drawText(in: rect) + return + } + + +// var attributes: [NSAttributedString.Key: Any] = [:] +// +// if let att = self.attributedText?.yy_attributes { +// att.forEach { +// let key = NSAttributedString.Key.init($0) +// attributes[key] = $1 +// } +// +// } else if let font = self.font { +// attributes = [ +// .font: font +// ] +// } + +// let textSize = text.size(withAttributes: attributes) +// let x = (rect.width - textSize.width) / 2 +// let y = (rect.height - textSize.height) / 2 + + let textSize = self.attributedText?.boundingRect(with: rect.size, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil).size ?? .zero + let x = 0.0 + let y = 0.0 + let textRect = CGRect(x: x, y: y, width: textSize.width, height: textSize.height) + + + context.setLineWidth(borderLineWidth) + context.setLineJoin(.round) + // 先描边 + context.setTextDrawingMode(.stroke) + if let image = self.borderImage { + self.textColor = UIColor(patternImage: image.sp_resized(to: rect.size)) + } else if let colors = self.borderColors, let startPoint = self.borderStartPoint, let endPoine = self.borderEndPoint { + self.textColor = UIColor(patternImage: UIImage.fa_getGradientImage(size: size, colors: colors, startPoint: startPoint, endPoint: endPoine)) + } else { + self.textColor = self.borderColor ?? .clear + } + super.drawText(in: textRect) + + // 再填充 + context.setTextDrawingMode(.fill) + if let image = self.textColorImage { + self.textColor = UIColor(patternImage: image.sp_resized(to: rect.size)) + } else if let colors = self.textColors, let startPoint = self.textStartPoint, let endPoine = self.textEndPoint { + self.textColor = UIColor(patternImage: UIImage.fa_getGradientImage(size: size, colors: colors, startPoint: startPoint, endPoint: endPoine)) + } else { + self.textColor = fa_textColor // 填充色 + } + super.drawText(in: textRect) + } + +} + +extension UIImage { + + static func fa_getGradientImage(size: CGSize, colors: [CGColor], startPoint: CGPoint, endPoint: CGPoint) -> UIImage{ + + UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale) + guard let context = UIGraphicsGetCurrentContext() else{return UIImage()} + let colorSpace = CGColorSpaceCreateDeviceRGB() + ///设置渐变颜色 + let gradientRef = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: nil)! + let startPoint = CGPoint(x: size.width * startPoint.x, y: size.height * startPoint.y) + let endPoint = CGPoint(x: size.width * endPoint.x, y: size.height * endPoint.y) + context.drawLinearGradient(gradientRef, start: startPoint, end: endPoint, options: CGGradientDrawingOptions(arrayLiteral: .drawsBeforeStartLocation,.drawsAfterEndLocation)) + let gradientImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return gradientImage ?? UIImage() + } + + func sp_resized(to size: CGSize) -> UIImage { + let renderer = UIGraphicsImageRenderer(size: size) + return renderer.image { _ in + self.draw(in: CGRect(origin: .zero, size: size)) + } + } + +} diff --git a/Fableon/Object/Base/WebView/FAAppWebViewController.swift b/Fableon/Object/Base/WebView/FAAppWebViewController.swift index ee2fa98..80c0c7a 100644 --- a/Fableon/Object/Base/WebView/FAAppWebViewController.swift +++ b/Fableon/Object/Base/WebView/FAAppWebViewController.swift @@ -14,7 +14,7 @@ class FAAppWebViewController: FABaseWebViewController { private var receiveDataCount = 0 - var theme: String? = "theme_2" + var theme: String? = "theme_1" override func viewDidLoad() { super.viewDidLoad() diff --git a/Fableon/Object/Class/Me/C/FAMeViewController.swift b/Fableon/Object/Class/Me/C/FAMeViewController.swift index b439c42..cc25240 100644 --- a/Fableon/Object/Class/Me/C/FAMeViewController.swift +++ b/Fableon/Object/Class/Me/C/FAMeViewController.swift @@ -95,7 +95,7 @@ extension FAMeViewController: JXPagingViewDelegate { } func tableHeaderViewHeight(in pagingView: JXPagingView) -> Int { - return Int(ceill(headerView.contentHeight)) + return Int(ceil(headerView.contentHeight)) } func tableHeaderView(in pagingView: JXPagingView) -> UIView { diff --git a/Fableon/Object/Class/Player/V/FANewVideoRechargeView.swift b/Fableon/Object/Class/Player/V/FANewVideoRechargeView.swift new file mode 100644 index 0000000..0365bc8 --- /dev/null +++ b/Fableon/Object/Class/Player/V/FANewVideoRechargeView.swift @@ -0,0 +1,36 @@ +// +// FANewVideoRechargeView.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/28. +// + +import UIKit + +class FANewVideoRechargeView: FAPanModalContentView { + + var model: FAPayDateModel? { + didSet { + + } + } + + var videoInfo: FAVideoInfoModel? { + didSet { + + } + } + + override init(frame: CGRect) { + super.init(frame: frame) + + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func allowsTapBackgroundToDismiss() -> Bool { + return false + } +} diff --git a/Fableon/Object/Class/Player/V/FAOldVideoRechargeView.swift b/Fableon/Object/Class/Player/V/FAOldVideoRechargeView.swift new file mode 100644 index 0000000..8dfb6d9 --- /dev/null +++ b/Fableon/Object/Class/Player/V/FAOldVideoRechargeView.swift @@ -0,0 +1,290 @@ +// +// FAOldVideoRechargeView.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/28. +// + +import UIKit +import HWPanModal + +class FAOldVideoRechargeView: FAPanModalContentView { + + var buyFinishHandle: (() -> Void)? + + var model: FAPayDateModel? { + didSet { + self.stackView.fa_removeAllArrangedSubview() + self.vipView.dataArr = model?.list_sub_vip ?? [] + self.coinsView.setDataArr(model?.list_coins ?? []) + + if let sort = model?.sort, sort.count > 0 { + sort.forEach { + if $0 == .vip, model?.list_sub_vip?.isEmpty == false { + self.stackView.addArrangedSubview(self.vipView) + } else if $0 == .coin, model?.list_coins?.isEmpty == false { + self.stackView.addArrangedSubview(self.coinsView) + } + } + } else { + if model?.list_sub_vip?.isEmpty == false { + self.stackView.addArrangedSubview(self.vipView) + } + if model?.list_coins?.isEmpty == false { + self.stackView.addArrangedSubview(self.coinsView) + } + } + +// self.stackView.addArrangedSubview(self.tipView) + + self.setNeedsLayoutUpdate() + } + } + + var videoInfo: FAVideoInfoModel? { + didSet { + self.coinsView.videoId = videoInfo?.short_play_video_id + self.coinsView.shortPlayId = videoInfo?.short_play_id + self.vipView.videoId = videoInfo?.short_play_video_id + self.vipView.shortPlayId = videoInfo?.short_play_id + videoCoinsView.coins = videoInfo?.coins ?? 0 + + self.requestRestore() + } + } + + private lazy var closeButton: UIButton = { + let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + FAStatAPI.requestEventStat(orderCode: nil, shortPlayId: self.videoInfo?.short_play_id, videoId: self.videoInfo?.short_play_video_id, eventKey: .payTemplateDialog, errorMsg: nil, otherParamenters: [ + "event_name" : "pay cancel" + ]) + Task { + await self.dismiss(animated: true) + } + })) + button.setImage(UIImage(named: "close_icon_02"), for: .normal) + return button + }() + + private lazy var scrollView: FAScrollView = { + let scrollView = FAScrollView() + scrollView.clipsToBounds = false + return scrollView + }() + + private lazy var stackView: UIStackView = { + let view = UIStackView() + view.axis = .vertical + view.spacing = 18 + return view + }() + + private lazy var coinsView: FAStoreCoinsView = { + let view = FAStoreCoinsView() + view.buyFinishHandle = { [weak self] in + self?.buyFinishHandle?() + Task { + await self?.dismiss(animated: true) + } + } + return view + }() + + private lazy var vipView: FAStoreVipView = { + let view = FAStoreVipView() + view.buyFinishHandle = { [weak self] in + self?.buyFinishHandle?() + Task { + await self?.dismiss(animated: true) + } + } + return view + }() + + private lazy var videoCoinsView: FAOldVideoRechargeView.CoinsView = { + let view = FAOldVideoRechargeView.CoinsView() + view.title = "Price".localized + ":" + return view + }() + + private lazy var totalCoinsView: FAOldVideoRechargeView.CoinsView = { + let view = FAOldVideoRechargeView.CoinsView() + view.title = "Balance".localized + ":" + view.coins = FALogin.manager.userInfo?.totalCoins ?? 0 + return view + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .medium) + label.textColor = .FFFFFF + label.text = "store_title_1".localized + return label + }() + + deinit { + NotificationCenter.default.removeObserver(self) + } + + override init(frame: CGRect) { + super.init(frame: frame) + NotificationCenter.default.addObserver(self, selector: #selector(userInfoUpdateNotification), name: FALogin.userInfoUpdateNotification, object: nil) + self.contentHeight = UIScreen.height - UIScreen.safeTop +// self.backgroundColor = ._000000.withAlphaComponent(0.6) + self.backgroundColor = .clear + self.mainScrollView = self.scrollView + fa_setupLayout() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func userInfoUpdateNotification() { + totalCoinsView.coins = FALogin.manager.userInfo?.totalCoins ?? 0 + } + + override func present(in view: UIView?) { + super.present(in: view) + self.hw_contentView.fa_addEffectView(style: .dark) + self.hw_contentView.fa_setRoundedCorner(topLeft: 24, topRight: 24, bottomLeft: 0, bottomRight: 0) + } + + override func allowsTapBackgroundToDismiss() -> Bool { + return false + } +} + +extension FAOldVideoRechargeView { + + private func fa_setupLayout() { + addSubview(closeButton) + addSubview(videoCoinsView) + addSubview(totalCoinsView) + addSubview(titleLabel) + addSubview(scrollView) + scrollView.addSubview(stackView) + + closeButton.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-16) + make.top.equalToSuperview().offset(12) + } + + videoCoinsView.snp.makeConstraints { make in + make.centerY.equalTo(closeButton) + make.left.equalToSuperview().offset(16) + } + + totalCoinsView.snp.makeConstraints { make in + make.centerY.equalTo(closeButton) + make.left.equalTo(videoCoinsView.snp.right).offset(15) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.top.equalToSuperview().offset(39) + } + + scrollView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalToSuperview().offset(74) + make.bottom.equalToSuperview() + } + + stackView.snp.makeConstraints { make in + make.left.centerX.equalToSuperview() + make.top.equalToSuperview() + make.bottom.equalToSuperview().offset(-(UIScreen.safeBottom + 10)) + } + } + +} + +extension FAOldVideoRechargeView { + + @objc private func requestRestore() { + guard let shortPlayId = self.videoInfo?.short_play_id, let videoId = self.videoInfo?.short_play_video_id else { return } + + FAIapManager.manager.restore(isLoding: false, shortPlayId: shortPlayId, videoId: videoId) { [weak self] isFinish, buyType in + if isFinish { + FALogin.manager.requestUserInfo(completer: nil) + self?.buyFinishHandle?() + Task { + await self?.dismiss(animated: true) + } + } + } + } + +} + + +extension FAOldVideoRechargeView { + + class CoinsView: UIView { + var title: String? { + didSet { + titleLabel.text = title + } + } + + var coins: Int = 0 { + didSet { + coinsLabel.text = "\(coins)" + } + } + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .regular) + label.textColor = .FFFFFF + return label + }() + + private lazy var iconImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "coins_icon_08")) + return imageView + }() + + private lazy var coinsLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .bold) + label.textColor = .F_9_F_494 + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + addSubview(titleLabel) + addSubview(iconImageView) + addSubview(coinsLabel) + + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.bottom.lessThanOrEqualToSuperview() + make.left.equalToSuperview() + } + + iconImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.bottom.lessThanOrEqualToSuperview() + make.left.equalTo(titleLabel.snp.right).offset(6) + } + + coinsLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.bottom.lessThanOrEqualToSuperview() + make.left.equalTo(iconImageView.snp.right).offset(4) + make.right.equalToSuperview() + } + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + } + +} diff --git a/Fableon/Object/Class/Player/VC/FAPlayerDetailViewController.swift b/Fableon/Object/Class/Player/VC/FAPlayerDetailViewController.swift index 6813f21..0d9568b 100644 --- a/Fableon/Object/Class/Player/VC/FAPlayerDetailViewController.swift +++ b/Fableon/Object/Class/Player/VC/FAPlayerDetailViewController.swift @@ -87,7 +87,19 @@ class FAPlayerDetailViewController: JXPlayerListViewController { } self.pause() + let myCoins = FALogin.manager.userInfo?.totalCoins ?? 0 + let lockCoins = videoInfo?.coins ?? 0 + if myCoins < lockCoins, (self.fa_viewModel.currentCell as? FAPlayerDetailCell)?.hasLastEpisodeUnlocked != true { + self.fa_viewModel.openRechargeView() + } else { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in + guard let self = self else { return } + if (self.fa_viewModel.currentCell as? FAPlayerDetailCell)?.hasLastEpisodeUnlocked != true { + self.fa_viewModel.handleUnlockVideo() + } + } + } } } diff --git a/Fableon/Object/Class/Player/VM/FAShortDetailViewModel.swift b/Fableon/Object/Class/Player/VM/FAShortDetailViewModel.swift index 135ab6c..d6a8cf7 100644 --- a/Fableon/Object/Class/Player/VM/FAShortDetailViewModel.swift +++ b/Fableon/Object/Class/Player/VM/FAShortDetailViewModel.swift @@ -37,8 +37,10 @@ class FAShortDetailViewModel: JXPlayerListViewModel, ObservableObject { weak var popView: UIView? + private var payDataRequest: FAPayDataRequest? - func requestDetailData(completer: ((_ code: Int) -> Void)?) { + + func requestDetailData(indexPath: IndexPath? = nil, completer: ((_ code: Int) -> Void)?) { FAAPI.requestShortDetailData(shortPlayId: shortPlayId) { [weak self] model, code, msg in guard let self = self else { return } if let model = model { @@ -48,7 +50,9 @@ class FAShortDetailViewModel: JXPlayerListViewModel, ObservableObject { guard let self = self else { return } var targetIndexPath = IndexPath(row: 0, section: 0) - if let videoInfo = model.video_info { + if let indexPath = indexPath, indexPath.row < (model.episodeList?.count ?? 0) { + targetIndexPath = indexPath + } else if let videoInfo = model.video_info { var row: Int? model.episodeList?.enumerated().forEach { if $1.short_play_video_id == videoInfo.short_play_video_id { @@ -125,11 +129,59 @@ extension FAShortDetailViewModel { guard let videoInfo = self.currentCell?.model as? FAVideoInfoModel else { return } guard self.popView == nil else { return } - - - + self.payDataRequest = FAPayDataRequest() + if let model = FAIapManager.manager.payDateModel { + self._openRechargeView(model, videoInfo) + self.payDataRequest?.requestProducts(isLoding: false) { [weak self] model in + guard let self = self else { return } + guard let model = model else { return } + if let view = self.popView as? FAOldVideoRechargeView { + view.model = model + } else if let view = self.popView as? FANewVideoRechargeView { + view.model = model + } + } + } else { + self.payDataRequest?.requestProducts(isLoding: true) { [weak self] model in + guard let self = self else { return } + guard let model = model else { return } + self._openRechargeView(model, videoInfo) + } + } } + private func _openRechargeView(_ model: FAPayDateModel, _ videoInfo: FAVideoInfoModel) { + guard self.popView == nil else { return } + FAStatAPI.requestEventStat(orderCode: nil, shortPlayId: videoInfo.short_play_id, videoId: videoInfo.short_play_video_id, eventKey: .payTemplateDialog, errorMsg: nil, otherParamenters: [ + "event_name" : "pay open" + ]) + +// if model.pay_mode == 1 { +// _openNewRechargeView(model, videoInfo) +// } else { + _openOldRechargeView(model, videoInfo) +// } + } + + private func _openOldRechargeView(_ model: FAPayDateModel, _ videoInfo: FAVideoInfoModel) { + let view = FAOldVideoRechargeView() + view.model = model + view.videoInfo = videoInfo + view.buyFinishHandle = { [weak self] in + guard let self = self else { return } + self.requestDetailData(indexPath: self.currentIndexPath, completer: nil) + } + view.present(in: nil) + self.popView = view + } + + private func _openNewRechargeView(_ model: FAPayDateModel, _ videoInfo: FAVideoInfoModel) { + let view = FANewVideoRechargeView() + view.model = model + view.videoInfo = videoInfo + view.present(in: nil) + self.popView = view + } } extension FAShortDetailViewModel { diff --git a/Fableon/Object/Class/Store/C/FACoinRecordViewController.swift b/Fableon/Object/Class/Store/C/FACoinRecordViewController.swift index bc4a8a8..099fbbc 100644 --- a/Fableon/Object/Class/Store/C/FACoinRecordViewController.swift +++ b/Fableon/Object/Class/Store/C/FACoinRecordViewController.swift @@ -9,6 +9,8 @@ import UIKit class FACoinRecordViewController: FAViewController { + private lazy var dataArr: [FARechargeRecordModel] = [] + private lazy var page = 1 private lazy var tableView: FATableView = { let tableView = FATableView(frame: .zero, style: .plain) @@ -19,6 +21,13 @@ class FACoinRecordViewController: FAViewController { tableView.separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16) tableView.contentInset = .init(top: 10, left: 0, bottom: UIScreen.safeBottom + 10, right: 0) tableView.register(UINib(nibName: "FAOrderRecordCell", bundle: nil), forCellReuseIdentifier: "cell") + tableView.ly_emptyView = FAEmpty.fa_emptyView() + tableView.fa_addRefreshHeader(insetTop: tableView.contentInset.top) { [weak self] in + self?.handleHeaderRefresh(nil) + } + tableView.fa_addRefreshFooter(insetBottom: 0) { [weak self] in + self?.handleFooterRefresh(nil) + } return tableView }() @@ -32,8 +41,21 @@ class FACoinRecordViewController: FAViewController { make.left.right.bottom.equalToSuperview() make.top.equalToSuperview() } + + requestDataArr(page: 1, completer: nil) } + override func handleHeaderRefresh(_ completer: (() -> Void)?) { + requestDataArr(page: 1) { [weak self] in + self?.tableView.fa_endHeaderRefreshing() + } + } + + override func handleFooterRefresh(_ completer: (() -> Void)?) { + requestDataArr(page: self.page + 1) { [weak self] in + self?.tableView.fa_endFooterRefreshing() + } + } } @@ -41,12 +63,47 @@ class FACoinRecordViewController: FAViewController { extension FACoinRecordViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let model = self.dataArr[indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAOrderRecordCell + if model.type == "send" { + cell.titleLabel.text = "Bonus Coins".localized + } else { + cell.titleLabel.text = "Recharge Coins".localized + } + cell.dateLabel.text = model.created_at + cell.countLabel.text = "+\(model.value ?? "0")" return cell } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 20 + return self.dataArr.count } } + +extension FACoinRecordViewController { + + private func requestDataArr(page: Int, completer: (() -> Void)?) { + FAStoreAPI.requestRechargeRecord(page: page, buyType: .coins) { [weak self] listModel in + guard let self = self else { return } + guard let listModel = listModel, let list = listModel.list else { + completer?() + self.tableView.fa_updateNoMoreDataState(listModel?.hasNextPage) + return + } + if page == 1 { + self.dataArr.removeAll() + } + self.dataArr += list + + self.page = page + self.tableView.reloadData() + + completer?() + self.tableView.fa_updateNoMoreDataState(listModel.hasNextPage) + } + + } + +} + diff --git a/Fableon/Object/Class/Store/C/FAConsumptionRecordsViewController.swift b/Fableon/Object/Class/Store/C/FAConsumptionRecordsViewController.swift index e50e964..9a16466 100644 --- a/Fableon/Object/Class/Store/C/FAConsumptionRecordsViewController.swift +++ b/Fableon/Object/Class/Store/C/FAConsumptionRecordsViewController.swift @@ -9,6 +9,8 @@ import UIKit class FAConsumptionRecordsViewController: FAViewController { + private lazy var dataArr: [FABuyRecordsModel] = [] + private lazy var page = 1 private lazy var tableView: FATableView = { let tableView = FATableView(frame: .zero, style: .plain) @@ -19,6 +21,13 @@ class FAConsumptionRecordsViewController: FAViewController { tableView.separatorInset = .init(top: 0, left: 34, bottom: 0, right: 34) tableView.contentInset = .init(top: 10, left: 0, bottom: UIScreen.safeBottom + 10, right: 0) tableView.register(UINib(nibName: "FAConsumptionRecordsCell", bundle: nil), forCellReuseIdentifier: "cell") + tableView.ly_emptyView = FAEmpty.fa_emptyView() + tableView.fa_addRefreshHeader(insetTop: tableView.contentInset.top) { [weak self] in + self?.handleHeaderRefresh(nil) + } + tableView.fa_addRefreshFooter(insetBottom: 0) { [weak self] in + self?.handleFooterRefresh(nil) + } return tableView }() @@ -27,6 +36,8 @@ class FAConsumptionRecordsViewController: FAViewController { self.title = "Consumption Records".localized fa_setupLayout() + + requestDataArr(page: 1, completer: nil) } @@ -35,6 +46,18 @@ class FAConsumptionRecordsViewController: FAViewController { self.navigationController?.setNavigationBarHidden(false, animated: true) fa_setNavigationStyle() } + + override func handleHeaderRefresh(_ completer: (() -> Void)?) { + requestDataArr(page: 1) { [weak self] in + self?.tableView.fa_endHeaderRefreshing() + } + } + + override func handleFooterRefresh(_ completer: (() -> Void)?) { + requestDataArr(page: self.page + 1) { [weak self] in + self?.tableView.fa_endFooterRefreshing() + } + } } @@ -57,10 +80,37 @@ extension FAConsumptionRecordsViewController: UITableViewDelegate, UITableViewDa func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAConsumptionRecordsCell + cell.model = dataArr[indexPath.row] return cell } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 20 + return dataArr.count } } + +extension FAConsumptionRecordsViewController { + + private func requestDataArr(page: Int, completer: (() -> Void)?) { + FAStoreAPI.requestBuyRecords(page: page) { [weak self] listModel in + guard let self = self else { return } + guard let listModel = listModel, let list = listModel.list else { + completer?() + self.tableView.fa_updateNoMoreDataState(listModel?.hasNextPage) + return + } + if page == 1 { + self.dataArr.removeAll() + } + self.dataArr += list + + self.page = page + self.tableView.reloadData() + + completer?() + self.tableView.fa_updateNoMoreDataState(listModel.hasNextPage) + } + + } + +} diff --git a/Fableon/Object/Class/Store/C/FARewardCoinsViewController.swift b/Fableon/Object/Class/Store/C/FARewardCoinsViewController.swift index 98944dc..a9a4b96 100644 --- a/Fableon/Object/Class/Store/C/FARewardCoinsViewController.swift +++ b/Fableon/Object/Class/Store/C/FARewardCoinsViewController.swift @@ -9,6 +9,9 @@ import UIKit class FARewardCoinsViewController: FAViewController { + private lazy var dataArr: [FASendCoinRecordModel] = [] + private lazy var page = 1 + private lazy var tableView: FATableView = { let tableView = FATableView(frame: .zero, style: .plain) tableView.delegate = self @@ -18,6 +21,13 @@ class FARewardCoinsViewController: FAViewController { tableView.separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16) tableView.contentInset = .init(top: 10, left: 0, bottom: UIScreen.safeBottom + 10, right: 0) tableView.register(UINib(nibName: "FARewardCoinsCell", bundle: nil), forCellReuseIdentifier: "cell") + tableView.ly_emptyView = FAEmpty.fa_emptyView() + tableView.fa_addRefreshHeader(insetTop: tableView.contentInset.top) { [weak self] in + self?.handleHeaderRefresh(nil) + } + tableView.fa_addRefreshFooter(insetBottom: 0) { [weak self] in + self?.handleFooterRefresh(nil) + } return tableView }() @@ -31,6 +41,8 @@ class FARewardCoinsViewController: FAViewController { make.left.right.bottom.equalToSuperview() make.top.equalToSuperview().offset(UIScreen.navBarHeight + 10) } + + requestDataArr(page: 1, completer: nil) } @@ -39,6 +51,18 @@ class FARewardCoinsViewController: FAViewController { self.navigationController?.setNavigationBarHidden(false, animated: true) fa_setNavigationStyle() } + + override func handleHeaderRefresh(_ completer: (() -> Void)?) { + requestDataArr(page: 1) { [weak self] in + self?.tableView.fa_endHeaderRefreshing() + } + } + + override func handleFooterRefresh(_ completer: (() -> Void)?) { + requestDataArr(page: self.page + 1) { [weak self] in + self?.tableView.fa_endFooterRefreshing() + } + } } @@ -46,10 +70,37 @@ class FARewardCoinsViewController: FAViewController { extension FARewardCoinsViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FARewardCoinsCell + cell.model = self.dataArr[indexPath.row] return cell } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 20 + return self.dataArr.count } } + +extension FARewardCoinsViewController { + + private func requestDataArr(page: Int, completer: (() -> Void)?) { + FAStoreAPI.reuqestSendCoinRecord(page: page) { [weak self] listModel in + guard let self = self else { return } + guard let listModel = listModel, let list = listModel.list else { + completer?() + self.tableView.fa_updateNoMoreDataState(listModel?.hasNextPage) + return + } + if page == 1 { + self.dataArr.removeAll() + } + self.dataArr += list + + self.page = page + self.tableView.reloadData() + + completer?() + self.tableView.fa_updateNoMoreDataState(listModel.hasNextPage) + } + + } + +} diff --git a/Fableon/Object/Class/Store/C/FAStoreViewController.swift b/Fableon/Object/Class/Store/C/FAStoreViewController.swift index 520f121..0a22f9d 100644 --- a/Fableon/Object/Class/Store/C/FAStoreViewController.swift +++ b/Fableon/Object/Class/Store/C/FAStoreViewController.swift @@ -9,6 +9,11 @@ import UIKit class FAStoreViewController: FAViewController { + private var payRequest: FAPayDataRequest? + + ///支付数据 + private var payDataModel = FAIapManager.manager.payDateModel + private lazy var scrollView: FAScrollView = { let scrollView = FAScrollView() return scrollView @@ -22,11 +27,84 @@ class FAStoreViewController: FAViewController { return label }() + private lazy var stackView: UIStackView = { + let view = UIStackView() + view.axis = .vertical + view.spacing = 18 + return view + }() + + private lazy var coinsView: FAStoreCoinsView = { + let view = FAStoreCoinsView() + view.buyFinishHandle = { [weak self] in + self?.buyFinish() + } + return view + }() + + private lazy var vipView: FAStoreVipView = { + let view = FAStoreVipView() + view.buyFinishHandle = { [weak self] in + self?.buyFinish() + } + return view + }() + + private lazy var tipView: UIView = { + let view = UIView() + view.addSubview(tipTitleLabel) + view.addSubview(tipTextLabel) + + tipTitleLabel.snp.makeConstraints { make in + make.top.equalToSuperview().offset(7) + make.left.equalToSuperview().offset(16) + } + + tipTextLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.right.lessThanOrEqualToSuperview().offset(-16) + make.top.equalTo(tipTitleLabel.snp.bottom).offset(4) + make.bottom.equalToSuperview() + } + return view + }() + + private lazy var tipTitleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .medium) + label.textColor = .FFFFFF + label.text = "store_tips_title".localized + return label + }() + + private lazy var tipTextLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 10, weight: .regular) + label.textColor = .FFFFFF + label.text = "store_tips".localized + label.numberOfLines = 0 + return label + }() + override func viewDidLoad() { super.viewDidLoad() self.title = "Store".localized + let barButtonItem = UIBarButtonItem(title: "Restore".localized, style: .plain, target: self, action: #selector(handleRestore)) + barButtonItem.tintColor = .FFFFFF + self.navigationItem.rightBarButtonItem = barButtonItem fa_setupLayout() + + updateLayout() + + requestPayData() + + FAIapManager.manager.restore(isLoding: false) { [weak self] isFinish, buyType in + if isFinish { + FALogin.manager.requestUserInfo(completer: nil) + self?.buyFinish() + } + } } override func viewWillAppear(_ animated: Bool) { @@ -35,7 +113,74 @@ class FAStoreViewController: FAViewController { fa_setNavigationStyle() } - + private func buyFinish() { + self.requestPayData() + } + + @objc private func handleRestore() { + FAIapManager.manager.restore { [weak self] isFinish, buyType in + if isFinish { + FALogin.manager.requestUserInfo(completer: nil) + self?.buyFinish() + } + } + } + + func updateLayout() { + self.stackView.fa_removeAllArrangedSubview() + guard let model = self.payDataModel else { return } + + if model.pay_mode == 1 { + self.addCoinsView() + } else { + if let sort = model.sort, sort.count > 0 { + sort.forEach { + if $0 == .vip { + self.addVipView() + } else if $0 == .coin { + self.addCoinsView() + } + } + } else { + self.addVipView() + self.addCoinsView() + } + } + self.stackView.addArrangedSubview(tipView) + } + + private func addCoinsView() { + guard let model = self.payDataModel else { return } + + var newList: [FAPayItem] = [] + + if model.show_type == 1, model.pay_mode == 1 { + if let list = model.list_coins, list.count > 0 { + list.forEach { + if $0.buy_type == .subCoins { + newList.append($0) + } + } + } + } else { + if let list = model.list_coins, list.count > 0 { + newList = list + } + } + if newList.count > 0 { + coinsView.setDataArr(newList) + self.stackView.addArrangedSubview(coinsView) + } + } + + private func addVipView() { + guard let list = payDataModel?.list_sub_vip else { return } + guard list.count > 0 else { return } + + self.vipView.dataArr = list + self.stackView.addArrangedSubview(self.vipView) + } + } extension FAStoreViewController { @@ -43,6 +188,7 @@ extension FAStoreViewController { private func fa_setupLayout() { view.addSubview(scrollView) scrollView.addSubview(titleLabel) + scrollView.addSubview(stackView) scrollView.snp.makeConstraints { make in make.left.right.bottom.equalToSuperview() @@ -53,6 +199,29 @@ extension FAStoreViewController { make.top.equalToSuperview().offset(0) make.centerX.equalToSuperview() } + + stackView.snp.makeConstraints { make in + make.left.centerX.equalToSuperview() + make.top.equalToSuperview().offset(29) + make.bottom.equalToSuperview().offset(-(UIScreen.safeBottom + 10)) + } } } + +extension FAStoreViewController { + + private func requestPayData() { + payRequest = FAPayDataRequest() + + payRequest?.requestProducts { [weak self] model in + guard let self = self else { return } + guard let model = model else { return } + self.payDataModel = model + + self.updateLayout() + } + } + + +} diff --git a/Fableon/Object/Class/Store/C/FAVipRecordViewController.swift b/Fableon/Object/Class/Store/C/FAVipRecordViewController.swift index 6d5c501..d920d4b 100644 --- a/Fableon/Object/Class/Store/C/FAVipRecordViewController.swift +++ b/Fableon/Object/Class/Store/C/FAVipRecordViewController.swift @@ -9,6 +9,9 @@ import UIKit class FAVipRecordViewController: FAViewController { + private lazy var dataArr: [FARechargeRecordModel] = [] + private lazy var page = 1 + private lazy var tableView: FATableView = { let tableView = FATableView(frame: .zero, style: .plain) tableView.delegate = self @@ -18,6 +21,13 @@ class FAVipRecordViewController: FAViewController { tableView.separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16) tableView.contentInset = .init(top: 10, left: 0, bottom: UIScreen.safeBottom + 10, right: 0) tableView.register(UINib(nibName: "FAOrderRecordCell", bundle: nil), forCellReuseIdentifier: "cell") + tableView.ly_emptyView = FAEmpty.fa_emptyView() + tableView.fa_addRefreshHeader(insetTop: tableView.contentInset.top) { [weak self] in + self?.handleHeaderRefresh(nil) + } + tableView.fa_addRefreshFooter(insetBottom: 0) { [weak self] in + self?.handleFooterRefresh(nil) + } return tableView }() @@ -31,9 +41,21 @@ class FAVipRecordViewController: FAViewController { make.left.right.bottom.equalToSuperview() make.top.equalToSuperview() } + + requestDataArr(page: 1, completer: nil) } - + override func handleHeaderRefresh(_ completer: (() -> Void)?) { + requestDataArr(page: 1) { [weak self] in + self?.tableView.fa_endHeaderRefreshing() + } + } + + override func handleFooterRefresh(_ completer: (() -> Void)?) { + requestDataArr(page: self.page + 1) { [weak self] in + self?.tableView.fa_endFooterRefreshing() + } + } } @@ -41,13 +63,43 @@ class FAVipRecordViewController: FAViewController { extension FAVipRecordViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let model = self.dataArr[indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAOrderRecordCell cell.iconImageView.isHidden = true + cell.titleLabel.text = model.type + cell.dateLabel.text = model.created_at + cell.countLabel.text = "+\(model.value ?? "")" return cell } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 20 + return self.dataArr.count + } + +} + +extension FAVipRecordViewController { + + private func requestDataArr(page: Int, completer: (() -> Void)?) { + FAStoreAPI.requestRechargeRecord(page: page, buyType: .vip) { [weak self] listModel in + guard let self = self else { return } + guard let listModel = listModel, let list = listModel.list else { + completer?() + self.tableView.fa_updateNoMoreDataState(listModel?.hasNextPage) + return + } + if page == 1 { + self.dataArr.removeAll() + } + self.dataArr += list + + self.page = page + self.tableView.reloadData() + + completer?() + self.tableView.fa_updateNoMoreDataState(listModel.hasNextPage) + } + } } diff --git a/Fableon/Object/Class/Store/M/FABuyRecordsModel.swift b/Fableon/Object/Class/Store/M/FABuyRecordsModel.swift new file mode 100644 index 0000000..6e1e539 --- /dev/null +++ b/Fableon/Object/Class/Store/M/FABuyRecordsModel.swift @@ -0,0 +1,21 @@ +// +// FABuyRecordsModel.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/28. +// + +import UIKit +import SmartCodable + +struct FABuyRecordsModel: SmartCodable { + + var created_at: String? + var short_play_id: String? + var coins: Int? + var short_play_video_id: String? + var coin_type: Int? + var image_url: String? + var name: String? + var episode: String? +} diff --git a/Fableon/Object/Class/Store/M/FAPayAlertModel.swift b/Fableon/Object/Class/Store/M/FAPayAlertModel.swift new file mode 100644 index 0000000..2dab468 --- /dev/null +++ b/Fableon/Object/Class/Store/M/FAPayAlertModel.swift @@ -0,0 +1,24 @@ +// +// FAPayAlertModel.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit +import SmartCodable + +class FAPayAlertModel: NSObject, SmartCodable { + + required override init() { + super.init() + } + + var coins_modal_easy_close: Bool? + + var info: FAPayItem? + + var forced_recharge: Bool? + + var close_label: String? +} diff --git a/Fableon/Object/Class/Store/M/FAPayDateModel.swift b/Fableon/Object/Class/Store/M/FAPayDateModel.swift new file mode 100644 index 0000000..bbaf865 --- /dev/null +++ b/Fableon/Object/Class/Store/M/FAPayDateModel.swift @@ -0,0 +1,167 @@ +// +// FAPayDateModel.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit +import SmartCodable +import StoreKit + +class FAPayDateModel: NSObject, SmartCodable { + + + required override init() { + super.init() + } + + enum SortName: String, SmartCaseDefaultable { + case coin = "list_coins" + case vip = "list_sub_vip" + } + + var list_coins: [FAPayItem]? + var list_sub_vip: [FAPayItem]? + var list_sub_coins: [FAPayItem]? + var sort: [SortName]? + + ///0: 老版支付 1:新版支付 + var pay_mode: Int? + ///0: 普通金币 1:金币包模式 + var show_type: Int? + +} + + +class FAPayItem: NSObject, SmartCodable { + + required override init() { + super.init() + } + + + enum VipTypeKey: String, SmartCaseDefaultable { + case week = "week" + case month = "month" + case quarter = "quarter" + case year = "year" + } + + enum SizeType: String, SmartCaseDefaultable { + case big = "big" + case small = "small" + } + + + var id: String? + var backhaul_price: String? + var coins: Int? + var status: String? + var price: String? + var send_coins: Int? + var buy_type: FAStoreAPI.BuyType? + var origin_price: String? + + var vip_type: String? + var vip_type_key: VipTypeKey? + var sort: String? + var fa_description: String? + var brief: String? + var title: String? + var auto_sub: String? + + + var send_coin_ttl: Int? + + var size: SizeType? + + + + var ios_template_id: String? + ///角标 + var corner_marker: String? + ///平台 + var platform: String? + ///货币符号 + var currency: String? + + var ext_info: FAPayExtInfo? + + ///0 无优惠 1 首次购买优惠 2 二次购买优惠 + var discount_type: Int? + + @SmartIgnored + var product: SKProduct? + + ///首冲优惠数据 + var introductionaryOffer: SKProductDiscount? { + return product?.introductoryPrice + } + + ///促销优惠数据 + var promotionalOffers: [SKProductDiscount]? { + return product?.discounts + } + + + static func mappingForKey() -> [SmartKeyTransformer]? { + return [ + CodingKeys.fa_description <--- ["description"] + ] + } + + + + func getTimeString() -> String? { + switch self.vip_type_key { + case .week: + return "week".localized + + case .month: + return "month".localized + + case .quarter: + return "quarter".localized + + case .year: + return "year".localized + + default: + return nil + } + } + + func getVipTitle() -> String? { + switch self.vip_type_key { + case .week: + return "Weekly VIP".localized + + case .month: + return "Monthly VIP".localized + + case .quarter: + return "Quarterly VIP".localized + + case .year: + return "Yearly VIP".localized + + default: + return nil + } + } + +} + +class FAPayExtInfo: NSObject, SmartCodable { + + required override init() { + super.init() + } + + var extra_day_coins: Int? + var receive_coins_rate: String? + var max_total_coins: Int? + var sub_coins_txt_list: [String]? + +} diff --git a/Fableon/Object/Class/Store/M/FARechargeRecordModel.swift b/Fableon/Object/Class/Store/M/FARechargeRecordModel.swift new file mode 100644 index 0000000..b4c30a5 --- /dev/null +++ b/Fableon/Object/Class/Store/M/FARechargeRecordModel.swift @@ -0,0 +1,17 @@ +// +// FARechargeRecordModel.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/28. +// + +import UIKit +import SmartCodable + +struct FARechargeRecordModel: SmartCodable { + + var type: String? + var value: String? + var created_at: String? + +} diff --git a/Fableon/Object/Class/Store/M/FASendCoinRecordModel.swift b/Fableon/Object/Class/Store/M/FASendCoinRecordModel.swift new file mode 100644 index 0000000..b7de292 --- /dev/null +++ b/Fableon/Object/Class/Store/M/FASendCoinRecordModel.swift @@ -0,0 +1,21 @@ +// +// FASendCoinRecordModel.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/28. +// + +import UIKit +import SmartCodable + +struct FASendCoinRecordModel: SmartCodable { + + var id: String? + var left_coins: String? + var created_at: String? + var type: String? + var diff_datetime: String? + var expired_time: TimeInterval? + var coins: Int? + var is_effective: Int? +} diff --git a/Fableon/Object/Class/Store/V/FACoinPackConfirmItem1View.swift b/Fableon/Object/Class/Store/V/FACoinPackConfirmItem1View.swift new file mode 100644 index 0000000..f86deac --- /dev/null +++ b/Fableon/Object/Class/Store/V/FACoinPackConfirmItem1View.swift @@ -0,0 +1,74 @@ +// +// FACoinPackConfirmItem1View.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/28. +// + +import UIKit + +class FACoinPackConfirmItem1View: FACoinPackConfirmItemView { + + override init(frame: CGRect) { + super.init(frame: frame) + titleLabel.text = "Bonus You Get".localized + + let view1 = getItemView(image: UIImage(named: "Group 2072750450"), text: "Weekly Refill Package".localized) + let view2 = getItemView(image: UIImage(named: "Group 2072750451"), text: "Dailu Bonuses".localized) + + addSubview(view1) + addSubview(view2) + + view1.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.top.equalTo(titleLabel.snp.bottom).offset(8) + make.height.equalTo(83) + make.bottom.equalToSuperview().offset(-16) + } + + view2.snp.makeConstraints { make in + make.left.equalTo(view1.snp.right).offset(12) + make.right.equalToSuperview().offset(-12) + make.width.height.top.equalTo(view1) + } + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func getItemView(image: UIImage?, text: String) -> UIView { + let view = UIView() + view.backgroundColor = ._40395_D.withAlphaComponent(0.75) + view.layer.cornerRadius = 8 + view.layer.masksToBounds = true + + let imageView = UIImageView(image: image) + imageView.setContentHuggingPriority(.required, for: .horizontal) + imageView.setContentCompressionResistancePriority(.required, for: .horizontal) + + let label = UILabel() + label.numberOfLines = 0 + label.font = .font(ofSize: 12, weight: .init(800)) + label.textColor = .FFFFFF + label.text = text + + + view.addSubview(imageView) + view.addSubview(label) + + imageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(8) + } + + label.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalTo(imageView.snp.right).offset(8) + make.right.lessThanOrEqualToSuperview().offset(-8) + } + + return view + } + +} diff --git a/Fableon/Object/Class/Store/V/FACoinPackConfirmItem2View.swift b/Fableon/Object/Class/Store/V/FACoinPackConfirmItem2View.swift new file mode 100644 index 0000000..fb3e96d --- /dev/null +++ b/Fableon/Object/Class/Store/V/FACoinPackConfirmItem2View.swift @@ -0,0 +1,123 @@ +// +// FACoinPackConfirmItem2View.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/28. +// + +import UIKit + +class FACoinPackConfirmItem2View: FACoinPackConfirmItemView { + + var list: [String]? { + didSet { + + self.stackView.fa_removeAllArrangedSubview() + + list?.forEach { + let view = FACoinPackConfirmItem2View.ItemView() + view.text = $0 + self.stackView.addArrangedSubview(view) + } + + } + } + + private lazy var lineView: UIView = { + let view = UIView() + view.backgroundColor = .FFFFFF.withAlphaComponent(0.1) + return view + }() + + private lazy var stackView: UIStackView = { + let view = UIStackView() + view.axis = .vertical + view.spacing = 24 + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + self.titleLabel.text = "How Do I Receive Coins?" + + fa_setupLayout() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension FACoinPackConfirmItem2View { + + private func fa_setupLayout() { + addSubview(lineView) + addSubview(stackView) + + lineView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.centerX.equalToSuperview() + make.top.equalTo(titleLabel.snp.bottom).offset(16) + make.height.equalTo(1) + } + + stackView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.centerX.equalToSuperview() + make.top.equalTo(lineView.snp.bottom).offset(16) + make.bottom.equalToSuperview().offset(-16) + } + } + +} + +extension FACoinPackConfirmItem2View { + + class ItemView: UIView { + var text: String? { + didSet { + textLabel.text = text + } + } + + private lazy var iconImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "coins_icon_06")) + return imageView + }() + + private lazy var textLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .regular) + label.textColor = .FFFFFF + label.numberOfLines = 0 + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + addSubview(iconImageView) + addSubview(textLabel) + + iconImageView.snp.makeConstraints { make in + make.left.equalToSuperview() + make.top.equalToSuperview() + } + + textLabel.snp.makeConstraints { make in + make.left.equalTo(iconImageView.snp.right).offset(12) + make.right.lessThanOrEqualToSuperview() + make.top.equalToSuperview() + make.bottom.equalToSuperview() + } + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + } +} diff --git a/Fableon/Object/Class/Store/V/FACoinPackConfirmItemView.swift b/Fableon/Object/Class/Store/V/FACoinPackConfirmItemView.swift new file mode 100644 index 0000000..0c1a3f9 --- /dev/null +++ b/Fableon/Object/Class/Store/V/FACoinPackConfirmItemView.swift @@ -0,0 +1,39 @@ +// +// FACoinPackConfirmItemView.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/28. +// + +import UIKit + +class FACoinPackConfirmItemView: UIView { + + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 18, weight: .bold) + label.textColor = ._20_A_1_FF + label.textAlignment = .center + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + backgroundColor = ._29223_C + layer.cornerRadius = 8 + layer.masksToBounds = true + + addSubview(titleLabel) + + titleLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.right.lessThanOrEqualToSuperview().offset(-16) + make.top.equalToSuperview().offset(16) + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/Fableon/Object/Class/Store/V/FACoinPackConfirmView.swift b/Fableon/Object/Class/Store/V/FACoinPackConfirmView.swift new file mode 100644 index 0000000..7f68890 --- /dev/null +++ b/Fableon/Object/Class/Store/V/FACoinPackConfirmView.swift @@ -0,0 +1,209 @@ +// +// FACoinPackConfirmView.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/28. +// + +import UIKit +import HWPanModal + +class FACoinPackConfirmView: FAPanModalContentView { + + var buyFinishHandle: (() -> Void)? + + var shortPlayId: String? + var videoId: String? + + var model: FAPayItem? { + didSet { + item2View.list = model?.ext_info?.sub_coins_txt_list + + var price = model?.price ?? "" + if model?.discount_type == 1, let introductoryPrice = model?.introductionaryOffer { + price = introductoryPrice.price.stringValue + } else if model?.discount_type == 2, let discount = model?.promotionalOffers?.first { + price = discount.price.stringValue + } + + priceLabel.text = "\(model?.currency ?? "")\(price)/\(model?.getTimeString() ?? "")" + + } + } + + private lazy var bgImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "弹窗背景色")) + return imageView + }() + + private lazy var bgIconImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "Ellipse 873")) + return imageView + }() + + private lazy var titleLabel: FALabel = { + let label = FALabel() + label.font = .font(ofSize: 24, weight: .init(700)) + label.textColors = [UIColor._53_A_2_F_1.cgColor, UIColor.C_5_DDF_5.cgColor] + label.textStartPoint = .init(x: 0, y: 0.5) + label.textEndPoint = .init(x: 1, y: 0.5) + label.text = "What You Get".localized + return label + }() + + private lazy var closeButton: UIButton = { + let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + Task { + await self.dismiss(animated: true) + } + })) + button.setImage(UIImage(named: "close_icon_01"), for: .normal) + return button + }() + + private lazy var scrollView: FAScrollView = { + let scrollView = FAScrollView() + scrollView.addObserver(self, forKeyPath: "contentSize", context: nil) + return scrollView + }() + + private lazy var item1View: FACoinPackConfirmItem1View = { + let view = FACoinPackConfirmItem1View() + return view + }() + + private lazy var item2View: FACoinPackConfirmItem2View = { + let view = FACoinPackConfirmItem2View() + return view + }() + + private lazy var priceLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 18, weight: .bold) + label.textColor = ._20_A_1_FF + return label + }() + + private lazy var continueButton: UIButton = { + let button = FAGradientButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in + guard let self = self else { return } + guard let model = self.model else { return } + FAIapManager.manager.start(model: model, shortPlayId: self.shortPlayId, videoId: self.videoId) { [weak self] finish in + guard let self = self else { return } + if finish { + self.buyFinishHandle?() + Task { + await self.dismiss(animated: true) + } + } + } + })) + button.layer.cornerRadius = 24 + button.layer.masksToBounds = true + button.fa_colors = [UIColor._53_A_2_F_1.cgColor, UIColor.C_5_DDF_5.cgColor] + button.fa_locations = [0, 1] + button.fa_startPoint = .init(x: 0, y: 0.5) + button.fa_endPoint = .init(x: 1, y: 0.5) + button.setTitle("Continue".localized, for: .normal) + button.setTitleColor(.FFFFFF, for: .normal) + button.titleLabel?.font = .font(ofSize: 14, weight: .bold) + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + self.backgroundColor = .clear + fa_setupLayout() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "contentSize" { + self.panModalSetNeedsLayoutUpdate() + } + } + + override func allowsTapBackgroundToDismiss() -> Bool { + return false + } + + override func cornerRadius() -> CGFloat { + return 0 + } + + override func longFormHeight() -> PanModalHeight { + let height = 96 + scrollView.contentSize.height + 92 + 10 + return PanModalHeightMake(.content, height + UIScreen.safeBottom) + } + +} + +extension FACoinPackConfirmView { + + private func fa_setupLayout() { + addSubview(bgImageView) + bgImageView.addSubview(bgIconImageView) + addSubview(titleLabel) + addSubview(closeButton) + addSubview(scrollView) + scrollView.addSubview(item1View) + scrollView.addSubview(item2View) + addSubview(priceLabel) + addSubview(continueButton) + + bgImageView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + bgIconImageView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalToSuperview() + } + + titleLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalToSuperview().offset(55) + } + + closeButton.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-16) + make.top.equalToSuperview().offset(24) + } + + scrollView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.top.equalToSuperview().offset(96) + make.bottom.equalToSuperview().offset(-(92 + UIScreen.safeBottom + 10)) + } + + item1View.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.centerX.equalToSuperview() + make.top.equalToSuperview() + } + + item2View.snp.makeConstraints { make in + make.left.equalToSuperview().offset(16) + make.centerX.equalToSuperview() + make.top.equalTo(item1View.snp.bottom).offset(12) + make.bottom.equalToSuperview() + } + + priceLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.centerY.equalTo(scrollView.snp.bottom).offset(22) + } + + continueButton.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.left.equalToSuperview().offset(16) + make.top.equalTo(scrollView.snp.bottom).offset(44) + make.height.equalTo(48) + } + } + +} diff --git a/Fableon/Object/Class/Store/V/FAConsumptionRecordsCell.swift b/Fableon/Object/Class/Store/V/FAConsumptionRecordsCell.swift index f42cc5a..c1a7760 100644 --- a/Fableon/Object/Class/Store/V/FAConsumptionRecordsCell.swift +++ b/Fableon/Object/Class/Store/V/FAConsumptionRecordsCell.swift @@ -9,6 +9,15 @@ import UIKit class FAConsumptionRecordsCell: FATableViewCell { + var model: FABuyRecordsModel? { + didSet { + titleLabel.text = "Purchase Single Episode".localized + subtitleLabel.text = "Ep.##".localizedReplace(text: "\(model?.episode ?? "")") + " " + "\(model?.name ?? "")" + dateLabel.text = model?.created_at + coinsLabel.text = "-\(model?.coins ?? 0)" + "Coins".localized + } + } + @IBOutlet weak var titleLabel: UILabel! @@ -22,7 +31,7 @@ class FAConsumptionRecordsCell: FATableViewCell { override func awakeFromNib() { super.awakeFromNib() - titleLabel.text = "Purchase Single Episode dafdaaafafaafdasfadfsafdafadsadfas"; + titleLabel.text = "Purchase Single Episode"; subtitleLabel.text = "Ep.8 Romantic Flash Marriage In Progress" dateLabel.text = "2024-6-10 23:41:18" coinsLabel.text = "-10 Coins" diff --git a/Fableon/Object/Class/Store/V/FARewardCoinsCell.swift b/Fableon/Object/Class/Store/V/FARewardCoinsCell.swift index fd4880b..00b2230 100644 --- a/Fableon/Object/Class/Store/V/FARewardCoinsCell.swift +++ b/Fableon/Object/Class/Store/V/FARewardCoinsCell.swift @@ -9,6 +9,27 @@ import UIKit class FARewardCoinsCell: FATableViewCell { + var model: FASendCoinRecordModel? { + didSet { + nameLabel.text = model?.type + dateLabel.text = model?.created_at + countLabel.text = "+\(model?.coins ?? 0)" + remainingLabel.text = model?.left_coins + + if model?.is_effective == 1 { + expiresIconImageView.isHidden = false + expiresLabel.isHidden = false + expiredLabel.isHidden = true + expiresLabel.text = "Expires in ## days".localizedReplace(text: model?.diff_datetime ?? "") + } else { + expiresIconImageView.isHidden = true + expiresLabel.isHidden = true + expiredLabel.isHidden = false + } + + + } + } @IBOutlet weak var dateLabel: UILabel! diff --git a/Fableon/Object/Class/Store/V/FAStoreCoinsBigCell.swift b/Fableon/Object/Class/Store/V/FAStoreCoinsBigCell.swift new file mode 100644 index 0000000..70f9e6b --- /dev/null +++ b/Fableon/Object/Class/Store/V/FAStoreCoinsBigCell.swift @@ -0,0 +1,173 @@ +// +// FAStoreCoinsBigCell.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit + +class FAStoreCoinsBigCell: FAStoreCoinsCell { + + override var fa_isSelected: Bool { + didSet { + if fa_isSelected { + bgImageView.image = UIImage(named: "Rectangle 6216") + } else { + bgImageView.image = UIImage(named: "Rectangle 6217") + } + } + } + + override var model: FAPayItem? { + didSet { + let coins = model?.coins ?? 0 + let sendCoins = model?.send_coins ?? 0 + + coinCountLabel.text = "\(coins)" + + priceLabel.text = (model?.currency ?? "") + " " + (model?.price ?? "") + + if sendCoins > 0 { + sendCoinsLabel.isHidden = false + sendCoinsRatioImageView.isHidden = false + sendCoinsLabel.text = "+\(sendCoins) " + "Coins".localized + + let ratio = String(format: "%.0f", CGFloat(sendCoins) / CGFloat(coins) * 100) + sendCoinsRatioLabel.text = "+\(ratio)%" + } else { + sendCoinsLabel.isHidden = true + sendCoinsRatioImageView.isHidden = true + } + + if let mark = model?.corner_marker, !mark.isEmpty { + self.hotImageView.isHidden = false + } else { + self.hotImageView.isHidden = true + } + } + } + + private lazy var bgImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "Rectangle 6217")) + return imageView + }() + + private lazy var coinImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "coins_icon_04")) + return imageView + }() + + private lazy var coinCountLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .medium) + label.textColor = .FFFFFF + return label + }() + + private lazy var sendCoinsLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 10, weight: .medium) + label.textColor = ._000000 + return label + }() + + private lazy var sendCoinsRatioImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "Rectangle 36")) + return imageView + }() + + private lazy var sendCoinsRatioLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .medium) + label.textColor = .FFFFFF + return label + }() + + private lazy var priceBgView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "Frame 3030")) + return imageView + }() + + private lazy var priceLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .heavy) + label.textColor = ._000000 + return label + }() + + private lazy var hotImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "hot_icon_01")) + return imageView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + fa_setupLayout() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension FAStoreCoinsBigCell { + + private func fa_setupLayout() { + contentView.addSubview(bgImageView) + bgImageView.addSubview(coinImageView) + bgImageView.addSubview(coinCountLabel) + bgImageView.addSubview(sendCoinsLabel) + bgImageView.addSubview(sendCoinsRatioImageView) + sendCoinsRatioImageView.addSubview(sendCoinsRatioLabel) + contentView.addSubview(priceBgView) + priceBgView.addSubview(priceLabel) + contentView.addSubview(hotImageView) + + bgImageView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + } + + coinImageView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalToSuperview().offset(7) + } + + coinCountLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(coinImageView.snp.bottom).offset(4) + } + + sendCoinsLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(coinCountLabel.snp.bottom).offset(0) + } + + sendCoinsRatioImageView.snp.makeConstraints { make in + make.top.right.equalToSuperview() + } + + sendCoinsRatioLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(7) + make.right.equalToSuperview().offset(-10) + } + + priceBgView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.bottom.equalToSuperview() + } + + priceLabel.snp.makeConstraints { make in + make.center.equalToSuperview() + } + + hotImageView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(17) + make.top.equalToSuperview().offset(-9) + } + } + +} diff --git a/Fableon/Object/Class/Store/V/FAStoreCoinsCell.swift b/Fableon/Object/Class/Store/V/FAStoreCoinsCell.swift new file mode 100644 index 0000000..df72d3a --- /dev/null +++ b/Fableon/Object/Class/Store/V/FAStoreCoinsCell.swift @@ -0,0 +1,25 @@ +// +// FAStoreCoinsCell.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit + +class FAStoreCoinsCell: UICollectionViewCell { + + var model: FAPayItem? + + var fa_isSelected = false + + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/Fableon/Object/Class/Store/V/FAStoreCoinsPackCell.swift b/Fableon/Object/Class/Store/V/FAStoreCoinsPackCell.swift new file mode 100644 index 0000000..5c01335 --- /dev/null +++ b/Fableon/Object/Class/Store/V/FAStoreCoinsPackCell.swift @@ -0,0 +1,230 @@ +// +// FAStoreCoinsPackCell.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit + +class FAStoreCoinsPackCell: FAStoreCoinsCell { + + + override var model: FAPayItem? { + didSet { + coinsLabel.text = "\(model?.ext_info?.max_total_coins ?? 0)" + ratioLabel.text = "\(model?.ext_info?.receive_coins_rate ?? "0%")" + + priceView.setNeedsUpdateConfiguration() + } + } + + private lazy var bgView: FAGradientView = { + let view = FAGradientView() + view.fa_colors = [UIColor._524_B_8_E.cgColor, UIColor._303265.cgColor] + view.fa_locations = [0, 1] + view.fa_startPoint = .init(x: 0, y: 0.5) + view.fa_endPoint = .init(x: 1, y: 0.5) + view.layer.cornerRadius = 12 + view.layer.masksToBounds = true + view.layer.borderWidth = 1 + view.layer.borderColor = UIColor.E_5_E_5_E_5.cgColor + return view + }() + + private lazy var bgAttachmentView1: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "coin_attachment_01")) + return imageView + }() + + private lazy var bgAttachmentView2: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "coin_attachment_02")) + return imageView + }() + + private lazy var coinPileImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "coins_icon_05")) + return imageView + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .init(700)) + label.textColor = .FFFFFF + label.text = "Weekly Refill".localized + return label + }() + + private lazy var coinsImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "coins_icon_06")) + return imageView + }() + + private lazy var coinsLabel: UILabel = { + let label = FALabel() + label.font = .font(ofSize: 18, weight: .init(900)) + label.textColors = [UIColor.FFCE_63.cgColor, UIColor.FFE_1_AA.cgColor] + label.textStartPoint = .init(x: 0, y: 0.5) + label.textEndPoint = .init(x: 1, y: 0.5) + return label + }() + + private lazy var ratioBgView: UIView = { + let view = UIView() + view.backgroundColor = .FFFFFF.withAlphaComponent(0.15) + view.layer.cornerRadius = 4 + view.layer.masksToBounds = true + return view + }() + + private lazy var ratioLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .regular) + label.textColor = .FFFFFF + return label + }() + + private lazy var priceView: UIButton = { + var config = UIButton.Configuration.plain() + config.titleAlignment = .center + config.titlePadding = 0 + config.contentInsets = .init(top: 0, leading: 10, bottom: 0, trailing: 10) + + let button = FAGradientButton(configuration: config) + button.isUserInteractionEnabled = false + button.layer.masksToBounds = true + button.layer.cornerRadius = 24 + button.fa_colors = [UIColor._53_A_2_F_1.cgColor, UIColor.C_5_DDF_5.cgColor] + button.fa_startPoint = .init(x: 0, y: 0.5) + button.fa_endPoint = .init(x: 0.5, y: 0.5) + button.fa_locations = [0, 1] + button.configurationUpdateHandler = { [weak self] button in + guard let self = self else { return } + + let currency = self.model?.currency ?? "" + let timeText = model?.getTimeString() ?? "" + let oldPrice = self.model?.price ?? "" + var discountPrice: String? = nil + + if self.model?.discount_type == 1, let introductoryPrice = self.model?.introductionaryOffer { + discountPrice = introductoryPrice.price.stringValue + } else if self.model?.discount_type == 2, let discount = self.model?.promotionalOffers?.first { + discountPrice = discount.price.stringValue + } + + if let discountPrice = discountPrice { + + let priceString = AttributedString("\(currency)\(discountPrice)", attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 18, weight: .bold), + .foregroundColor : UIColor._114_CEE + ])) + + + button.configuration?.attributedTitle = priceString + + var subtitle = AttributedString("\(currency)\(oldPrice)", attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 12, weight: .regular), + .foregroundColor : UIColor._636_F_7_B.withAlphaComponent(0.2), + .strikethroughStyle: NSUnderlineStyle.single.rawValue, + .strikethroughColor: UIColor._636_F_7_B.withAlphaComponent(0.2) + ])) + + button.configuration?.attributedSubtitle = subtitle + + } else { + + button.configuration?.attributedTitle = AttributedString("\(currency)\(oldPrice)", attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 18, weight: .bold), + .foregroundColor : UIColor._114_CEE + ])) + + button.configuration?.attributedSubtitle = AttributedString("/\(timeText)", attributes: AttributeContainer([ + .font : UIFont.font(ofSize: 12, weight: .regular), + .foregroundColor : UIColor._000000.withAlphaComponent(0.5) + ])) + } + + } + + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + fa_setupLayout() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension FAStoreCoinsPackCell { + + private func fa_setupLayout() { + contentView.addSubview(bgView) + bgView.addSubview(bgAttachmentView1) + bgView.addSubview(bgAttachmentView2) + bgView.addSubview(coinPileImageView) + bgView.addSubview(titleLabel) + bgView.addSubview(coinsImageView) + bgView.addSubview(coinsLabel) + bgView.addSubview(ratioBgView) + ratioBgView.addSubview(ratioLabel) + bgView.addSubview(priceView) + + bgView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + bgAttachmentView1.snp.makeConstraints { make in + make.left.equalToSuperview().offset(0) + make.top.equalToSuperview().offset(0) + } + + bgAttachmentView2.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-26) + make.centerY.equalToSuperview() + } + + coinPileImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(12) + } + + titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview().offset(19) + make.left.equalTo(coinPileImageView.snp.right).offset(12) + } + + coinsImageView.snp.makeConstraints { make in + make.left.equalTo(titleLabel) + make.bottom.equalToSuperview().offset(-21) + } + + coinsLabel.snp.makeConstraints { make in + make.centerY.equalTo(coinsImageView) + make.left.equalTo(coinsImageView.snp.right).offset(4) + } + + ratioBgView.snp.makeConstraints { make in + make.left.equalTo(coinsLabel.snp.right).offset(4) + make.height.equalTo(18) + make.centerY.equalTo(coinsImageView) + } + + ratioLabel.snp.makeConstraints { make in + make.center.equalToSuperview() + make.left.equalToSuperview().offset(8) + } + + priceView.snp.makeConstraints { make in + make.right.equalToSuperview().offset(-12) + make.centerY.equalToSuperview() + make.height.equalTo(48) + make.width.greaterThanOrEqualTo(88) + } + } + +} diff --git a/Fableon/Object/Class/Store/V/FAStoreCoinsSmallCell.swift b/Fableon/Object/Class/Store/V/FAStoreCoinsSmallCell.swift new file mode 100644 index 0000000..52fe138 --- /dev/null +++ b/Fableon/Object/Class/Store/V/FAStoreCoinsSmallCell.swift @@ -0,0 +1,161 @@ +// +// FAStoreCoinsSmallCell.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit + +class FAStoreCoinsSmallCell: FAStoreCoinsCell { + + override var model: FAPayItem? { + didSet { + let coins = model?.coins ?? 0 + let sendCoins = model?.send_coins ?? 0 + + coinsLabel.text = "\(coins)" + + priceLabel.text = "\(model?.currency ?? "") \(model?.price ?? "")" + + if let mark = model?.corner_marker, !mark.isEmpty { + self.hotImageView.isHidden = false + } else { + self.hotImageView.isHidden = true + } + + if sendCoins > 0 { + sendCoinsLabel.isHidden = false + sendCoinsRatioView.isHidden = false + sendCoinsLabel.text = "+\(sendCoins) " + "Coins".localized + + let ratio = String(format: "%.0f", CGFloat(sendCoins) / CGFloat(coins) * 100) + sendCoinsRatioLabel.text = "+\(ratio)%" + } else { + sendCoinsLabel.isHidden = true + sendCoinsRatioView.isHidden = true + } + } + } + + private lazy var coinImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "coins_icon_07")) + return imageView + }() + + private lazy var coinsLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .medium) + label.textColor = .FFFFFF + return label + }() + + private lazy var sendCoinsLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 10, weight: .medium) + label.textColor = ._000000 + return label + }() + + private lazy var priceBgView: UIView = { + let view = UIView() + view.backgroundColor = .B_8_EFFD + return view + }() + + private lazy var priceLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 13, weight: .bold) + label.textColor = ._000000 + return label + }() + + private lazy var sendCoinsRatioView: UIView = { + let view = UIView() + view.backgroundColor = ._2_D_75_FF + view.fa_setRoundedCorner(topLeft: 0, topRight: 0, bottomLeft: 8, bottomRight: 0) + return view + }() + + private lazy var sendCoinsRatioLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .medium) + label.textColor = .FFFFFF + return label + }() + + private lazy var hotImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "hot_icon_01")) + return imageView + }() + + + + override init(frame: CGRect) { + super.init(frame: frame) + contentView.backgroundColor = ._84_C_0_FF.withAlphaComponent(0.77) + contentView.layer.cornerRadius = 9 + contentView.layer.masksToBounds = true + + fa_setupLayout() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension FAStoreCoinsSmallCell { + + private func fa_setupLayout() { + contentView.addSubview(coinImageView) + contentView.addSubview(coinsLabel) + contentView.addSubview(sendCoinsLabel) + contentView.addSubview(priceBgView) + priceBgView.addSubview(priceLabel) + addSubview(hotImageView) + contentView.addSubview(sendCoinsRatioView) + sendCoinsRatioView.addSubview(sendCoinsRatioLabel) + + coinImageView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalToSuperview().offset(20) + } + + coinsLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(coinImageView.snp.bottom).offset(5) + } + + sendCoinsLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(coinsLabel.snp.bottom).offset(0) + } + + priceBgView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.height.equalTo(28) + } + + priceLabel.snp.makeConstraints { make in + make.center.equalToSuperview() + } + + hotImageView.snp.makeConstraints { make in + make.left.equalToSuperview() + make.top.equalToSuperview().offset(-8) + } + + sendCoinsRatioView.snp.makeConstraints { make in + make.right.top.equalToSuperview() + make.height.equalTo(17) + } + + sendCoinsRatioLabel.snp.makeConstraints { make in + make.center.equalToSuperview() + make.left.equalToSuperview().offset(7) + } + } + +} diff --git a/Fableon/Object/Class/Store/V/FAStoreCoinsView.swift b/Fableon/Object/Class/Store/V/FAStoreCoinsView.swift new file mode 100644 index 0000000..46ea103 --- /dev/null +++ b/Fableon/Object/Class/Store/V/FAStoreCoinsView.swift @@ -0,0 +1,212 @@ +// +// FAStoreCoinsView.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit + +class FAStoreCoinsView: UIView { + + var shortPlayId: String? + var videoId: String? + + var buyFinishHandle: (() -> Void)? + + private lazy var dataArr: [[FAPayItem]] = [] + + private var selectedIndexPath: IndexPath? + + 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 + }() + + deinit { + collectionView.removeObserver(self, forKeyPath: "contentSize") + } + + override init(frame: CGRect) { + super.init(frame: frame) + fa_setupLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "contentSize" { + let height = self.collectionView.contentSize.height + 1 + self.collectionView.snp.updateConstraints { make in + make.height.equalTo(height) + } + } + } + + func setDataArr(_ arr: [FAPayItem]) { + self.dataArr.removeAll() + var bigArr: [FAPayItem] = [] + var smallArr: [FAPayItem] = [] + var coinPackArr: [FAPayItem] = [] + + arr.forEach { + if $0.buy_type == .subCoins { + coinPackArr.append($0) + } else if $0.size == .big { + bigArr.append($0) + } else { + smallArr.append($0) + } + } + + if bigArr.count > 0 { + self.dataArr.append(bigArr) + } + if coinPackArr.count > 0 { + self.dataArr.append(coinPackArr) + } + if smallArr.count > 0 { + self.dataArr.append(smallArr) + } + self.collectionView.reloadData() + } + +} + +extension FAStoreCoinsView { + + private func fa_setupLayout() { + addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.edges.equalToSuperview() + make.height.equalTo(1) + } + } + +} + +extension FAStoreCoinsView { + + private func bigLayoutSection() -> NSCollectionLayoutSection { + let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(1))) + + let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(100)), subitems: [item]) + group.interItemSpacing = .fixed(1) + + let layoutSection = NSCollectionLayoutSection(group: group) + layoutSection.interGroupSpacing = 10 + layoutSection.contentInsets = .init(top: 0, leading: 16, bottom: 0, trailing: 16) + return layoutSection + } + + private func smallLayoutSection() -> NSCollectionLayoutSection { + let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1 / 3), heightDimension: .fractionalHeight(1))) + + let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(121)), subitems: [item]) + group.interItemSpacing = .fixed(8) + + let layoutSection = NSCollectionLayoutSection(group: group) + layoutSection.interGroupSpacing = 10 + layoutSection.contentInsets = .init(top: 0, leading: 16, bottom: 0, trailing: 16) + return layoutSection + } + + private func coinsBigLayoutSection() -> NSCollectionLayoutSection { + let item = NSCollectionLayoutItem(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))) + + let group = NSCollectionLayoutGroup.horizontal(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(84)), subitems: [item]) + + + let layoutSection = NSCollectionLayoutSection(group: group) + layoutSection.interGroupSpacing = 10 + layoutSection.contentInsets = .init(top: 0, leading: 16, bottom: 0, trailing: 16) + return layoutSection + } +} + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension FAStoreCoinsView: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let model = dataArr[indexPath.section][indexPath.row] + + var identifier = "FAStoreCoinsBigCell" + if model.buy_type == .subCoins { + identifier = "FAStoreCoinsPackCell" + } else if model.size == .small { + identifier = "FAStoreCoinsSmallCell" + } + + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! FAStoreCoinsCell + cell.model = model + cell.fa_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.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/Fableon/Object/Class/Store/V/FAStoreVipCell.swift b/Fableon/Object/Class/Store/V/FAStoreVipCell.swift new file mode 100644 index 0000000..d58b61f --- /dev/null +++ b/Fableon/Object/Class/Store/V/FAStoreVipCell.swift @@ -0,0 +1,278 @@ +// +// FAStoreVipCell.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/27. +// + +import UIKit +import YYText + +class FAStoreVipCell: UICollectionViewCell { + + var item: FAPayItem? { + didSet { + nameLabel.text = item?.title + desLabel.text = item?.fa_description +// coinTimeLabel.text = item?.auto_sub + + if let coins = item?.send_coins, coins > 0 { + sendCoinLabel.text = "+\("extra".localized) \(coins)" + sendCoinLabel.isHidden = false + coinImageView.isHidden = false + } else { + sendCoinLabel.isHidden = true + coinImageView.isHidden = true + } + + let currency = item?.currency ?? "" + let oldPrice = item?.price ?? "" + let time = "/\(item?.getTimeString() ?? "")" + var offerPrice: String? + + if item?.discount_type == 1, let offer = item?.introductionaryOffer { + offerPrice = offer.price.stringValue + } else if item?.discount_type == 2, let offer = item?.promotionalOffers?.first { + offerPrice = offer.price.stringValue + } + + if let price = offerPrice { + let priceString = NSMutableAttributedString(string: currency + price) + priceString.yy_font = .font(ofSize: 28, weight: .init(800)) + priceLabel.attributedText = priceString + + let oldPriceStr = NSMutableAttributedString(string: oldPrice) + oldPriceStr.yy_strikethroughColor = discountLabel.textColor + oldPriceStr.yy_strikethroughStyle = .single + discountLabel.attributedText = oldPriceStr + + discountLabel.isHidden = false + + } else { + let priceString = NSMutableAttributedString(string: currency + oldPrice) + priceString.yy_font = .font(ofSize: 28, weight: .init(800)) + let timeString = NSMutableAttributedString(string: time) + timeString.yy_font = .font(ofSize: 14, weight: .medium) + timeString.yy_baselineOffset = 1 + priceString.append(timeString) + + priceLabel.attributedText = priceString + discountLabel.isHidden = true + } + + + + switch item?.vip_type_key { + case .week: + bgView.image = UIImage(named: "vip_bg_image_week") + bgIconView.image = UIImage(named: "vip_bg_icon_week") + vipImageView.image = UIImage(named: "vip_image_week") + subBgView.backgroundColor = .E_9_F_0_FE + nameBgView.backgroundColor = ._6398_FF + priceLabel.textColors = [UIColor.FFFFFF.cgColor, UIColor.FFFFFF.cgColor] + desLabel.textColor = .FFFFFF + sendCoinLabel.textColor = ._679_AFF + + case .month: + bgView.image = UIImage(named: "vip_bg_image_month") + bgIconView.image = UIImage(named: "vip_bg_icon_month") + vipImageView.image = UIImage(named: "vip_image_month") + subBgView.backgroundColor = .E_2_F_9_FF + nameBgView.backgroundColor = ._58_D_4_F_1 + priceLabel.textColors = [UIColor.FFFFFF.cgColor, UIColor.FFFFFF.cgColor] + desLabel.textColor = .FFFFFF + sendCoinLabel.textColor = ._2_DD_7_FF + + case .quarter: + bgView.image = UIImage(named: "vip_bg_image_quarter") + bgIconView.image = UIImage(named: "vip_bg_icon_quarter") + vipImageView.image = UIImage(named: "vip_image_quarter") + subBgView.backgroundColor = .EFE_8_FF + nameBgView.backgroundColor = ._8_D_5_BF_6 + priceLabel.textColors = [UIColor.FFFFFF.cgColor, UIColor.FFFFFF.cgColor] + desLabel.textColor = .FFFFFF + sendCoinLabel.textColor = ._8853_FF + + case .year: + bgView.image = UIImage(named: "vip_bg_image_year") + bgIconView.image = UIImage(named: "vip_bg_icon_year") + vipImageView.image = UIImage(named: "vip_image_year") + subBgView.backgroundColor = .FFF_8_D_0 + nameBgView.backgroundColor = .F_4_A_93_E + priceLabel.textColors = [UIColor.DB_9_E_42.cgColor, UIColor._56280_F.cgColor] + desLabel.textColor = ._814_E_20 + sendCoinLabel.textColor = .C_78_C_3_B + + default: + break + } + + coinTimeLabel.textColor = sendCoinLabel.textColor + } + } + + private lazy var bgView: UIImageView = { + let view = UIImageView() + return view + }() + + private lazy var bgIconView: UIImageView = { + let view = UIImageView() + return view + }() + + private lazy var subBgView: UIView = { + let view = UIView() + view.fa_setRoundedCorner(topLeft: 0, topRight: 0, bottomLeft: 15, bottomRight: 15) + return view + }() + + private lazy var nameBgView: UIView = { + let view = UIView() + view.layer.cornerRadius = 4 + view.layer.masksToBounds = true + return view + }() + + private lazy var nameLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 14, weight: .init(800)) + label.textColor = .FFFFFF + return label + }() + + private lazy var vipImageView: UIImageView = { + let imageView = UIImageView() + return imageView + }() + + private lazy var priceLabel: FALabel = { + let label = FALabel() + label.textStartPoint = .init(x: 0, y: 0.5) + label.textEndPoint = .init(x: 1, y: 0.5) + return label + }() + + private lazy var discountLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 16, weight: .medium) + label.textColor = .FFFFFF.withAlphaComponent(0.4) + return label + }() + + private lazy var desLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .regular) + return label + }() + + private lazy var sendCoinLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 12, weight: .medium) + return label + }() + + private lazy var coinImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "coins_icon_08")) + return imageView + }() + + private lazy var coinTimeLabel: UILabel = { + let label = UILabel() + label.font = .font(ofSize: 10, weight: .regular) + return label + }() + + + override init(frame: CGRect) { + super.init(frame: frame) + + fa_setupLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +extension FAStoreVipCell { + + private func fa_setupLayout() { + contentView.addSubview(bgView) + contentView.addSubview(subBgView) + bgView.addSubview(bgIconView) + bgView.addSubview(nameBgView) + nameBgView.addSubview(nameLabel) + bgView.addSubview(vipImageView) + bgView.addSubview(priceLabel) + bgView.addSubview(desLabel) + bgView.addSubview(discountLabel) + subBgView.addSubview(sendCoinLabel) + subBgView.addSubview(coinImageView) + subBgView.addSubview(coinTimeLabel) + + bgView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + } + + subBgView.snp.makeConstraints { make in + make.bottom.equalToSuperview() + make.top.equalTo(bgView.snp.bottom) + make.left.equalToSuperview().offset(30) + make.centerX.equalToSuperview() + } + + bgIconView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalToSuperview() + } + + nameBgView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.top.equalToSuperview().offset(13) + make.height.equalTo(18) + } + + nameLabel.snp.makeConstraints { make in + make.center.equalToSuperview() + make.left.equalToSuperview().offset(12) + } + + vipImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().offset(-16) + } + + priceLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.top.equalToSuperview().offset(36) + } + + desLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(12) + make.bottom.equalToSuperview().offset(-10) + } + + discountLabel.snp.makeConstraints { make in + make.left.equalTo(priceLabel.snp.right).offset(4) + make.centerY.equalTo(priceLabel).offset(3) + } + + sendCoinLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(12) + } + + coinImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalTo(sendCoinLabel.snp.right).offset(3) + } + + coinTimeLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.right.equalToSuperview().offset(-11) + } + + } + +} diff --git a/Fableon/Object/Class/Store/V/FAStoreVipView.swift b/Fableon/Object/Class/Store/V/FAStoreVipView.swift new file mode 100644 index 0000000..ae3e6b5 --- /dev/null +++ b/Fableon/Object/Class/Store/V/FAStoreVipView.swift @@ -0,0 +1,113 @@ +// +// FAStoreVipView.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/27. +// + +import UIKit + +class FAStoreVipView: UIView { + + var dataArr: [FAPayItem] = [] { + didSet { + collectionView.reloadData() + } + } + + var shortPlayId: String? + var videoId: String? + + var buyFinishHandle: (() -> Void)? + + 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]) + group.interItemSpacing = .fixed(14) + + let layoutSection = NSCollectionLayoutSection(group: group) + 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: FACollectionView = { + let collectionView = FACollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.isScrollEnabled = false + collectionView.addObserver(self, forKeyPath: "contentSize", context: nil) + collectionView.register(FAStoreVipCell.self, forCellWithReuseIdentifier: "cell") + return collectionView + }() + + deinit { + collectionView.removeObserver(self, forKeyPath: "contentSize") + } + + override init(frame: CGRect) { + super.init(frame: frame) + fa_setupLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + if keyPath == "contentSize" { + let height = self.collectionView.contentSize.height + 1 + self.collectionView.snp.updateConstraints { make in + make.height.equalTo(height) + } + } + } + +} + +extension FAStoreVipView { + + private func fa_setupLayout() { + addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.edges.equalToSuperview() + make.height.equalTo(1) + } + } + +} + + +//MARK: UICollectionViewDelegate UICollectionViewDataSource +extension FAStoreVipView: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! FAStoreVipCell + 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/Fableon/Object/Libs/AdjustStateManager/FAAdjustStateManager.swift b/Fableon/Object/Libs/AdjustStateManager/FAAdjustStateManager.swift new file mode 100644 index 0000000..c70e7ae --- /dev/null +++ b/Fableon/Object/Libs/AdjustStateManager/FAAdjustStateManager.swift @@ -0,0 +1,23 @@ +// +// FAAdjustStateManager.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/23. +// + +import UIKit + +class FAAdjustStateManager { + static let manager = FAAdjustStateManager() + + var webpageURL: URL? + ///是否需要重试 + var isNeedRetry = false + + var allowOpenMessage = true + + var isOpenApp = false + + var idfaAuthorizationFinish = false + +} diff --git a/Fableon/Object/Libs/Empty/FAEmpty.swift b/Fableon/Object/Libs/Empty/FAEmpty.swift index a854db5..f63c490 100644 --- a/Fableon/Object/Libs/Empty/FAEmpty.swift +++ b/Fableon/Object/Libs/Empty/FAEmpty.swift @@ -11,7 +11,7 @@ import LYEmptyView struct FAEmpty { - static func fa_emptyView(image: UIImage?, title: String?) -> LYEmptyView { + static func fa_emptyView(image: UIImage? = UIImage(named: "__shop-72"), title: String? = "empty_title_02".localized) -> LYEmptyView { let view = LYEmptyView.emptyActionView(with: image, titleStr: title, detailStr: nil, btnTitleStr: nil) { // btnClickBlock?() diff --git a/Fableon/Object/Libs/FAIap/FAIapManager.swift b/Fableon/Object/Libs/FAIap/FAIapManager.swift new file mode 100644 index 0000000..560c2b1 --- /dev/null +++ b/Fableon/Object/Libs/FAIap/FAIapManager.swift @@ -0,0 +1,292 @@ +// +// FAIapManager.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit +import StoreKit + +class FAIapManager { + typealias CompletionHandler = ((_ finish: Bool) -> Void) + ///内购模版前缀 + static let IAPPrefix = "fableon" + + + static let manager = FAIapManager() + + ///成功回调 + private var completionHandler: CompletionHandler? + + private var shortPlayId: String? + private var videoId: String? + + private lazy var iapManager: JXIAPManager = { + let manager = JXIAPManager() + manager.delegate = self + return manager + }() + + private var orderCode: String? + private var payId: String? + + ///预加载数据 + private var payRequest: FAPayDataRequest? + var payDateModel: FAPayDateModel? + + + ///恢复购买使用 + ///等待恢复的数据 + private var waitRestoreModel: FAWaitRestoreModel? = UserDefaults.fa_object(forKey: kFAWaitRestoreIAPDefaultsKey, as: FAWaitRestoreModel.self) + + + ///开始内购 + func start(model: FAPayItem, shortPlayId: String? = nil, videoId: String? = nil, hudShowView: UIView? = nil, handler: CompletionHandler? = nil) { + + if let _ = self.waitRestoreModel { + FAToast.show(text: "pay_error_6".localized) + handler?(false) + return + } + + guard let payId = model.id else { + handler?(false) + return + } + self.shortPlayId = shortPlayId + self.videoId = videoId + self.completionHandler = handler + self.waitRestoreModel = FAWaitRestoreModel() + self.waitRestoreModel?.buyType = model.buy_type + let productId = getProductId(templateId: model.ios_template_id) ?? "" + var isDiscount = false + var identifierDiscount: String? = nil + if model.discount_type == 1, let _ = model.introductionaryOffer { + isDiscount = true + } else if model.discount_type == 2, let discount = model.promotionalOffers?.first { + isDiscount = true + identifierDiscount = discount.identifier + } + + FAHUD.show(containerView: hudShowView) + + FAStoreAPI.requestCreateOrder(payId: payId, shortPlayId: shortPlayId ?? "0", videoId: videoId ?? "0", isDiscount: isDiscount, identifierDiscount: identifierDiscount) { orderModel in + guard let orderModel = orderModel else { + FAHUD.dismiss() + self.waitRestoreModel = nil + self.completionHandler?(false) + self.clean() + return + } + self.orderCode = orderModel.order_code + self.payId = payId + self.waitRestoreModel?.payId = payId + self.waitRestoreModel?.orderCode = orderModel.order_code + + var discount: SKPaymentDiscount? = nil + + if let identifierDiscount = identifierDiscount, + let signData = orderModel.discount?.sign_data, + let keyIdentifier = signData.keyIdentifier, + let nonce = UUID(uuidString: signData.nonce ?? ""), + let signature = signData.signature, + let timestamp = signData.timestamp + { + discount = SKPaymentDiscount(identifier: identifierDiscount, + keyIdentifier: keyIdentifier, + nonce: nonce, + signature: signature, + timestamp: NSNumber(value: timestamp)) + } + + self.iapManager.start(productId: productId, orderId: self.orderCode ?? "", applicationUsername: orderModel.discount?.sign_data?.applicationUsername, discount: discount) + + } + + } + + func restore(isLoding: Bool = true, shortPlayId: String? = nil, videoId: String? = nil, completer: ((_ isFinish: Bool, _ buyType: FAStoreAPI.BuyType?) -> Void)?) { + let buyType = self.waitRestoreModel?.buyType + + guard let waitRestoreModel = self.waitRestoreModel, + let orderCode = waitRestoreModel.orderCode, + let payId = waitRestoreModel.payId, + let receipt = waitRestoreModel.receipt, + let transactionId = waitRestoreModel.transactionId + else { + if isLoding { + FAToast.show(text: "pay_error_5".localized) + } + completer?(false, buyType) + return + } + + if isLoding { + FAHUD.show() + } + + let verifyData = self.getVerifyOrderParameters(orderCode: orderCode, payId: payId, transactionId: transactionId, purchaseToken: receipt) + + let statParamenters: [String : Any] = [ + "type" : isLoding ? "manual" : "auto", + "pay_data" : verifyData.toJsonString() ?? "" + ] + FAStatAPI.requestEventStat(orderCode: orderCode, shortPlayId: shortPlayId, videoId: videoId, eventKey: .payRestore, errorMsg: "restore", otherParamenters: statParamenters) + + FAStoreAPI.requestVerifyOrder(parameters: verifyData) { response in + if isLoding { + FAHUD.dismiss() + } + + guard let model = response.data else { + completer?(false, buyType) + return + } + self.waitRestoreModel = nil + UserDefaults.fa_setObject(nil, forKey: kFAWaitRestoreIAPDefaultsKey) + + if model.status == "success" { + if buyType == .subVip { + FALogin.manager.userInfo?.is_vip = true + } + + if isLoding { + FAToast.show(text: "success".localized) + } + completer?(true, buyType) + if buyType == .subVip { + NotificationCenter.default.post(name: FAIapManager.buyVipFinishNotification, object: nil) + } + } else { + completer?(false, buyType) + } + } + + } + + + func getProductId(templateId: String?) -> String? { + guard let templateId = templateId else { return nil } + return FAIapManager.IAPPrefix + "." + templateId + } + + func clean() { + self.orderCode = nil + self.payId = nil + self.shortPlayId = nil + self.videoId = nil + self.completionHandler = nil + } +} + +//MARK: JXIAPManagerDelegate +extension FAIapManager: JXIAPManagerDelegate { + + func jx_iapPaySuccess(productId: String, receipt: String, transactionIdentifier: String) { + guard let orderCode = self.orderCode, let payId = self.payId else { + self.waitRestoreModel = nil + self.completionHandler?(false) + self.clean() + FAHUD.dismiss() + return + } + + self.waitRestoreModel?.productId = productId + self.waitRestoreModel?.receipt = receipt + self.waitRestoreModel?.transactionId = transactionIdentifier + + UserDefaults.fa_setObject(self.waitRestoreModel, forKey: kFAWaitRestoreIAPDefaultsKey) + + #if DEBUG + let verifyData = self.getVerifyOrderParameters(orderCode: orderCode, payId: payId, transactionId: transactionIdentifier, purchaseToken: receipt) + #else + let verifyData = self.getVerifyOrderParameters(orderCode: orderCode, payId: payId, transactionId: transactionIdentifier, purchaseToken: receipt) + #endif + + FAStoreAPI.requestVerifyOrder(parameters: verifyData) { response in + FAHUD.dismiss() + + guard let model = response.data else { + FAStatAPI.requestEventStat(orderCode: self.orderCode, shortPlayId: self.shortPlayId, videoId: self.videoId, eventKey: .payCallback, errorMsg: verifyData.toJsonString()) + self.completionHandler?(false) + self.clean() + return + } + + let buyType = self.waitRestoreModel?.buyType + self.waitRestoreModel = nil + UserDefaults.fa_setObject(nil, forKey: kFAWaitRestoreIAPDefaultsKey) + + if model.status == "success" { + if buyType == .subVip { + FALogin.manager.userInfo?.is_vip = true + } + + FAToast.show(text: "success".localized) + self.completionHandler?(true) + if buyType == .subVip { + NotificationCenter.default.post(name: FAIapManager.buyVipFinishNotification, object: nil) + } + } else { + FAToast.show(text: "pay_error_4".localized) + FAStatAPI.requestEventStat(orderCode: self.orderCode, shortPlayId: self.shortPlayId, videoId: self.videoId, eventKey: .payCallback, errorMsg: verifyData.toJsonString()) + self.completionHandler?(false) + } + self.clean() + } + + } + + func jx_iapPayFailed(productId: String, code: JXIAPManagerCode, msg: String?) { + FAHUD.dismiss() + + if code == .noProduct { + FAToast.show(text: "pay_error_2".localized) + } else if code == .cancelled { + FAToast.show(text: "pay_error_3".localized) + } + + if code == .cancelled { + FAStatAPI.requestEventStat(orderCode: self.orderCode, shortPlayId: self.shortPlayId, videoId: self.videoId, eventKey: .payCancel, errorMsg: "user cancel") + } else { + FAStatAPI.requestEventStat(orderCode: self.orderCode, shortPlayId: self.shortPlayId, videoId: self.videoId, eventKey: .payError, errorMsg: msg) + } + + self.completionHandler?(false) + + self.waitRestoreModel = nil + self.clean() + } +} + +extension FAIapManager { + + func getVerifyOrderParameters(orderCode: String, payId: String, transactionId: String, purchaseToken: String) -> [String : Any] { + let parameters: [String : Any] = [ + "order_code" : orderCode, + "pay_setting_id" : payId, + "pkg_name" : kFAAPPBundleIdentifier, + "transaction_id": transactionId, + "purchases_token" : purchaseToken + ] + return parameters + } + + ///预加载支付项 + func preloadingProducts() { + JXIAPManager.manager.fetchReceipt { _ in + self.payRequest = FAPayDataRequest() + self.payRequest?.requestProducts(isLoding: false, isToast: false) { model in + + } + } + } + +} + +extension FAIapManager { + ///成功购买会员 + @objc static let buyVipFinishNotification = NSNotification.Name(rawValue: "FAIapManager.buyVipFinishNotification") + +} diff --git a/Fableon/Object/Libs/FAIap/FAIapOrderModel.swift b/Fableon/Object/Libs/FAIap/FAIapOrderModel.swift new file mode 100644 index 0000000..6be1acb --- /dev/null +++ b/Fableon/Object/Libs/FAIap/FAIapOrderModel.swift @@ -0,0 +1,37 @@ +// +// FAIapOrderModel.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit +import SmartCodable + +struct FAIapOrderModel: SmartCodable { + + var code: Int? + var message: String? + var order_code: String? + var is_backhaul: String? + var money: String? + var discount: FAIapOrderDiscountModel? + +} + +struct FAIapOrderDiscountModel: SmartCodable { + + var discount_code: String? + var is_discount: Bool? + var sign_data: FAIapOrderDiscountSign? +} + +struct FAIapOrderDiscountSign: SmartCodable { + + var keyIdentifier: String? + var nonce: String? + var timestamp: TimeInterval? + var applicationUsername: String? + var signature: String? +} + diff --git a/Fableon/Object/Libs/FAIap/FAIapVerifyModel.swift b/Fableon/Object/Libs/FAIap/FAIapVerifyModel.swift new file mode 100644 index 0000000..12c5241 --- /dev/null +++ b/Fableon/Object/Libs/FAIap/FAIapVerifyModel.swift @@ -0,0 +1,17 @@ +// +// FAIapVerifyModel.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit +import SmartCodable + +struct FAIapVerifyModel: SmartCodable { + + var money: String? + var status: String? + var is_backhaul: String? + var code: String? +} diff --git a/Fableon/Object/Libs/FAIap/FAPayDataRequest.swift b/Fableon/Object/Libs/FAIap/FAPayDataRequest.swift new file mode 100644 index 0000000..e68f8fd --- /dev/null +++ b/Fableon/Object/Libs/FAIap/FAPayDataRequest.swift @@ -0,0 +1,181 @@ +// +// FAPayDataRequest.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit +import StoreKit + +class FAPayDataRequest: NSObject { + + private var oldTemplateModel: FAPayDateModel? + private(set) var newTemplateModel: FAPayDateModel? + + private var payAlertModel: FAPayAlertModel? + + private var completerBlock: ((_ model: FAPayDateModel?) -> Void)? + private var payAlertBlock: ((_ model: FAPayAlertModel?) -> Void)? + + private var isLoding = false + private var isToast = false + + + func requestProducts(isLoding: Bool = false, isToast: Bool = true, completer: @escaping ((_ model: FAPayDateModel?) -> Void)) { + self.payAlertBlock = nil + self.completerBlock = completer + self.isLoding = isLoding + self.isToast = isToast + + if isLoding { + FAHUD.show() + } + + FAStoreAPI.requestPayTemplate(isToast: isToast) { [weak self] model in + guard let self = self else { return } + guard let model = model else { + if isLoding { + FAHUD.dismiss() + } + self.completerBlock?(nil) + return + } + self.oldTemplateModel = model + + var productIdArr: [String] = [] + model.list_sub_vip?.forEach { item in + productIdArr.append(FAIapManager.manager.getProductId(templateId: item.ios_template_id) ?? "") + } + model.list_coins?.forEach { item in + productIdArr.append(FAIapManager.manager.getProductId(templateId: item.ios_template_id) ?? "") + } + + let set = Set(productIdArr) + let productsRequest = SKProductsRequest(productIdentifiers: set) + productsRequest.delegate = self + productsRequest.start() + + } + } + + func requestCoinAlertData(completer: @escaping ((_ model: FAPayAlertModel?) -> Void)) { + self.completerBlock = nil + self.payAlertBlock = completer + +// BRStoreAPI.requestCoinAlertInfo { [weak self] model in +// guard let self = self else { return } +// +// guard let model = model else { +// self.payAlertBlock?(nil) +// return +// } +// self.payAlertModel = model +// +// let productId = BRIAP.manager.getProductId(templateId: model.info?.ios_template_id) ?? "" +// +// let set = Set([productId]) +// let productsRequest = SKProductsRequest(productIdentifiers: set) +// productsRequest.delegate = self +// productsRequest.start() +// } + + } + + ///挽留信息 + func requestRetainVipPayInfo(completer: ((_ model: FAPayAlertModel?) -> Void)?) { + self.completerBlock = nil + self.payAlertBlock = completer + +// FAStoreAPI.requestRetainVipPayInfo { [weak self] model in +// guard let self = self else { return } +// guard let model = model else { +// self.payAlertBlock?(nil) +// return +// } +// self.payAlertModel = model +// +// let productId = BRIAP.manager.getProductId(templateId: model.info?.ios_template_id) ?? "" +// +// let set = Set([productId]) +// let productsRequest = SKProductsRequest(productIdentifiers: set) +// productsRequest.delegate = self +// productsRequest.start() +// } + } + +} + +//MARK: SKProductsRequestDelegate +extension FAPayDataRequest: SKProductsRequestDelegate { + + func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { + if isLoding { + FAHUD.dismiss() + } + let products = response.products + + if let block = self.completerBlock { + guard let templateModel = self.oldTemplateModel else { return } + + var newCoinList: [FAPayItem] = [] + var newVipList: [FAPayItem] = [] + + templateModel.list_coins?.forEach { item in + let productId = FAIapManager.manager.getProductId(templateId: item.ios_template_id) ?? "" + for product in products { + if productId == product.productIdentifier { + item.price = product.price.stringValue + item.currency = product.priceLocale.currencySymbol + item.product = product + newCoinList.append(item) + break + } + } + } + + templateModel.list_sub_vip?.forEach { item in + let productId = FAIapManager.manager.getProductId(templateId: item.ios_template_id) ?? "" + for product in products { + if productId == product.productIdentifier { + item.price = product.price.stringValue + item.currency = product.priceLocale.currencySymbol + item.product = product + newVipList.append(item) + break + } + } + } + + + + templateModel.list_coins = newCoinList + templateModel.list_sub_vip = newVipList + + self.newTemplateModel = templateModel + FAIapManager.manager.payDateModel = templateModel + + DispatchQueue.main.async { + block(templateModel) + } + + } else if let block = self.payAlertBlock { + guard let coinalertModel = self.payAlertModel else { return } + let productId = FAIapManager.manager.getProductId(templateId: coinalertModel.info?.ios_template_id) ?? "" + + for product in products { + if productId == product.productIdentifier { + coinalertModel.info?.price = product.price.stringValue + coinalertModel.info?.currency = product.priceLocale.currencySymbol + coinalertModel.info?.product = product + break + } + } + + DispatchQueue.main.async { + block(coinalertModel) + } + } + + } +} diff --git a/Fableon/Object/Libs/FAIap/FAWaitRestoreModel.swift b/Fableon/Object/Libs/FAIap/FAWaitRestoreModel.swift new file mode 100644 index 0000000..181dc11 --- /dev/null +++ b/Fableon/Object/Libs/FAIap/FAWaitRestoreModel.swift @@ -0,0 +1,49 @@ +// +// FAWaitRestoreModel.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit + +class FAWaitRestoreModel: NSObject, NSSecureCoding { + + + var orderCode: String? + var payId: String? + var productId: String? + var receipt: String? + var buyType: FAStoreAPI.BuyType? + var transactionId: String? + + + required override init() { } + + static var supportsSecureCoding: Bool { + get { + return true + } + } + + func encode(with coder: NSCoder) { + coder.encode(orderCode, forKey: "orderCode") + coder.encode(payId, forKey: "payId") + coder.encode(productId, forKey: "productId") + coder.encode(receipt, forKey: "receipt") + coder.encode(buyType?.rawValue, forKey: "buyType") + coder.encode(transactionId, forKey: "transactionId") + } + + required init?(coder: NSCoder) { + super.init() + orderCode = coder.decodeObject(of: NSString.self, forKey: "orderCode") as? String + payId = coder.decodeObject(of: NSString.self, forKey: "payId") as? String + productId = coder.decodeObject(of: NSString.self, forKey: "productId") as? String + receipt = coder.decodeObject(of: NSString.self, forKey: "receipt") as? String + transactionId = coder.decodeObject(of: NSString.self, forKey: "transactionId") as? String + if let type = coder.decodeObject(of: NSString.self, forKey: "buyType") as? String { + buyType = FAStoreAPI.BuyType(rawValue: type) + } + } +} diff --git a/Fableon/Object/Libs/JXIAPManager/JXIAPManager.swift b/Fableon/Object/Libs/JXIAPManager/JXIAPManager.swift new file mode 100644 index 0000000..41847a8 --- /dev/null +++ b/Fableon/Object/Libs/JXIAPManager/JXIAPManager.swift @@ -0,0 +1,215 @@ +// +// JXIAPManager.swift +// Fableon +// +// Created by 湖北秦九 on 2025/10/24. +// + +import UIKit +import StoreKit + +@objc protocol JXIAPManagerDelegate { + /// 购买成功 + @objc optional func jx_iapPaySuccess(productId: String, receipt: String, transactionIdentifier: String) + /// 购买失败 + @objc optional func jx_iapPayFailed(productId: String, code: JXIAPManagerCode, msg: String?) + /// 恢复商品(仅限永久有效商品) + @objc optional func iapPayRestore(productIds: [String], transactionIds: [String]) +} + +@objc enum JXIAPManagerCode: Int { + ///未知错误 + case unknown + ///取消交易 + case cancelled + ///没有商品 + case noProduct +} + +class JXIAPManager: NSObject { + + + static let manager: JXIAPManager = JXIAPManager() + + weak var delegate: JXIAPManagerDelegate? + + private var payment: SKPayment? + + private var product: SKProduct? + private var productId: String? + + private var discount: SKPaymentDiscount? + private var orderId: String? + private var applicationUsername: String? + + var receiptCompletion: ((URL?) -> Void)? + + override init() { + super.init() + SKPaymentQueue.default().add(self) + } + + + func start(productId: String, orderId: String, applicationUsername: String?, discount: SKPaymentDiscount? = nil) { + self.product = nil + self.productId = productId + self.orderId = orderId + self.discount = discount + self.applicationUsername = applicationUsername + + let set = Set([productId]) + let productsRequest = SKProductsRequest(productIdentifiers: set) + productsRequest.delegate = self + productsRequest.start() + } + + /// 购买商品 + private func buyProduct() { + guard let product = self.product else { return } + + // 要购买商品,开个小票 + let payment = SKMutablePayment(product: product) + payment.applicationUsername = self.applicationUsername + if let discount = self.discount { + payment.paymentDiscount = discount + self.discount = nil + } + + self.payment = payment + + // 去收银台排队,准备购买 + SKPaymentQueue.default().add(payment) + } + +} + +//MARK: -------------- SKProductsRequestDelegate -------------- +extension JXIAPManager: SKProductsRequestDelegate { + func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { + guard let product = response.products.first else { + DispatchQueue.main.async { + if let productId = self.productId { + self.productId = nil + self.delegate?.jx_iapPayFailed?(productId: productId, code: .noProduct, msg: nil) + } + } + return + } + self.product = product + + self.buyProduct() + + } +} + +//MARK: -------------- SKPaymentTransactionObserver -------------- +extension JXIAPManager: SKPaymentTransactionObserver { + ///购买回调 + func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { + + for transaction in transactions { + switch transaction.transactionState { + case .purchased: + DispatchQueue.main.async { + self.completeTransaction(transaction: transaction) + } + SKPaymentQueue.default().finishTransaction(transaction) + + case .failed: + DispatchQueue.main.async { + self.failedTransaction(transaction: transaction) + } + SKPaymentQueue.default().finishTransaction(transaction) +// case .restored: +// self.restoreTransaction(transaction: transaction) + + case .purchasing: + break + default: + SKPaymentQueue.default().finishTransaction(transaction) + break + } + + + } + } + +// func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool { +// return true +// } + + /// 恢复购买回调 + func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { + + } + +} + +extension JXIAPManager { + + + private func completeTransaction(transaction: SKPaymentTransaction) { + guard let encodeStr = getAppStoreReceipt() else { return } + guard let transactionIdentifier = transaction.transactionIdentifier else { return } + + guard let productId = self.productId, productId == transaction.payment.productIdentifier else { return } + self.productId = nil + self.delegate?.jx_iapPaySuccess?(productId: productId, receipt: encodeStr, transactionIdentifier: transactionIdentifier) + + } + + private func failedTransaction(transaction: SKPaymentTransaction) { + let error = transaction.error as? SKError + guard let productId = self.productId else { return } + self.productId = nil + + switch error?.code { + case SKError.paymentCancelled: + self.delegate?.jx_iapPayFailed?(productId: productId, code: .cancelled, msg: error?.localizedDescription) + default: + self.delegate?.jx_iapPayFailed?(productId: productId, code: .unknown, msg: error?.localizedDescription) + } + + } + + + +} + +extension JXIAPManager: SKRequestDelegate { + + func getAppStoreReceipt() -> String? { + guard let receiptURL = Bundle.main.appStoreReceiptURL else { return nil } + let receiptData = NSData(contentsOf: receiptURL) + return receiptData?.base64EncodedString(options: .endLineWithLineFeed) + } + + ///获取凭证 + func fetchReceipt(completion: @escaping (URL?) -> Void) { + let receiptURL = Bundle.main.appStoreReceiptURL + if let url = receiptURL, FileManager.default.fileExists(atPath: url.path) { + completion(url) + return + } + + let request = SKReceiptRefreshRequest() + request.delegate = self + request.start() + // 在 delegate 回调里处理 + self.receiptCompletion = completion + } + + func requestDidFinish(_ request: SKRequest) { + let receiptURL = Bundle.main.appStoreReceiptURL + if let url = receiptURL, FileManager.default.fileExists(atPath: url.path) { + receiptCompletion?(url) + } else { + receiptCompletion?(nil) + } + } + + func request(_ request: SKRequest, didFailWithError error: Error) { + print("Receipt request failed: \(error)") + receiptCompletion?(nil) + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#114CEE.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#114CEE.colorset/Contents.json new file mode 100644 index 0000000..dacf4a5 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#114CEE.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xEE", + "green" : "0x4C", + "red" : "0x11" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#20A1FF.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#20A1FF.colorset/Contents.json new file mode 100644 index 0000000..3609705 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#20A1FF.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xA1", + "red" : "0x20" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#29223C.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#29223C.colorset/Contents.json new file mode 100644 index 0000000..bd73052 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#29223C.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x3C", + "green" : "0x22", + "red" : "0x29" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#2D75FF.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#2D75FF.colorset/Contents.json new file mode 100644 index 0000000..91986c7 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#2D75FF.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0x75", + "red" : "0x2D" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#2DD7FF.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#2DD7FF.colorset/Contents.json new file mode 100644 index 0000000..09bb6ee --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#2DD7FF.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xD7", + "red" : "0x2D" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#303265.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#303265.colorset/Contents.json new file mode 100644 index 0000000..bc2bbb7 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#303265.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x65", + "green" : "0x32", + "red" : "0x30" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#40395D.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#40395D.colorset/Contents.json new file mode 100644 index 0000000..9e516ee --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#40395D.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x5D", + "green" : "0x39", + "red" : "0x40" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#524B8E.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#524B8E.colorset/Contents.json new file mode 100644 index 0000000..9b8a234 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#524B8E.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x8E", + "green" : "0x4B", + "red" : "0x52" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#53A2F1.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#53A2F1.colorset/Contents.json new file mode 100644 index 0000000..dd9119a --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#53A2F1.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF1", + "green" : "0xA2", + "red" : "0x53" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#56280F.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#56280F.colorset/Contents.json new file mode 100644 index 0000000..8e73b66 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#56280F.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x0F", + "green" : "0x28", + "red" : "0x56" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#58D4F1.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#58D4F1.colorset/Contents.json new file mode 100644 index 0000000..8701b40 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#58D4F1.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF1", + "green" : "0xD4", + "red" : "0x58" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#636F7B.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#636F7B.colorset/Contents.json new file mode 100644 index 0000000..c156453 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#636F7B.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x7B", + "green" : "0x6F", + "red" : "0x63" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#6398FF.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#6398FF.colorset/Contents.json new file mode 100644 index 0000000..4ca0a32 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#6398FF.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0x98", + "red" : "0x63" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#679AFF.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#679AFF.colorset/Contents.json new file mode 100644 index 0000000..a3cac30 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#679AFF.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0x9A", + "red" : "0x67" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#814E20.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#814E20.colorset/Contents.json new file mode 100644 index 0000000..2c072af --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#814E20.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x20", + "green" : "0x4E", + "red" : "0x81" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#84C0FF.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#84C0FF.colorset/Contents.json new file mode 100644 index 0000000..c4600a5 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#84C0FF.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xC0", + "red" : "0x84" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#8853FF.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#8853FF.colorset/Contents.json new file mode 100644 index 0000000..e577578 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#8853FF.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0x53", + "red" : "0x88" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#8D5BF6.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#8D5BF6.colorset/Contents.json new file mode 100644 index 0000000..7f625b0 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#8D5BF6.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF6", + "green" : "0x5B", + "red" : "0x8D" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#B8EFFD.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#B8EFFD.colorset/Contents.json new file mode 100644 index 0000000..50ec7d9 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#B8EFFD.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFD", + "green" : "0xEF", + "red" : "0xB8" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#C5DDF5.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#C5DDF5.colorset/Contents.json new file mode 100644 index 0000000..9321bdc --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#C5DDF5.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF5", + "green" : "0xDD", + "red" : "0xC5" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#C78C3B.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#C78C3B.colorset/Contents.json new file mode 100644 index 0000000..1bcb624 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#C78C3B.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x3B", + "green" : "0x8C", + "red" : "0xC7" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#DB9E42.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#DB9E42.colorset/Contents.json new file mode 100644 index 0000000..3dbeaaf --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#DB9E42.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x42", + "green" : "0x9E", + "red" : "0xDB" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#E2F9FF.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#E2F9FF.colorset/Contents.json new file mode 100644 index 0000000..25434e0 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#E2F9FF.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xF9", + "red" : "0xE2" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#E5E5E5.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#E5E5E5.colorset/Contents.json new file mode 100644 index 0000000..290eae7 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#E5E5E5.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xE5", + "green" : "0xE5", + "red" : "0xE5" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#E9F0FE.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#E9F0FE.colorset/Contents.json new file mode 100644 index 0000000..682feab --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#E9F0FE.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFE", + "green" : "0xF0", + "red" : "0xE9" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#EFE8FF.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#EFE8FF.colorset/Contents.json new file mode 100644 index 0000000..e7f18d5 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#EFE8FF.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xE8", + "red" : "0xEF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#F4A93E.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#F4A93E.colorset/Contents.json new file mode 100644 index 0000000..7807f65 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#F4A93E.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x3E", + "green" : "0xA9", + "red" : "0xF4" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#F9F494.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#F9F494.colorset/Contents.json new file mode 100644 index 0000000..a61ee66 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#F9F494.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x94", + "green" : "0xF4", + "red" : "0xF9" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#FFCE63.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#FFCE63.colorset/Contents.json new file mode 100644 index 0000000..094b08d --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#FFCE63.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x63", + "green" : "0xCE", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#FFE1AA.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#FFE1AA.colorset/Contents.json new file mode 100644 index 0000000..32c57e5 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#FFE1AA.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xAA", + "green" : "0xE1", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/color/#FFF8D0.colorset/Contents.json b/Fableon/Source/Assets.xcassets/color/#FFF8D0.colorset/Contents.json new file mode 100644 index 0000000..c9e30c7 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/color/#FFF8D0.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xD0", + "green" : "0xF8", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/Ellipse 873.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/Ellipse 873.imageset/Contents.json new file mode 100644 index 0000000..767ca84 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/Ellipse 873.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Ellipse 873@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Ellipse 873@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/Ellipse 873.imageset/Ellipse 873@2x.png b/Fableon/Source/Assets.xcassets/image/Ellipse 873.imageset/Ellipse 873@2x.png new file mode 100644 index 0000000..b0c4ff1 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Ellipse 873.imageset/Ellipse 873@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Ellipse 873.imageset/Ellipse 873@3x.png b/Fableon/Source/Assets.xcassets/image/Ellipse 873.imageset/Ellipse 873@3x.png new file mode 100644 index 0000000..fbe06fb Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Ellipse 873.imageset/Ellipse 873@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Frame 3030.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/Frame 3030.imageset/Contents.json new file mode 100644 index 0000000..886807c --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/Frame 3030.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame 3030@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame 3030@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/Frame 3030.imageset/Frame 3030@2x.png b/Fableon/Source/Assets.xcassets/image/Frame 3030.imageset/Frame 3030@2x.png new file mode 100644 index 0000000..9e0bbdd Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Frame 3030.imageset/Frame 3030@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Frame 3030.imageset/Frame 3030@3x.png b/Fableon/Source/Assets.xcassets/image/Frame 3030.imageset/Frame 3030@3x.png new file mode 100644 index 0000000..86ee10b Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Frame 3030.imageset/Frame 3030@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Group 2072750450.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/Group 2072750450.imageset/Contents.json new file mode 100644 index 0000000..d78b721 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/Group 2072750450.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group 2072750450@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group 2072750450@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/Group 2072750450.imageset/Group 2072750450@2x.png b/Fableon/Source/Assets.xcassets/image/Group 2072750450.imageset/Group 2072750450@2x.png new file mode 100644 index 0000000..2888907 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Group 2072750450.imageset/Group 2072750450@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Group 2072750450.imageset/Group 2072750450@3x.png b/Fableon/Source/Assets.xcassets/image/Group 2072750450.imageset/Group 2072750450@3x.png new file mode 100644 index 0000000..1136402 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Group 2072750450.imageset/Group 2072750450@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Group 2072750451.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/Group 2072750451.imageset/Contents.json new file mode 100644 index 0000000..274cecb --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/Group 2072750451.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group 2072750451@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group 2072750451@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/Group 2072750451.imageset/Group 2072750451@2x.png b/Fableon/Source/Assets.xcassets/image/Group 2072750451.imageset/Group 2072750451@2x.png new file mode 100644 index 0000000..b8cbd91 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Group 2072750451.imageset/Group 2072750451@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Group 2072750451.imageset/Group 2072750451@3x.png b/Fableon/Source/Assets.xcassets/image/Group 2072750451.imageset/Group 2072750451@3x.png new file mode 100644 index 0000000..825ca41 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Group 2072750451.imageset/Group 2072750451@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Rectangle 36.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/Rectangle 36.imageset/Contents.json new file mode 100644 index 0000000..c2a4324 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/Rectangle 36.imageset/Contents.json @@ -0,0 +1,44 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 36@2x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 27, + "right" : 24 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "2x" + }, + { + "filename" : "Rectangle 36@3x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 31, + "right" : 35 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/Rectangle 36.imageset/Rectangle 36@2x.png b/Fableon/Source/Assets.xcassets/image/Rectangle 36.imageset/Rectangle 36@2x.png new file mode 100644 index 0000000..cafe86b Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Rectangle 36.imageset/Rectangle 36@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Rectangle 36.imageset/Rectangle 36@3x.png b/Fableon/Source/Assets.xcassets/image/Rectangle 36.imageset/Rectangle 36@3x.png new file mode 100644 index 0000000..54ebdca Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Rectangle 36.imageset/Rectangle 36@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Rectangle 6216.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/Rectangle 6216.imageset/Contents.json new file mode 100644 index 0000000..3e5b274 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/Rectangle 6216.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 6216@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Rectangle 6216@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/Rectangle 6216.imageset/Rectangle 6216@2x.png b/Fableon/Source/Assets.xcassets/image/Rectangle 6216.imageset/Rectangle 6216@2x.png new file mode 100644 index 0000000..5782c22 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Rectangle 6216.imageset/Rectangle 6216@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Rectangle 6216.imageset/Rectangle 6216@3x.png b/Fableon/Source/Assets.xcassets/image/Rectangle 6216.imageset/Rectangle 6216@3x.png new file mode 100644 index 0000000..0a4900c Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Rectangle 6216.imageset/Rectangle 6216@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Rectangle 6217.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/Rectangle 6217.imageset/Contents.json new file mode 100644 index 0000000..ac4c8f9 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/Rectangle 6217.imageset/Contents.json @@ -0,0 +1,44 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 6217@2x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 71, + "right" : 65 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "2x" + }, + { + "filename" : "Rectangle 6217@3x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 89, + "right" : 103 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/Rectangle 6217.imageset/Rectangle 6217@2x.png b/Fableon/Source/Assets.xcassets/image/Rectangle 6217.imageset/Rectangle 6217@2x.png new file mode 100644 index 0000000..00b8a35 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Rectangle 6217.imageset/Rectangle 6217@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/Rectangle 6217.imageset/Rectangle 6217@3x.png b/Fableon/Source/Assets.xcassets/image/Rectangle 6217.imageset/Rectangle 6217@3x.png new file mode 100644 index 0000000..89c9dec Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/Rectangle 6217.imageset/Rectangle 6217@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/close_icon_01.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/close_icon_01.imageset/Contents.json new file mode 100644 index 0000000..0b1b5d4 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/close_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group 958@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group 958@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/close_icon_01.imageset/Group 958@2x.png b/Fableon/Source/Assets.xcassets/image/close_icon_01.imageset/Group 958@2x.png new file mode 100644 index 0000000..abbaf6b Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/close_icon_01.imageset/Group 958@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/close_icon_01.imageset/Group 958@3x.png b/Fableon/Source/Assets.xcassets/image/close_icon_01.imageset/Group 958@3x.png new file mode 100644 index 0000000..d688f03 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/close_icon_01.imageset/Group 958@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/close_icon_02.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/close_icon_02.imageset/Contents.json new file mode 100644 index 0000000..975a697 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/close_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group 1891@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group 1891@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/close_icon_02.imageset/Group 1891@2x.png b/Fableon/Source/Assets.xcassets/image/close_icon_02.imageset/Group 1891@2x.png new file mode 100644 index 0000000..f3239a7 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/close_icon_02.imageset/Group 1891@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/close_icon_02.imageset/Group 1891@3x.png b/Fableon/Source/Assets.xcassets/image/close_icon_02.imageset/Group 1891@3x.png new file mode 100644 index 0000000..673acfb Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/close_icon_02.imageset/Group 1891@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/coin_attachment_01.imageset/90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 2@2x.png b/Fableon/Source/Assets.xcassets/image/coin_attachment_01.imageset/90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 2@2x.png new file mode 100644 index 0000000..366e1cf Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/coin_attachment_01.imageset/90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 2@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/coin_attachment_01.imageset/90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 2@3x.png b/Fableon/Source/Assets.xcassets/image/coin_attachment_01.imageset/90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 2@3x.png new file mode 100644 index 0000000..94e80d5 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/coin_attachment_01.imageset/90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 2@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/coin_attachment_01.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/coin_attachment_01.imageset/Contents.json new file mode 100644 index 0000000..b2118fb --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/coin_attachment_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 2@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 2@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/coin_attachment_02.imageset/90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 1@2x.png b/Fableon/Source/Assets.xcassets/image/coin_attachment_02.imageset/90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 1@2x.png new file mode 100644 index 0000000..b2fb57f Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/coin_attachment_02.imageset/90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 1@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/coin_attachment_02.imageset/90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 1@3x.png b/Fableon/Source/Assets.xcassets/image/coin_attachment_02.imageset/90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 1@3x.png new file mode 100644 index 0000000..c741702 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/coin_attachment_02.imageset/90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 1@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/coin_attachment_02.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/coin_attachment_02.imageset/Contents.json new file mode 100644 index 0000000..ab67421 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/coin_attachment_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "90ad09d41707261a78f7b0c4f06813390209096846c76-uqCzGu_fw240webp 1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/coins_icon_04.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/coins_icon_04.imageset/Contents.json new file mode 100644 index 0000000..6974814 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/coins_icon_04.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/Fableon/Source/Assets.xcassets/image/coins_icon_04.imageset/金币@2x.png b/Fableon/Source/Assets.xcassets/image/coins_icon_04.imageset/金币@2x.png new file mode 100644 index 0000000..1008e64 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/coins_icon_04.imageset/金币@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/coins_icon_04.imageset/金币@3x.png b/Fableon/Source/Assets.xcassets/image/coins_icon_04.imageset/金币@3x.png new file mode 100644 index 0000000..92844ee Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/coins_icon_04.imageset/金币@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/coins_icon_05.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/coins_icon_05.imageset/Contents.json new file mode 100644 index 0000000..24ecf7d --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/coins_icon_05.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "查看图片 1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "查看图片 1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/coins_icon_05.imageset/查看图片 1@2x.png b/Fableon/Source/Assets.xcassets/image/coins_icon_05.imageset/查看图片 1@2x.png new file mode 100644 index 0000000..64309b6 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/coins_icon_05.imageset/查看图片 1@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/coins_icon_05.imageset/查看图片 1@3x.png b/Fableon/Source/Assets.xcassets/image/coins_icon_05.imageset/查看图片 1@3x.png new file mode 100644 index 0000000..f9e9b5b Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/coins_icon_05.imageset/查看图片 1@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/coins_icon_06.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/coins_icon_06.imageset/Contents.json new file mode 100644 index 0000000..6974814 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/coins_icon_06.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/Fableon/Source/Assets.xcassets/image/coins_icon_06.imageset/金币@2x.png b/Fableon/Source/Assets.xcassets/image/coins_icon_06.imageset/金币@2x.png new file mode 100644 index 0000000..8aa57a8 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/coins_icon_06.imageset/金币@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/coins_icon_06.imageset/金币@3x.png b/Fableon/Source/Assets.xcassets/image/coins_icon_06.imageset/金币@3x.png new file mode 100644 index 0000000..515b3f5 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/coins_icon_06.imageset/金币@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/coins_icon_07.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/coins_icon_07.imageset/Contents.json new file mode 100644 index 0000000..6974814 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/coins_icon_07.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/Fableon/Source/Assets.xcassets/image/coins_icon_07.imageset/金币@2x.png b/Fableon/Source/Assets.xcassets/image/coins_icon_07.imageset/金币@2x.png new file mode 100644 index 0000000..1008e64 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/coins_icon_07.imageset/金币@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/coins_icon_07.imageset/金币@3x.png b/Fableon/Source/Assets.xcassets/image/coins_icon_07.imageset/金币@3x.png new file mode 100644 index 0000000..92844ee Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/coins_icon_07.imageset/金币@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/coins_icon_08.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/coins_icon_08.imageset/Contents.json new file mode 100644 index 0000000..6974814 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/coins_icon_08.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/Fableon/Source/Assets.xcassets/image/coins_icon_08.imageset/金币@2x.png b/Fableon/Source/Assets.xcassets/image/coins_icon_08.imageset/金币@2x.png new file mode 100644 index 0000000..f38f7ad Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/coins_icon_08.imageset/金币@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/coins_icon_08.imageset/金币@3x.png b/Fableon/Source/Assets.xcassets/image/coins_icon_08.imageset/金币@3x.png new file mode 100644 index 0000000..e97bc81 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/coins_icon_08.imageset/金币@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/hot_icon_01.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/hot_icon_01.imageset/Contents.json new file mode 100644 index 0000000..5472a49 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/hot_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "hot@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "hot@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/hot_icon_01.imageset/hot@2x.png b/Fableon/Source/Assets.xcassets/image/hot_icon_01.imageset/hot@2x.png new file mode 100644 index 0000000..4a8217d Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/hot_icon_01.imageset/hot@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/hot_icon_01.imageset/hot@3x.png b/Fableon/Source/Assets.xcassets/image/hot_icon_01.imageset/hot@3x.png new file mode 100644 index 0000000..7b87577 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/hot_icon_01.imageset/hot@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_icon_month.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_month.imageset/Contents.json new file mode 100644 index 0000000..561c851 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_month.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame 3023@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame 3023@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_icon_month.imageset/Frame 3023@2x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_month.imageset/Frame 3023@2x.png new file mode 100644 index 0000000..06d6393 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_month.imageset/Frame 3023@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_icon_month.imageset/Frame 3023@3x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_month.imageset/Frame 3023@3x.png new file mode 100644 index 0000000..fe9a1b0 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_month.imageset/Frame 3023@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_icon_quarter.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_quarter.imageset/Contents.json new file mode 100644 index 0000000..5cb2587 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_quarter.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame 3027@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame 3027@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_icon_quarter.imageset/Frame 3027@2x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_quarter.imageset/Frame 3027@2x.png new file mode 100644 index 0000000..8643560 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_quarter.imageset/Frame 3027@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_icon_quarter.imageset/Frame 3027@3x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_quarter.imageset/Frame 3027@3x.png new file mode 100644 index 0000000..6b4212d Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_quarter.imageset/Frame 3027@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_icon_week.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_week.imageset/Contents.json new file mode 100644 index 0000000..4267934 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_week.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame 3024@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame 3024@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_icon_week.imageset/Frame 3024@2x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_week.imageset/Frame 3024@2x.png new file mode 100644 index 0000000..b64c8df Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_week.imageset/Frame 3024@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_icon_week.imageset/Frame 3024@3x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_week.imageset/Frame 3024@3x.png new file mode 100644 index 0000000..6af7061 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_week.imageset/Frame 3024@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_icon_year.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_year.imageset/Contents.json new file mode 100644 index 0000000..93dd767 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_year.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame 3025@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame 3025@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_icon_year.imageset/Frame 3025@2x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_year.imageset/Frame 3025@2x.png new file mode 100644 index 0000000..7d37ead Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_year.imageset/Frame 3025@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_icon_year.imageset/Frame 3025@3x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_year.imageset/Frame 3025@3x.png new file mode 100644 index 0000000..76d099a Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_icon_year.imageset/Frame 3025@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_image_month.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/vip_bg_image_month.imageset/Contents.json new file mode 100644 index 0000000..65c72fa --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/vip_bg_image_month.imageset/Contents.json @@ -0,0 +1,44 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 6202@2x 1.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 44, + "right" : 44 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "2x" + }, + { + "filename" : "Rectangle 6202@3x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 59, + "right" : 57 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_image_month.imageset/Rectangle 6202@2x 1.png b/Fableon/Source/Assets.xcassets/image/vip_bg_image_month.imageset/Rectangle 6202@2x 1.png new file mode 100644 index 0000000..deca1dd Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_image_month.imageset/Rectangle 6202@2x 1.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_image_month.imageset/Rectangle 6202@3x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_image_month.imageset/Rectangle 6202@3x.png new file mode 100644 index 0000000..f2b0a38 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_image_month.imageset/Rectangle 6202@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_image_quarter.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/vip_bg_image_quarter.imageset/Contents.json new file mode 100644 index 0000000..86064d3 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/vip_bg_image_quarter.imageset/Contents.json @@ -0,0 +1,44 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 6205@2x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 50, + "right" : 39 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "2x" + }, + { + "filename" : "Rectangle 6205@3x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 58, + "right" : 52 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_image_quarter.imageset/Rectangle 6205@2x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_image_quarter.imageset/Rectangle 6205@2x.png new file mode 100644 index 0000000..1b007c4 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_image_quarter.imageset/Rectangle 6205@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_image_quarter.imageset/Rectangle 6205@3x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_image_quarter.imageset/Rectangle 6205@3x.png new file mode 100644 index 0000000..039f0ee Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_image_quarter.imageset/Rectangle 6205@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_image_week.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/vip_bg_image_week.imageset/Contents.json new file mode 100644 index 0000000..2576f9f --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/vip_bg_image_week.imageset/Contents.json @@ -0,0 +1,44 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 6203@2x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 56, + "right" : 73 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "2x" + }, + { + "filename" : "Rectangle 6203@3x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 110, + "right" : 80 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_image_week.imageset/Rectangle 6203@2x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_image_week.imageset/Rectangle 6203@2x.png new file mode 100644 index 0000000..91d308d Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_image_week.imageset/Rectangle 6203@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_image_week.imageset/Rectangle 6203@3x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_image_week.imageset/Rectangle 6203@3x.png new file mode 100644 index 0000000..0cd2ca0 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_image_week.imageset/Rectangle 6203@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_image_year.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/vip_bg_image_year.imageset/Contents.json new file mode 100644 index 0000000..0555305 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/vip_bg_image_year.imageset/Contents.json @@ -0,0 +1,44 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle 6204@2x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 37, + "right" : 38 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "2x" + }, + { + "filename" : "Rectangle 6204@3x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "left" : 61, + "right" : 54 + }, + "center" : { + "mode" : "tile", + "width" : 1 + }, + "mode" : "3-part-horizontal" + }, + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_image_year.imageset/Rectangle 6204@2x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_image_year.imageset/Rectangle 6204@2x.png new file mode 100644 index 0000000..c01dd21 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_image_year.imageset/Rectangle 6204@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_bg_image_year.imageset/Rectangle 6204@3x.png b/Fableon/Source/Assets.xcassets/image/vip_bg_image_year.imageset/Rectangle 6204@3x.png new file mode 100644 index 0000000..fe49c3e Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_bg_image_year.imageset/Rectangle 6204@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_image_month.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/vip_image_month.imageset/Contents.json new file mode 100644 index 0000000..6119043 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/vip_image_month.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle@2x 2.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Rectangle@3x 2.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/vip_image_month.imageset/Rectangle@2x 2.png b/Fableon/Source/Assets.xcassets/image/vip_image_month.imageset/Rectangle@2x 2.png new file mode 100644 index 0000000..02bc4d8 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_image_month.imageset/Rectangle@2x 2.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_image_month.imageset/Rectangle@3x 2.png b/Fableon/Source/Assets.xcassets/image/vip_image_month.imageset/Rectangle@3x 2.png new file mode 100644 index 0000000..eb1ff9d Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_image_month.imageset/Rectangle@3x 2.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_image_quarter.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/vip_image_quarter.imageset/Contents.json new file mode 100644 index 0000000..4bee434 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/vip_image_quarter.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle@2x 3.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Rectangle@3x 3.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/vip_image_quarter.imageset/Rectangle@2x 3.png b/Fableon/Source/Assets.xcassets/image/vip_image_quarter.imageset/Rectangle@2x 3.png new file mode 100644 index 0000000..e83ad4b Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_image_quarter.imageset/Rectangle@2x 3.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_image_quarter.imageset/Rectangle@3x 3.png b/Fableon/Source/Assets.xcassets/image/vip_image_quarter.imageset/Rectangle@3x 3.png new file mode 100644 index 0000000..bf05e60 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_image_quarter.imageset/Rectangle@3x 3.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_image_week.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/vip_image_week.imageset/Contents.json new file mode 100644 index 0000000..798426a --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/vip_image_week.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Rectangle@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/vip_image_week.imageset/Rectangle@2x.png b/Fableon/Source/Assets.xcassets/image/vip_image_week.imageset/Rectangle@2x.png new file mode 100644 index 0000000..37cdc94 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_image_week.imageset/Rectangle@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_image_week.imageset/Rectangle@3x.png b/Fableon/Source/Assets.xcassets/image/vip_image_week.imageset/Rectangle@3x.png new file mode 100644 index 0000000..f957fc1 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_image_week.imageset/Rectangle@3x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_image_year.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/vip_image_year.imageset/Contents.json new file mode 100644 index 0000000..ed91453 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/vip_image_year.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Rectangle@2x 4.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Rectangle@3x 4.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/vip_image_year.imageset/Rectangle@2x 4.png b/Fableon/Source/Assets.xcassets/image/vip_image_year.imageset/Rectangle@2x 4.png new file mode 100644 index 0000000..7c61a99 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_image_year.imageset/Rectangle@2x 4.png differ diff --git a/Fableon/Source/Assets.xcassets/image/vip_image_year.imageset/Rectangle@3x 4.png b/Fableon/Source/Assets.xcassets/image/vip_image_year.imageset/Rectangle@3x 4.png new file mode 100644 index 0000000..4b50af3 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/vip_image_year.imageset/Rectangle@3x 4.png differ diff --git a/Fableon/Source/Assets.xcassets/image/弹窗背景色.imageset/Contents.json b/Fableon/Source/Assets.xcassets/image/弹窗背景色.imageset/Contents.json new file mode 100644 index 0000000..746f0d7 --- /dev/null +++ b/Fableon/Source/Assets.xcassets/image/弹窗背景色.imageset/Contents.json @@ -0,0 +1,50 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "弹窗背景色@2x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "bottom" : 79, + "left" : 66, + "right" : 55, + "top" : 47 + }, + "center" : { + "height" : 1, + "mode" : "tile", + "width" : 1 + }, + "mode" : "9-part" + }, + "scale" : "2x" + }, + { + "filename" : "弹窗背景色@3x.png", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "bottom" : 103, + "left" : 43, + "right" : 46, + "top" : 96 + }, + "center" : { + "height" : 1, + "mode" : "tile", + "width" : 1 + }, + "mode" : "9-part" + }, + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Fableon/Source/Assets.xcassets/image/弹窗背景色.imageset/弹窗背景色@2x.png b/Fableon/Source/Assets.xcassets/image/弹窗背景色.imageset/弹窗背景色@2x.png new file mode 100644 index 0000000..bb3717f Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/弹窗背景色.imageset/弹窗背景色@2x.png differ diff --git a/Fableon/Source/Assets.xcassets/image/弹窗背景色.imageset/弹窗背景色@3x.png b/Fableon/Source/Assets.xcassets/image/弹窗背景色.imageset/弹窗背景色@3x.png new file mode 100644 index 0000000..e1a13a9 Binary files /dev/null and b/Fableon/Source/Assets.xcassets/image/弹窗背景色.imageset/弹窗背景色@3x.png differ diff --git a/Fableon/Source/Info.plist b/Fableon/Source/Info.plist index 0eb786d..3a365ad 100755 --- a/Fableon/Source/Info.plist +++ b/Fableon/Source/Info.plist @@ -2,6 +2,17 @@ + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + fableonapp + + + UIApplicationSceneManifest UIApplicationSupportsMultipleScenes diff --git a/Fableon/Source/en.lproj/Localizable.strings b/Fableon/Source/en.lproj/Localizable.strings index d43e924..8ceed3d 100644 --- a/Fableon/Source/en.lproj/Localizable.strings +++ b/Fableon/Source/en.lproj/Localizable.strings @@ -35,6 +35,25 @@ "Coin Record" = "Coin Record"; "VIP Record" = "VIP Record"; "Expired" = "Expired"; +"Weekly Refill" = "Weekly Refill"; +"week" = "week"; +"month" = "month"; +"quarter" = "quarter"; +"year" = "year"; +"extra" = "Extra"; +"success" = "Success"; +"Restore" = "Restore"; +"Bonus Coins" = "Bonus Coins"; +"Recharge Coins" = "Recharge Coins"; +"Expires in ## days" = "Expires in ## days"; +"What You Get" = "What You Get"; +"Bonus You Get" = "Bonus You Get"; +"Weekly Refill Package" = "Weekly Refill Package"; +"Dailu Bonuses" = "Dailu Bonuses"; +"Continue" = "Continue"; +"Purchase Single Episode" = "Purchase Single Episode"; +"Price" = "Price"; +"Balance" = "Balance"; "store_title_1" = "VIP Auto renew,cancel anytime"; @@ -48,3 +67,13 @@ "empty_title_01" = "No search results"; "empty_title_02" = "There is no data for the moment."; + +"pay_error_1" = "You are already a member!"; +"pay_error_2" = "Invalid in-app purchase"; +"pay_error_3" = "Payment has been cancelled"; +"pay_error_4" = "Purchase Failed"; +"pay_error_5" = "No in-app purchases can be restored"; +"pay_error_6" = "You have unfinished in-app purchases, please restore them first."; + +"store_tips_title" = "Tips"; +"store_tips" = "1. Coins are virtual items and cannot be refunded. Use it for this product.
2. Both the coins and the reward coins will never expire.
3. Coins will be used first when unlocking episodes. If the amount is insufficient, reward coins will automatically be used.
4. The purchase has not been credited, click torefresh.
5. For other questions, contact us via Profile>Help &feedback."; diff --git a/Podfile b/Podfile index d550278..06a7d0b 100755 --- a/Podfile +++ b/Podfile @@ -35,5 +35,6 @@ target 'Fableon' do pod 'ZLPhotoBrowser' pod 'JXPagingView/Paging' pod 'JXSegmentedView' + pod 'Adjust' end diff --git a/Podfile.lock b/Podfile.lock index e84196d..c7d7b9c 100755 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,4 +1,9 @@ PODS: + - Adjust (5.4.5): + - Adjust/Adjust (= 5.4.5) + - Adjust/Adjust (5.4.5): + - AdjustSignature (= 3.47.0) + - AdjustSignature (3.47.0) - Alamofire (5.10.2) - collection-view-layouts/Core (0.2.2) - collection-view-layouts/TagsLayout (0.2.2): @@ -135,6 +140,7 @@ PODS: - ZLPhotoBrowser/Core (4.7.3) DEPENDENCIES: + - Adjust - Alamofire - collection-view-layouts/TagsLayout - FDFullscreenPopGesture @@ -157,6 +163,8 @@ DEPENDENCIES: SPEC REPOS: https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git: + - Adjust + - AdjustSignature - Alamofire - collection-view-layouts - FDFullscreenPopGesture @@ -191,6 +199,8 @@ SPEC REPOS: - ZLPhotoBrowser SPEC CHECKSUMS: + Adjust: 010c8b2b582add6ba200469c82c4d8c9e5ddb198 + AdjustSignature: d634fc6b66295c38807f3b4e50978c1f72355950 Alamofire: 7193b3b92c74a07f85569e1a6c4f4237291e7496 collection-view-layouts: 474ec4cce601a26247737227a97fa6a4eb30213f FDFullscreenPopGesture: a8a620179e3d9c40e8e00256dcee1c1a27c6d0f0 @@ -224,6 +234,6 @@ SPEC CHECKSUMS: YYText: 5c461d709e24d55a182d1441c41dc639a18a4849 ZLPhotoBrowser: d5928f08485c90a0b349a3a1e804e82c83ccf193 -PODFILE CHECKSUM: c10e19ff4733165cdd0ed3fdb4286a0d13c54469 +PODFILE CHECKSUM: ee93369942c41a1babdca4fd27a29e21997fc357 COCOAPODS: 1.16.2