Compare commits

...

2 Commits

Author SHA1 Message Date
湖北秦九
54742c4a67 1.0.1 2025-10-29 10:26:37 +08:00
湖北秦九
c97bd5b30a 1.0.1发布 2025-10-29 10:26:27 +08:00
178 changed files with 5772 additions and 28 deletions

View File

@ -7,6 +7,37 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* 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 */; }; 03E239632EAA1945004A8CEC /* AppDelegate+FAConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E239612EAA1945004A8CEC /* AppDelegate+FAConfig.swift */; };
03E239642EAA1945004A8CEC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E239602EAA1945004A8CEC /* AppDelegate.swift */; }; 03E239642EAA1945004A8CEC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E239602EAA1945004A8CEC /* AppDelegate.swift */; };
03E239652EAA1945004A8CEC /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E239622EAA1945004A8CEC /* SceneDelegate.swift */; }; 03E239652EAA1945004A8CEC /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E239622EAA1945004A8CEC /* SceneDelegate.swift */; };
@ -265,6 +296,38 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
031FDEAB2EAF05FB00F4CAC7 /* FAStoreVipCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreVipCell.swift; sourceTree = "<group>"; };
031FDEAD2EB093B100F4CAC7 /* FABuyRecordsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FABuyRecordsModel.swift; sourceTree = "<group>"; };
031FDEAF2EB09AB300F4CAC7 /* FARechargeRecordModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FARechargeRecordModel.swift; sourceTree = "<group>"; };
031FDEB12EB0A5AF00F4CAC7 /* FASendCoinRecordModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FASendCoinRecordModel.swift; sourceTree = "<group>"; };
031FDEB32EB0AD7D00F4CAC7 /* FACoinPackConfirmView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinPackConfirmView.swift; sourceTree = "<group>"; };
031FDEB52EB0B77F00F4CAC7 /* FACoinPackConfirmItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinPackConfirmItemView.swift; sourceTree = "<group>"; };
031FDEB72EB0B80400F4CAC7 /* FACoinPackConfirmItem1View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinPackConfirmItem1View.swift; sourceTree = "<group>"; };
031FDEB92EB0B82600F4CAC7 /* FACoinPackConfirmItem2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinPackConfirmItem2View.swift; sourceTree = "<group>"; };
031FDEBB2EB0C97A00F4CAC7 /* FAOldVideoRechargeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAOldVideoRechargeView.swift; sourceTree = "<group>"; };
031FDEBD2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FANewVideoRechargeView.swift; sourceTree = "<group>"; };
039CE6032EAA2612007B5EED /* AppDelegate+FAAdjust.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+FAAdjust.swift"; sourceTree = "<group>"; };
039CE6082EAA2F62007B5EED /* FAAdjustStateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAAdjustStateManager.swift; sourceTree = "<group>"; };
039CE60A2EAA31CB007B5EED /* FAStatAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStatAPI.swift; sourceTree = "<group>"; };
039CE60D2EAA32A8007B5EED /* FAOpenAppModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAOpenAppModel.swift; sourceTree = "<group>"; };
039CE6102EAB0D47007B5EED /* JXIAPManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JXIAPManager.swift; sourceTree = "<group>"; };
039CE6132EAB0E0D007B5EED /* FAIapManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAIapManager.swift; sourceTree = "<group>"; };
039CE6152EAB0E30007B5EED /* FAWaitRestoreModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAWaitRestoreModel.swift; sourceTree = "<group>"; };
039CE6172EAB0E7E007B5EED /* FAStoreAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreAPI.swift; sourceTree = "<group>"; };
039CE6192EAB0EE0007B5EED /* FAIapVerifyModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAIapVerifyModel.swift; sourceTree = "<group>"; };
039CE61B2EAB0F29007B5EED /* FAIapOrderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAIapOrderModel.swift; sourceTree = "<group>"; };
039CE61D2EAB0FE4007B5EED /* Fableon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Fableon.entitlements; sourceTree = "<group>"; };
039CE61F2EAB114B007B5EED /* FAPayDateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAPayDateModel.swift; sourceTree = "<group>"; };
039CE6212EAB1335007B5EED /* FAUserDefaultsKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAUserDefaultsKey.swift; sourceTree = "<group>"; };
039CE6232EAB29D1007B5EED /* FAPayDataRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAPayDataRequest.swift; sourceTree = "<group>"; };
039CE6252EAB2A72007B5EED /* FAPayAlertModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAPayAlertModel.swift; sourceTree = "<group>"; };
039CE6272EAB50A9007B5EED /* FAStoreCoinsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreCoinsView.swift; sourceTree = "<group>"; };
039CE6292EAB5A8C007B5EED /* FAStoreCoinsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreCoinsCell.swift; sourceTree = "<group>"; };
039CE62B2EAB5B07007B5EED /* FAStoreCoinsBigCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreCoinsBigCell.swift; sourceTree = "<group>"; };
039CE62D2EAB5B18007B5EED /* FAStoreCoinsSmallCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreCoinsSmallCell.swift; sourceTree = "<group>"; };
039CE62F2EAB5B26007B5EED /* FAStoreCoinsPackCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreCoinsPackCell.swift; sourceTree = "<group>"; };
039CE6312EAB796F007B5EED /* FALabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FALabel.swift; sourceTree = "<group>"; };
03DBD8502EAEFF5F00DD04B8 /* FAStoreVipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStoreVipView.swift; sourceTree = "<group>"; };
03E239602EAA1945004A8CEC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 03E239602EAA1945004A8CEC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
03E239612EAA1945004A8CEC /* AppDelegate+FAConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+FAConfig.swift"; sourceTree = "<group>"; }; 03E239612EAA1945004A8CEC /* AppDelegate+FAConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+FAConfig.swift"; sourceTree = "<group>"; };
03E239622EAA1945004A8CEC /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; }; 03E239622EAA1945004A8CEC /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
@ -538,6 +601,54 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
039CE6072EAA2F37007B5EED /* AdjustStateManager */ = {
isa = PBXGroup;
children = (
039CE6082EAA2F62007B5EED /* FAAdjustStateManager.swift */,
);
path = AdjustStateManager;
sourceTree = "<group>";
};
039CE60C2EAA328A007B5EED /* Model */ = {
isa = PBXGroup;
children = (
039CE60D2EAA32A8007B5EED /* FAOpenAppModel.swift */,
);
path = Model;
sourceTree = "<group>";
};
039CE60F2EAB0D2D007B5EED /* JXIAPManager */ = {
isa = PBXGroup;
children = (
039CE6102EAB0D47007B5EED /* JXIAPManager.swift */,
);
path = JXIAPManager;
sourceTree = "<group>";
};
039CE6122EAB0DE1007B5EED /* FAIap */ = {
isa = PBXGroup;
children = (
039CE6132EAB0E0D007B5EED /* FAIapManager.swift */,
039CE6232EAB29D1007B5EED /* FAPayDataRequest.swift */,
039CE6192EAB0EE0007B5EED /* FAIapVerifyModel.swift */,
039CE61B2EAB0F29007B5EED /* FAIapOrderModel.swift */,
039CE6152EAB0E30007B5EED /* FAWaitRestoreModel.swift */,
);
path = FAIap;
sourceTree = "<group>";
};
039CE61E2EAB10EC007B5EED /* M */ = {
isa = PBXGroup;
children = (
039CE61F2EAB114B007B5EED /* FAPayDateModel.swift */,
039CE6252EAB2A72007B5EED /* FAPayAlertModel.swift */,
031FDEAD2EB093B100F4CAC7 /* FABuyRecordsModel.swift */,
031FDEAF2EB09AB300F4CAC7 /* FARechargeRecordModel.swift */,
031FDEB12EB0A5AF00F4CAC7 /* FASendCoinRecordModel.swift */,
);
path = M;
sourceTree = "<group>";
};
03E2395F2EAA1900004A8CEC /* Object */ = { 03E2395F2EAA1900004A8CEC /* Object */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -552,6 +663,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
03E2396A2EAA1A29004A8CEC /* Controller */, 03E2396A2EAA1A29004A8CEC /* Controller */,
039CE60C2EAA328A007B5EED /* Model */,
03E2396C2EAA1A29004A8CEC /* Define */, 03E2396C2EAA1A29004A8CEC /* Define */,
03E2397A2EAA1A29004A8CEC /* Extension */, 03E2397A2EAA1A29004A8CEC /* Extension */,
03E239812EAA1A29004A8CEC /* Request */, 03E239812EAA1A29004A8CEC /* Request */,
@ -575,6 +687,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
03E2396B2EAA1A29004A8CEC /* FADefine.swift */, 03E2396B2EAA1A29004A8CEC /* FADefine.swift */,
039CE6212EAB1335007B5EED /* FAUserDefaultsKey.swift */,
); );
path = Define; path = Define;
sourceTree = "<group>"; sourceTree = "<group>";
@ -610,6 +723,8 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
03E2397B2EAA1A29004A8CEC /* FAAPI.swift */, 03E2397B2EAA1A29004A8CEC /* FAAPI.swift */,
039CE60A2EAA31CB007B5EED /* FAStatAPI.swift */,
039CE6172EAB0E7E007B5EED /* FAStoreAPI.swift */,
); );
path = FAAPI; path = FAAPI;
sourceTree = "<group>"; sourceTree = "<group>";
@ -637,6 +752,7 @@
03E239872EAA1A29004A8CEC /* FAScrollView.swift */, 03E239872EAA1A29004A8CEC /* FAScrollView.swift */,
03E239882EAA1A29004A8CEC /* FATableView.swift */, 03E239882EAA1A29004A8CEC /* FATableView.swift */,
03E239892EAA1A29004A8CEC /* FATableViewCell.swift */, 03E239892EAA1A29004A8CEC /* FATableViewCell.swift */,
039CE6312EAB796F007B5EED /* FALabel.swift */,
); );
path = View; path = View;
sourceTree = "<group>"; sourceTree = "<group>";
@ -862,6 +978,8 @@
03E23A0E2EAA1A4E004A8CEC /* FAPlayerDetailControlView.swift */, 03E23A0E2EAA1A4E004A8CEC /* FAPlayerDetailControlView.swift */,
03E23A0F2EAA1A4E004A8CEC /* FAPlayerProgressView.swift */, 03E23A0F2EAA1A4E004A8CEC /* FAPlayerProgressView.swift */,
03E23A102EAA1A4E004A8CEC /* FAVideoLockView.swift */, 03E23A102EAA1A4E004A8CEC /* FAVideoLockView.swift */,
031FDEBB2EB0C97A00F4CAC7 /* FAOldVideoRechargeView.swift */,
031FDEBD2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift */,
); );
path = V; path = V;
sourceTree = "<group>"; sourceTree = "<group>";
@ -885,10 +1003,10 @@
03E23A162EAA1A4E004A8CEC /* Player */ = { 03E23A162EAA1A4E004A8CEC /* Player */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
03E23A062EAA1A4E004A8CEC /* M */,
03E23A082EAA1A4E004A8CEC /* UI */, 03E23A082EAA1A4E004A8CEC /* UI */,
03E23A112EAA1A4E004A8CEC /* V */,
03E23A132EAA1A4E004A8CEC /* VC */, 03E23A132EAA1A4E004A8CEC /* VC */,
03E23A062EAA1A4E004A8CEC /* M */,
03E23A112EAA1A4E004A8CEC /* V */,
03E23A152EAA1A4E004A8CEC /* VM */, 03E23A152EAA1A4E004A8CEC /* VM */,
); );
path = Player; path = Player;
@ -932,13 +1050,13 @@
03E23A262EAA1A4E004A8CEC /* C */ = { 03E23A262EAA1A4E004A8CEC /* C */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
03E23A232EAA1A4E004A8CEC /* FAStoreViewController.swift */,
03E23A252EAA1A4E004A8CEC /* FAWalletViewController.swift */,
03E23A1F2EAA1A4E004A8CEC /* FACoinRecordViewController.swift */, 03E23A1F2EAA1A4E004A8CEC /* FACoinRecordViewController.swift */,
03E23A202EAA1A4E004A8CEC /* FAConsumptionRecordsViewController.swift */, 03E23A202EAA1A4E004A8CEC /* FAConsumptionRecordsViewController.swift */,
03E23A212EAA1A4E004A8CEC /* FAOrderRecordsViewController.swift */, 03E23A212EAA1A4E004A8CEC /* FAOrderRecordsViewController.swift */,
03E23A222EAA1A4E004A8CEC /* FARewardCoinsViewController.swift */, 03E23A222EAA1A4E004A8CEC /* FARewardCoinsViewController.swift */,
03E23A232EAA1A4E004A8CEC /* FAStoreViewController.swift */,
03E23A242EAA1A4E004A8CEC /* FAVipRecordViewController.swift */, 03E23A242EAA1A4E004A8CEC /* FAVipRecordViewController.swift */,
03E23A252EAA1A4E004A8CEC /* FAWalletViewController.swift */,
); );
path = C; path = C;
sourceTree = "<group>"; sourceTree = "<group>";
@ -955,6 +1073,17 @@
03E23A2D2EAA1A4E004A8CEC /* FAWalletCell.swift */, 03E23A2D2EAA1A4E004A8CEC /* FAWalletCell.swift */,
03E23A2E2EAA1A4E004A8CEC /* FAWalletCell.xib */, 03E23A2E2EAA1A4E004A8CEC /* FAWalletCell.xib */,
03E23A2F2EAA1A4E004A8CEC /* FAWalletHeaderView.swift */, 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; path = V;
sourceTree = "<group>"; sourceTree = "<group>";
@ -964,6 +1093,7 @@
children = ( children = (
03E23A262EAA1A4E004A8CEC /* C */, 03E23A262EAA1A4E004A8CEC /* C */,
03E23A302EAA1A4E004A8CEC /* V */, 03E23A302EAA1A4E004A8CEC /* V */,
039CE61E2EAB10EC007B5EED /* M */,
); );
path = Store; path = Store;
sourceTree = "<group>"; sourceTree = "<group>";
@ -971,6 +1101,9 @@
03E23A962EAA1A65004A8CEC /* Libs */ = { 03E23A962EAA1A65004A8CEC /* Libs */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
039CE6122EAB0DE1007B5EED /* FAIap */,
039CE60F2EAB0D2D007B5EED /* JXIAPManager */,
039CE6072EAA2F37007B5EED /* AdjustStateManager */,
03E23A982EAA1A7F004A8CEC /* Empty */, 03E23A982EAA1A7F004A8CEC /* Empty */,
03E23A9B2EAA1A7F004A8CEC /* FADeviceId */, 03E23A9B2EAA1A7F004A8CEC /* FADeviceId */,
03E23A9D2EAA1A7F004A8CEC /* FALocalized */, 03E23A9D2EAA1A7F004A8CEC /* FALocalized */,
@ -1468,6 +1601,7 @@
children = ( children = (
03E239602EAA1945004A8CEC /* AppDelegate.swift */, 03E239602EAA1945004A8CEC /* AppDelegate.swift */,
03E239612EAA1945004A8CEC /* AppDelegate+FAConfig.swift */, 03E239612EAA1945004A8CEC /* AppDelegate+FAConfig.swift */,
039CE6032EAA2612007B5EED /* AppDelegate+FAAdjust.swift */,
03E239622EAA1945004A8CEC /* SceneDelegate.swift */, 03E239622EAA1945004A8CEC /* SceneDelegate.swift */,
); );
path = App; path = App;
@ -1505,6 +1639,7 @@
F3YR32SS1P8731297KD665X5 /* Fableon */ = { F3YR32SS1P8731297KD665X5 /* Fableon */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
039CE61D2EAB0FE4007B5EED /* Fableon.entitlements */,
F3U31ND27BB4R9G072AN9BM3 /* App */, F3U31ND27BB4R9G072AN9BM3 /* App */,
03E2395F2EAA1900004A8CEC /* Object */, 03E2395F2EAA1900004A8CEC /* Object */,
F3IF9S30I6U4301G5521663T /* Source */, F3IF9S30I6U4301G5521663T /* Source */,
@ -1687,11 +1822,16 @@
F3Q1B8O8164TYQ9724T34V29 /* OACAbleView.swift in Sources */, F3Q1B8O8164TYQ9724T34V29 /* OACAbleView.swift in Sources */,
F36MZ39RQ295569BV1915908 /* FOMptyView.swift in Sources */, F36MZ39RQ295569BV1915908 /* FOMptyView.swift in Sources */,
F323CZU4466303487TY7K12U /* YHRegister.swift in Sources */, F323CZU4466303487TY7K12U /* YHRegister.swift in Sources */,
031FDEB02EB09AB300F4CAC7 /* FARechargeRecordModel.swift in Sources */,
F35T0LSS4O974ZP1IJ27TB30 /* BEedbackCenterController.swift in Sources */, F35T0LSS4O974ZP1IJ27TB30 /* BEedbackCenterController.swift in Sources */,
F39356S815VILHH18O4HK577 /* BHSSwiftRefresh.swift in Sources */, F39356S815VILHH18O4HK577 /* BHSSwiftRefresh.swift in Sources */,
F33O83184592105998E35JY1 /* VRServiceCenterItem.swift in Sources */, F33O83184592105998E35JY1 /* VRServiceCenterItem.swift in Sources */,
F347N086226Y53261B0285I3 /* QLoginRoundedView.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 */, F35S8138Z362EG3435ME1455 /* CSGleeView.swift in Sources */,
031FDEB42EB0AD7D00F4CAC7 /* FACoinPackConfirmView.swift in Sources */,
F31451S3X15B941342J922Q3 /* TConfigCell.swift in Sources */, F31451S3X15B941342J922Q3 /* TConfigCell.swift in Sources */,
F3670DZ2KI89TBO3L9L36P57 /* XLoginView.swift in Sources */, F3670DZ2KI89TBO3L9L36P57 /* XLoginView.swift in Sources */,
F3V24670CK2EGR4595CDQ261 /* AWUModalMageCell.swift in Sources */, F3V24670CK2EGR4595CDQ261 /* AWUModalMageCell.swift in Sources */,
@ -1702,6 +1842,7 @@
F381909BQ5JWXYI119A49469 /* DREychainCell.swift in Sources */, F381909BQ5JWXYI119A49469 /* DREychainCell.swift in Sources */,
F39R66F983517825QO331145 /* QCenterEfineView.swift in Sources */, F39R66F983517825QO331145 /* QCenterEfineView.swift in Sources */,
F3II3AF834346F516W51V693 /* GAMainRecommendedView.swift in Sources */, F3II3AF834346F516W51V693 /* GAMainRecommendedView.swift in Sources */,
031FDEAE2EB093B100F4CAC7 /* FABuyRecordsModel.swift in Sources */,
F377Y22Z3J44H22J23963544 /* CDNewsController.swift in Sources */, F377Y22Z3J44H22J23963544 /* CDNewsController.swift in Sources */,
F358686093028548K00WQIO1 /* NGleeVionView.swift in Sources */, F358686093028548K00WQIO1 /* NGleeVionView.swift in Sources */,
F3019606DA7P36H41G408X13 /* ZStreamCell.swift in Sources */, F3019606DA7P36H41G408X13 /* ZStreamCell.swift in Sources */,
@ -1716,11 +1857,13 @@
03E239632EAA1945004A8CEC /* AppDelegate+FAConfig.swift in Sources */, 03E239632EAA1945004A8CEC /* AppDelegate+FAConfig.swift in Sources */,
03E239642EAA1945004A8CEC /* AppDelegate.swift in Sources */, 03E239642EAA1945004A8CEC /* AppDelegate.swift in Sources */,
03E239652EAA1945004A8CEC /* SceneDelegate.swift in Sources */, 03E239652EAA1945004A8CEC /* SceneDelegate.swift in Sources */,
039CE6092EAA2F71007B5EED /* FAAdjustStateManager.swift in Sources */,
F333U95746V7VK13QI9275B3 /* UMenuTransformerCell.swift in Sources */, F333U95746V7VK13QI9275B3 /* UMenuTransformerCell.swift in Sources */,
F3Q234J5M18F1Q5G2948P056 /* NPVBoutModalController.swift in Sources */, F3Q234J5M18F1Q5G2948P056 /* NPVBoutModalController.swift in Sources */,
F38Y7NBX3M0RE4O142264411 /* EZEFlow.swift in Sources */, F38Y7NBX3M0RE4O142264411 /* EZEFlow.swift in Sources */,
F363P024H4W1T8LN2546W883 /* YBanner.swift in Sources */, F363P024H4W1T8LN2546W883 /* YBanner.swift in Sources */,
F3P4D170962A2JAF14W0520R /* ADCheckMageView.swift in Sources */, F3P4D170962A2JAF14W0520R /* ADCheckMageView.swift in Sources */,
031FDEBA2EB0B82600F4CAC7 /* FACoinPackConfirmItem2View.swift in Sources */,
F3206101P0DVK224N85S1W62 /* XIPathEviceController.swift in Sources */, F3206101P0DVK224N85S1W62 /* XIPathEviceController.swift in Sources */,
F31N0S2575E3YC5AOG80WE90 /* TYElyon.swift in Sources */, F31N0S2575E3YC5AOG80WE90 /* TYElyon.swift in Sources */,
F3JX29E3JQ40V23245TS8Z36 /* YRegisterController.swift in Sources */, F3JX29E3JQ40V23245TS8Z36 /* YRegisterController.swift in Sources */,
@ -1729,6 +1872,7 @@
F39EH05VP4YX3F00P5162031 /* YFddebcdbeeffcebdfCenterView.swift in Sources */, F39EH05VP4YX3F00P5162031 /* YFddebcdbeeffcebdfCenterView.swift in Sources */,
F32T95836171611R26I33D4D /* AJMEychainView.swift in Sources */, F32T95836171611R26I33D4D /* AJMEychainView.swift in Sources */,
F3242J387RV5714007E92OH3 /* BEOConfigController.swift in Sources */, F3242J387RV5714007E92OH3 /* BEOConfigController.swift in Sources */,
031FDEAC2EAF05FB00F4CAC7 /* FAStoreVipCell.swift in Sources */,
F3N346893L1U6DP8228XK227 /* UOLaunch.swift in Sources */, F3N346893L1U6DP8228XK227 /* UOLaunch.swift in Sources */,
F39S0H806843N21RM0O95488 /* VARResultController.swift in Sources */, F39S0H806843N21RM0O95488 /* VARResultController.swift in Sources */,
F3D03219Y8NV50N2S66014LX /* GPOOllectionFableon.swift in Sources */, F3D03219Y8NV50N2S66014LX /* GPOOllectionFableon.swift in Sources */,
@ -1738,20 +1882,25 @@
03E23AAC2EAA1A7F004A8CEC /* FALogin.swift in Sources */, 03E23AAC2EAA1A7F004A8CEC /* FALogin.swift in Sources */,
03E23AAD2EAA1A7F004A8CEC /* FAKeychainHelper.swift in Sources */, 03E23AAD2EAA1A7F004A8CEC /* FAKeychainHelper.swift in Sources */,
03E23AAE2EAA1A7F004A8CEC /* FATool.swift in Sources */, 03E23AAE2EAA1A7F004A8CEC /* FATool.swift in Sources */,
039CE6262EAB2A72007B5EED /* FAPayAlertModel.swift in Sources */,
03E23AAF2EAA1A7F004A8CEC /* FSPagerSwiftUIView.swift in Sources */, 03E23AAF2EAA1A7F004A8CEC /* FSPagerSwiftUIView.swift in Sources */,
03E23AB02EAA1A7F004A8CEC /* FADeviceIDManager.swift in Sources */, 03E23AB02EAA1A7F004A8CEC /* FADeviceIDManager.swift in Sources */,
03E23AB12EAA1A7F004A8CEC /* FATokenModel.swift in Sources */, 03E23AB12EAA1A7F004A8CEC /* FATokenModel.swift in Sources */,
03E23AB22EAA1A7F004A8CEC /* FAEmpty.swift in Sources */, 03E23AB22EAA1A7F004A8CEC /* FAEmpty.swift in Sources */,
03E23AB32EAA1A7F004A8CEC /* FAHUD.swift in Sources */, 03E23AB32EAA1A7F004A8CEC /* FAHUD.swift in Sources */,
03E23AB42EAA1A7F004A8CEC /* FAPagerViewTransformer.swift in Sources */, 03E23AB42EAA1A7F004A8CEC /* FAPagerViewTransformer.swift in Sources */,
039CE62A2EAB5A8C007B5EED /* FAStoreCoinsCell.swift in Sources */,
039CE6242EAB29D1007B5EED /* FAPayDataRequest.swift in Sources */,
03E23AB52EAA1A7F004A8CEC /* FAToast.swift in Sources */, 03E23AB52EAA1A7F004A8CEC /* FAToast.swift in Sources */,
03E23AB62EAA1A7F004A8CEC /* FAUserInfo.swift in Sources */, 03E23AB62EAA1A7F004A8CEC /* FAUserInfo.swift in Sources */,
03E23AB72EAA1A7F004A8CEC /* FALocalized.swift in Sources */, 03E23AB72EAA1A7F004A8CEC /* FALocalized.swift in Sources */,
039CE6222EAB1340007B5EED /* FAUserDefaultsKey.swift in Sources */,
03E23AB82EAA1A7F004A8CEC /* FAWaterfallFlowLayout.swift in Sources */, 03E23AB82EAA1A7F004A8CEC /* FAWaterfallFlowLayout.swift in Sources */,
F3K238643L913I6RK4G7S006 /* CSceneRegister.swift in Sources */, F3K238643L913I6RK4G7S006 /* CSceneRegister.swift in Sources */,
F3ZT3I4VAGB5405FWL36UW12 /* ZFGEtworkCell.swift in Sources */, F3ZT3I4VAGB5405FWL36UW12 /* ZFGEtworkCell.swift in Sources */,
F35O71332554S53191121042 /* UOModalCell.swift in Sources */, F35O71332554S53191121042 /* UOModalCell.swift in Sources */,
F3L30772U87770116598U282 /* NLaunchDelegate.swift in Sources */, F3L30772U87770116598U282 /* NLaunchDelegate.swift in Sources */,
039CE62C2EAB5B07007B5EED /* FAStoreCoinsBigCell.swift in Sources */,
F38L64O0092TC8W47P271967 /* COcalizedController.swift in Sources */, F38L64O0092TC8W47P271967 /* COcalizedController.swift in Sources */,
03E23A322EAA1A4E004A8CEC /* FAFeedbackViewController.swift in Sources */, 03E23A322EAA1A4E004A8CEC /* FAFeedbackViewController.swift in Sources */,
03E23A332EAA1A4E004A8CEC /* FAHomeRecommendedItemView.swift in Sources */, 03E23A332EAA1A4E004A8CEC /* FAHomeRecommendedItemView.swift in Sources */,
@ -1777,6 +1926,7 @@
03E23A472EAA1A4E004A8CEC /* FAPlayerDetailControlView.swift in Sources */, 03E23A472EAA1A4E004A8CEC /* FAPlayerDetailControlView.swift in Sources */,
03E23A482EAA1A4E004A8CEC /* FARewardCoinsCell.swift in Sources */, 03E23A482EAA1A4E004A8CEC /* FARewardCoinsCell.swift in Sources */,
03E23A492EAA1A4E004A8CEC /* FAVideoInfoModel.swift in Sources */, 03E23A492EAA1A4E004A8CEC /* FAVideoInfoModel.swift in Sources */,
039CE6202EAB114B007B5EED /* FAPayDateModel.swift in Sources */,
03E23A4A2EAA1A4E004A8CEC /* FAConsumptionRecordsViewController.swift in Sources */, 03E23A4A2EAA1A4E004A8CEC /* FAConsumptionRecordsViewController.swift in Sources */,
03E23A4B2EAA1A4E004A8CEC /* FAMeCoinsView.swift in Sources */, 03E23A4B2EAA1A4E004A8CEC /* FAMeCoinsView.swift in Sources */,
03E23A4C2EAA1A4E004A8CEC /* FAHomeViewModel.swift in Sources */, 03E23A4C2EAA1A4E004A8CEC /* FAHomeViewModel.swift in Sources */,
@ -1789,6 +1939,7 @@
03E23A532EAA1A4E004A8CEC /* FAVideoLockView.swift in Sources */, 03E23A532EAA1A4E004A8CEC /* FAVideoLockView.swift in Sources */,
03E23A542EAA1A4E004A8CEC /* FAHomeViewController.swift in Sources */, 03E23A542EAA1A4E004A8CEC /* FAHomeViewController.swift in Sources */,
03E23A552EAA1A4E004A8CEC /* FAPopularListViewController.swift in Sources */, 03E23A552EAA1A4E004A8CEC /* FAPopularListViewController.swift in Sources */,
039CE6042EAA2621007B5EED /* AppDelegate+FAAdjust.swift in Sources */,
03E23A562EAA1A4E004A8CEC /* FAPlayerEpUIButton.swift in Sources */, 03E23A562EAA1A4E004A8CEC /* FAPlayerEpUIButton.swift in Sources */,
03E23A572EAA1A4E004A8CEC /* FAHistoryCell.swift in Sources */, 03E23A572EAA1A4E004A8CEC /* FAHistoryCell.swift in Sources */,
03E23A582EAA1A4E004A8CEC /* FASearchViewController.swift in Sources */, 03E23A582EAA1A4E004A8CEC /* FASearchViewController.swift in Sources */,
@ -1821,6 +1972,8 @@
03E23A732EAA1A4E004A8CEC /* FASearchRecordCell.swift in Sources */, 03E23A732EAA1A4E004A8CEC /* FASearchRecordCell.swift in Sources */,
03E23A742EAA1A4E004A8CEC /* FAShortPlayModel.swift in Sources */, 03E23A742EAA1A4E004A8CEC /* FAShortPlayModel.swift in Sources */,
03E23A752EAA1A4E004A8CEC /* FASearchRecommendCell.swift in Sources */, 03E23A752EAA1A4E004A8CEC /* FASearchRecommendCell.swift in Sources */,
039CE6162EAB0E30007B5EED /* FAWaitRestoreModel.swift in Sources */,
039CE6302EAB5B26007B5EED /* FAStoreCoinsPackCell.swift in Sources */,
03E23A762EAA1A4E004A8CEC /* FASearchHomeView.swift in Sources */, 03E23A762EAA1A4E004A8CEC /* FASearchHomeView.swift in Sources */,
03E23A772EAA1A4E004A8CEC /* FAHomeNewContentCell.swift in Sources */, 03E23A772EAA1A4E004A8CEC /* FAHomeNewContentCell.swift in Sources */,
03E23A782EAA1A4E004A8CEC /* FAOrderRecordsViewController.swift in Sources */, 03E23A782EAA1A4E004A8CEC /* FAOrderRecordsViewController.swift in Sources */,
@ -1830,11 +1983,13 @@
03E23A7C2EAA1A4E004A8CEC /* FAMeCell.swift in Sources */, 03E23A7C2EAA1A4E004A8CEC /* FAMeCell.swift in Sources */,
03E23A7D2EAA1A4E004A8CEC /* FAWalletHeaderView.swift in Sources */, 03E23A7D2EAA1A4E004A8CEC /* FAWalletHeaderView.swift in Sources */,
03E23A7E2EAA1A4E004A8CEC /* FAPlayerProgressView.swift in Sources */, 03E23A7E2EAA1A4E004A8CEC /* FAPlayerProgressView.swift in Sources */,
031FDEB82EB0B80400F4CAC7 /* FACoinPackConfirmItem1View.swift in Sources */,
03E23A7F2EAA1A4E004A8CEC /* FAMeItemModel.swift in Sources */, 03E23A7F2EAA1A4E004A8CEC /* FAMeItemModel.swift in Sources */,
03E23A802EAA1A4E004A8CEC /* FAGenresListCell.swift in Sources */, 03E23A802EAA1A4E004A8CEC /* FAGenresListCell.swift in Sources */,
03E23A812EAA1A4E004A8CEC /* FACollectCell.swift in Sources */, 03E23A812EAA1A4E004A8CEC /* FACollectCell.swift in Sources */,
03E23A822EAA1A4E004A8CEC /* FASearchRecommendView.swift in Sources */, 03E23A822EAA1A4E004A8CEC /* FASearchRecommendView.swift in Sources */,
03E23A832EAA1A4E004A8CEC /* FAHomeMustSeeShortView.swift in Sources */, 03E23A832EAA1A4E004A8CEC /* FAHomeMustSeeShortView.swift in Sources */,
039CE60B2EAA31CB007B5EED /* FAStatAPI.swift in Sources */,
03E23A842EAA1A4E004A8CEC /* FACoinRecordViewController.swift in Sources */, 03E23A842EAA1A4E004A8CEC /* FACoinRecordViewController.swift in Sources */,
F3511I08934IV2C80B9Z6X2T /* QDVBoutLayout.swift in Sources */, F3511I08934IV2C80B9Z6X2T /* QDVBoutLayout.swift in Sources */,
F311250SJ7I3DZ74X009990N /* XJSelectorCell.swift in Sources */, F311250SJ7I3DZ74X009990N /* XJSelectorCell.swift in Sources */,
@ -1842,22 +1997,27 @@
F3O215L13X5155316V412904 /* AOkenView.swift in Sources */, F3O215L13X5155316V412904 /* AOkenView.swift in Sources */,
F3G0PR235Z2X602JE530X692 /* EQThimraPathController.swift in Sources */, F3G0PR235Z2X602JE530X692 /* EQThimraPathController.swift in Sources */,
F3563UZ4T03I36J2R93Y31N6 /* IMRefreshManagerController.swift in Sources */, F3563UZ4T03I36J2R93Y31N6 /* IMRefreshManagerController.swift in Sources */,
039CE6112EAB0D49007B5EED /* JXIAPManager.swift in Sources */,
F3981047B9214665100286YG /* BZOEarchView.swift in Sources */, F3981047B9214665100286YG /* BZOEarchView.swift in Sources */,
03E23ABA2EAA1D85004A8CEC /* MNNItemController.swift in Sources */, 03E23ABA2EAA1D85004A8CEC /* MNNItemController.swift in Sources */,
F3817W6E02182D9A62U02849 /* WHAOastMainCell.swift in Sources */, F3817W6E02182D9A62U02849 /* WHAOastMainCell.swift in Sources */,
F32C0941C670UO0IU4114086 /* XHDelegate.swift in Sources */, F32C0941C670UO0IU4114086 /* XHDelegate.swift in Sources */,
039CE61A2EAB0EE0007B5EED /* FAIapVerifyModel.swift in Sources */,
F3XWY0D08020883935X46FG7 /* FHeaderController.swift in Sources */, F3XWY0D08020883935X46FG7 /* FHeaderController.swift in Sources */,
F3223B1O80A345NB22134220 /* LAgerShortController.swift in Sources */, F3223B1O80A345NB22134220 /* LAgerShortController.swift in Sources */,
F3Q51496018LRJ1691985D08 /* PYMOastCell.swift in Sources */, F3Q51496018LRJ1691985D08 /* PYMOastCell.swift in Sources */,
F33Q5651983154V88746Y001 /* ODPlayerCell.swift in Sources */, F33Q5651983154V88746Y001 /* ODPlayerCell.swift in Sources */,
F3774560U03XA8151BTQ2Z61 /* HYLLayoutView.swift in Sources */, F3774560U03XA8151BTQ2Z61 /* HYLLayoutView.swift in Sources */,
F3N85463XSRG9VFK665G9028 /* XKRefreshCell.swift in Sources */, F3N85463XSRG9VFK665G9028 /* XKRefreshCell.swift in Sources */,
039CE6282EAB50A9007B5EED /* FAStoreCoinsView.swift in Sources */,
F35K41930D96476I83FHZ0D9 /* YLZFableonAlignmentCell.swift in Sources */, F35K41930D96476I83FHZ0D9 /* YLZFableonAlignmentCell.swift in Sources */,
F3M8439X72Q55JY9G8U502D9 /* DXYFire.swift in Sources */, F3M8439X72Q55JY9G8U502D9 /* DXYFire.swift in Sources */,
03E239912EAA1A29004A8CEC /* Dictionary+FAAdd.swift in Sources */, 03E239912EAA1A29004A8CEC /* Dictionary+FAAdd.swift in Sources */,
03E239922EAA1A29004A8CEC /* FAImageView.swift in Sources */, 03E239922EAA1A29004A8CEC /* FAImageView.swift in Sources */,
031FDEBE2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift in Sources */,
03E239932EAA1A29004A8CEC /* Date+FAAdd.swift in Sources */, 03E239932EAA1A29004A8CEC /* Date+FAAdd.swift in Sources */,
03E239942EAA1A29004A8CEC /* UserDefaults+FAAdd.swift in Sources */, 03E239942EAA1A29004A8CEC /* UserDefaults+FAAdd.swift in Sources */,
039CE6322EAB796F007B5EED /* FALabel.swift in Sources */,
03E239952EAA1A29004A8CEC /* Font+FAAdd.swift in Sources */, 03E239952EAA1A29004A8CEC /* Font+FAAdd.swift in Sources */,
03E239962EAA1A29004A8CEC /* FACryptorService.swift in Sources */, 03E239962EAA1A29004A8CEC /* FACryptorService.swift in Sources */,
03E239972EAA1A29004A8CEC /* FANetworkManager.swift in Sources */, 03E239972EAA1A29004A8CEC /* FANetworkManager.swift in Sources */,
@ -1866,6 +2026,7 @@
03E2399A2EAA1A29004A8CEC /* FAViewController.swift in Sources */, 03E2399A2EAA1A29004A8CEC /* FAViewController.swift in Sources */,
03E2399B2EAA1A29004A8CEC /* FAWebMessageModel.swift in Sources */, 03E2399B2EAA1A29004A8CEC /* FAWebMessageModel.swift in Sources */,
03E2399C2EAA1A29004A8CEC /* FAGradientButton.swift in Sources */, 03E2399C2EAA1A29004A8CEC /* FAGradientButton.swift in Sources */,
039CE6142EAB0E0D007B5EED /* FAIapManager.swift in Sources */,
03E2399D2EAA1A29004A8CEC /* FAGradientView.swift in Sources */, 03E2399D2EAA1A29004A8CEC /* FAGradientView.swift in Sources */,
03E2399E2EAA1A29004A8CEC /* FADefine.swift in Sources */, 03E2399E2EAA1A29004A8CEC /* FADefine.swift in Sources */,
03E2399F2EAA1A29004A8CEC /* FABaseWebViewController.swift in Sources */, 03E2399F2EAA1A29004A8CEC /* FABaseWebViewController.swift in Sources */,
@ -1874,18 +2035,22 @@
03E239A22EAA1A29004A8CEC /* FATabBarController.swift in Sources */, 03E239A22EAA1A29004A8CEC /* FATabBarController.swift in Sources */,
03E239A32EAA1A29004A8CEC /* UIStackView+FAAdd.swift in Sources */, 03E239A32EAA1A29004A8CEC /* UIStackView+FAAdd.swift in Sources */,
03E239A42EAA1A29004A8CEC /* UIScreen+FAAdd.swift in Sources */, 03E239A42EAA1A29004A8CEC /* UIScreen+FAAdd.swift in Sources */,
039CE6182EAB0E7E007B5EED /* FAStoreAPI.swift in Sources */,
03E239A52EAA1A29004A8CEC /* FACollectionView.swift in Sources */, 03E239A52EAA1A29004A8CEC /* FACollectionView.swift in Sources */,
03E239A62EAA1A29004A8CEC /* UIView+FAAdd.swift in Sources */, 03E239A62EAA1A29004A8CEC /* UIView+FAAdd.swift in Sources */,
03E239A72EAA1A29004A8CEC /* FAScrollView.swift in Sources */, 03E239A72EAA1A29004A8CEC /* FAScrollView.swift in Sources */,
03E239A82EAA1A29004A8CEC /* FATableView.swift in Sources */, 03E239A82EAA1A29004A8CEC /* FATableView.swift in Sources */,
03E239A92EAA1A29004A8CEC /* CGMutablePath+FARoundedCorner.swift in Sources */, 03E239A92EAA1A29004A8CEC /* CGMutablePath+FARoundedCorner.swift in Sources */,
031FDEBC2EB0C97A00F4CAC7 /* FAOldVideoRechargeView.swift in Sources */,
03E239AA2EAA1A29004A8CEC /* FAPanModalContentView.swift in Sources */, 03E239AA2EAA1A29004A8CEC /* FAPanModalContentView.swift in Sources */,
03E239AB2EAA1A29004A8CEC /* FATableViewCell.swift in Sources */, 03E239AB2EAA1A29004A8CEC /* FATableViewCell.swift in Sources */,
03E239AC2EAA1A29004A8CEC /* FABaseWebViewController+Script.swift in Sources */, 03E239AC2EAA1A29004A8CEC /* FABaseWebViewController+Script.swift in Sources */,
03E239AD2EAA1A29004A8CEC /* FAAppWebViewController.swift in Sources */, 03E239AD2EAA1A29004A8CEC /* FAAppWebViewController.swift in Sources */,
03E239AE2EAA1A29004A8CEC /* SwiftUIExtension.swift in Sources */, 03E239AE2EAA1A29004A8CEC /* SwiftUIExtension.swift in Sources */,
039CE60E2EAA32A8007B5EED /* FAOpenAppModel.swift in Sources */,
03E239AF2EAA1A29004A8CEC /* FAAPI.swift in Sources */, 03E239AF2EAA1A29004A8CEC /* FAAPI.swift in Sources */,
03E239B02EAA1A29004A8CEC /* FAWebView.swift in Sources */, 03E239B02EAA1A29004A8CEC /* FAWebView.swift in Sources */,
039CE61C2EAB0F29007B5EED /* FAIapOrderModel.swift in Sources */,
03E239B12EAA1A29004A8CEC /* FANetworkMonitor.swift in Sources */, 03E239B12EAA1A29004A8CEC /* FANetworkMonitor.swift in Sources */,
03E239B22EAA1A29004A8CEC /* UINavigationBar+FAAdd.swift in Sources */, 03E239B22EAA1A29004A8CEC /* UINavigationBar+FAAdd.swift in Sources */,
F3EKB04Y9O2888900KQEO504 /* NHCornerController.swift in Sources */, F3EKB04Y9O2888900KQEO504 /* NHCornerController.swift in Sources */,
@ -1903,6 +2068,7 @@
F32G5ID338260A2W78SS4618 /* TCFableonCell.swift in Sources */, F32G5ID338260A2W78SS4618 /* TCFableonCell.swift in Sources */,
F31E84U5Y6YW6430Z54X1K40 /* UCHVion.swift in Sources */, F31E84U5Y6YW6430Z54X1K40 /* UCHVion.swift in Sources */,
F3554P018N62D11QM20Y695Q /* BCMonitorEcommendController.swift in Sources */, F3554P018N62D11QM20Y695Q /* BCMonitorEcommendController.swift in Sources */,
039CE62E2EAB5B18007B5EED /* FAStoreCoinsSmallCell.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -2056,6 +2222,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Fableon/Fableon.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 6XALB8RSYF; DEVELOPMENT_TEAM = 6XALB8RSYF;
@ -2076,7 +2243,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.hn.qinjiu.fableon; PRODUCT_BUNDLE_IDENTIFIER = com.hn.qinjiu.fableon;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@ -2096,6 +2263,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Fableon/Fableon.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 6XALB8RSYF; DEVELOPMENT_TEAM = 6XALB8RSYF;
@ -2115,7 +2283,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.hn.qinjiu.fableon; PRODUCT_BUNDLE_IDENTIFIER = com.hn.qinjiu.fableon;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";

View File

@ -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 {
///URLAPP
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
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)
}
}
}

View File

@ -28,6 +28,8 @@ extension AppDelegate {
let appearance = UINavigationBarAppearance.defaultAppearance() let appearance = UINavigationBarAppearance.defaultAppearance()
UINavigationBar.appearance().scrollEdgeAppearance = appearance UINavigationBar.appearance().scrollEdgeAppearance = appearance
UINavigationBar.appearance().standardAppearance = appearance UINavigationBar.appearance().standardAppearance = appearance
registerAdjust()
} }
} }

View File

@ -19,6 +19,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
window = UIWindow(windowScene: windowScene) window = UIWindow(windowScene: windowScene)
window?.rootViewController = FATabBarController() window?.rootViewController = FATabBarController()
window?.makeKeyAndVisible() window?.makeKeyAndVisible()
FAAdjustStateManager.manager.isOpenApp = true
} }
func sceneDidDisconnect(_ scene: UIScene) { func sceneDidDisconnect(_ scene: UIScene) {
@ -39,14 +40,13 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
} }
func sceneWillEnterForeground(_ scene: UIScene) { func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground. DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// Use this method to undo the changes made on entering the background. self.handleOpenAppMessage(webpageURL: nil)
}
} }
func sceneDidEnterBackground(_ scene: UIScene) { func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background. FAAdjustStateManager.manager.allowOpenMessage = true
// 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.
} }

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:fableon.go.link</string>
<string>applinks:kuzt.adj.st</string>
</array>
</dict>
</plist>

View File

@ -39,6 +39,14 @@ class FATabBarController: UITabBarController {
self.tabBar.standardAppearance = appearance self.tabBar.standardAppearance = appearance
self.tabBar.isTranslucent = false 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? { override var childForStatusBarStyle: UIViewController? {

View File

@ -10,7 +10,7 @@ import UIKit
/// ///
let kFAOsVersion: String = UIDevice.current.systemVersion 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 ///app
let kFAAPPVersion: String = (Bundle.main.infoDictionary!["CFBundleShortVersionString"] as? String) ?? "0" let kFAAPPVersion: String = (Bundle.main.infoDictionary!["CFBundleShortVersionString"] as? String) ?? "0"

View File

@ -0,0 +1,9 @@
//
// FAUserDefaultsKey.swift
// Fableon
//
// Created by on 2025/10/24.
//
let kFAWaitRestoreIAPDefaultsKey = "kFAWaitRestoreIAPDefaultsKey"

View File

@ -9,6 +9,7 @@ import Foundation
import SmartCodable import SmartCodable
import YYCategories import YYCategories
extension String: SmartCodable { extension String: SmartCodable {
static func timeZone() -> String { static func timeZone() -> String {
@ -39,3 +40,22 @@ extension String {
static let color_333333 = "#333333" static let color_333333 = "#333333"
static let color_D9D9D9 = "#D9D9D9" 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
}
}

View File

@ -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?
}

View File

@ -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<String>) 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<String>) in
}
}
}

View File

@ -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<FAIapOrderModel>) 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<FAIapVerifyModel>) -> Void)?) {
FANetworkManager.manager.request(FABaseURL + "/applePaid",
method: .post,
parameters: parameters,
isLoding: false,
isToast: true,
) { (response: FANetworkManager.Response<FAIapVerifyModel>) 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<FAPayDateModel>) in
completer?(response.data)
}
}
static func requestBuyRecords(page: Int, completer: ((_ listModel: FANetworkManager.List<FABuyRecordsModel>?) -> 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<FANetworkManager.List<FABuyRecordsModel>>) in
completer?(response.data)
}
}
static func requestRechargeRecord(page: Int, buyType: BuyType, completer: ((_ listModel: FANetworkManager.List<FARechargeRecordModel>?) -> 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<FANetworkManager.List<FARechargeRecordModel>>) in
completer?(response.data)
}
}
static func reuqestSendCoinRecord(page: Int, completer: ((_ listModel: FANetworkManager.List<FASendCoinRecordModel>?) -> 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<FANetworkManager.List<FASendCoinRecordModel>>) in
completer?(response.data)
}
}
}

View File

@ -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))
}
}
}

View File

@ -14,7 +14,7 @@ class FAAppWebViewController: FABaseWebViewController {
private var receiveDataCount = 0 private var receiveDataCount = 0
var theme: String? = "theme_2" var theme: String? = "theme_1"
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()

View File

@ -95,7 +95,7 @@ extension FAMeViewController: JXPagingViewDelegate {
} }
func tableHeaderViewHeight(in pagingView: JXPagingView) -> Int { func tableHeaderViewHeight(in pagingView: JXPagingView) -> Int {
return Int(ceill(headerView.contentHeight)) return Int(ceil(headerView.contentHeight))
} }
func tableHeaderView(in pagingView: JXPagingView) -> UIView { func tableHeaderView(in pagingView: JXPagingView) -> UIView {

View File

@ -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
}
}

View File

@ -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")
}
}
}

View File

@ -87,7 +87,19 @@ class FAPlayerDetailViewController: JXPlayerListViewController {
} }
self.pause() 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()
}
}
}
} }
} }

View File

@ -37,8 +37,10 @@ class FAShortDetailViewModel: JXPlayerListViewModel, ObservableObject {
weak var popView: UIView? 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 FAAPI.requestShortDetailData(shortPlayId: shortPlayId) { [weak self] model, code, msg in
guard let self = self else { return } guard let self = self else { return }
if let model = model { if let model = model {
@ -48,7 +50,9 @@ class FAShortDetailViewModel: JXPlayerListViewModel, ObservableObject {
guard let self = self else { return } guard let self = self else { return }
var targetIndexPath = IndexPath(row: 0, section: 0) 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? var row: Int?
model.episodeList?.enumerated().forEach { model.episodeList?.enumerated().forEach {
if $1.short_play_video_id == videoInfo.short_play_video_id { 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 let videoInfo = self.currentCell?.model as? FAVideoInfoModel else { return }
guard self.popView == nil 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 { extension FAShortDetailViewModel {

View File

@ -9,6 +9,8 @@ import UIKit
class FACoinRecordViewController: FAViewController { class FACoinRecordViewController: FAViewController {
private lazy var dataArr: [FARechargeRecordModel] = []
private lazy var page = 1
private lazy var tableView: FATableView = { private lazy var tableView: FATableView = {
let tableView = FATableView(frame: .zero, style: .plain) 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.separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16)
tableView.contentInset = .init(top: 10, left: 0, bottom: UIScreen.safeBottom + 10, right: 0) tableView.contentInset = .init(top: 10, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
tableView.register(UINib(nibName: "FAOrderRecordCell", bundle: nil), forCellReuseIdentifier: "cell") 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 return tableView
}() }()
@ -32,8 +41,21 @@ class FACoinRecordViewController: FAViewController {
make.left.right.bottom.equalToSuperview() make.left.right.bottom.equalToSuperview()
make.top.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 { extension FACoinRecordViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let model = self.dataArr[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAOrderRecordCell 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 return cell
} }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 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)
}
}
}

View File

@ -9,6 +9,8 @@ import UIKit
class FAConsumptionRecordsViewController: FAViewController { class FAConsumptionRecordsViewController: FAViewController {
private lazy var dataArr: [FABuyRecordsModel] = []
private lazy var page = 1
private lazy var tableView: FATableView = { private lazy var tableView: FATableView = {
let tableView = FATableView(frame: .zero, style: .plain) 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.separatorInset = .init(top: 0, left: 34, bottom: 0, right: 34)
tableView.contentInset = .init(top: 10, left: 0, bottom: UIScreen.safeBottom + 10, right: 0) tableView.contentInset = .init(top: 10, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
tableView.register(UINib(nibName: "FAConsumptionRecordsCell", bundle: nil), forCellReuseIdentifier: "cell") 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 return tableView
}() }()
@ -27,6 +36,8 @@ class FAConsumptionRecordsViewController: FAViewController {
self.title = "Consumption Records".localized self.title = "Consumption Records".localized
fa_setupLayout() fa_setupLayout()
requestDataArr(page: 1, completer: nil)
} }
@ -36,6 +47,18 @@ class FAConsumptionRecordsViewController: FAViewController {
fa_setNavigationStyle() 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()
}
}
} }
extension FAConsumptionRecordsViewController { extension FAConsumptionRecordsViewController {
@ -57,10 +80,37 @@ extension FAConsumptionRecordsViewController: UITableViewDelegate, UITableViewDa
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAConsumptionRecordsCell let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAConsumptionRecordsCell
cell.model = dataArr[indexPath.row]
return cell return cell
} }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 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)
}
}
}

View File

@ -9,6 +9,9 @@ import UIKit
class FARewardCoinsViewController: FAViewController { class FARewardCoinsViewController: FAViewController {
private lazy var dataArr: [FASendCoinRecordModel] = []
private lazy var page = 1
private lazy var tableView: FATableView = { private lazy var tableView: FATableView = {
let tableView = FATableView(frame: .zero, style: .plain) let tableView = FATableView(frame: .zero, style: .plain)
tableView.delegate = self tableView.delegate = self
@ -18,6 +21,13 @@ class FARewardCoinsViewController: FAViewController {
tableView.separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16) 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.contentInset = .init(top: 10, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
tableView.register(UINib(nibName: "FARewardCoinsCell", bundle: nil), forCellReuseIdentifier: "cell") 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 return tableView
}() }()
@ -31,6 +41,8 @@ class FARewardCoinsViewController: FAViewController {
make.left.right.bottom.equalToSuperview() make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.navBarHeight + 10) make.top.equalToSuperview().offset(UIScreen.navBarHeight + 10)
} }
requestDataArr(page: 1, completer: nil)
} }
@ -40,16 +52,55 @@ class FARewardCoinsViewController: FAViewController {
fa_setNavigationStyle() 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()
}
}
} }
//MARK: UITableViewDelegate UITableViewDataSource //MARK: UITableViewDelegate UITableViewDataSource
extension FARewardCoinsViewController: UITableViewDelegate, UITableViewDataSource { extension FARewardCoinsViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FARewardCoinsCell let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FARewardCoinsCell
cell.model = self.dataArr[indexPath.row]
return cell return cell
} }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 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)
}
}
}

View File

@ -9,6 +9,11 @@ import UIKit
class FAStoreViewController: FAViewController { class FAStoreViewController: FAViewController {
private var payRequest: FAPayDataRequest?
///
private var payDataModel = FAIapManager.manager.payDateModel
private lazy var scrollView: FAScrollView = { private lazy var scrollView: FAScrollView = {
let scrollView = FAScrollView() let scrollView = FAScrollView()
return scrollView return scrollView
@ -22,11 +27,84 @@ class FAStoreViewController: FAViewController {
return label 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() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
self.title = "Store".localized 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() 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) { override func viewWillAppear(_ animated: Bool) {
@ -35,6 +113,73 @@ class FAStoreViewController: FAViewController {
fa_setNavigationStyle() 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)
}
} }
@ -43,6 +188,7 @@ extension FAStoreViewController {
private func fa_setupLayout() { private func fa_setupLayout() {
view.addSubview(scrollView) view.addSubview(scrollView)
scrollView.addSubview(titleLabel) scrollView.addSubview(titleLabel)
scrollView.addSubview(stackView)
scrollView.snp.makeConstraints { make in scrollView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview() make.left.right.bottom.equalToSuperview()
@ -53,6 +199,29 @@ extension FAStoreViewController {
make.top.equalToSuperview().offset(0) make.top.equalToSuperview().offset(0)
make.centerX.equalToSuperview() 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()
}
}
}

View File

@ -9,6 +9,9 @@ import UIKit
class FAVipRecordViewController: FAViewController { class FAVipRecordViewController: FAViewController {
private lazy var dataArr: [FARechargeRecordModel] = []
private lazy var page = 1
private lazy var tableView: FATableView = { private lazy var tableView: FATableView = {
let tableView = FATableView(frame: .zero, style: .plain) let tableView = FATableView(frame: .zero, style: .plain)
tableView.delegate = self tableView.delegate = self
@ -18,6 +21,13 @@ class FAVipRecordViewController: FAViewController {
tableView.separatorInset = .init(top: 0, left: 16, bottom: 0, right: 16) 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.contentInset = .init(top: 10, left: 0, bottom: UIScreen.safeBottom + 10, right: 0)
tableView.register(UINib(nibName: "FAOrderRecordCell", bundle: nil), forCellReuseIdentifier: "cell") 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 return tableView
}() }()
@ -31,9 +41,21 @@ class FAVipRecordViewController: FAViewController {
make.left.right.bottom.equalToSuperview() make.left.right.bottom.equalToSuperview()
make.top.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 { extension FAVipRecordViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let model = self.dataArr[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAOrderRecordCell let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FAOrderRecordCell
cell.iconImageView.isHidden = true cell.iconImageView.isHidden = true
cell.titleLabel.text = model.type
cell.dateLabel.text = model.created_at
cell.countLabel.text = "+\(model.value ?? "")"
return cell return cell
} }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 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)
}
} }
} }

View File

@ -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?
}

View File

@ -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?
}

View File

@ -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]?
}

View File

@ -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?
}

View File

@ -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?
}

View File

@ -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
}
}

View File

@ -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")
}
}
}

View File

@ -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")
}
}

View File

@ -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)
}
}
}

View File

@ -9,6 +9,15 @@ import UIKit
class FAConsumptionRecordsCell: FATableViewCell { 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! @IBOutlet weak var titleLabel: UILabel!
@ -22,7 +31,7 @@ class FAConsumptionRecordsCell: FATableViewCell {
override func awakeFromNib() { override func awakeFromNib() {
super.awakeFromNib() super.awakeFromNib()
titleLabel.text = "Purchase Single Episode dafdaaafafaafdasfadfsafdafadsadfas"; titleLabel.text = "Purchase Single Episode";
subtitleLabel.text = "Ep.8 Romantic Flash Marriage In Progress" subtitleLabel.text = "Ep.8 Romantic Flash Marriage In Progress"
dateLabel.text = "2024-6-10 23:41:18" dateLabel.text = "2024-6-10 23:41:18"
coinsLabel.text = "-10 Coins" coinsLabel.text = "-10 Coins"

View File

@ -9,6 +9,27 @@ import UIKit
class FARewardCoinsCell: FATableViewCell { 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! @IBOutlet weak var dateLabel: UILabel!

View File

@ -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)
}
}
}

View File

@ -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")
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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?()
}
}
}
}
}

View File

@ -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)
}
}
}

View File

@ -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?()
}
}
}
}

View File

@ -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
}

View File

@ -11,7 +11,7 @@ import LYEmptyView
struct FAEmpty { 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) { let view = LYEmptyView.emptyActionView(with: image, titleStr: title, detailStr: nil, btnTitleStr: nil) {
// btnClickBlock?() // btnClickBlock?()

View File

@ -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")
}

View File

@ -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?
}

View File

@ -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?
}

View File

@ -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)
}
}
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

View File

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 B

View File

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -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
}
}

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