Compare commits

...

2 Commits

Author SHA1 Message Date
湖北秦九
8bbb3d10ad 1 2025-10-31 13:36:26 +08:00
湖北秦九
e0163dabe3 1 2025-10-31 13:36:20 +08:00
125 changed files with 4130 additions and 46 deletions

View File

@ -17,6 +17,29 @@
031FDEBA2EB0B82600F4CAC7 /* FACoinPackConfirmItem2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEB92EB0B82600F4CAC7 /* FACoinPackConfirmItem2View.swift */; }; 031FDEBA2EB0B82600F4CAC7 /* FACoinPackConfirmItem2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEB92EB0B82600F4CAC7 /* FACoinPackConfirmItem2View.swift */; };
031FDEBC2EB0C97A00F4CAC7 /* FAOldVideoRechargeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEBB2EB0C97A00F4CAC7 /* FAOldVideoRechargeView.swift */; }; 031FDEBC2EB0C97A00F4CAC7 /* FAOldVideoRechargeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEBB2EB0C97A00F4CAC7 /* FAOldVideoRechargeView.swift */; };
031FDEBE2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEBD2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift */; }; 031FDEBE2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEBD2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift */; };
031FDEC02EB1C08900F4CAC7 /* FAMeCoinsPackButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEBF2EB1C08900F4CAC7 /* FAMeCoinsPackButton.swift */; };
031FDEC22EB1E19D00F4CAC7 /* FACoinPackViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEC12EB1E19D00F4CAC7 /* FACoinPackViewController.swift */; };
031FDEC42EB1E45300F4CAC7 /* FACoinPackHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEC32EB1E45300F4CAC7 /* FACoinPackHeaderView.swift */; };
031FDEC62EB1E4E600F4CAC7 /* FACoinPackTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEC52EB1E4E600F4CAC7 /* FACoinPackTitleView.swift */; };
031FDEC82EB1F89F00F4CAC7 /* FACoinsPackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEC72EB1F89F00F4CAC7 /* FACoinsPackModel.swift */; };
031FDECA2EB1F8F200F4CAC7 /* FACoinsPackReceiveModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEC92EB1F8F200F4CAC7 /* FACoinsPackReceiveModel.swift */; };
031FDECC2EB1FDF500F4CAC7 /* FACoinsPackClaimListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDECB2EB1FDF500F4CAC7 /* FACoinsPackClaimListView.swift */; };
031FDECE2EB2006000F4CAC7 /* FACoinsPackClaimListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDECD2EB2006000F4CAC7 /* FACoinsPackClaimListCell.swift */; };
031FDED02EB2167200F4CAC7 /* FACoinsPackBuyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDECF2EB2167200F4CAC7 /* FACoinsPackBuyView.swift */; };
031FDED22EB2F69200F4CAC7 /* FALoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDED12EB2F69200F4CAC7 /* FALoginView.swift */; };
031FDED42EB2FF4000F4CAC7 /* FALogin+Apple.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDED32EB2FF3A00F4CAC7 /* FALogin+Apple.swift */; };
031FDED62EB300F400F4CAC7 /* FAThirdSignModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDED52EB300F400F4CAC7 /* FAThirdSignModel.swift */; };
031FDED92EB30D6E00F4CAC7 /* FASettingCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 031FDED82EB30D6E00F4CAC7 /* FASettingCell.xib */; };
031FDEDA2EB30D6E00F4CAC7 /* FASettingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDED72EB30D6E00F4CAC7 /* FASettingCell.swift */; };
031FDEDC2EB3141E00F4CAC7 /* FASettingFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEDB2EB3141E00F4CAC7 /* FASettingFooterView.swift */; };
031FDEDF2EB3423000F4CAC7 /* FacebookLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 031FDEDE2EB3423000F4CAC7 /* FacebookLogin */; };
031FDEE12EB344AB00F4CAC7 /* FALogin+Facebook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEE02EB344A200F4CAC7 /* FALogin+Facebook.swift */; };
031FDEE42EB348AA00F4CAC7 /* FABaseAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEE32EB348AA00F4CAC7 /* FABaseAlert.swift */; };
031FDEE62EB34FBC00F4CAC7 /* FARemoveCollectAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEE52EB34FBC00F4CAC7 /* FARemoveCollectAlert.swift */; };
031FDEE82EB358AE00F4CAC7 /* FAHomeCoinsPackButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEE72EB358AE00F4CAC7 /* FAHomeCoinsPackButton.swift */; };
031FDEEA2EB35D2600F4CAC7 /* FACoinPackCanReceiveModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEE92EB35D2600F4CAC7 /* FACoinPackCanReceiveModel.swift */; };
031FDEEC2EB35DF600F4CAC7 /* FACoinsPackAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEEB2EB35DF600F4CAC7 /* FACoinsPackAlert.swift */; };
031FDEEE2EB3682000F4CAC7 /* FAVipRetainAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031FDEED2EB3682000F4CAC7 /* FAVipRetainAlert.swift */; };
039CE6042EAA2621007B5EED /* AppDelegate+FAAdjust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6032EAA2612007B5EED /* AppDelegate+FAAdjust.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 */; }; 039CE6092EAA2F71007B5EED /* FAAdjustStateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE6082EAA2F62007B5EED /* FAAdjustStateManager.swift */; };
039CE60B2EAA31CB007B5EED /* FAStatAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE60A2EAA31CB007B5EED /* FAStatAPI.swift */; }; 039CE60B2EAA31CB007B5EED /* FAStatAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE60A2EAA31CB007B5EED /* FAStatAPI.swift */; };
@ -189,6 +212,11 @@
03E23AB72EAA1A7F004A8CEC /* FALocalized.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E23A9C2EAA1A7F004A8CEC /* FALocalized.swift */; }; 03E23AB72EAA1A7F004A8CEC /* FALocalized.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E23A9C2EAA1A7F004A8CEC /* FALocalized.swift */; };
03E23AB82EAA1A7F004A8CEC /* FAWaterfallFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E23AAA2EAA1A7F004A8CEC /* FAWaterfallFlowLayout.swift */; }; 03E23AB82EAA1A7F004A8CEC /* FAWaterfallFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E23AAA2EAA1A7F004A8CEC /* FAWaterfallFlowLayout.swift */; };
03E23ABA2EAA1D85004A8CEC /* MNNItemController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E23AB92EAA1D85004A8CEC /* MNNItemController.swift */; }; 03E23ABA2EAA1D85004A8CEC /* MNNItemController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E23AB92EAA1D85004A8CEC /* MNNItemController.swift */; };
03E9A7362EB44F26000D1067 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 03E9A7352EB44F26000D1067 /* FirebaseMessaging */; };
03E9A7382EB44F26000D1067 /* FirebasePerformance in Frameworks */ = {isa = PBXBuildFile; productRef = 03E9A7372EB44F26000D1067 /* FirebasePerformance */; };
03E9A73A2EB45154000D1067 /* AppDelegate+FAApns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A7392EB45149000D1067 /* AppDelegate+FAApns.swift */; };
03E9A73C2EB45507000D1067 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 03E9A73B2EB45507000D1067 /* GoogleService-Info.plist */; };
03E9A73E2EB460F2000D1067 /* FAApnsAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03E9A73D2EB460F2000D1067 /* FAApnsAlert.swift */; };
B86XD3O90WO2R4725L084287 /* Pods_Fableon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4THBP0A1283PFXW440071Q5 /* Pods_Fableon.framework */; }; B86XD3O90WO2R4725L084287 /* Pods_Fableon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4THBP0A1283PFXW440071Q5 /* Pods_Fableon.framework */; };
F3019606DA7P36H41G408X13 /* ZStreamCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F38E33739F391364D0151P7Z /* ZStreamCell.swift */; }; F3019606DA7P36H41G408X13 /* ZStreamCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F38E33739F391364D0151P7Z /* ZStreamCell.swift */; };
F30470W590T8274E1642349G /* CControlCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32MR5F8X6Q3HSZ560BD0159 /* CControlCell.swift */; }; F30470W590T8274E1642349G /* CControlCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32MR5F8X6Q3HSZ560BD0159 /* CControlCell.swift */; };
@ -306,6 +334,28 @@
031FDEB92EB0B82600F4CAC7 /* FACoinPackConfirmItem2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinPackConfirmItem2View.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>"; }; 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>"; }; 031FDEBD2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FANewVideoRechargeView.swift; sourceTree = "<group>"; };
031FDEBF2EB1C08900F4CAC7 /* FAMeCoinsPackButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAMeCoinsPackButton.swift; sourceTree = "<group>"; };
031FDEC12EB1E19D00F4CAC7 /* FACoinPackViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinPackViewController.swift; sourceTree = "<group>"; };
031FDEC32EB1E45300F4CAC7 /* FACoinPackHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinPackHeaderView.swift; sourceTree = "<group>"; };
031FDEC52EB1E4E600F4CAC7 /* FACoinPackTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinPackTitleView.swift; sourceTree = "<group>"; };
031FDEC72EB1F89F00F4CAC7 /* FACoinsPackModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinsPackModel.swift; sourceTree = "<group>"; };
031FDEC92EB1F8F200F4CAC7 /* FACoinsPackReceiveModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinsPackReceiveModel.swift; sourceTree = "<group>"; };
031FDECB2EB1FDF500F4CAC7 /* FACoinsPackClaimListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinsPackClaimListView.swift; sourceTree = "<group>"; };
031FDECD2EB2006000F4CAC7 /* FACoinsPackClaimListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinsPackClaimListCell.swift; sourceTree = "<group>"; };
031FDECF2EB2167200F4CAC7 /* FACoinsPackBuyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinsPackBuyView.swift; sourceTree = "<group>"; };
031FDED12EB2F69200F4CAC7 /* FALoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FALoginView.swift; sourceTree = "<group>"; };
031FDED32EB2FF3A00F4CAC7 /* FALogin+Apple.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FALogin+Apple.swift"; sourceTree = "<group>"; };
031FDED52EB300F400F4CAC7 /* FAThirdSignModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAThirdSignModel.swift; sourceTree = "<group>"; };
031FDED72EB30D6E00F4CAC7 /* FASettingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FASettingCell.swift; sourceTree = "<group>"; };
031FDED82EB30D6E00F4CAC7 /* FASettingCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FASettingCell.xib; sourceTree = "<group>"; };
031FDEDB2EB3141E00F4CAC7 /* FASettingFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FASettingFooterView.swift; sourceTree = "<group>"; };
031FDEE02EB344A200F4CAC7 /* FALogin+Facebook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FALogin+Facebook.swift"; sourceTree = "<group>"; };
031FDEE32EB348AA00F4CAC7 /* FABaseAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FABaseAlert.swift; sourceTree = "<group>"; };
031FDEE52EB34FBC00F4CAC7 /* FARemoveCollectAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FARemoveCollectAlert.swift; sourceTree = "<group>"; };
031FDEE72EB358AE00F4CAC7 /* FAHomeCoinsPackButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAHomeCoinsPackButton.swift; sourceTree = "<group>"; };
031FDEE92EB35D2600F4CAC7 /* FACoinPackCanReceiveModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinPackCanReceiveModel.swift; sourceTree = "<group>"; };
031FDEEB2EB35DF600F4CAC7 /* FACoinsPackAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FACoinsPackAlert.swift; sourceTree = "<group>"; };
031FDEED2EB3682000F4CAC7 /* FAVipRetainAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAVipRetainAlert.swift; sourceTree = "<group>"; };
039CE6032EAA2612007B5EED /* AppDelegate+FAAdjust.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+FAAdjust.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>"; }; 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>"; }; 039CE60A2EAA31CB007B5EED /* FAStatAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAStatAPI.swift; sourceTree = "<group>"; };
@ -479,6 +529,9 @@
03E23AA82EAA1A7F004A8CEC /* FAToast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAToast.swift; sourceTree = "<group>"; }; 03E23AA82EAA1A7F004A8CEC /* FAToast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAToast.swift; sourceTree = "<group>"; };
03E23AAA2EAA1A7F004A8CEC /* FAWaterfallFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAWaterfallFlowLayout.swift; sourceTree = "<group>"; }; 03E23AAA2EAA1A7F004A8CEC /* FAWaterfallFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAWaterfallFlowLayout.swift; sourceTree = "<group>"; };
03E23AB92EAA1D85004A8CEC /* MNNItemController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MNNItemController.swift; sourceTree = "<group>"; }; 03E23AB92EAA1D85004A8CEC /* MNNItemController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MNNItemController.swift; sourceTree = "<group>"; };
03E9A7392EB45149000D1067 /* AppDelegate+FAApns.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+FAApns.swift"; sourceTree = "<group>"; };
03E9A73B2EB45507000D1067 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
03E9A73D2EB460F2000D1067 /* FAApnsAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAApnsAlert.swift; sourceTree = "<group>"; };
19196I43BR665O55RD205171 /* Pods-Fableon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Fableon.debug.xcconfig"; path = "Target Support Files/Pods-Fableon/Pods-Fableon.debug.xcconfig"; sourceTree = "<group>"; }; 19196I43BR665O55RD205171 /* Pods-Fableon.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Fableon.debug.xcconfig"; path = "Target Support Files/Pods-Fableon/Pods-Fableon.debug.xcconfig"; sourceTree = "<group>"; };
C4THBP0A1283PFXW440071Q5 /* Pods_Fableon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Fableon.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C4THBP0A1283PFXW440071Q5 /* Pods_Fableon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Fableon.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DCD59738B6J31K33W4Z524S0 /* Pods-Fableon.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Fableon.release.xcconfig"; path = "Target Support Files/Pods-Fableon/Pods-Fableon.release.xcconfig"; sourceTree = "<group>"; }; DCD59738B6J31K33W4Z524S0 /* Pods-Fableon.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Fableon.release.xcconfig"; path = "Target Support Files/Pods-Fableon/Pods-Fableon.release.xcconfig"; sourceTree = "<group>"; };
@ -594,6 +647,9 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
03E9A7362EB44F26000D1067 /* FirebaseMessaging in Frameworks */,
031FDEDF2EB3423000F4CAC7 /* FacebookLogin in Frameworks */,
03E9A7382EB44F26000D1067 /* FirebasePerformance in Frameworks */,
B86XD3O90WO2R4725L084287 /* Pods_Fableon.framework in Frameworks */, B86XD3O90WO2R4725L084287 /* Pods_Fableon.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -601,6 +657,18 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
031FDEE22EB3487700F4CAC7 /* Alert */ = {
isa = PBXGroup;
children = (
031FDEE32EB348AA00F4CAC7 /* FABaseAlert.swift */,
031FDEE52EB34FBC00F4CAC7 /* FARemoveCollectAlert.swift */,
031FDEEB2EB35DF600F4CAC7 /* FACoinsPackAlert.swift */,
031FDEED2EB3682000F4CAC7 /* FAVipRetainAlert.swift */,
03E9A73D2EB460F2000D1067 /* FAApnsAlert.swift */,
);
path = Alert;
sourceTree = "<group>";
};
039CE6072EAA2F37007B5EED /* AdjustStateManager */ = { 039CE6072EAA2F37007B5EED /* AdjustStateManager */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -645,6 +713,9 @@
031FDEAD2EB093B100F4CAC7 /* FABuyRecordsModel.swift */, 031FDEAD2EB093B100F4CAC7 /* FABuyRecordsModel.swift */,
031FDEAF2EB09AB300F4CAC7 /* FARechargeRecordModel.swift */, 031FDEAF2EB09AB300F4CAC7 /* FARechargeRecordModel.swift */,
031FDEB12EB0A5AF00F4CAC7 /* FASendCoinRecordModel.swift */, 031FDEB12EB0A5AF00F4CAC7 /* FASendCoinRecordModel.swift */,
031FDEC72EB1F89F00F4CAC7 /* FACoinsPackModel.swift */,
031FDEC92EB1F8F200F4CAC7 /* FACoinsPackReceiveModel.swift */,
031FDEE92EB35D2600F4CAC7 /* FACoinPackCanReceiveModel.swift */,
); );
path = M; path = M;
sourceTree = "<group>"; sourceTree = "<group>";
@ -849,6 +920,7 @@
03E239DF2EAA1A4E004A8CEC /* FASearchResultCell.swift */, 03E239DF2EAA1A4E004A8CEC /* FASearchResultCell.swift */,
03E239E02EAA1A4E004A8CEC /* FASearchResultCell.xib */, 03E239E02EAA1A4E004A8CEC /* FASearchResultCell.xib */,
03E239E12EAA1A4E004A8CEC /* FASearchResultView.swift */, 03E239E12EAA1A4E004A8CEC /* FASearchResultView.swift */,
031FDEE72EB358AE00F4CAC7 /* FAHomeCoinsPackButton.swift */,
); );
path = V; path = V;
sourceTree = "<group>"; sourceTree = "<group>";
@ -882,6 +954,7 @@
03E239E92EAA1A4E004A8CEC /* FAMeListViewController.swift */, 03E239E92EAA1A4E004A8CEC /* FAMeListViewController.swift */,
03E239EA2EAA1A4E004A8CEC /* FAMeViewController.swift */, 03E239EA2EAA1A4E004A8CEC /* FAMeViewController.swift */,
03E239EB2EAA1A4E004A8CEC /* FASettingViewController.swift */, 03E239EB2EAA1A4E004A8CEC /* FASettingViewController.swift */,
031FDEC12EB1E19D00F4CAC7 /* FACoinPackViewController.swift */,
); );
path = C; path = C;
sourceTree = "<group>"; sourceTree = "<group>";
@ -905,6 +978,16 @@
03E239F42EAA1A4E004A8CEC /* FAMeCoinsView.swift */, 03E239F42EAA1A4E004A8CEC /* FAMeCoinsView.swift */,
03E239F52EAA1A4E004A8CEC /* FAMeHeaderView.swift */, 03E239F52EAA1A4E004A8CEC /* FAMeHeaderView.swift */,
03E239F62EAA1A4E004A8CEC /* FAMeTableViewHeaderView.swift */, 03E239F62EAA1A4E004A8CEC /* FAMeTableViewHeaderView.swift */,
031FDEBF2EB1C08900F4CAC7 /* FAMeCoinsPackButton.swift */,
031FDEC52EB1E4E600F4CAC7 /* FACoinPackTitleView.swift */,
031FDEC32EB1E45300F4CAC7 /* FACoinPackHeaderView.swift */,
031FDECB2EB1FDF500F4CAC7 /* FACoinsPackClaimListView.swift */,
031FDECD2EB2006000F4CAC7 /* FACoinsPackClaimListCell.swift */,
031FDECF2EB2167200F4CAC7 /* FACoinsPackBuyView.swift */,
031FDED12EB2F69200F4CAC7 /* FALoginView.swift */,
031FDED72EB30D6E00F4CAC7 /* FASettingCell.swift */,
031FDED82EB30D6E00F4CAC7 /* FASettingCell.xib */,
031FDEDB2EB3141E00F4CAC7 /* FASettingFooterView.swift */,
); );
path = V; path = V;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1101,6 +1184,7 @@
03E23A962EAA1A65004A8CEC /* Libs */ = { 03E23A962EAA1A65004A8CEC /* Libs */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
031FDEE22EB3487700F4CAC7 /* Alert */,
039CE6122EAB0DE1007B5EED /* FAIap */, 039CE6122EAB0DE1007B5EED /* FAIap */,
039CE60F2EAB0D2D007B5EED /* JXIAPManager */, 039CE60F2EAB0D2D007B5EED /* JXIAPManager */,
039CE6072EAA2F37007B5EED /* AdjustStateManager */, 039CE6072EAA2F37007B5EED /* AdjustStateManager */,
@ -1145,8 +1229,11 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
03E23A9E2EAA1A7F004A8CEC /* FALogin.swift */, 03E23A9E2EAA1A7F004A8CEC /* FALogin.swift */,
031FDED32EB2FF3A00F4CAC7 /* FALogin+Apple.swift */,
031FDEE02EB344A200F4CAC7 /* FALogin+Facebook.swift */,
03E23A9F2EAA1A7F004A8CEC /* FATokenModel.swift */, 03E23A9F2EAA1A7F004A8CEC /* FATokenModel.swift */,
03E23AA02EAA1A7F004A8CEC /* FAUserInfo.swift */, 03E23AA02EAA1A7F004A8CEC /* FAUserInfo.swift */,
031FDED52EB300F400F4CAC7 /* FAThirdSignModel.swift */,
); );
path = FALogin; path = FALogin;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1555,6 +1642,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
F32J2363K02108Z62849Y019 /* Assets.xcassets */, F32J2363K02108Z62849Y019 /* Assets.xcassets */,
03E9A73B2EB45507000D1067 /* GoogleService-Info.plist */,
F3718184F516RJ76435E1IQ3 /* Info.plist */, F3718184F516RJ76435E1IQ3 /* Info.plist */,
F3S22333V8I503E2Z4YYN165 /* LaunchScreen.storyboard */, F3S22333V8I503E2Z4YYN165 /* LaunchScreen.storyboard */,
F3A0557617AU32W3L218F159 /* Localizable.strings */, F3A0557617AU32W3L218F159 /* Localizable.strings */,
@ -1600,9 +1688,10 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
03E239602EAA1945004A8CEC /* AppDelegate.swift */, 03E239602EAA1945004A8CEC /* AppDelegate.swift */,
03E239622EAA1945004A8CEC /* SceneDelegate.swift */,
03E239612EAA1945004A8CEC /* AppDelegate+FAConfig.swift */, 03E239612EAA1945004A8CEC /* AppDelegate+FAConfig.swift */,
039CE6032EAA2612007B5EED /* AppDelegate+FAAdjust.swift */, 039CE6032EAA2612007B5EED /* AppDelegate+FAAdjust.swift */,
03E239622EAA1945004A8CEC /* SceneDelegate.swift */, 03E9A7392EB45149000D1067 /* AppDelegate+FAApns.swift */,
); );
path = App; path = App;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1713,6 +1802,10 @@
); );
mainGroup = F31ABI705806054356280I22; mainGroup = F31ABI705806054356280I22;
minimizedProjectReferenceProxies = 1; minimizedProjectReferenceProxies = 1;
packageReferences = (
031FDEDD2EB3423000F4CAC7 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */,
03E9A7342EB44F26000D1067 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
);
preferredProjectObjectVersion = 77; preferredProjectObjectVersion = 77;
productRefGroup = F3B536I6734E05L319J6654P /* Products */; productRefGroup = F3B536I6734E05L319J6654P /* Products */;
projectDirPath = ""; projectDirPath = "";
@ -1744,6 +1837,7 @@
03E23A862EAA1A4E004A8CEC /* FAHistoryCell.xib in Resources */, 03E23A862EAA1A4E004A8CEC /* FAHistoryCell.xib in Resources */,
03E23A872EAA1A4E004A8CEC /* FAHomeRecommendedCell.xib in Resources */, 03E23A872EAA1A4E004A8CEC /* FAHomeRecommendedCell.xib in Resources */,
03E23A882EAA1A4E004A8CEC /* FAMeCell.xib in Resources */, 03E23A882EAA1A4E004A8CEC /* FAMeCell.xib in Resources */,
031FDED92EB30D6E00F4CAC7 /* FASettingCell.xib in Resources */,
03E23A892EAA1A4E004A8CEC /* FASearchResultCell.xib in Resources */, 03E23A892EAA1A4E004A8CEC /* FASearchResultCell.xib in Resources */,
03E23A8A2EAA1A4E004A8CEC /* FASearchRecommendCell.xib in Resources */, 03E23A8A2EAA1A4E004A8CEC /* FASearchRecommendCell.xib in Resources */,
03E23A8B2EAA1A4E004A8CEC /* FAHomeSectionTitleView.xib in Resources */, 03E23A8B2EAA1A4E004A8CEC /* FAHomeSectionTitleView.xib in Resources */,
@ -1756,6 +1850,7 @@
03E23A922EAA1A4E004A8CEC /* FAConsumptionRecordsCell.xib in Resources */, 03E23A922EAA1A4E004A8CEC /* FAConsumptionRecordsCell.xib in Resources */,
03E23A932EAA1A4E004A8CEC /* FASearchRecordCell.xib in Resources */, 03E23A932EAA1A4E004A8CEC /* FASearchRecordCell.xib in Resources */,
03E23A942EAA1A4E004A8CEC /* FARankingListCell.xib in Resources */, 03E23A942EAA1A4E004A8CEC /* FARankingListCell.xib in Resources */,
03E9A73C2EB45507000D1067 /* GoogleService-Info.plist in Resources */,
03E23A952EAA1A4E004A8CEC /* FAAboutCell.xib in Resources */, 03E23A952EAA1A4E004A8CEC /* FAAboutCell.xib in Resources */,
F3R81ZR0ZIP4Q85CX4Z5J3I4 /* LaunchScreen.storyboard in Resources */, F3R81ZR0ZIP4Q85CX4Z5J3I4 /* LaunchScreen.storyboard in Resources */,
F362972840Y56X1RPFI92W64 /* XKRefreshCell.xib in Resources */, F362972840Y56X1RPFI92W64 /* XKRefreshCell.xib in Resources */,
@ -1838,12 +1933,14 @@
F336143N569M8JG811WM9549 /* XHEedbackDetailView.swift in Sources */, F336143N569M8JG811WM9549 /* XHEedbackDetailView.swift in Sources */,
F3F0952W592747Y1H6263E06 /* UCVBbfdebaffdFlowCell.swift in Sources */, F3F0952W592747Y1H6263E06 /* UCVBbfdebaffdFlowCell.swift in Sources */,
F3SUP8DL68BIIEF1B8Z6U863 /* CFQConfigView.swift in Sources */, F3SUP8DL68BIIEF1B8Z6U863 /* CFQConfigView.swift in Sources */,
031FDEE12EB344AB00F4CAC7 /* FALogin+Facebook.swift in Sources */,
F30E153206675C3SJ3974VL1 /* OJQUnechoSectionView.swift in Sources */, F30E153206675C3SJ3974VL1 /* OJQUnechoSectionView.swift in Sources */,
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 */, 031FDEAE2EB093B100F4CAC7 /* FABuyRecordsModel.swift in Sources */,
F377Y22Z3J44H22J23963544 /* CDNewsController.swift in Sources */, F377Y22Z3J44H22J23963544 /* CDNewsController.swift in Sources */,
031FDEC22EB1E19D00F4CAC7 /* FACoinPackViewController.swift in Sources */,
F358686093028548K00WQIO1 /* NGleeVionView.swift in Sources */, F358686093028548K00WQIO1 /* NGleeVionView.swift in Sources */,
F3019606DA7P36H41G408X13 /* ZStreamCell.swift in Sources */, F3019606DA7P36H41G408X13 /* ZStreamCell.swift in Sources */,
F392T441X031FH7N0HA03SZ7 /* WPSectionView.swift in Sources */, F392T441X031FH7N0HA03SZ7 /* WPSectionView.swift in Sources */,
@ -1865,6 +1962,7 @@
F3P4D170962A2JAF14W0520R /* ADCheckMageView.swift in Sources */, F3P4D170962A2JAF14W0520R /* ADCheckMageView.swift in Sources */,
031FDEBA2EB0B82600F4CAC7 /* FACoinPackConfirmItem2View.swift in Sources */, 031FDEBA2EB0B82600F4CAC7 /* FACoinPackConfirmItem2View.swift in Sources */,
F3206101P0DVK224N85S1W62 /* XIPathEviceController.swift in Sources */, F3206101P0DVK224N85S1W62 /* XIPathEviceController.swift in Sources */,
031FDEC82EB1F89F00F4CAC7 /* FACoinsPackModel.swift in Sources */,
F31N0S2575E3YC5AOG80WE90 /* TYElyon.swift in Sources */, F31N0S2575E3YC5AOG80WE90 /* TYElyon.swift in Sources */,
F3JX29E3JQ40V23245TS8Z36 /* YRegisterController.swift in Sources */, F3JX29E3JQ40V23245TS8Z36 /* YRegisterController.swift in Sources */,
F381171472542RD403O12925 /* QZOast.swift in Sources */, F381171472542RD403O12925 /* QZOast.swift in Sources */,
@ -1882,6 +1980,7 @@
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 */,
031FDECC2EB1FDF500F4CAC7 /* FACoinsPackClaimListView.swift in Sources */,
039CE6262EAB2A72007B5EED /* FAPayAlertModel.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 */,
@ -1896,6 +1995,7 @@
03E23AB72EAA1A7F004A8CEC /* FALocalized.swift in Sources */, 03E23AB72EAA1A7F004A8CEC /* FALocalized.swift in Sources */,
039CE6222EAB1340007B5EED /* FAUserDefaultsKey.swift in Sources */, 039CE6222EAB1340007B5EED /* FAUserDefaultsKey.swift in Sources */,
03E23AB82EAA1A7F004A8CEC /* FAWaterfallFlowLayout.swift in Sources */, 03E23AB82EAA1A7F004A8CEC /* FAWaterfallFlowLayout.swift in Sources */,
031FDEC62EB1E4E600F4CAC7 /* FACoinPackTitleView.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 */,
@ -1908,6 +2008,7 @@
03E23A352EAA1A4E004A8CEC /* FARecommendPlayerControlView.swift in Sources */, 03E23A352EAA1A4E004A8CEC /* FARecommendPlayerControlView.swift in Sources */,
03E23A362EAA1A4E004A8CEC /* FAHomeMustSeeView.swift in Sources */, 03E23A362EAA1A4E004A8CEC /* FAHomeMustSeeView.swift in Sources */,
03E23A372EAA1A4E004A8CEC /* FAHomeItem.swift in Sources */, 03E23A372EAA1A4E004A8CEC /* FAHomeItem.swift in Sources */,
03E9A73E2EB460F2000D1067 /* FAApnsAlert.swift in Sources */,
03E23A382EAA1A4E004A8CEC /* FACategoryModel.swift in Sources */, 03E23A382EAA1A4E004A8CEC /* FACategoryModel.swift in Sources */,
03E23A392EAA1A4E004A8CEC /* FANewListViewController.swift in Sources */, 03E23A392EAA1A4E004A8CEC /* FANewListViewController.swift in Sources */,
03E23A3A2EAA1A4E004A8CEC /* FAHomeRecommendedCell.swift in Sources */, 03E23A3A2EAA1A4E004A8CEC /* FAHomeRecommendedCell.swift in Sources */,
@ -1928,12 +2029,14 @@
03E23A492EAA1A4E004A8CEC /* FAVideoInfoModel.swift in Sources */, 03E23A492EAA1A4E004A8CEC /* FAVideoInfoModel.swift in Sources */,
039CE6202EAB114B007B5EED /* FAPayDateModel.swift in Sources */, 039CE6202EAB114B007B5EED /* FAPayDateModel.swift in Sources */,
03E23A4A2EAA1A4E004A8CEC /* FAConsumptionRecordsViewController.swift in Sources */, 03E23A4A2EAA1A4E004A8CEC /* FAConsumptionRecordsViewController.swift in Sources */,
031FDED02EB2167200F4CAC7 /* FACoinsPackBuyView.swift in Sources */,
03E23A4B2EAA1A4E004A8CEC /* FAMeCoinsView.swift in Sources */, 03E23A4B2EAA1A4E004A8CEC /* FAMeCoinsView.swift in Sources */,
03E23A4C2EAA1A4E004A8CEC /* FAHomeViewModel.swift in Sources */, 03E23A4C2EAA1A4E004A8CEC /* FAHomeViewModel.swift in Sources */,
03E23A4D2EAA1A4E004A8CEC /* FAGenresViewController.swift in Sources */, 03E23A4D2EAA1A4E004A8CEC /* FAGenresViewController.swift in Sources */,
03E23A4E2EAA1A4E004A8CEC /* FAHomeBannerContentCell.swift in Sources */, 03E23A4E2EAA1A4E004A8CEC /* FAHomeBannerContentCell.swift in Sources */,
03E23A4F2EAA1A4E004A8CEC /* FAEpSelectorView.swift in Sources */, 03E23A4F2EAA1A4E004A8CEC /* FAEpSelectorView.swift in Sources */,
03E23A502EAA1A4E004A8CEC /* FAMeHeaderView.swift in Sources */, 03E23A502EAA1A4E004A8CEC /* FAMeHeaderView.swift in Sources */,
031FDEC02EB1C08900F4CAC7 /* FAMeCoinsPackButton.swift in Sources */,
03E23A512EAA1A4E004A8CEC /* FAConsumptionRecordsCell.swift in Sources */, 03E23A512EAA1A4E004A8CEC /* FAConsumptionRecordsCell.swift in Sources */,
03E23A522EAA1A4E004A8CEC /* FARankingListHeaderView.swift in Sources */, 03E23A522EAA1A4E004A8CEC /* FARankingListHeaderView.swift in Sources */,
03E23A532EAA1A4E004A8CEC /* FAVideoLockView.swift in Sources */, 03E23A532EAA1A4E004A8CEC /* FAVideoLockView.swift in Sources */,
@ -1941,20 +2044,25 @@
03E23A552EAA1A4E004A8CEC /* FAPopularListViewController.swift in Sources */, 03E23A552EAA1A4E004A8CEC /* FAPopularListViewController.swift in Sources */,
039CE6042EAA2621007B5EED /* AppDelegate+FAAdjust.swift in Sources */, 039CE6042EAA2621007B5EED /* AppDelegate+FAAdjust.swift in Sources */,
03E23A562EAA1A4E004A8CEC /* FAPlayerEpUIButton.swift in Sources */, 03E23A562EAA1A4E004A8CEC /* FAPlayerEpUIButton.swift in Sources */,
031FDEE42EB348AA00F4CAC7 /* FABaseAlert.swift in Sources */,
03E23A572EAA1A4E004A8CEC /* FAHistoryCell.swift in Sources */, 03E23A572EAA1A4E004A8CEC /* FAHistoryCell.swift in Sources */,
03E23A582EAA1A4E004A8CEC /* FASearchViewController.swift in Sources */, 03E23A582EAA1A4E004A8CEC /* FASearchViewController.swift in Sources */,
03E23A592EAA1A4E004A8CEC /* FAWalletCell.swift in Sources */, 03E23A592EAA1A4E004A8CEC /* FAWalletCell.swift in Sources */,
03E23A5A2EAA1A4E004A8CEC /* FASearchInputView.swift in Sources */, 03E23A5A2EAA1A4E004A8CEC /* FASearchInputView.swift in Sources */,
031FDEE82EB358AE00F4CAC7 /* FAHomeCoinsPackButton.swift in Sources */,
03E23A5B2EAA1A4E004A8CEC /* FAAboutCell.swift in Sources */, 03E23A5B2EAA1A4E004A8CEC /* FAAboutCell.swift in Sources */,
03E23A5C2EAA1A4E004A8CEC /* FAGenresListViewController.swift in Sources */, 03E23A5C2EAA1A4E004A8CEC /* FAGenresListViewController.swift in Sources */,
03E23A5D2EAA1A4E004A8CEC /* FARecommendViewModel.swift in Sources */, 03E23A5D2EAA1A4E004A8CEC /* FARecommendViewModel.swift in Sources */,
03E23A5E2EAA1A4E004A8CEC /* FAHomeMustSeeContentView.swift in Sources */, 03E23A5E2EAA1A4E004A8CEC /* FAHomeMustSeeContentView.swift in Sources */,
03E23A5F2EAA1A4E004A8CEC /* FAEpSelectorCell.swift in Sources */, 03E23A5F2EAA1A4E004A8CEC /* FAEpSelectorCell.swift in Sources */,
031FDEEC2EB35DF600F4CAC7 /* FACoinsPackAlert.swift in Sources */,
03E23A602EAA1A4E004A8CEC /* FASearchResultView.swift in Sources */, 03E23A602EAA1A4E004A8CEC /* FASearchResultView.swift in Sources */,
03E23A612EAA1A4E004A8CEC /* FAHomeMustSeeContentCell.swift in Sources */, 03E23A612EAA1A4E004A8CEC /* FAHomeMustSeeContentCell.swift in Sources */,
03E23A622EAA1A4E004A8CEC /* FAShortDetailModel.swift in Sources */, 03E23A622EAA1A4E004A8CEC /* FAShortDetailModel.swift in Sources */,
03E23A632EAA1A4E004A8CEC /* FASearchResultCell.swift in Sources */, 03E23A632EAA1A4E004A8CEC /* FASearchResultCell.swift in Sources */,
03E23A642EAA1A4E004A8CEC /* FAMeTableViewHeaderView.swift in Sources */, 03E23A642EAA1A4E004A8CEC /* FAMeTableViewHeaderView.swift in Sources */,
031FDEE62EB34FBC00F4CAC7 /* FARemoveCollectAlert.swift in Sources */,
031FDECE2EB2006000F4CAC7 /* FACoinsPackClaimListCell.swift in Sources */,
03E23A652EAA1A4E004A8CEC /* FARewardCoinsViewController.swift in Sources */, 03E23A652EAA1A4E004A8CEC /* FARewardCoinsViewController.swift in Sources */,
03E23A662EAA1A4E004A8CEC /* FAStoreViewController.swift in Sources */, 03E23A662EAA1A4E004A8CEC /* FAStoreViewController.swift in Sources */,
03E23A672EAA1A4E004A8CEC /* FAMeListViewController.swift in Sources */, 03E23A672EAA1A4E004A8CEC /* FAMeListViewController.swift in Sources */,
@ -1964,7 +2072,9 @@
03E23A6B2EAA1A4E004A8CEC /* FARecommendPlayerCell.swift in Sources */, 03E23A6B2EAA1A4E004A8CEC /* FARecommendPlayerCell.swift in Sources */,
03E23A6C2EAA1A4E004A8CEC /* FAHomeNewView.swift in Sources */, 03E23A6C2EAA1A4E004A8CEC /* FAHomeNewView.swift in Sources */,
03E23A6D2EAA1A4E004A8CEC /* FARankingListViewController.swift in Sources */, 03E23A6D2EAA1A4E004A8CEC /* FARankingListViewController.swift in Sources */,
031FDEEA2EB35D2600F4CAC7 /* FACoinPackCanReceiveModel.swift in Sources */,
03E23A6E2EAA1A4E004A8CEC /* FARankingListCell.swift in Sources */, 03E23A6E2EAA1A4E004A8CEC /* FARankingListCell.swift in Sources */,
031FDED22EB2F69200F4CAC7 /* FALoginView.swift in Sources */,
03E23A6F2EAA1A4E004A8CEC /* FAHomeBannerCell.swift in Sources */, 03E23A6F2EAA1A4E004A8CEC /* FAHomeBannerCell.swift in Sources */,
03E23A702EAA1A4E004A8CEC /* FAGenresCell.swift in Sources */, 03E23A702EAA1A4E004A8CEC /* FAGenresCell.swift in Sources */,
03E23A712EAA1A4E004A8CEC /* FAHomeSectionTitleView.swift in Sources */, 03E23A712EAA1A4E004A8CEC /* FAHomeSectionTitleView.swift in Sources */,
@ -1981,12 +2091,14 @@
03E23A7A2EAA1A4E004A8CEC /* FASearchRecordView.swift in Sources */, 03E23A7A2EAA1A4E004A8CEC /* FASearchRecordView.swift in Sources */,
03E23A7B2EAA1A4E004A8CEC /* FACollectViewController.swift in Sources */, 03E23A7B2EAA1A4E004A8CEC /* FACollectViewController.swift in Sources */,
03E23A7C2EAA1A4E004A8CEC /* FAMeCell.swift in Sources */, 03E23A7C2EAA1A4E004A8CEC /* FAMeCell.swift in Sources */,
03E9A73A2EB45154000D1067 /* AppDelegate+FAApns.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 */, 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 */,
031FDED62EB300F400F4CAC7 /* FAThirdSignModel.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 */, 039CE60B2EAA31CB007B5EED /* FAStatAPI.swift in Sources */,
@ -2014,11 +2126,14 @@
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 */,
031FDEDC2EB3141E00F4CAC7 /* FASettingFooterView.swift in Sources */,
031FDEBE2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift in Sources */, 031FDEBE2EB0C99900F4CAC7 /* FANewVideoRechargeView.swift in Sources */,
03E239932EAA1A29004A8CEC /* Date+FAAdd.swift in Sources */, 03E239932EAA1A29004A8CEC /* Date+FAAdd.swift in Sources */,
031FDEDA2EB30D6E00F4CAC7 /* FASettingCell.swift in Sources */,
03E239942EAA1A29004A8CEC /* UserDefaults+FAAdd.swift in Sources */, 03E239942EAA1A29004A8CEC /* UserDefaults+FAAdd.swift in Sources */,
039CE6322EAB796F007B5EED /* FALabel.swift in Sources */, 039CE6322EAB796F007B5EED /* FALabel.swift in Sources */,
03E239952EAA1A29004A8CEC /* Font+FAAdd.swift in Sources */, 03E239952EAA1A29004A8CEC /* Font+FAAdd.swift in Sources */,
031FDEEE2EB3682000F4CAC7 /* FAVipRetainAlert.swift in Sources */,
03E239962EAA1A29004A8CEC /* FACryptorService.swift in Sources */, 03E239962EAA1A29004A8CEC /* FACryptorService.swift in Sources */,
03E239972EAA1A29004A8CEC /* FANetworkManager.swift in Sources */, 03E239972EAA1A29004A8CEC /* FANetworkManager.swift in Sources */,
03E239982EAA1A29004A8CEC /* UIScrollView+FARefresh.swift in Sources */, 03E239982EAA1A29004A8CEC /* UIScrollView+FARefresh.swift in Sources */,
@ -2035,6 +2150,7 @@
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 */,
031FDED42EB2FF4000F4CAC7 /* FALogin+Apple.swift in Sources */,
039CE6182EAB0E7E007B5EED /* FAStoreAPI.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 */,
@ -2046,9 +2162,11 @@
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 */,
031FDEC42EB1E45300F4CAC7 /* FACoinPackHeaderView.swift in Sources */,
03E239AE2EAA1A29004A8CEC /* SwiftUIExtension.swift in Sources */, 03E239AE2EAA1A29004A8CEC /* SwiftUIExtension.swift in Sources */,
039CE60E2EAA32A8007B5EED /* FAOpenAppModel.swift in Sources */, 039CE60E2EAA32A8007B5EED /* FAOpenAppModel.swift in Sources */,
03E239AF2EAA1A29004A8CEC /* FAAPI.swift in Sources */, 03E239AF2EAA1A29004A8CEC /* FAAPI.swift in Sources */,
031FDECA2EB1F8F200F4CAC7 /* FACoinsPackReceiveModel.swift in Sources */,
03E239B02EAA1A29004A8CEC /* FAWebView.swift in Sources */, 03E239B02EAA1A29004A8CEC /* FAWebView.swift in Sources */,
039CE61C2EAB0F29007B5EED /* FAIapOrderModel.swift in Sources */, 039CE61C2EAB0F29007B5EED /* FAIapOrderModel.swift in Sources */,
03E239B12EAA1A29004A8CEC /* FANetworkMonitor.swift in Sources */, 03E239B12EAA1A29004A8CEC /* FANetworkMonitor.swift in Sources */,
@ -2233,6 +2351,7 @@
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
INFOPLIST_KEY_NSCameraUsageDescription = "The APP needs to access your album to provide screenshots for feedback."; INFOPLIST_KEY_NSCameraUsageDescription = "The APP needs to access your album to provide screenshots for feedback.";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "The APP needs to access your album to provide screenshots for feedback."; INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "The APP needs to access your album to provide screenshots for feedback.";
INFOPLIST_KEY_NSUserTrackingUsageDescription = "We will use your advertising identifier (IDFA) to provide a personalized advertising experience.";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = ""; INFOPLIST_KEY_UIMainStoryboardFile = "";
@ -2273,6 +2392,7 @@
INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO;
INFOPLIST_KEY_NSCameraUsageDescription = "The APP needs to access your album to provide screenshots for feedback."; INFOPLIST_KEY_NSCameraUsageDescription = "The APP needs to access your album to provide screenshots for feedback.";
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "The APP needs to access your album to provide screenshots for feedback."; INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "The APP needs to access your album to provide screenshots for feedback.";
INFOPLIST_KEY_NSUserTrackingUsageDescription = "We will use your advertising identifier (IDFA) to provide a personalized advertising experience.";
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = ""; INFOPLIST_KEY_UIMainStoryboardFile = "";
@ -2317,6 +2437,43 @@
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
031FDEDD2EB3423000F4CAC7 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/facebook/facebook-ios-sdk";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 14.1.0;
};
};
03E9A7342EB44F26000D1067 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/firebase/firebase-ios-sdk";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 12.5.0;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
031FDEDE2EB3423000F4CAC7 /* FacebookLogin */ = {
isa = XCSwiftPackageProductDependency;
package = 031FDEDD2EB3423000F4CAC7 /* XCRemoteSwiftPackageReference "facebook-ios-sdk" */;
productName = FacebookLogin;
};
03E9A7352EB44F26000D1067 /* FirebaseMessaging */ = {
isa = XCSwiftPackageProductDependency;
package = 03E9A7342EB44F26000D1067 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
productName = FirebaseMessaging;
};
03E9A7372EB44F26000D1067 /* FirebasePerformance */ = {
isa = XCSwiftPackageProductDependency;
package = 03E9A7342EB44F26000D1067 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
productName = FirebasePerformance;
};
/* End XCSwiftPackageProductDependency section */
}; };
rootObject = F3LK276P33H73Y39H9X6VTDI /* Project object */; rootObject = F3LK276P33H73Y39H9X6VTDI /* Project object */;
} }

View File

@ -133,7 +133,7 @@ extension SceneDelegate {
var result = false var result = false
#if canImport(FacebookCore) #if canImport(FacebookCore)
let result = ApplicationDelegate.shared.application(UIApplication.shared, continue: userActivity) result = ApplicationDelegate.shared.application(UIApplication.shared, continue: userActivity)
#endif #endif
if !result { if !result {

View File

@ -0,0 +1,123 @@
//
// AppDelegate+FAApns.swift
// Fableon
//
// Created by on 2025/10/31.
//
import UIKit
import FirebaseMessaging
import SmartCodable
import FirebaseCore
extension SceneDelegate {
func requestFirebase() {
FirebaseApp.configure()
Messaging.messaging().delegate = self
}
func requestApns() {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.badge, .sound, .alert]) { grant, error in
if !grant {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.showApnsAlert()
}
}
FAAdjustStateManager.manager.apnsAuthorizationFinish = true
FATool.sceneDelegate?.retryHandleOpenAppMessage()
FATool.requestIDFAAuthorization(nil)
FAStatAPI.uploadApnsAuthorizationStatus()
}
UIApplication.shared.registerForRemoteNotifications()
}
func setBadgeCount(_ count: Int) {
if #available(iOS 16.0, *) {
UNUserNotificationCenter.current().setBadgeCount(count)
} else {
UIApplication.shared.applicationIconBadgeNumber = count
}
}
private func showApnsAlert() {
guard let date = UserDefaults.standard.object(forKey: kFAApnsAlertDefaultsKey) as? Date else {
UserDefaults.standard.set(Date(), forKey: kFAApnsAlertDefaultsKey)
return
}
#if !DEBUG
if date.fa_isToday { return }
UserDefaults.standard.set(Date(), forKey: kFAApnsAlertDefaultsKey)
#endif
let view = FAApnsAlert()
view.show()
}
}
extension AppDelegate {
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: any Error) {
}
}
//MARK: -------------- UNUserNotificationCenterDelegate --------------
extension SceneDelegate: UNUserNotificationCenterDelegate {
///APP
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.badge, .banner])
}
///app
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
setBadgeCount(0)
guard let userInfo: [String : Any] = response.notification.request.content.userInfo as? [String : Any] else {
completionHandler()
return
}
guard let model = FAOpenAppModel.deserialize(from: userInfo) else {
completionHandler()
return
}
FAStatAPI.requestStatApnsClick(id: model.message_id ?? "", title: response.notification.request.content.title)
if model.path == .videoDetail, let shortPlayId = model.short_play_id {
let vc = FAPlayerDetailViewController()
vc.shortPlayId = shortPlayId
FATool.topViewController?.navigationController?.pushViewController(vc, animated: true)
} else if model.path == .feedback {
let vc = FAAppWebViewController()
vc.webUrl = kFAFeedBackListWebUrl
FATool.topViewController?.navigationController?.pushViewController(vc, animated: true)
}
completionHandler()
}
}
//MARK: MessagingDelegate
extension SceneDelegate: MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
if let token = fcmToken {
FAStatAPI.requestUploadApnsDeviceToken(token: token)
}
}
}

View File

@ -9,6 +9,7 @@ import UIKit
import MJRefresh import MJRefresh
import IQKeyboardManagerSwift import IQKeyboardManagerSwift
import IQKeyboardToolbarManager import IQKeyboardToolbarManager
import FacebookCore
extension AppDelegate { extension AppDelegate {
@ -30,6 +31,11 @@ extension AppDelegate {
UINavigationBar.appearance().standardAppearance = appearance UINavigationBar.appearance().standardAppearance = appearance
registerAdjust() registerAdjust()
}
func fa_registThirdparty(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) {
ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)
} }
} }

View File

@ -6,6 +6,7 @@
// //
import UIKit import UIKit
import FacebookCore
@main @main
class AppDelegate: UIResponder, UIApplicationDelegate { class AppDelegate: UIResponder, UIApplicationDelegate {
@ -14,6 +15,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FANetworkMonitor.manager.startMonitoring() FANetworkMonitor.manager.startMonitoring()
fa_registThirdparty(application, didFinishLaunchingWithOptions: launchOptions)
self.fa_config() self.fa_config()
NotificationCenter.default.addObserver(self, selector: #selector(networkStatusDidChangeNotification), name: FANetworkMonitor.networkStatusDidChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(networkStatusDidChangeNotification), name: FANetworkMonitor.networkStatusDidChangeNotification, object: nil)

View File

@ -6,40 +6,54 @@
// //
import UIKit import UIKit
import YYText
class SceneDelegate: UIResponder, UIWindowSceneDelegate { class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow? var window: UIWindow?
private var onLineTimer: Timer?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return } guard let windowScene = (scene as? UIWindowScene) else { return }
NotificationCenter.default.addObserver(self, selector: #selector(networkStatusDidChangeNotification), name: FANetworkMonitor.networkStatusDidChangeNotification, object: nil)
FATool.sceneDelegate = self
FATool.windowScene = windowScene FATool.windowScene = windowScene
window = UIWindow(windowScene: windowScene) window = UIWindow(windowScene: windowScene)
window?.rootViewController = FATabBarController() window?.rootViewController = FATabBarController()
window?.makeKeyAndVisible() window?.makeKeyAndVisible()
FAAdjustStateManager.manager.isOpenApp = true FAAdjustStateManager.manager.isOpenApp = true
onLineTimer = Timer.scheduledTimer(timeInterval: 60 * 10, target: YYTextWeakProxy(target: self), selector: #selector(handleOnLine), userInfo: nil, repeats: true)
if let webpageURL = connectionOptions.userActivities.first?.webpageURL {
self.handleOpenAppMessage(webpageURL: webpageURL)
} else if let url = connectionOptions.urlContexts.first?.url {
self.handleOpenAppMessage(webpageURL: url)
}
requestFirebase()
requestApns()
} }
func sceneDidDisconnect(_ scene: UIScene) { func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
} }
func sceneDidBecomeActive(_ scene: UIScene) { func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state. self.handleOnLine()
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. FAStatAPI.requestEnterApp()
self.setBadgeCount(0)
} }
func sceneWillResignActive(_ scene: UIScene) { func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state. FAStatAPI.requestLeaveApp()
// This may occur due to temporary interruptions (ex. an incoming phone call).
} }
func sceneWillEnterForeground(_ scene: UIScene) { func sceneWillEnterForeground(_ scene: UIScene) {
FAStatAPI.uploadApnsAuthorizationStatus()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.handleOpenAppMessage(webpageURL: nil) self.handleOpenAppMessage(webpageURL: nil)
} }
@ -52,3 +66,14 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
} }
extension SceneDelegate {
@objc private func handleOnLine() {
FAStatAPI.requestStatOnLine()
}
@objc private func networkStatusDidChangeNotification() {
FATool.requestIDFAAuthorization(nil)
self.retryHandleOpenAppMessage()
}
}

View File

@ -2,10 +2,16 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
<key>com.apple.developer.associated-domains</key> <key>com.apple.developer.associated-domains</key>
<array> <array>
<string>applinks:fableon.go.link</string> <string>applinks:fableon.go.link</string>
<string>applinks:kuzt.adj.st</string> <string>applinks:kuzt.adj.st</string>
</array> </array>
<key>keychain-access-groups</key>
<array/>
</dict> </dict>
</plist> </plist>

View File

@ -7,3 +7,5 @@
let kFAWaitRestoreIAPDefaultsKey = "kFAWaitRestoreIAPDefaultsKey" let kFAWaitRestoreIAPDefaultsKey = "kFAWaitRestoreIAPDefaultsKey"
let kFAApnsAlertDefaultsKey = "kFAApnsAlertDefaultsKey"

View File

@ -22,7 +22,7 @@ extension Date {
} }
/// ///
var br_isToday: Bool { var fa_isToday: Bool {
get { get {
return Calendar.current.isDateInToday(self) return Calendar.current.isDateInToday(self)
} }

View File

@ -6,6 +6,7 @@
// //
import UIKit import UIKit
import SmartCodable
extension FAAPI { extension FAAPI {
/// [ "state" : isCollect, "id" : shortPlayId,] /// [ "state" : isCollect, "id" : shortPlayId,]
@ -249,6 +250,34 @@ struct FAAPI {
completer?(response.data) completer?(response.data)
} }
} }
}
//MARK:
extension FAAPI {
///
static func requestSignThirdLogin(model: FAThirdSignModel, completer: ((_ token: FATokenModel?) -> Void)?) {
FANetworkManager.manager.request(FABaseURL + "/customer/login",
method: .post,
parameters: model.toDictionary(),
isLoding: false,
isToast: true) { (response: FANetworkManager.Response<FATokenModel>) in
completer?(response.data)
}
}
static func requestLogout(completer: ((_ token: FATokenModel?) -> Void)?) {
FANetworkManager.manager.request(FABaseURL + "/customer/signout",
method: .post,
parameters: nil,
isLoding: true,
isToast: true) { (response: FANetworkManager.Response<FATokenModel>) in
completer?(response.data)
}
}
} }

View File

@ -84,4 +84,97 @@ class FAStatAPI: NSObject {
} }
} }
///APP
static func requestEnterApp() {
FANetworkManager.manager.request(FABaseURL + "/customer/enterTheApp",
method: .post,
parameters: nil,
isLoding: false,
isToast: false,
) { (response: FANetworkManager.Response<String>) in
}
}
///APP
static func requestLeaveApp() {
FANetworkManager.manager.request(FABaseURL + "/customer/leaveApp",
method: .post,
parameters: nil,
isLoding: false,
isToast: false,
) { (response: FANetworkManager.Response<String>) in
}
}
static func requestStatOnLine() {
FANetworkManager.manager.request(FABaseURL + "/customer/onLine",
method: .post,
parameters: nil,
isLoding: false,
isToast: false,
) { (response: FANetworkManager.Response<String>) in
}
}
static func requestUploadApnsAuthorizationStatus(_ status: Bool) {
let parameters = [
"is_open_notice" : status ? 1 : 0
]
FANetworkManager.manager.request(FABaseURL + "/customer/uploadNoticeStatus",
method: .post,
parameters: parameters,
isLoding: false,
isToast: false,
) { (response: FANetworkManager.Response<String>) in
}
}
static func requestStatApnsClick(id: String, title: String) {
let parameters = [
"message_id" : id,
"title" : title
]
FANetworkManager.manager.request(FABaseURL + "/message/sendReport",
method: .post,
parameters: parameters,
isLoding: false,
isToast: false,
) { (response: FANetworkManager.Response<String>) in
}
}
static func requestUploadApnsDeviceToken(token: String) {
let parameters = [
"fcm_token": token
]
FANetworkManager.manager.request(FABaseURL + "/customer/firebaseToken",
method: .post,
parameters: parameters,
isLoding: false,
isToast: false,
) { (response: FANetworkManager.Response<String>) in
}
}
}
extension FAStatAPI {
///
static func uploadApnsAuthorizationStatus() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
if settings.authorizationStatus == .authorized {
requestUploadApnsAuthorizationStatus(true)
} else if settings.authorizationStatus == .denied {
requestUploadApnsAuthorizationStatus(false)
}
}
}
} }

View File

@ -131,4 +131,70 @@ class FAStoreAPI: NSObject {
completer?(response.data) completer?(response.data)
} }
} }
///
static func requestCoinsPackData(completer: ((_ model: FACoinsPackModel?) -> Void)?) {
FANetworkManager.manager.request(FABaseURL + "/getReceiveDayCoinInfo",
method: .get,
parameters: nil,
isLoding: false,
isToast: true,
) { (response: FANetworkManager.Response<FACoinsPackModel>) in
completer?(response.data)
}
}
///
static func requestReceiveCoinsPackCoins(id: String?, completer: ((_ finish: Bool) -> Void)?) {
var parameters: [String : Any]? = nil
if let id = id {
parameters = [
"id" : id
]
}
FANetworkManager.manager.request(FABaseURL + "/receiveDayCoin",
method: .post,
parameters: parameters,
isLoding: true,
isToast: true,
) { (response: FANetworkManager.Response<FACoinsPackModel>) in
if response.isSuccess {
completer?(true)
} else {
completer?(false)
}
}
}
///
static func requestCoinBagCanReceiveInfo(completer: ((_ model: FACoinPackCanReceiveModel?) -> Void)?) {
FANetworkManager.manager.request(FABaseURL + "/getReceiveDayCoin",
method: .get,
parameters: nil,
isLoding: false,
isToast: true,
) { (response: FANetworkManager.Response<FACoinPackCanReceiveModel>) in
completer?(response.data)
}
}
///
static func requestVipRetainPayInfo(completer: ((_ model: FAPayAlertModel?) -> Void)?) {
FANetworkManager.manager.request(FABaseURL + "/getRetainVipPaySetting",
method: .get,
parameters: nil,
isLoding: true,
isToast: true,
) { (response: FANetworkManager.Response<FAPayAlertModel>) in
if let _ = response.data?.info {
completer?(response.data)
} else {
completer?(nil)
}
}
}
} }

View File

@ -16,3 +16,6 @@ let kFAFeedBackHomeWebUrl = FACampaignWebURL + "/pages/leave/index"
let kFAFeedBackListWebUrl = FACampaignWebURL + "/pages/leave/list" let kFAFeedBackListWebUrl = FACampaignWebURL + "/pages/leave/list"
/// ///
let kFAFeedBackDetailWebUrl = FACampaignWebURL + "/pages/leave/detail" let kFAFeedBackDetailWebUrl = FACampaignWebURL + "/pages/leave/detail"
///
let kFALogoutWebUrl = FACampaignWebURL + "/pages/setting/logout"

View File

@ -186,7 +186,8 @@ extension FANetworkManager {
"model" : UIDevice.current.machineModelName ?? "", "model" : UIDevice.current.machineModelName ?? "",
"idfa" : ASIdentifierManager.shared().advertisingIdentifier.uuidString, "idfa" : ASIdentifierManager.shared().advertisingIdentifier.uuidString,
"device-id" : FADeviceIDManager.shared.id, //id "device-id" : FADeviceIDManager.shared.id, //id
"device-gaid" : UIDevice.current.identifierForVendor?.uuidString ?? "" "device-gaid" : UIDevice.current.identifierForVendor?.uuidString ?? "",
"product-prefix" : FAIapManager.IAPPrefix
] ]
return HTTPHeaders(dic) return HTTPHeaders(dic)
} }

View File

@ -14,7 +14,7 @@ class FAAppWebViewController: FABaseWebViewController {
private var receiveDataCount = 0 private var receiveDataCount = 0
var theme: String? = "theme_1" var theme: String? = "theme_3"
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
@ -26,6 +26,8 @@ class FAAppWebViewController: FABaseWebViewController {
self.title = "Feedback".localized self.title = "Feedback".localized
} else if webUrl == kFAFeedBackDetailWebUrl { } else if webUrl == kFAFeedBackDetailWebUrl {
self.title = "Feedback Details".localized self.title = "Feedback Details".localized
} else if webUrl == kFALogoutWebUrl {
self.title = "Account Deletion".localized
} }
} }
@ -51,6 +53,7 @@ extension FAAppWebViewController {
"time_zone" : String.timeZone(), "time_zone" : String.timeZone(),
"lang" : FALocalized.manager.currentLocalizedKey, "lang" : FALocalized.manager.currentLocalizedKey,
"type" : "ios", "type" : "ios",
"device-id" : FADeviceIDManager.shared.id
] ]
if let theme = theme { if let theme = theme {

View File

@ -18,6 +18,8 @@ let kFAWebMessageOpenFeedbackList = "openFeedbackList"
let kFAWebMessageOpenFeedbackDetail = "openFeedbackDetail" let kFAWebMessageOpenFeedbackDetail = "openFeedbackDetail"
/// ///
let kFAWebMessageOpenPhotoPicker = "openPhotoPicker" let kFAWebMessageOpenPhotoPicker = "openPhotoPicker"
///
let kFAWebMessageAccountDeletionFinish = "accountLogout"
extension FABaseWebViewController { extension FABaseWebViewController {
@ -50,7 +52,8 @@ extension FABaseWebViewController {
let data = model.data let data = model.data
if type == "login" { if type == "login" {
// VPLoginManager.manager.openLogin() let view = FALoginView()
view.present(in: nil)
} else if type == "open_notify" { } else if type == "open_notify" {
// openNotify() // openNotify()
@ -69,6 +72,9 @@ extension FABaseWebViewController {
} }
} }
case kFAWebMessageAccountDeletionFinish:
self.navigationController?.popToRootViewController(animated: true)
default: default:
break break

View File

@ -38,6 +38,7 @@ class FAWebView: WKWebView {
kFAWebMessageOpenFeedbackList, kFAWebMessageOpenFeedbackList,
kFAWebMessageOpenFeedbackDetail, kFAWebMessageOpenFeedbackDetail,
kFAWebMessageOpenPhotoPicker, kFAWebMessageOpenPhotoPicker,
kFAWebMessageAccountDeletionFinish,
] ]

View File

@ -62,6 +62,17 @@ class FAHomeViewController: FAViewController {
return view return view
}() }()
private lazy var coinPackButton: FAHomeCoinsPackButton = {
let view = FAHomeCoinsPackButton()
view.addAction(UIAction(handler: { [weak self] _ in
guard let self = self else { return }
let vc = FACoinPackViewController()
self.navigationController?.pushViewController(vc, animated: true)
}), for: .touchUpInside)
return view
}()
deinit { deinit {
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
} }
@ -72,6 +83,8 @@ class FAHomeViewController: FAViewController {
fa_setupLayout() fa_setupLayout()
requestAllData(completer: nil) requestAllData(completer: nil)
requestCoinBagCanReceiveInfo()
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
@ -104,6 +117,7 @@ extension FAHomeViewController {
view.addSubview(searchButton) view.addSubview(searchButton)
view.addSubview(collectionView) view.addSubview(collectionView)
view.addSubview(playHistoryView) view.addSubview(playHistoryView)
view.addSubview(coinPackButton)
titleView.snp.makeConstraints { make in titleView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16) make.left.equalToSuperview().offset(16)
@ -125,6 +139,11 @@ extension FAHomeViewController {
make.centerX.equalToSuperview() make.centerX.equalToSuperview()
make.bottom.equalToSuperview().offset(-10) make.bottom.equalToSuperview().offset(-10)
} }
coinPackButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-21)
make.bottom.equalTo(playHistoryView.snp.top).offset(5)
}
} }
} }
@ -280,4 +299,22 @@ extension FAHomeViewController {
} }
} }
private func requestCoinBagCanReceiveInfo() {
FAStoreAPI.requestCoinBagCanReceiveInfo { [weak self] model in
guard let self = self else { return }
guard let model = model else { return }
let coins = model.coins ?? 0
if coins > 0 {
let view = FACoinsPackAlert()
view.coinsCount = coins
view.show(in: self.view)
view.clickHighlightButton = { [weak self] in
let vc = FACoinPackViewController()
self?.navigationController?.pushViewController(vc, animated: true)
}
}
}
}
} }

View File

@ -0,0 +1,50 @@
//
// FAHomeCoinsPackButton.swift
// Fableon
//
// Created by on 2025/10/30.
//
import UIKit
import SnapKit
class FAHomeCoinsPackButton: UIControl {
private lazy var bgImageView = UIImageView(image: UIImage(named: "Group 2072750470"))
private lazy var textBgImageView = UIImageView(image: UIImage(named: "按钮"))
private lazy var textLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 10, weight: .init(900)).withBoldItalic()
label.textColor = .FFFFFF
label.text = "Daily Coins".localized
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(bgImageView)
addSubview(textBgImageView)
textBgImageView.addSubview(textLabel)
bgImageView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
}
textBgImageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.bottom.equalToSuperview()
make.top.equalToSuperview().offset(50)
}
textLabel.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.centerX.equalToSuperview()
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,216 @@
//
// FACoinPackViewController.swift
// Fableon
//
// Created by on 2025/10/29.
//
import UIKit
import YYText
class FACoinPackViewController: FAViewController {
private var model: FACoinsPackModel? {
didSet {
headerView.model = model
claimView.dataArr = model?.receive_list ?? []
}
}
private var payDataModel : FAPayDateModel? {
didSet {
var arr: [FAPayItem] = []
payDataModel?.list_coins?.forEach {
if $0.buy_type == .subCoins {
arr.append($0)
}
}
self.buyView.dataArr = arr
}
}
private var payDataRequest: FAPayDataRequest?
private lazy var bgIconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "查看图片 25"))
return imageView
}()
private lazy var scrollView: FAScrollView = {
let scrollView = FAScrollView()
return scrollView
}()
private lazy var headerView: FACoinPackHeaderView = {
let view = FACoinPackHeaderView()
view.clickClaimButton = { [weak self] in
self?.requestReceiveCoins(nil)
}
return view
}()
private lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.spacing = 19
return stackView
}()
private lazy var claimView: FACoinsPackClaimListView = {
let view = FACoinsPackClaimListView()
view.clickClaimButton = { [weak self] id in
guard let self = self else { return }
self.requestReceiveCoins(id)
}
return view
}()
private lazy var buyView: FACoinsPackBuyView = {
let view = FACoinsPackBuyView()
view.buyFinishHandle = { [weak self] in
self?.requestCoinsPackData()
}
return view
}()
private lazy var tipView: UIView = {
let view = UIView()
view.addSubview(tipTitleLabel)
view.addSubview(tipTextLabel)
tipTitleLabel.snp.makeConstraints { make in
make.top.equalToSuperview()
make.left.equalToSuperview().offset(16)
}
tipTextLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.right.lessThanOrEqualToSuperview().offset(-16)
make.top.equalTo(tipTitleLabel.snp.bottom).offset(4)
make.bottom.equalToSuperview()
}
return view
}()
private lazy var tipTitleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .medium)
label.textColor = .FFFFFF.withAlphaComponent(0.5)
label.text = "coins_pack_tips_title".localized
return label
}()
private lazy var tipTextLabel: UILabel = {
let att = NSMutableAttributedString(string: "coins_pack_tips".localized)
att.yy_lineSpacing = 3
let label = UILabel()
label.font = .font(ofSize: 10, weight: .regular)
label.textColor = .FFFFFF.withAlphaComponent(0.5)
label.attributedText = att
label.numberOfLines = 0
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "My Refills".localized
payDataModel = FAIapManager.manager.payDateModel
fa_setupLayout()
requestCoinsPackData()
updateLayout()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
self.fa_setNavigationStyle()
}
private func updateLayout() {
stackView.fa_removeAllArrangedSubview()
if self.claimView.dataArr.count > 0 {
stackView.addArrangedSubview(self.claimView)
} else if self.buyView.dataArr.count > 0 {
stackView.addArrangedSubview(self.buyView)
}
stackView.addArrangedSubview(tipView)
}
}
extension FACoinPackViewController {
private func fa_setupLayout() {
view.addSubview(bgIconImageView)
view.addSubview(scrollView)
scrollView.addSubview(headerView)
scrollView.addSubview(stackView)
bgIconImageView.snp.makeConstraints { make in
make.right.equalToSuperview()
make.top.equalToSuperview()
}
scrollView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
make.bottom.equalToSuperview()
}
headerView.snp.makeConstraints { make in
make.left.centerX.equalToSuperview()
make.top.equalToSuperview()
}
stackView.snp.makeConstraints { make in
make.left.centerX.equalToSuperview()
make.top.equalTo(headerView.snp.bottom).offset(16)
make.bottom.equalToSuperview().offset(-(UIScreen.safeBottom + 10))
}
}
}
extension FACoinPackViewController {
private func requestCoinsPackData() {
FAStoreAPI.requestCoinsPackData { [weak self] model in
guard let self = self else { return }
guard let model = model else { return }
self.model = model
if (model.receive_list?.count ?? 0) == 0 {
self.requestPayData()
}
self.updateLayout()
}
}
private func requestPayData() {
self.payDataRequest = FAPayDataRequest()
self.payDataRequest?.requestProducts { [weak self] model in
guard let self = self else { return }
guard let model = model else { return }
self.payDataModel = model
self.updateLayout()
}
}
private func requestReceiveCoins(_ id: String?) {
FAStoreAPI.requestReceiveCoinsPackCoins(id: id) { [weak self] finish in
guard let self = self else { return }
self.requestCoinsPackData()
}
}
}

View File

@ -16,11 +16,12 @@ class FAMeListViewController: FAViewController, JXPagingViewListViewDelegate {
let arr = [ let arr = [
FAMeItemModel(type: .feedback, name: "Feedback".localized, icon: UIImage(named: "icon_feedback")), FAMeItemModel(type: .feedback, name: "Feedback".localized, icon: UIImage(named: "icon_feedback")),
FAMeItemModel(type: .about, name: "About".localized, icon: UIImage(named: "icon_about")), FAMeItemModel(type: .about, name: "About".localized, icon: UIImage(named: "icon_about")),
// FAMeItemModel(type: .setting, name: "Setting".localized, icon: UIImage(named: "icon_setting"))
FAMeItemModel(type: .privacyPolicy, name: "Privacy Policy".localized, icon: UIImage(named: "icon_privacy")), FAMeItemModel(type: .privacyPolicy, name: "Privacy Policy".localized, icon: UIImage(named: "icon_privacy")),
FAMeItemModel(type: .userAgreement, name: "User Agreement".localized, icon: UIImage(named: "icon_user")), FAMeItemModel(type: .userAgreement, name: "User Agreement".localized, icon: UIImage(named: "icon_user")),
FAMeItemModel(type: .visitWebsite, name: "Visit Website".localized, icon: UIImage(named: "icon_visit")), FAMeItemModel(type: .visitWebsite, name: "Visit Website".localized, icon: UIImage(named: "icon_visit")),
FAMeItemModel(type: .setting, name: "Setting".localized, icon: UIImage(named: "icon_setting"))
] ]
return arr return arr
}() }()

View File

@ -9,10 +9,34 @@ import UIKit
class FASettingViewController: FAViewController { class FASettingViewController: FAViewController {
private lazy var dataArr: [FAMeItemModel] = [
FAMeItemModel(type: .deleteAccount, name: "Account Deletion".localized, icon: nil)
]
private lazy var tableView: FATableView = {
let tableView = FATableView(frame: .zero, style: .plain)
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = 48
tableView.contentInset = .init(top: 25, left: 0, bottom: 0, right: 0)
tableView.separatorStyle = .none
tableView.register(UINib(nibName: "FASettingCell", bundle: nil), forCellReuseIdentifier: "cell")
return tableView
}()
private lazy var footerView: FASettingFooterView = {
let view = FASettingFooterView(frame: .init(x: 0, y: 0, width: UIScreen.width, height: 60))
return view
}()
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
self.title = "Settings".localized self.title = "Settings".localized
NotificationCenter.default.addObserver(self, selector: #selector(userInfoUpdateNotification), name: FALogin.userInfoUpdateNotification, object: nil)
userInfoUpdateNotification()
fa_setupLayout()
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
@ -21,6 +45,51 @@ class FASettingViewController: FAViewController {
self.fa_setNavigationStyle() self.fa_setNavigationStyle()
} }
@objc private func userInfoUpdateNotification() {
if FALogin.manager.isLogin {
tableView.tableFooterView = footerView
} else {
tableView.tableFooterView = nil
}
}
} }
extension FASettingViewController {
private func fa_setupLayout() {
view.addSubview(tableView)
tableView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
make.bottom.equalToSuperview()
}
}
}
//MARK: UITableViewDelegate UITableViewDataSource
extension FASettingViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = dataArr[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FASettingCell
cell.titleLabel.text = item.name
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.dataArr.count
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let vc = FAAppWebViewController()
vc.webUrl = kFALogoutWebUrl
vc.theme = "theme_4"
self.navigationController?.pushViewController(vc, animated: true)
}
}

View File

@ -22,6 +22,7 @@ struct FAMeItemModel {
case purchaseRecords case purchaseRecords
/// ///
case rewardCoins case rewardCoins
case deleteAccount
} }

View File

@ -0,0 +1,266 @@
//
// FACoinPackHeaderView.swift
// Fableon
//
// Created by on 2025/10/29.
//
import UIKit
class FACoinPackHeaderView: UIView {
var clickClaimButton: (() -> Void)?
var model: FACoinsPackModel? {
didSet {
coinsView1.coins = model?.week_max_total
coinsView2.coins = model?.week_total
activeCountLabel.text = "\(model?.receive_count ?? 0)"
if let coin = model?.receive_coins, coin > 0 {
claimButton.isEnabled = true
} else {
claimButton.isEnabled = false
}
claimButton.setNeedsUpdateConfiguration()
}
}
private lazy var titleView: FACoinPackTitleView = {
let view = FACoinPackTitleView()
view.dotColor = .C_5_DDF_5
view.font = .font(ofSize: 14, weight: .regular)
view.textColors = [UIColor.C_5_DDF_5.cgColor, UIColor.C_5_DDF_5.cgColor]
view.text = "REWARDS OVERVIEW".localized
return view
}()
private lazy var coinsView1: CoinsView = {
let view = CoinsView()
view.title = "Weekly Total".localized
view.coins = 0
return view
}()
private lazy var coinsView2: CoinsView = {
let view = CoinsView()
view.title = "Claimable Coins".localized
view.coins = 0
return view
}()
private lazy var lineView1: UIView = {
let view = UIImageView(image: UIImage(named: "间隔线"))
return view
}()
private lazy var lineView2: UIView = {
let view = UIImageView(image: UIImage(named: "横间隔虚线"))
return view
}()
private lazy var activeCountTitleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .regular)
label.textColor = .FFFFFF.withAlphaComponent(0.5)
label.text = "Active Refills".localized + ": "
return label
}()
private lazy var activeCountLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .medium)
label.textColor = ._53_A_2_F_1
label.text = "0"
return label
}()
private lazy var claimButton: UIButton = {
let config = UIButton.Configuration.plain()
let button = FAGradientButton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in
self?.clickClaimButton?()
}))
button.isEnabled = false
button.layer.cornerRadius = 24
button.layer.masksToBounds = true
button.fa_locations = [0, 1]
button.fa_startPoint = .init(x: 0, y: 0.5)
button.fa_endPoint = .init(x: 1, y: 0.5)
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
guard let button = button as? FAGradientButton else { return }
if button.isEnabled {
button.fa_colors = [UIColor._53_A_2_F_1.cgColor, UIColor.C_5_DDF_5.cgColor]
let coinImage = UIImage(named: "coins_icon_03")!
let coinText = NSTextAttachment(image: coinImage)
coinText.bounds = .init(x: 0, y: -2.5, width: coinImage.size.width, height: coinImage.size.height)
let coinAtt = AttributedString(NSAttributedString(attachment: coinText))
let textAtt = AttributedString("Claim All".localized + " ", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor.FFFFFF
]))
let countAtt = AttributedString(" \(0)".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor.FFFFFF
]))
button.configuration?.attributedTitle = textAtt + coinAtt + countAtt
} else {
button.fa_colors = [UIColor._6_D_6_D_6_D.cgColor, UIColor._6_D_6_D_6_D.cgColor]
button.configuration?.attributedTitle = AttributedString("Get a Refill to Claim".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor.FFFFFF.withAlphaComponent(0.5)
]))
}
}
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension FACoinPackHeaderView {
private func fa_setupLayout() {
addSubview(titleView)
addSubview(coinsView1)
addSubview(coinsView2)
addSubview(lineView1)
addSubview(activeCountTitleLabel)
addSubview(activeCountLabel)
addSubview(claimButton)
addSubview(lineView2)
titleView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.top.equalToSuperview().offset(20)
}
coinsView1.snp.makeConstraints { make in
make.left.equalToSuperview().offset(28)
make.top.equalTo(titleView.snp.bottom).offset(23)
}
lineView1.snp.makeConstraints { make in
make.centerY.equalTo(coinsView1)
make.left.equalTo(coinsView1.snp.right).offset(18)
}
coinsView2.snp.makeConstraints { make in
make.centerY.equalTo(lineView1)
make.left.equalTo(lineView1.snp.right).offset(18)
}
activeCountTitleLabel.snp.makeConstraints { make in
make.left.equalTo(coinsView1)
make.top.equalTo(coinsView1.snp.bottom).offset(23)
}
activeCountLabel.snp.makeConstraints { make in
make.left.equalTo(activeCountTitleLabel.snp.right)
make.centerY.equalTo(activeCountTitleLabel)
}
claimButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(23)
make.centerX.equalToSuperview()
make.top.equalTo(activeCountTitleLabel.snp.bottom).offset(12)
make.height.equalTo(48)
make.bottom.equalToSuperview().offset(-16)
}
lineView2.snp.makeConstraints { make in
make.left.equalTo(claimButton)
make.right.equalTo(claimButton)
make.bottom.equalToSuperview()
}
}
}
extension FACoinPackHeaderView {
class CoinsView: UIView {
var title: String? {
didSet {
titleLabel.text = title
}
}
var coins: Int? {
didSet {
coinsLabel.text = "\(coins ?? 0)"
}
}
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .bold)
label.textColor = .FFFFFF.withAlphaComponent(0.5)
return label
}()
private lazy var iconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "coins_icon_09"))
return imageView
}()
private lazy var coinsLabel: UILabel = {
let label = FALabel()
label.font = .font(ofSize: 18, weight: .bold)
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)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(titleLabel)
addSubview(iconImageView)
addSubview(coinsLabel)
titleLabel.snp.makeConstraints { make in
make.left.equalToSuperview()
make.top.equalToSuperview()
make.right.lessThanOrEqualToSuperview()
}
iconImageView.snp.makeConstraints { make in
make.left.equalToSuperview()
make.top.equalTo(titleLabel.snp.bottom).offset(4)
make.bottom.equalToSuperview()
}
coinsLabel.snp.makeConstraints { make in
make.centerY.equalTo(iconImageView)
make.left.equalTo(iconImageView.snp.right).offset(4)
make.right.lessThanOrEqualToSuperview()
}
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}

View File

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

View File

@ -0,0 +1,112 @@
//
// FACoinsPackBuyView.swift
// Fableon
//
// Created by on 2025/10/29.
//
import UIKit
class FACoinsPackBuyView: UIView {
var buyFinishHandle: (() -> Void)?
var dataArr: [FAPayItem] = []
private lazy var titleView: FACoinPackTitleView = {
let view = FACoinPackTitleView()
view.dotColor = .C_5_DDF_5
view.textColors = [UIColor.C_5_DDF_5.cgColor, UIColor.C_5_DDF_5.cgColor]
view.font = .font(ofSize: 14, weight: .regular)
view.text = "Weekly Refill".localized.uppercased()
return view
}()
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.itemSize = .init(width: UIScreen.width - 32, height: 84)
layout.minimumLineSpacing = 12
return layout
}()
private lazy var collectionView: FACollectionView = {
let collectionView = FACollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.addObserver(self, forKeyPath: "contentSize", context: nil)
collectionView.register(FAStoreCoinsPackCell.self, forCellWithReuseIdentifier: "cell")
return collectionView
}()
deinit {
collectionView.removeObserver(self, forKeyPath: "contentSize")
}
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "contentSize" {
let height = self.collectionView.contentSize.height + 1
collectionView.snp.updateConstraints { make in
make.height.equalTo(height)
}
}
}
}
extension FACoinsPackBuyView {
private func fa_setupLayout() {
addSubview(titleView)
addSubview(collectionView)
titleView.snp.makeConstraints { make in
make.top.equalToSuperview()
make.centerX.equalToSuperview()
}
collectionView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(30)
make.height.equalTo(1)
make.bottom.equalToSuperview()
}
}
}
//MARK: UICollectionViewDelegate UICollectionViewDataSource
extension FACoinsPackBuyView: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! FAStoreCoinsPackCell
cell.model = self.dataArr[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataArr.count
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let model = self.dataArr[indexPath.row]
let view = 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)
}
}

View File

@ -0,0 +1,280 @@
//
// FACoinsPackClaimListCell.swift
// Fableon
//
// Created by on 2025/10/29.
//
import UIKit
import YYText
class FACoinsPackClaimListCell: UICollectionViewCell {
var clickClaimButton: ((_ id: String?) -> Void)?
var model: FACoinsPackReceiveModel? {
didSet {
coinsView1.coins = model?.week_max_total
coinsView2.coins = model?.week_remaining_total
claimButton.isEnabled = (model?.receive_coins ?? 0) > 0
claimButton.setNeedsUpdateConfiguration()
let titleAtt = NSMutableAttributedString(string: "\(model?.title ?? "")")
titleAtt.yy_color = .FFFFFF
titleAtt.yy_font = .font(ofSize: 14, weight: .bold)
let dayAtt = NSMutableAttributedString(string: " (Day \(model?.day_text ?? ""))")
dayAtt.yy_color = ._20_A_1_FF
dayAtt.yy_font = .font(ofSize: 14, weight: .regular)
titleAtt.append(dayAtt)
titleLabel.attributedText = titleAtt
}
}
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 bgIconImageView1 = UIImageView(image: UIImage(named: "coin_attachment_01"))
private lazy var bgIconImageView2 = UIImageView(image: UIImage(named: "coin_attachment_03"))
private lazy var bgIconImageView4 = UIImageView(image: UIImage(named: "coin_attachment_04"))
private lazy var bgIconImageView5 = UIImageView(image: UIImage(named: "coin_attachment_05"))
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.numberOfLines = 0
return label
}()
private lazy var lineView1 = UIImageView(image: UIImage(named: "横间隔虚线"))
private lazy var lineView2 = UIImageView(image: UIImage(named: "间隔线"))
private lazy var coinsView1: CoinsView = {
let view = CoinsView()
view.title = "Total Reward".localized
return view
}()
private lazy var coinsView2: CoinsView = {
let view = CoinsView()
view.title = "Remaining".localized
return view
}()
private lazy var claimButton: UIButton = {
var config = UIButton.Configuration.plain()
config.titleAlignment = .center
let button = FAGradientButton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
self.clickClaimButton?(self.model?.id)
}))
button.layer.cornerRadius = 24
button.layer.masksToBounds = true
button.fa_locations = [0, 1]
button.fa_startPoint = .init(x: 0, y: 0.5)
button.fa_endPoint = .init(x: 1, y: 0.5)
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
guard let button = button as? FAGradientButton else { return }
if button.isEnabled {
button.fa_colors = [UIColor._53_A_2_F_1.cgColor, UIColor.C_5_DDF_5.cgColor]
let coinImage = UIImage(named: "coins_icon_03")!
let coinText = NSTextAttachment(image: coinImage)
coinText.bounds = .init(x: 0, y: -2.5, width: coinImage.size.width, height: coinImage.size.height)
let coinAtt = AttributedString(NSAttributedString(attachment: coinText))
let countAtt = AttributedString(" \(self.model?.receive_coins ?? 0)", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 12, weight: .bold),
.foregroundColor : UIColor._000000.withAlphaComponent(0.5)
]))
button.configuration?.attributedTitle = AttributedString("Claim".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor._114_CEE
]))
button.configuration?.attributedSubtitle = coinAtt + countAtt
} else {
button.fa_colors = [UIColor.BCBCBC.cgColor, UIColor.BCBCBC.cgColor]
button.configuration?.attributedTitle = AttributedString("Claim".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor.FFFFFF_0_8
]))
button.configuration?.attributedSubtitle = nil
}
}
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension FACoinsPackClaimListCell {
private func fa_setupLayout() {
contentView.addSubview(bgView)
bgView.addSubview(bgIconImageView5)
bgView.addSubview(bgIconImageView4)
bgView.addSubview(bgIconImageView1)
bgView.addSubview(bgIconImageView2)
bgView.addSubview(titleLabel)
bgView.addSubview(lineView1)
bgView.addSubview(lineView2)
bgView.addSubview(coinsView1)
bgView.addSubview(coinsView2)
bgView.addSubview(claimButton)
bgView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
bgIconImageView1.snp.makeConstraints { make in
make.left.top.equalToSuperview()
}
bgIconImageView2.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.right.equalToSuperview().offset(-8)
}
bgIconImageView4.snp.makeConstraints { make in
make.top.equalToSuperview()
make.centerX.equalTo(bgIconImageView2)
}
bgIconImageView5.snp.makeConstraints { make in
make.bottom.equalToSuperview()
make.left.equalToSuperview().offset(0)
}
titleLabel.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.centerY.equalTo(self.bgView.snp.top).offset(25)
make.right.lessThanOrEqualToSuperview().offset(-12)
}
lineView1.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(48)
}
coinsView1.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.bottom.equalToSuperview().offset(-18)
}
lineView2.snp.makeConstraints { make in
make.centerY.equalTo(coinsView1)
make.left.equalTo(coinsView1.snp.right).offset(12)
}
coinsView2.snp.makeConstraints { make in
make.centerY.equalTo(coinsView1)
make.left.equalTo(lineView2.snp.right).offset(12)
}
claimButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-12)
make.centerY.equalTo(coinsView1)
make.height.equalTo(48)
make.width.equalTo(120)
}
}
}
extension FACoinsPackClaimListCell {
class CoinsView: UIView {
var title: String? {
didSet {
titleLabel.text = title
}
}
var coins: Int? {
didSet {
coinsLabel.text = "\(coins ?? 0)"
}
}
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .bold)
label.textColor = .DFEFFF
return label
}()
private lazy var iconImageView = UIImageView(image: UIImage(named: "coins_icon_03"))
private lazy var coinsLabel: UILabel = {
let label = FALabel()
label.font = .font(ofSize: 14, weight: .bold)
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)
label.text = "0"
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(titleLabel)
addSubview(iconImageView)
addSubview(coinsLabel)
titleLabel.snp.makeConstraints { make in
make.left.equalToSuperview()
make.top.equalToSuperview()
make.right.lessThanOrEqualToSuperview()
}
iconImageView.snp.makeConstraints { make in
make.left.equalToSuperview()
make.bottom.equalToSuperview()
make.top.equalTo(titleLabel.snp.bottom).offset(8)
}
coinsLabel.snp.makeConstraints { make in
make.centerY.equalTo(iconImageView)
make.left.equalTo(iconImageView.snp.right).offset(4)
make.right.lessThanOrEqualToSuperview()
}
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}

View File

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

View File

@ -0,0 +1,121 @@
//
// FALoginView.swift
// Fableon
//
// Created by on 2025/10/30.
//
import UIKit
class FALoginView: FAPanModalContentView {
private lazy var bgView = UIImageView(image: UIImage(named: "弹窗背景色"))
private lazy var bgIconImageView = UIImageView(image: UIImage(named: "Ellipse 873"))
private lazy var logoImageView = UIImageView(image: UIImage(named: "logo_image_02"))
private lazy var stackView: UIStackView = {
let view = UIStackView(arrangedSubviews: [appleButton, facebookButton])
view.axis = .vertical
view.spacing = 18
return view
}()
private lazy var appleButton: UIButton = {
let button = self.createButton(image: UIImage(named: "apple_logo_icon_01"), title: "Login with Apple".localized)
button.addAction(UIAction(handler: { [weak self] _ in
self?.login(type: .apple)
}), for: .touchUpInside)
return button
}()
private lazy var facebookButton: UIButton = {
let button = self.createButton(image: UIImage(named: "facebook_logo_icon_01"), title: "Login with Facebook".localized)
button.addAction(UIAction(handler: { [weak self] _ in
self?.login(type: .faceBook)
}), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear
contentHeight = 300
fa_setupLayout()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func cornerRadius() -> CGFloat {
return 0
}
private func createButton(image: UIImage?, title: String) -> UIButton {
var config = UIButton.Configuration.plain()
config.image = image
config.imagePadding = 12
config.attributedTitle = AttributedString(title, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor._333333
]))
config.background.backgroundColor = .FFFFFF
let button = UIButton(configuration: config)
button.layer.cornerRadius = 24
button.layer.masksToBounds = true
button.snp.makeConstraints { make in
make.height.equalTo(48)
}
return button
}
private func login(type: FALogin.LoginType) {
FAHUD.show()
FALogin.manager.thirdLogin(type: type, presentingViewController: nil) { [weak self] isFinish in
FAHUD.dismiss()
guard let self = self else { return }
if isFinish {
Task {
await self.dismiss(animated: true)
}
}
}
}
}
extension FALoginView {
private func fa_setupLayout() {
addSubview(bgView)
bgView.addSubview(bgIconImageView)
bgView.addSubview(logoImageView)
addSubview(stackView)
bgView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
bgIconImageView.snp.makeConstraints { make in
make.top.centerX.equalToSuperview()
}
logoImageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(20)
}
stackView.snp.makeConstraints { make in
make.top.equalToSuperview().offset(112)
make.left.equalToSuperview().offset(37)
make.centerX.equalToSuperview()
}
}
}

View File

@ -0,0 +1,95 @@
//
// FAMeCoinsPackButton.swift
// Fableon
//
// Created by on 2025/10/29.
//
import UIKit
class FAMeCoinsPackButton: UIControl {
private lazy var bgView: FAGradientView = {
let view = FAGradientView()
view.isUserInteractionEnabled = false
view.fa_colors = [UIColor._114_CEE.cgColor, UIColor._000000.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._629_DFA.cgColor
return view
}()
private lazy var iconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "礼物11"))
return imageView
}()
private lazy var titleLabel: FALabel = {
let label = FALabel()
label.font = .font(ofSize: 14, weight: .init(900)).withBoldItalic()
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: 0.7, y: 0.5)
label.text = "Daily reward ready!".localized
return label
}()
private lazy var subtitleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .medium).withBoldItalic()
label.textColor = .FFFFFF
label.text = "Claim your rewards now.".localized
return label
}()
private lazy var indicatorImageView = UIImageView(image: UIImage(named: "arrow"))
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension FAMeCoinsPackButton {
private func fa_setupLayout() {
addSubview(bgView)
bgView.addSubview(iconImageView)
bgView.addSubview(titleLabel)
bgView.addSubview(subtitleLabel)
bgView.addSubview(indicatorImageView)
bgView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
iconImageView.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.left.equalToSuperview().offset(12)
}
titleLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(13)
make.left.equalTo(iconImageView.snp.right).offset(8)
}
subtitleLabel.snp.makeConstraints { make in
make.left.equalTo(titleLabel)
make.bottom.equalToSuperview().offset(-13)
}
indicatorImageView.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.right.equalToSuperview().offset(-12)
}
}
}

View File

@ -9,7 +9,7 @@ import UIKit
class FAMeHeaderView: UIView { class FAMeHeaderView: UIView {
var contentHeight: CGFloat = 40 + 66 + 40 + 28 + 24 var contentHeight: CGFloat = 40 + 66 + 40 + 28 + 16 + 60 + 16
var userInfo: FAUserInfo? { var userInfo: FAUserInfo? {
didSet { didSet {
@ -22,6 +22,8 @@ class FAMeHeaderView: UIView {
coinsView.count = userInfo?.coin_left_total ?? 0 coinsView.count = userInfo?.coin_left_total ?? 0
bonusCoinsView.count = userInfo?.send_coin_left_total ?? 0 bonusCoinsView.count = userInfo?.send_coin_left_total ?? 0
loginButton.isHidden = FALogin.manager.isLogin
} }
} }
@ -69,6 +71,32 @@ class FAMeHeaderView: UIView {
return view return view
}() }()
private lazy var coinPackButton: FAMeCoinsPackButton = {
let button = FAMeCoinsPackButton()
button.addAction(UIAction(handler: { [weak self] _ in
guard let self = self else { return }
let vc = FACoinPackViewController()
self.viewController?.navigationController?.pushViewController(vc, animated: true)
}), for: .touchUpInside)
return button
}()
private lazy var loginButton: UIButton = {
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
let view = FALoginView()
view.present(in: nil)
}))
button.layer.cornerRadius = 14
button.layer.masksToBounds = true
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.FFFEE_9.cgColor
button.setTitle("Log in".localized, for: .normal)
button.setTitleColor(.FFFFFF, for: .normal)
button.titleLabel?.font = .font(ofSize: 14, weight: .medium)
return button
}()
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
@ -88,6 +116,8 @@ extension FAMeHeaderView {
addSubview(idLabel) addSubview(idLabel)
addSubview(coinsView) addSubview(coinsView)
addSubview(bonusCoinsView) addSubview(bonusCoinsView)
addSubview(coinPackButton)
addSubview(loginButton)
avatarImageView.snp.makeConstraints { make in avatarImageView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16) make.left.equalToSuperview().offset(16)
@ -115,6 +145,20 @@ extension FAMeHeaderView {
make.top.equalTo(coinsView) make.top.equalTo(coinsView)
make.left.equalTo(coinsView.snp.right).offset(40) make.left.equalTo(coinsView.snp.right).offset(40)
} }
coinPackButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.top.equalTo(coinsView.snp.bottom).offset(16)
make.height.equalTo(60)
}
loginButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-16)
make.centerY.equalTo(avatarImageView)
make.width.equalTo(76)
make.height.equalTo(28)
}
} }
} }

View File

@ -0,0 +1,26 @@
//
// FASettingCell.swift
// Fableon
//
// Created by on 2025/10/30.
//
import UIKit
class FASettingCell: FATableViewCell {
@IBOutlet weak var titleLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="24128" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24063"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="91" id="KGk-i7-Jjw" customClass="FASettingCell" customModule="Fableon" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="362" height="91"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="362" height="91"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eS3-W5-VR6">
<rect key="frame" x="16" y="0.0" width="330" height="91"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="A4A-hn-CZz">
<rect key="frame" x="16" y="37" width="36" height="17"/>
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="14"/>
<color key="textColor" name="#FFFFFF"/>
<nil key="highlightedColor"/>
</label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" image="Frame 3015" translatesAutoresizingMaskIntoConstraints="NO" id="f2b-1S-BSh">
<rect key="frame" x="304" y="40.666666666666664" width="10" height="10"/>
</imageView>
</subviews>
<color key="backgroundColor" name="#5CA8FF_0.2"/>
<constraints>
<constraint firstItem="A4A-hn-CZz" firstAttribute="leading" secondItem="eS3-W5-VR6" secondAttribute="leading" constant="16" id="OqO-BA-gyr"/>
<constraint firstItem="A4A-hn-CZz" firstAttribute="centerY" secondItem="eS3-W5-VR6" secondAttribute="centerY" id="WQz-Lc-m7d"/>
<constraint firstAttribute="trailing" secondItem="f2b-1S-BSh" secondAttribute="trailing" constant="16" id="ppx-bH-uVc"/>
<constraint firstItem="f2b-1S-BSh" firstAttribute="centerY" secondItem="eS3-W5-VR6" secondAttribute="centerY" id="sJc-Wi-mjw"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="12"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="boolean" keyPath="masksToBounds" value="YES"/>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="eS3-W5-VR6" secondAttribute="bottom" id="7pH-iM-ihT"/>
<constraint firstItem="eS3-W5-VR6" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" id="MKW-Dz-3xN"/>
<constraint firstAttribute="trailing" secondItem="eS3-W5-VR6" secondAttribute="trailing" constant="16" id="bZG-Qv-nXN"/>
<constraint firstItem="eS3-W5-VR6" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="16" id="fSl-zo-SIR"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="titleLabel" destination="A4A-hn-CZz" id="yn4-Ke-t3P"/>
</connections>
<point key="canvasLocation" x="117.55725190839694" y="119.36619718309859"/>
</tableViewCell>
</objects>
<resources>
<image name="Frame 3015" width="10" height="10"/>
<namedColor name="#5CA8FF_0.2">
<color red="0.36078431372549019" green="0.6588235294117647" blue="1" alpha="0.20000000298023224" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
<namedColor name="#FFFFFF">
<color red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

View File

@ -0,0 +1,48 @@
//
// FASettingFooterView.swift
// Fableon
//
// Created by on 2025/10/30.
//
import UIKit
class FASettingFooterView: UIView {
private lazy var logoutButton: UIButton = {
let button = FAGradientButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
FALogin.manager.logout(completer: nil)
}))
button.layer.cornerRadius = 24
button.layer.masksToBounds = true
button.fa_colors = [UIColor.BEDFFF.cgColor, UIColor._52_A_2_F_1.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("Log out".localized, for: .normal)
button.setTitleColor(._000000, for: .normal)
button.titleLabel?.font = .font(ofSize: 18, weight: .semibold)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(logoutButton)
logoutButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.bottom.equalToSuperview()
make.height.equalTo(48)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -122,6 +122,22 @@ class FACollectViewController: FAViewController {
} }
} }
private func removeCollect(_ indexPath: IndexPath) {
let alert = FARemoveCollectAlert()
alert.show()
alert.clickHighlightButton = { [weak self] in
guard let self = self else { return }
let model = self.dataArr[indexPath.row]
guard let shortPlayId = model.short_play_id else { return }
FAAPI.requestShortCollect(isCollect: false, shortPlayId: shortPlayId, videoId: model.short_play_video_id) { [weak self] in
guard let self = self else { return }
self.dataArr.remove(at: indexPath.row)
self.collectionView.deleteItems(at: [indexPath])
}
}
}
} }
extension FACollectViewController { extension FACollectViewController {
@ -147,13 +163,10 @@ extension FACollectViewController: UICollectionViewDataSource, UICollectionViewD
cell.clickDeleteButton = { [weak self] cell in cell.clickDeleteButton = { [weak self] cell in
guard let self = self else { return } guard let self = self else { return }
guard let indexPath = self.collectionView.indexPath(for: cell) else { return } guard let indexPath = self.collectionView.indexPath(for: cell) else { return }
guard let shortPlayId = cell.model?.short_play_id else { return }
FAAPI.requestShortCollect(isCollect: false, shortPlayId: shortPlayId, videoId: cell.model?.short_play_video_id) { [weak self] in self.removeCollect(indexPath)
guard let self = self else { return }
self.dataArr.remove(at: indexPath.row)
self.collectionView.deleteItems(at: [indexPath])
}
} }
return cell return cell
} }

View File

@ -173,12 +173,24 @@ extension FAEpSelectorView: UICollectionViewDelegate, UICollectionViewDataSource
} }
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
model?.episodeList?.count ?? 0 return model?.episodeList?.count ?? 0
} }
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let epList = self.model?.episodeList else { return }
if self.selectedIndex == indexPath.row { return } if self.selectedIndex == indexPath.row { return }
self.selectedIndex = indexPath.row
let lastIndex = indexPath.row - 1
var lastIsLock = false
if lastIndex > 0 && lastIndex < epList.count {
let lastModel = epList[lastIndex]
lastIsLock = lastModel.is_lock ?? false
}
if lastIsLock {
FAToast.show(text: "buy_fail_toast_02".localized)
return
}
self.didSelected?(indexPath.row) self.didSelected?(indexPath.row)
self.dismiss(animated: true) { self.dismiss(animated: true) {

View File

@ -11,6 +11,7 @@ import HWPanModal
class FAOldVideoRechargeView: FAPanModalContentView { class FAOldVideoRechargeView: FAPanModalContentView {
var buyFinishHandle: (() -> Void)? var buyFinishHandle: (() -> Void)?
var didDismissHandle: (() -> Void)?
var model: FAPayDateModel? { var model: FAPayDateModel? {
didSet { didSet {
@ -35,7 +36,7 @@ class FAOldVideoRechargeView: FAPanModalContentView {
} }
} }
// self.stackView.addArrangedSubview(self.tipView) self.stackView.addArrangedSubview(self.tipView)
self.setNeedsLayoutUpdate() self.setNeedsLayoutUpdate()
} }
@ -62,6 +63,7 @@ class FAOldVideoRechargeView: FAPanModalContentView {
Task { Task {
await self.dismiss(animated: true) await self.dismiss(animated: true)
} }
self.didDismissHandle?()
})) }))
button.setImage(UIImage(named: "close_icon_02"), for: .normal) button.setImage(UIImage(named: "close_icon_02"), for: .normal)
return button return button
@ -123,6 +125,42 @@ class FAOldVideoRechargeView: FAPanModalContentView {
return label return label
}() }()
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
}()
deinit { deinit {
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
} }

View File

@ -171,6 +171,10 @@ extension FAShortDetailViewModel {
guard let self = self else { return } guard let self = self else { return }
self.requestDetailData(indexPath: self.currentIndexPath, completer: nil) self.requestDetailData(indexPath: self.currentIndexPath, completer: nil)
} }
view.didDismissHandle = { [weak self] in
guard let self = self else { return }
self._showVipRetainAlert(videoInfo)
}
view.present(in: nil) view.present(in: nil)
self.popView = view self.popView = view
} }
@ -182,6 +186,25 @@ extension FAShortDetailViewModel {
view.present(in: nil) view.present(in: nil)
self.popView = view self.popView = view
} }
private func _showVipRetainAlert(_ videoInfo: FAVideoInfoModel) {
payDataRequest = FAPayDataRequest()
payDataRequest?.requestVipRetainPayInfo { [weak self] model in
guard let self = self else { return }
guard let model = model else { return }
let view = FAVipRetainAlert()
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.show(in: FATool.keyWindow)
}
}
} }
extension FAShortDetailViewModel { extension FAShortDetailViewModel {

View File

@ -0,0 +1,13 @@
//
// FACoinPackCanReceiveModel.swift
// Fableon
//
// Created by on 2025/10/30.
//
import UIKit
import SmartCodable
struct FACoinPackCanReceiveModel: SmartCodable {
var coins: Int?
}

View File

@ -0,0 +1,25 @@
//
// FACoinsPackModel.swift
// Fableon
//
// Created by on 2025/10/29.
//
import UIKit
import SmartCodable
struct FACoinsPackModel: SmartCodable {
//
var receive_count: Int?
//
var week_total: Int?
//
var week_remaining_total: Int?
//
var week_max_total: Int?
//
var receive_coins: Int?
var receive_list: [FACoinsPackReceiveModel]?
}

View File

@ -0,0 +1,25 @@
//
// FACoinsPackReceiveModel.swift
// Fableon
//
// Created by on 2025/10/29.
//
import UIKit
import SmartCodable
struct FACoinsPackReceiveModel: SmartCodable {
var id: String?
var title: String?
//
var week_remaining_total: Int?
//
var receive_coins: Int?
//
var week_total: Int?
//
var week_max_total: Int?
//
var day_text: String?
}

View File

@ -10,14 +10,15 @@ import UIKit
class FAAdjustStateManager { class FAAdjustStateManager {
static let manager = FAAdjustStateManager() static let manager = FAAdjustStateManager()
var allowOpenMessage = true
var webpageURL: URL? var webpageURL: URL?
/// ///
var isNeedRetry = false var isNeedRetry = false
var allowOpenMessage = true
var isOpenApp = false var isOpenApp = false
var idfaAuthorizationFinish = false var idfaAuthorizationFinish = false
var apnsAuthorizationFinish = false
} }

View File

@ -0,0 +1,104 @@
//
// FAApnsAlert.swift
// Fableon
//
// Created by on 2025/10/31.
//
import UIKit
class FAApnsAlert: FABaseAlert {
private lazy var imageView = UIImageView(image: UIImage(named: "__magnifying"))
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.numberOfLines = 0
label.font = .font(ofSize: 18, weight: .semibold)
label.textColor = .FFFFFF
label.text = "apns_alert_title".localized
return label
}()
private lazy var textLabel: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.numberOfLines = 0
label.font = .font(ofSize: 12, weight: .regular)
label.textColor = .FFFFFF
label.text = "apns_alert_text".localized
return label
}()
private lazy var laterButton: UIButton = {
let button = UIButton(type: .custom)
button.layer.cornerRadius = 18
button.layer.masksToBounds = true
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.FFFFFF.cgColor
button.setTitle("Later".localized, for: .normal)
button.setTitleColor(.FFFFFF, for: .normal)
button.titleLabel?.font = .font(ofSize: 14, weight: .semibold)
button.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
highlightButton.setTitle("Open".localized, for: .normal)
fa_setupLayout()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func handleHighlightButton() {
super.handleHighlightButton()
FATool.openApnsSetting()
}
}
extension FAApnsAlert {
private func fa_setupLayout() {
containerView.addSubview(imageView)
contentView.addSubview(titleLabel)
contentView.addSubview(textLabel)
contentView.addSubview(laterButton)
contentView.addSubview(highlightButton)
imageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(-62)
}
titleLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.right.lessThanOrEqualToSuperview().offset(-10)
make.top.equalTo(imageView.snp.bottom).offset(10)
}
textLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.right.lessThanOrEqualToSuperview().offset(-20)
make.top.equalTo(titleLabel.snp.bottom).offset(12)
}
laterButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(23)
make.top.equalTo(textLabel.snp.bottom).offset(21)
make.bottom.equalToSuperview().offset(-20)
make.height.equalTo(36)
}
highlightButton.snp.makeConstraints { make in
make.width.height.top.equalTo(laterButton)
make.left.equalTo(laterButton.snp.right).offset(11)
make.right.equalToSuperview().offset(-23)
}
}
}

View File

@ -0,0 +1,169 @@
//
// FABaseAlert.swift
// Fableon
//
// Created by on 2025/10/30.
//
import UIKit
import SnapKit
class FABaseAlert: UIView {
var clickHighlightButton: (() -> Void)?
var contentWidth: CGFloat = UIScreen.width - 70 {
didSet {
containerView.snp.updateConstraints { make in
make.width.equalTo(contentWidth)
}
}
}
private(set) var containerView: UIView = {
let view = UIView()
return view
}()
private(set) var contentView: UIView = {
let view = UIView()
view.backgroundColor = ._404040
view.layer.cornerRadius = 16
view.layer.masksToBounds = true
return view
}()
private(set) lazy var closeButton: UIButton = {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "close_icon_03"), for: .normal)
button.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
return button
}()
lazy var highlightButton: FAGradientButton = {
let button = FAGradientButton(type: .custom)
button.fa_colors = [UIColor.BEDFFF.cgColor, UIColor._52_A_2_F_1.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.layer.cornerRadius = 16
button.layer.masksToBounds = true
button.setTitleColor(._000000, for: .normal)
button.titleLabel?.font = .font(ofSize: 14, weight: .semibold)
button.addTarget(self, action: #selector(handleHighlightButton), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = ._000000.withAlphaComponent(0.5)
addSubview(containerView)
containerView.addSubview(contentView)
containerView.addSubview(closeButton)
containerView.snp.makeConstraints { make in
make.center.equalToSuperview()
make.width.equalTo(contentWidth)
}
contentView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
make.bottom.equalToSuperview().offset(-66)
}
closeButton.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.bottom.equalToSuperview()
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@discardableResult
@objc func show(in view: UIView? = nil) -> Self {
guard self.superview == nil else { return self }
var inView: UIView
if let view = view {
inView = view
} else {
inView = FABaseAlert.Window.manager.createWindow()
}
inView.addSubview(self)
self.frame = inView.bounds
showAnimation()
return self
}
@objc func dismiss() {
dismissAnimation()
}
@objc func handleHighlightButton() {
self.dismissAnimation()
self.clickHighlightButton?()
}
}
extension FABaseAlert {
private func showAnimation() {
containerView.transform = CGAffineTransform(translationX: 0, y: 200)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0) {
self.containerView.transform = CGAffineTransform.identity
}
}
private func dismissAnimation() {
UIView.animate(withDuration: 0.3) {
self.alpha = 0
self.containerView.transform = CGAffineTransform(translationX: 0, y: 500)
} completion: { _ in
self.removeFromSuperview()
FABaseAlert.Window.manager.dismissWindow()
}
}
}
extension FABaseAlert {
class Window {
static let manager = Window()
private(set) var window: UIWindow?
func createWindow() -> UIWindow {
guard let window = window else {
let window = UIWindow(windowScene: FATool.windowScene!)
window.backgroundColor = .clear
window.windowLevel = .alert
window.isHidden = false
self.window = window
return window
}
return window
}
func dismissWindow() {
guard let window = self.window else { return }
var isHidden = true
window.subviews.forEach {
if $0.isKind(of: FABaseAlert.self) {
isHidden = false
}
}
if isHidden {
window.isHidden = true
self.window = nil
}
}
}
}

View File

@ -0,0 +1,117 @@
//
// FACoinsPackAlert.swift
// Fableon
//
// Created by on 2025/10/30.
//
import UIKit
class FACoinsPackAlert: FABaseAlert {
var coinsCount: Int? {
didSet {
coinsView.setNeedsUpdateConfiguration()
}
}
private lazy var imageView = UIImageView(image: UIImage(named: "签到"))
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 18, weight: .bold)
label.textColor = .FFFFFF
label.textAlignment = .center
label.numberOfLines = 0
label.text = "coins_pack_alert_title".localized
return label
}()
private lazy var coinsView: UIButton = {
var config = UIButton.Configuration.plain()
config.image = UIImage(named: "coins_icon_10")
config.imagePadding = 8
config.contentInsets = .zero
let button = UIButton(configuration: config)
button.isUserInteractionEnabled = false
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
button.configuration?.attributedTitle = AttributedString("+\(self.coinsCount ?? 0)", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 24, weight: .init(700)),
.foregroundColor : UIColor.F_6_CF_87
]))
}
return button
}()
private lazy var laterButton: UIButton = {
let button = UIButton(type: .custom)
button.layer.cornerRadius = 24
button.layer.masksToBounds = true
button.layer.borderColor = UIColor._777777.cgColor
button.layer.borderWidth = 1
button.setTitle("Later".localized, for: .normal)
button.setTitleColor(._777777, for: .normal)
button.titleLabel?.font = .font(ofSize: 14, weight: .bold)
button.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.highlightButton.setTitle("Claim Now".localized, for: .normal)
self.highlightButton.setTitleColor(.FFFFFF, for: .normal)
self.highlightButton.fa_colors = [UIColor._53_A_2_F_1.cgColor, UIColor.C_5_DDF_5.cgColor]
self.highlightButton.layer.cornerRadius = 24
self.highlightButton.titleLabel?.font = .font(ofSize: 14, weight: .bold)
fa_setupLayout()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension FACoinsPackAlert {
private func fa_setupLayout() {
contentView.addSubview(imageView)
contentView.addSubview(titleLabel)
contentView.addSubview(coinsView)
contentView.addSubview(highlightButton)
contentView.addSubview(laterButton)
imageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(24)
}
titleLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.right.lessThanOrEqualToSuperview().offset(-10)
make.top.equalTo(imageView.snp.bottom).offset(16)
}
coinsView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(titleLabel.snp.bottom).offset(16)
}
highlightButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(18)
make.centerX.equalToSuperview()
make.top.equalTo(coinsView.snp.bottom).offset(16)
make.height.equalTo(48)
}
laterButton.snp.makeConstraints { make in
make.left.right.height.equalTo(highlightButton)
make.top.equalTo(highlightButton.snp.bottom).offset(12)
make.bottom.equalToSuperview().offset(-24)
}
}
}

View File

@ -0,0 +1,93 @@
//
// FARemoveCollectAlert.swift
// Fableon
//
// Created by on 2025/10/30.
//
import UIKit
class FARemoveCollectAlert: FABaseAlert {
private lazy var imageView = UIImageView(image: UIImage(named: "alert_image_01"))
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 18, weight: .semibold)
label.textColor = .FFFFFF
label.text = "remove_collect_alert_title".localized
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
private lazy var textLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .regular)
label.textColor = .FFFFFF
label.numberOfLines = 0
label.textAlignment = .center
label.text = "remove_collect_alert_text".localized
return label
}()
private lazy var cancelButton: UIButton = {
let button = UIButton(type: .custom)
button.layer.cornerRadius = 18
button.layer.masksToBounds = true
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.FFFFFF.cgColor
button.setTitle("Cancel".localized, for: .normal)
button.setTitleColor(.FFFFFF, for: .normal)
button.titleLabel?.font = .font(ofSize: 14, weight: .medium)
button.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
highlightButton.setTitle("Remove".localized, for: .normal)
containerView.addSubview(imageView)
contentView.addSubview(titleLabel)
contentView.addSubview(textLabel)
contentView.addSubview(cancelButton)
contentView.addSubview(highlightButton)
imageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(-62)
}
titleLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(imageView.snp.bottom).offset(10)
make.right.lessThanOrEqualToSuperview().offset(-10)
}
textLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.right.lessThanOrEqualToSuperview().offset(-20)
make.top.equalTo(titleLabel.snp.bottom).offset(12)
}
cancelButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(24)
make.top.equalTo(textLabel.snp.bottom).offset(21)
make.bottom.equalTo(-20)
make.height.equalTo(36)
}
highlightButton.snp.makeConstraints { make in
make.left.equalTo(cancelButton.snp.right).offset(11)
make.right.equalToSuperview().offset(-24)
make.width.height.top.equalTo(cancelButton)
}
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,307 @@
//
// FAVipRetainAlert.swift
// Fableon
//
// Created by on 2025/10/30.
//
import UIKit
class FAVipRetainAlert: FABaseAlert {
var buyFinishHandle: (() -> Void)?
var model: FAPayAlertModel? {
didSet {
let payItem = model?.info
titleView.text = payItem?.getVipTitle()
itemView.payItem = payItem
}
}
var videoInfo: FAVideoInfoModel?
private lazy var titleView: FACoinPackTitleView = {
let view = FACoinPackTitleView()
view.dotColor = ._20_A_1_FF
view.textColors = [UIColor._53_A_2_F_1.cgColor, UIColor.C_5_DDF_5.cgColor]
view.font = .font(ofSize: 24, weight: .init(900))
view.text = "Weekly Refill".localized.uppercased()
return view
}()
private lazy var itemView: ItemView = {
let view = ItemView()
return view
}()
private lazy var textLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 18, weight: .bold).withBoldItalic()
label.textColor = .C_5_DDF_5
label.text = "vip_retain_alert_text".localized
label.textAlignment = .center
label.numberOfLines = 0
return label
}()
private lazy var buyButton: UIButton = {
let button = FAGradientButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
guard let payItem = self.model?.info else { return }
FAIapManager.manager.start(model: payItem, shortPlayId: self.videoInfo?.short_play_id, videoId: self.videoInfo?.short_play_video_id) { [weak self] finish in
guard let self = self else { return }
if finish {
FALogin.manager.requestUserInfo(completer: nil)
self.dismiss()
self.buyFinishHandle?()
}
}
}))
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: 0.7, y: 0.5)
button.setTitle("Buy Now".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)
contentWidth = UIScreen.width - 55
contentView.backgroundColor = .clear
fa_setupLayout()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension FAVipRetainAlert {
private func fa_setupLayout() {
contentView.addSubview(titleView)
contentView.addSubview(itemView)
contentView.addSubview(textLabel)
contentView.addSubview(buyButton)
titleView.snp.makeConstraints { make in
make.top.equalToSuperview()
make.centerX.equalToSuperview()
}
itemView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalTo(titleView.snp.bottom).offset(12)
make.height.equalTo(84)
}
textLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.right.lessThanOrEqualToSuperview().offset(-10)
make.top.equalTo(itemView.snp.bottom).offset(12)
}
buyButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(20)
make.centerX.equalToSuperview()
make.top.equalTo(textLabel.snp.bottom).offset(12)
make.height.equalTo(48)
make.bottom.equalToSuperview()
}
}
}
extension FAVipRetainAlert {
class ItemView: UIView {
var payItem: FAPayItem? {
didSet {
nameLabel.text = payItem?.getVipTitle()
button.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 bgIconImageView1: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "coin_attachment_01"))
return imageView
}()
private lazy var bgIconImageView2: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "coin_attachment_02"))
return imageView
}()
private lazy var bgIconImageView3 = UIImageView(image: UIImage(named: "coin_attachment_04"))
private lazy var bgIconImageView4 = UIImageView(image: UIImage(named: "coin_attachment_05"))
private lazy var vipIconImageView = UIImageView(image: UIImage(named: "皇冠-金"))
private lazy var nameLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 18, weight: .bold)
label.textColor = .FFFFFF
return label
}()
private lazy var textLabel: UILabel = {
let label = FALabel()
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: 0.7, y: 0.5)
label.font = .font(ofSize: 12, weight: .regular)
return label
}()
private lazy var button: UIButton = {
var config = UIButton.Configuration.plain()
config.contentInsets = .init(top: 0, leading: 10, bottom: 0, trailing: 10)
config.titleAlignment = .center
let button = FAGradientButton(configuration: config)
button.isUserInteractionEnabled = false
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: 0.5, y: 0.5)
button.layer.cornerRadius = 24
button.layer.masksToBounds = true
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
let currency = self.payItem?.currency ?? ""
let timeString = self.payItem?.getTimeString() ?? ""
let oldPrice = self.payItem?.price ?? ""
var discountPrice: String? = nil
if self.payItem?.discount_type == 1, let introductoryPrice = self.payItem?.introductionaryOffer {
discountPrice = introductoryPrice.price.stringValue
} else if self.payItem?.discount_type == 2, let discount = self.payItem?.promotionalOffers?.first {
discountPrice = discount.price.stringValue
}
if let discountPrice = discountPrice {
button.configuration?.attributedTitle = AttributedString("\(currency)\(discountPrice)", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 18, weight: .bold),
.foregroundColor : UIColor._114_CEE
]))
button.configuration?.attributedSubtitle = AttributedString("\(currency)\(oldPrice)", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 12, weight: .regular),
.foregroundColor : UIColor._000000.withAlphaComponent(0.5),
.strikethroughStyle: NSUnderlineStyle.single.rawValue,
.strikethroughColor: UIColor._000000.withAlphaComponent(0.5)
]))
} else {
button.configuration?.attributedTitle = AttributedString("\(currency)\(oldPrice)", attributes: AttributeContainer([
.font : UIFont.font(ofSize: 18, weight: .bold),
.foregroundColor : UIColor._114_CEE
]))
button.configuration?.attributedSubtitle = AttributedString("/\(timeString)", 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)
nameLabel.text = "Weekly VIP"
textLabel.text = "Unlimited access to all series"
addSubview(bgView)
bgView.addSubview(bgIconImageView4)
bgView.addSubview(bgIconImageView3)
bgView.addSubview(bgIconImageView1)
bgView.addSubview(bgIconImageView2)
bgView.addSubview(vipIconImageView)
bgView.addSubview(nameLabel)
bgView.addSubview(textLabel)
bgView.addSubview(button)
bgView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
bgIconImageView1.snp.makeConstraints { make in
make.left.equalToSuperview().offset(0)
make.top.equalToSuperview().offset(0)
}
bgIconImageView2.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-26)
make.centerY.equalToSuperview()
}
bgIconImageView3.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.centerX.equalTo(bgIconImageView2)
}
bgIconImageView4.snp.makeConstraints { make in
make.left.bottom.equalToSuperview()
}
vipIconImageView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.top.equalToSuperview().offset(18)
}
nameLabel.snp.makeConstraints { make in
make.centerY.equalTo(vipIconImageView)
make.left.equalTo(vipIconImageView.snp.right).offset(4)
}
textLabel.snp.makeConstraints { make in
make.left.equalTo(vipIconImageView)
make.bottom.equalToSuperview().offset(-18)
}
button.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.right.equalToSuperview().offset(-12)
make.height.equalTo(48)
make.width.greaterThanOrEqualTo(88)
}
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}

View File

@ -83,25 +83,25 @@ class FAPayDataRequest: NSObject {
} }
/// ///
func requestRetainVipPayInfo(completer: ((_ model: FAPayAlertModel?) -> Void)?) { func requestVipRetainPayInfo(completer: ((_ model: FAPayAlertModel?) -> Void)?) {
self.completerBlock = nil self.completerBlock = nil
self.payAlertBlock = completer self.payAlertBlock = completer
// FAStoreAPI.requestRetainVipPayInfo { [weak self] model in FAStoreAPI.requestVipRetainPayInfo { [weak self] model in
// guard let self = self else { return } guard let self = self else { return }
// guard let model = model else { guard let model = model else {
// self.payAlertBlock?(nil) self.payAlertBlock?(nil)
// return return
// } }
// self.payAlertModel = model self.payAlertModel = model
//
// let productId = BRIAP.manager.getProductId(templateId: model.info?.ios_template_id) ?? "" let productId = FAIapManager.manager.getProductId(templateId: model.info?.ios_template_id) ?? ""
//
// let set = Set([productId]) let set = Set([productId])
// let productsRequest = SKProductsRequest(productIdentifiers: set) let productsRequest = SKProductsRequest(productIdentifiers: set)
// productsRequest.delegate = self productsRequest.delegate = self
// productsRequest.start() productsRequest.start()
// } }
} }
} }

View File

@ -0,0 +1,115 @@
//
// FALogin+Apple.swift
// Fableon
//
// Created by on 2025/10/30.
//
import UIKit
import AuthenticationServices
extension FALogin {
private struct AssociatedKeys {
static var appleLoginHandle: Int?
}
private var appleLoginHandle: ((_ model: FAThirdSignModel?) -> Void)? {
set {
objc_setAssociatedObject(self, &AssociatedKeys.appleLoginHandle, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
get {
return objc_getAssociatedObject(self, &AssociatedKeys.appleLoginHandle) as? ((_ model: FAThirdSignModel?) -> Void)
}
}
func appleLogin(completer: ((_ model: FAThirdSignModel?) -> Void)?) {
self.appleLoginHandle = completer
let appleIDProvider = ASAuthorizationAppleIDProvider()
let request = appleIDProvider.createRequest()
request.requestedScopes = [.fullName, .email]
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.presentationContextProvider = self
authorizationController.performRequests()
}
private func jwtDecode(jwtStr: String) -> [String: Any]? {
let segments = jwtStr.components(separatedBy: ".")
guard segments.count > 1 else { return nil }
var base64String = segments[1]
// Base64
let requiredLength = 4 * Int(ceil(Double(base64String.count) / 4.0))
let paddingLength = requiredLength - base64String.count
if paddingLength > 0 {
base64String += String(repeating: "=", count: paddingLength)
}
// URL
base64String = base64String.replacingOccurrences(of: "-", with: "+")
base64String = base64String.replacingOccurrences(of: "_", with: "/")
// Base64
guard let data = Data(base64Encoded: base64String),
let jsonObject = try? JSONSerialization.jsonObject(with: data, options: []),
let payload = jsonObject as? [String: Any] else {
return nil
}
return payload
}
}
//MARK: ASAuthorizationControllerDelegate
extension FALogin: ASAuthorizationControllerDelegate {
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
let userIdentifier = appleIDCredential.user
let fullName = appleIDCredential.fullName
let email = appleIDCredential.email
let identityToken = appleIDCredential.identityToken.flatMap { String(data: $0, encoding: .utf8) }
let identityTokenParams = self.jwtDecode(jwtStr: identityToken ?? "")
var model = FAThirdSignModel()
model.platform = .apple
model.third_id = userIdentifier
model.giving_name = fullName?.givenName
model.family_name = fullName?.familyName
model.avator = identityTokenParams?["picture"] as? String
model.email = identityTokenParams?["email"] as? String
debugLog(userIdentifier)
debugLog(fullName)
debugLog(email)
appleLoginHandle?(model)
appleLoginHandle = nil
}
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
appleLoginHandle?(nil)
appleLoginHandle = nil
}
}
//MARK: ASAuthorizationControllerPresentationContextProviding
extension FALogin: ASAuthorizationControllerPresentationContextProviding {
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return FATool.keyWindow!
}
}

View File

@ -0,0 +1,58 @@
//
// FALogin+Facebook.swift
// Fableon
//
// Created by on 2025/10/30.
//
import UIKit
import FacebookLogin
extension FALogin {
func facebookLogin(presentingViewController: UIViewController?, completer: ((_ model: FAThirdSignModel?) -> Void)?) {
let loginManager = LoginManager()
loginManager.logOut()
loginManager.defaultAudience = .everyone
loginManager.logIn(permissions: ["public_profile", "email"], from: presentingViewController) { result, error in
guard error == nil, let result = result else {
completer?(nil)
return
}
if result.isCancelled {
completer?(nil)
return
}
let request = GraphRequest(graphPath: "me", parameters: ["fields" : "id,name,email,picture"], httpMethod: .get)
request.start { connection, result, error in
guard let result = result as? [String : Any] else {
completer?(nil)
return
}
var model = FAThirdSignModel()
model.platform = .faceBook
model.third_id = result["id"] as? String
model.email = result["email"] as? String
if let picture = result["picture"] as? [String : Any],
let data = picture["data"] as? [String : Any],
let url = data["url"] as? String
{
model.avator = url
}
if let name = result["name"] as? String {
model.family_name = name
} else {
model.family_name = result["first_name"] as? String
model.giving_name = result["last_name"] as? String
}
completer?(model)
}
}
}
}

View File

@ -6,16 +6,28 @@
// //
import UIKit import UIKit
import SmartCodable
let kFAUserTokenDefaultsKey = "kFAUserTokenDefaultsKey" let kFAUserTokenDefaultsKey = "kFAUserTokenDefaultsKey"
let kFAUserInfoDefaultsKey = "kFAUserInfoDefaultsKey" let kFAUserInfoDefaultsKey = "kFAUserInfoDefaultsKey"
class FALogin: NSObject { class FALogin: NSObject {
enum LoginType: String, SmartCaseDefaultable {
case apple = "Apple"
case faceBook = "Facebook"
case google = "Google"
case tiktok = "Tiktok"
}
static let manager = FALogin() static let manager = FALogin()
private(set) var token = UserDefaults.fa_object(forKey: kFAUserTokenDefaultsKey, as: FATokenModel.self) private(set) var token = UserDefaults.fa_object(forKey: kFAUserTokenDefaultsKey, as: FATokenModel.self)
private(set) var userInfo = UserDefaults.fa_object(forKey: kFAUserInfoDefaultsKey, as: FAUserInfo.self) private(set) var userInfo = UserDefaults.fa_object(forKey: kFAUserInfoDefaultsKey, as: FAUserInfo.self)
var isLogin: Bool {
return !(userInfo?.is_tourist ?? true)
}
private func setToken(_ token: FATokenModel?) { private func setToken(_ token: FATokenModel?) {
self.token = token self.token = token
@ -27,6 +39,67 @@ class FALogin: NSObject {
UserDefaults.fa_setObject(userInfo, forKey: kFAUserInfoDefaultsKey) UserDefaults.fa_setObject(userInfo, forKey: kFAUserInfoDefaultsKey)
} }
///
func thirdLogin(type: LoginType, presentingViewController: UIViewController?, completer: ((_ isFinish: Bool) -> Void)?) {
switch type {
case .apple:
appleLogin { [weak self] model in
self?.requestSignThirdLogin(thirdSignModel: model, completer: completer)
}
case .faceBook:
facebookLogin(presentingViewController: presentingViewController) { [weak self] model in
self?.requestSignThirdLogin(thirdSignModel: model, completer: completer)
}
default:
break
}
}
///
private func requestSignThirdLogin(thirdSignModel: FAThirdSignModel?, completer: ((_ isFinish: Bool) -> Void)?) {
guard let thirdSignModel = thirdSignModel else {
completer?(false)
return
}
FAStatAPI.requestLeaveApp()
FAAPI.requestSignThirdLogin(model: thirdSignModel) { [weak self] token in
guard let self = self else { return }
guard let token = token else {
completer?(false)
return
}
self.setToken(token)
self.userInfo?.is_tourist = false
self.requestUserInfo(completer: nil)
FAStatAPI.requestEnterApp()
FAStatAPI.requestStatOnLine()
completer?(true)
NotificationCenter.default.post(name: FALogin.userInfoUpdateNotification, object: nil)
}
}
func logout(completer: ((_ isFinish: Bool) -> Void)?) {
FAStatAPI.requestLeaveApp()
FAAPI.requestLogout { [weak self] token in
guard let self = self else { return }
if let token = token {
self.setToken(token)
self.userInfo?.is_tourist = true
self.requestUserInfo(completer: nil)
FAStatAPI.requestEnterApp()
FAStatAPI.requestStatOnLine()
completer?(true)
NotificationCenter.default.post(name: FALogin.userInfoUpdateNotification, object: nil)
} else {
completer?(false)
}
}
}
} }
extension FALogin { extension FALogin {

View File

@ -0,0 +1,23 @@
//
// FAThirdSignModel.swift
// Fableon
//
// Created by on 2025/10/30.
//
import UIKit
import SmartCodable
struct FAThirdSignModel: SmartCodable {
var third_id: String?
var email: String?
//
var family_name: String?
//
var giving_name: String?
var avator: String?
var platform: FALogin.LoginType?
}

View File

@ -6,6 +6,8 @@
// //
import Foundation import Foundation
import UIKit import UIKit
import AppTrackingTransparency
import AdSupport
#if DEBUG #if DEBUG
func debugLog(_ msg: Any, file: String = #file, function: String = #function, line: Int = #line) { func debugLog(_ msg: Any, file: String = #file, function: String = #function, line: Int = #line) {
@ -18,6 +20,8 @@ func debugLog(_ msg: Any) { }
class FATool { class FATool {
static var sceneDelegate: SceneDelegate?
static var windowScene: UIWindowScene? static var windowScene: UIWindowScene?
static var keyWindow: UIWindow? { static var keyWindow: UIWindow? {
@ -52,3 +56,38 @@ class FATool {
} }
} }
extension FATool {
static func requestIDFAAuthorization(_ completion: ((String?) -> Void)? = nil) {
if FAAdjustStateManager.manager.idfaAuthorizationFinish {
completion?(ASIdentifierManager.shared().advertisingIdentifier.uuidString)
return
}
guard FANetworkMonitor.manager.isReachable == true, FAAdjustStateManager.manager.apnsAuthorizationFinish, FAAdjustStateManager.manager.isOpenApp else {
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
ATTrackingManager.requestTrackingAuthorization { status in
FAAdjustStateManager.manager.idfaAuthorizationFinish = true
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
FATool.sceneDelegate?.retryHandleOpenAppMessage()
completion?(idfa)
}
}
}
///
static func openApnsSetting() {
if #available(iOS 16.0, *) {
if let url = URL(string: UIApplication.openNotificationSettingsURLString) {
UIApplication.shared.open(url)
}
} else {
if let url = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url)
}
}
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x40",
"green" : "0x40",
"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" : "0xFA",
"green" : "0x9D",
"red" : "0x62"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

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

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xBC",
"green" : "0xBC",
"red" : "0xBC"
}
},
"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" : "0xEF",
"red" : "0xDF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 804 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

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

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