Compare commits

...

11 Commits
main ... IAA

Author SHA1 Message Date
澜声世纪
67eeef0447 1.开屏广告 2025-12-11 11:17:46 +08:00
澜声世纪
4c250038e0 1.w2a 启动页 通知 2025-12-10 10:23:22 +08:00
澜声世纪
b7a0a86a1a 1.文件目录修改 2025-12-08 17:32:39 +08:00
澜声世纪
41b7bc80c4 1.vip挽留 2025-12-08 17:06:30 +08:00
澜声世纪
50d1cd9069 1.ui调整 2025-12-08 16:04:26 +08:00
澜声世纪
2c7cef9572 1.混淆代码 2025-12-08 13:48:40 +08:00
HiBit
6d48234e48 1.代码提交 2025-12-08 10:34:34 +08:00
HiBit
5fb7a4d9bc 登陆功能 2025-12-02 13:27:27 +08:00
HiBit
2b3125d826 1.代码提交 2025-11-29 14:05:30 +08:00
HiBit
b21751e738 web view 2025-11-28 15:03:10 +08:00
HiBit
86b603c64f 1.缺省页 UI 2025-11-26 16:55:40 +08:00
795 changed files with 56698 additions and 413 deletions

12
Podfile
View File

@ -20,7 +20,7 @@ target 'SynthReel' do
pod 'YYCategories' pod 'YYCategories'
pod 'YYText' pod 'YYText'
pod 'Kingfisher' pod 'Kingfisher'
pod 'SmartCodable' pod 'SmartCodable','5.0.15'
pod 'Moya' pod 'Moya'
pod 'SVProgressHUD' pod 'SVProgressHUD'
pod 'Toast' pod 'Toast'
@ -31,5 +31,13 @@ target 'SynthReel' do
pod 'MJRefresh' pod 'MJRefresh'
pod 'collection-view-layouts/TagsLayout' pod 'collection-view-layouts/TagsLayout'
pod 'HWPanModal' pod 'HWPanModal'
pod 'LYEmptyView'
pod 'ZLPhotoBrowser'
pod 'Adjust'
# # AdMob SDK
pod 'Google-Mobile-Ads-SDK'
#
# # AppLovin SDK
pod 'AppLovinSDK'
end end

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,24 @@
{ {
"originHash" : "0e62262c59a183f44748a161870cc0f2b76e1b0e46f648559704e4be9de523b9", "originHash" : "deb3ea9bc9f7ea16d149d6025086ec795e4f5fc740540ae9e5e8308e4e3e8aec",
"pins" : [ "pins" : [
{
"identity" : "abseil-cpp-binary",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/abseil-cpp-binary.git",
"state" : {
"revision" : "bbe8b69694d7873315fd3a4ad41efe043e1c07c5",
"version" : "1.2024072200.0"
}
},
{
"identity" : "app-check",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/app-check.git",
"state" : {
"revision" : "61b85103a1aeed8218f17c794687781505fbbef5",
"version" : "11.2.0"
}
},
{ {
"identity" : "estabbarcontroller", "identity" : "estabbarcontroller",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
@ -10,6 +28,114 @@
"version" : "2.9.0" "version" : "2.9.0"
} }
}, },
{
"identity" : "facebook-ios-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/facebook/facebook-ios-sdk",
"state" : {
"revision" : "c19607d535864533523d1f437c84035e5fb101cf",
"version" : "14.1.0"
}
},
{
"identity" : "firebase-ios-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk",
"state" : {
"revision" : "087bb95235f676c1a37e928769a5b6645dcbd325",
"version" : "12.6.0"
}
},
{
"identity" : "google-ads-on-device-conversion-ios-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/googleads/google-ads-on-device-conversion-ios-sdk",
"state" : {
"revision" : "35b601a60fbbea2de3ea461f604deaaa4d8bbd0c",
"version" : "3.2.0"
}
},
{
"identity" : "googleappmeasurement",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
"revision" : "c2d59acf17a8ba7ed80a763593c67c9c7c006ad1",
"version" : "12.5.0"
}
},
{
"identity" : "googledatatransport",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleDataTransport.git",
"state" : {
"revision" : "617af071af9aa1d6a091d59a202910ac482128f9",
"version" : "10.1.0"
}
},
{
"identity" : "googleutilities",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleUtilities.git",
"state" : {
"revision" : "60da361632d0de02786f709bdc0c4df340f7613e",
"version" : "8.1.0"
}
},
{
"identity" : "grpc-binary",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/grpc-binary.git",
"state" : {
"revision" : "75b31c842f664a0f46a2e590a570e370249fd8f6",
"version" : "1.69.1"
}
},
{
"identity" : "gtm-session-fetcher",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/gtm-session-fetcher.git",
"state" : {
"revision" : "fb7f2740b1570d2f7599c6bb9531bf4fad6974b7",
"version" : "5.0.0"
}
},
{
"identity" : "interop-ios-for-google-sdks",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/interop-ios-for-google-sdks.git",
"state" : {
"revision" : "040d087ac2267d2ddd4cca36c757d1c6a05fdbfe",
"version" : "101.0.0"
}
},
{
"identity" : "leveldb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/leveldb.git",
"state" : {
"revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1",
"version" : "1.22.5"
}
},
{
"identity" : "nanopb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/nanopb.git",
"state" : {
"revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1",
"version" : "2.30910.0"
}
},
{
"identity" : "promises",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/promises.git",
"state" : {
"revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac",
"version" : "2.4.0"
}
},
{ {
"identity" : "snapkit", "identity" : "snapkit",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
@ -18,6 +144,15 @@
"revision" : "2842e6e84e82eb9a8dac0100ca90d9444b0307f4", "revision" : "2842e6e84e82eb9a8dac0100ca90d9444b0307f4",
"version" : "5.7.1" "version" : "5.7.1"
} }
},
{
"identity" : "swift-protobuf",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-protobuf.git",
"state" : {
"revision" : "c169a5744230951031770e27e475ff6eefe51f9d",
"version" : "1.33.3"
}
} }
], ],
"version" : 3 "version" : 3

View File

@ -2,7 +2,7 @@
// SRHomeApi.swift // SRHomeApi.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/14. // Created by on 2025/11/14.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
@ -118,5 +118,4 @@ struct SRHomeApi {
} }
} }
} }
} }

View File

@ -2,7 +2,7 @@
// SRShortApi.swift // SRShortApi.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/18. // Created by on 2025/11/18.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
@ -29,6 +29,20 @@ struct SRShortApi {
} }
} }
static func requestRecommand() async -> ([SRShortModel]?, Int?, String?) {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/getDetailsRecommand")
param.method = .get
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRNetwork.List<SRShortModel>>) in
if response.isSuccess {
continuation.resume(returning:(response.data?.list, response.code, response.msg))
} else {
continuation.resume(returning:(nil, response.code, response.msg))
}
}
}
}
static func requestShortCollect(shortId: String, videoId: String?, isCollect: Bool) async -> Bool { static func requestShortCollect(shortId: String, videoId: String?, isCollect: Bool) async -> Bool {
await withCheckedContinuation { continuation in await withCheckedContinuation { continuation in
var path = "" var path = ""
@ -75,6 +89,24 @@ struct SRShortApi {
} }
} }
static func requestCoinUnlockVideo(shortId: String?, videoId: String?) async -> SRVideoUnlockResult? {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/buy_video")
param.isToast = true
param.method = .post
param.parameters = [
"short_play_id": shortId ?? "",
"video_id": videoId ?? "0"
]
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRVideoUnlockResult>) in
continuation.resume(returning: response.data)
}
}
}
} }

View File

@ -0,0 +1,98 @@
//
// SRStatAPI.swift
// SynthReel
//
// Created by on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRStatAPI: NSObject {
enum EventKey: String {
case payError = "pay_error" //
case payCallback = "pay_callback" //
case payRestore = "pay_restore" //restore
case forceUpdate = "force_update"//
case payCancel = "pay_cancel" //
case payTemplateDialog = "pay_template_dialog"//
}
static func requestEventStat(orderCode: String?, shortPlayId: String?, videoId: String?, eventKey: EventKey, errorMsg: String?, otherParamenters: [String : Any]? = nil) async {
var eventName = ""
switch eventKey {
case .payRestore:
eventName = "pay restore"
case .payCallback:
eventName = "pay callback failed"
case .forceUpdate:
eventName = "force update"
case .payCancel:
eventName = "user pay canceled"
default:
eventName = "platform pay failed"
}
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/createOrder")
param.method = .post
param.parameters = [
"userId" : SRLogin.manager.userInfo?.customer_id ?? "",
"short_play_video_id" : videoId ?? "0",
"short_play_id" : shortPlayId ?? "0",
"event_key" : eventKey.rawValue,
"order_code": orderCode ?? "",
"event_name" : eventName,
]
if let errorMsg = errorMsg {
param.parameters?["error_msg"] = errorMsg
}
if let otherParamenters = otherParamenters {
otherParamenters.forEach {
param.parameters?[$0] = $1
}
}
SRNetwork.request(parameters: param) { (_: SRNetwork.Response<String>) in
continuation.resume()
}
}
}
static func requestUploadApnsAuthorizationStatus(_ status: Bool) async {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/customer/uploadNoticeStatus")
param.method = .post
param.parameters = [
"is_open_notice" : status ? 1 : 0
]
SRNetwork.request(parameters: param) { (_: SRNetwork.Response<String>) in
continuation.resume()
}
}
}
}
extension SRStatAPI {
///
static func uploadApnsAuthorizationStatus() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
if settings.authorizationStatus == .authorized {
Task {
await requestUploadApnsAuthorizationStatus(true)
}
} else if settings.authorizationStatus == .denied {
Task {
await requestUploadApnsAuthorizationStatus(false)
}
}
}
}
}

View File

@ -0,0 +1,121 @@
//
// SRStoreAPI.swift
// SynthReel
//
// Created by on 2025/12/2.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import SmartCodable
class SRStoreAPI: NSObject {
enum BuyType: String, SmartCaseDefaultable {
case coins = "coins"
case subVip = "sub_vip"
case subCoins = "sub_coins"
case vip = "vip"
}
static func requestPayTemplate() async -> SRPayDateModel? {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/paySettingsV4")
param.method = .post
param.parameters = [
"discount" : "1",
"purchases_token" : SRPayManger.manager.getAppStoreReceipt() ?? "",
]
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRPayDateModel>) in
continuation.resume(returning: response.data)
}
}
}
static func requestCreateOrder(payId: String, shortPlayId: String, videoId: String, isDiscount: Bool = false, identifierDiscount: String? = nil) async -> SRIapOrderModel? {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/createOrder")
param.method = .post
param.parameters = [
"payment_channel" : "apple",
"short_play_id" : shortPlayId,
"video_id" : videoId,
"pay_setting_id" : payId,
"is_discount" : isDiscount ? 1 : 0,
"product_discount" : identifierDiscount ?? "",
]
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRIapOrderModel>) in
continuation.resume(returning: response.data)
}
}
}
static func requestVerifyOrder(parameters: [String : Any]) async -> SRIapVerifyModel? {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/applePaid")
param.method = .post
param.parameters = parameters
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRIapVerifyModel>) in
continuation.resume(returning: response.data)
}
}
}
static func requestCoinsPackData() async -> SRCoinsPackModel? {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/getReceiveDayCoinInfo")
param.method = .get
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRCoinsPackModel>) in
continuation.resume(returning: response.data)
}
}
}
static func requestCoinBagCanReceiveInfo() async -> SRCoinPackCanReceiveModel? {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/getReceiveDayCoin")
param.method = .get
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRCoinPackCanReceiveModel>) in
continuation.resume(returning: response.data)
}
}
}
static func requestReceiveCoinsPackCoins(id: String?) async -> Bool {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/receiveDayCoin")
param.method = .post
param.parameters = [
"id" : id ?? ""
]
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<String>) in
if response.isSuccess {
continuation.resume(returning: true)
}else{
continuation.resume(returning: false)
}
}
}
}
static func requestVipRetainPayInfo() async -> SRPayAlertModel? {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/getRetainVipPaySetting")
param.method = .get
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRPayAlertModel>) in
if let model = response.data,
model.info != nil {
continuation.resume(returning: model)
} else {
continuation.resume(returning: nil)
}
}
}
}
}

View File

@ -2,7 +2,7 @@
// SRUserApi.swift // SRUserApi.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/13. // Created by on 2025/11/13.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
@ -22,4 +22,119 @@ struct SRUserApi {
} }
} }
} }
static func requestregister() async -> SRTokenModel? {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/customer/register")
param.method = .get
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRTokenModel>) in
continuation.resume(returning: response.data)
}
}
}
static func requestLeave() async {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/customer/leaveApp")
param.method = .post
SRNetwork.request(parameters: param) { (_: SRNetwork.Response<String>) in
continuation.resume()
}
}
}
static func requestEnterApp() async {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/customer/enterTheApp")
param.method = .post
SRNetwork.request(parameters: param) { (_: SRNetwork.Response<String>) in
continuation.resume()
}
}
}
static func requestStatOnLine() async {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/customer/onLine")
param.method = .post
SRNetwork.request(parameters: param) { (_: SRNetwork.Response<String>) in
continuation.resume()
}
}
}
static func requestStatApnsClick(id: String, title: String) async {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/message/sendReport")
param.method = .post
param.parameters = [
"message_id" : id,
"title" : title
]
SRNetwork.request(parameters: param) { (_: SRNetwork.Response<String>) in
continuation.resume()
}
}
}
static func requestUploadApnsDeviceToken(token: String) async {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/customer/firebaseToken")
param.method = .post
param.parameters = [
"fcm_token": token
]
SRNetwork.request(parameters: param) { (_: SRNetwork.Response<String>) in
continuation.resume()
}
}
}
static func requestSignThirdLogin(model: SRThirdModel) async -> SRTokenModel? {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/customer/login")
param.method = .post
param.parameters = model.toDictionary()
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRTokenModel>) in
continuation.resume(returning: response.data)
}
}
}
static func requestLogout() async -> SRTokenModel? {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/customer/signout")
param.method = .post
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRTokenModel>) in
continuation.resume(returning: response.data)
}
}
}
static func requestUploadW2a(data: String) async {
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/w2aSelfAttribution")
param.method = .post
param.parameters = [
"data": data
]
SRNetwork.request(parameters: param) { (_: SRNetwork.Response<String>) in
continuation.resume()
}
}
}
static func requestVersionUpdateData() async -> SRVersionUpdateModel?{
await withCheckedContinuation { continuation in
var param = SRNetwork.Parameters(path: "/customer/versionControl")
param.method = .get
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRVersionUpdateModel>) in
continuation.resume(returning: response.data)
}
}
}
} }

View File

@ -0,0 +1,225 @@
////
//// VPAppOpenAdManager.swift
//// Veloria
////
//// Created by on 2025/7/15.
////
//
//import UIKit
//
//
//@objc protocol VPAppOpenAdManagerDelegate: NSObjectProtocol {
//
// ///广
// @objc optional func appOpenAdManagerDidLoadFinish(adManager: VPAppOpenAdManager)
// ///广
// @objc optional func appOpenAdManager(adManager: VPAppOpenAdManager, didLoadFail error: Error)
// ///广
// @objc optional func appOpenAdManager(adManager: VPAppOpenAdManager, didDisplayFail error: Error)
// ///广
// @objc optional func appOpenAdManagerDidShow(adManager: VPAppOpenAdManager)
// ///广
// @objc optional func appOpenAdManagerDidDismiss(adManager: VPAppOpenAdManager)
// ///广
// @objc optional func appOpenAdManagerDidClick(adManager: VPAppOpenAdManager)
// ///
// @objc optional func appOpenAdManager(adManager: VPAppOpenAdManager, didOtherFail error: Error)
//
//}
//
//
//@objc protocol VPAppOpenAdDelegate: NSObjectProtocol {
//
// ///广
// @objc optional func appOpenAd(ad: VPAppOpenAd, didLoadFail error: Error)
// ///广
// @objc optional func appOpenAdDidLoadFinish(ad: VPAppOpenAd)
// ///广
// @objc optional func appOpenAd(ad: VPAppOpenAd, didDisplayFail error: Error)
// ///广
// @objc optional func appOpenAdDidShow(ad: VPAppOpenAd)
// ///广
// @objc optional func appOpenAdDidDismiss(ad: VPAppOpenAd)
// ///广
// @objc optional func appOpenAdDidClick(ad: VPAppOpenAd)
//}
//
//@objc protocol VPAppOpenAd: NSObjectProtocol {
//
// weak var delegate: VPAppOpenAdDelegate? { get set }
//
// var adPlatformKey: String { get }
//
// var adUnitID: String { get }
//
// var isReady: Bool { get }
//
// func loadAd()
// func showAd()
//
//}
//
//class VPAppOpenAdManager: NSObject {
//
// static let manager = VPAppOpenAdManager()
//
// weak var delegate: VPAppOpenAdManagerDelegate?
//
// private(set) var appOpenAd: VPAppOpenAd? {
// didSet {
// oldValue?.delegate = nil
// appOpenAd?.delegate = self
// }
// }
//
// private var isLoading = false
// private var isShowing = false
// ///
// private var needAutoShow = false
//
// private var timeOutTimer: Timer?
//
// private var adsDate: Date?
//
// deinit {
// NotificationCenter.default.removeObserver(self)
// }
//
// override init() {
// super.init()
// NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackgroundNotification), name: UIApplication.didEnterBackgroundNotification, object: nil)
// }
//
// func showAdIfAvailable() {
//
// if self.appOpenAd?.isReady == true {
// self.showAd()
// } else {
// self.timeOutTimer = Timer.scheduledTimer(timeInterval: 5, target: (self), selector: #selector(handleTimeOutTimer), userInfo: nil, repeats: false)
//
// self.needAutoShow = true
// self.loadAd()
// }
// }
//
// func loadAd() {
// guard !isLoading else { return }
//
// self.isLoading = true
// appOpenAd = VPApplovinAppOpenAd()
// appOpenAd?.loadAd()
// }
//
// func showAd() {
// guard self.appOpenAd?.isReady == true else { return }
// guard !self.isShowing else { return }
//
//
// self.isShowing = true
// self.appOpenAd?.showAd()
// }
//
// @objc private func handleTimeOutTimer() {
// self.needAutoShow = false
// cleanTimer()
//
// let error = NSError(domain: "time-out", code: -1)
// requestStatAd(type: "load_failed", errorMsg: error.localizedDescription)
// self.delegate?.appOpenAdManager?(adManager: self, didOtherFail: error)
// }
//
// private func cleanTimer() {
// self.timeOutTimer?.invalidate()
// self.timeOutTimer = nil
// }
//
// private func clean() {
// appOpenAd = nil
// delegate = nil
// isShowing = false
// self.adsDate = nil
// }
//}
//
////MARK: -------------- VPAppOpenAdDelegate --------------
//extension VPAppOpenAdManager: VPAppOpenAdDelegate {
//
// ///广
// func appOpenAd(ad: VPAppOpenAd, didLoadFail error: Error) {
// cleanTimer()
// isLoading = false
// requestStatAd(type: "load_failed", errorMsg: error.localizedDescription)
//
// self.delegate?.appOpenAdManager?(adManager: self, didLoadFail: error)
//
// clean()
// }
// ///广
// func appOpenAdDidLoadFinish(ad: VPAppOpenAd) {
// cleanTimer()
// self.isLoading = false
// self.delegate?.appOpenAdManagerDidLoadFinish?(adManager: self)
//
// if needAutoShow {
// self.needAutoShow = false
// self.showAd()
// }
//
// }
// ///广
// func appOpenAd(ad: VPAppOpenAd, didDisplayFail error: Error) {
// requestStatAd(type: "show_failed", errorMsg: error.localizedDescription)
// self.delegate?.appOpenAdManager?(adManager: self, didDisplayFail: error)
// clean()
// }
// ///广
// func appOpenAdDidShow(ad: VPAppOpenAd) {
// adsDate = Date()
// requestStatAd(type: "start", errorMsg: nil)
// self.delegate?.appOpenAdManagerDidShow?(adManager: self)
// }
// ///广
// func appOpenAdDidDismiss(ad: VPAppOpenAd) {
// requestStatAd(type: "close", errorMsg: nil)
// self.delegate?.appOpenAdManagerDidDismiss?(adManager: self)
// clean()
// }
// ///广
// func appOpenAdDidClick(ad: VPAppOpenAd) {
// requestStatAd(type: "click", errorMsg: nil)
// self.delegate?.appOpenAdManagerDidClick?(adManager: self)
// }
//}
//
//
//extension VPAppOpenAdManager {
//
// private func requestStatAd(type: String, errorMsg: String?) {
// guard let appOpenAd = self.appOpenAd else { return }
//
// var seconds = 0
// if let adsDate = self.adsDate {
// if type == "click" || type == "close" {
// seconds = Int(Date().timeIntervalSince(adsDate))
// }
// }
//
//
// let model = VPStatAdModel()
// model.type = type
// model.view_seconds = seconds
// model.ads_id = appOpenAd.adUnitID
// model.ad_platform_key = VPAdPlatformKey(rawValue: appOpenAd.adPlatformKey)
// model.error_msg = errorMsg
// model.scene = .splash
//
//// VPStatAPI.requestStatAd(model: model)
// }
//
//
// @objc private func didEnterBackgroundNotification() {
// guard self.isShowing else { return }
//
// self.requestStatAd(type: "Interrupt", errorMsg: nil)
// }
//}

View File

@ -0,0 +1,84 @@
////
//// VPApplovinAppOpenAd.swift
//// Veloria
////
//// Created by on 2025/7/15.
////
//
//import UIKit
//#if canImport(AppLovinSDK)
//import AppLovinSDK
//#endif
//
//class VPApplovinAppOpenAd: NSObject, VPAppOpenAd {
//
//
//
// private lazy var appOpenAd = MAAppOpenAd(adUnitIdentifier: adUnitID)
//
// weak var delegate: (any VPAppOpenAdDelegate)?
//
// var adPlatformKey: String {
// return VPAdPlatformKey.applovin.rawValue
// }
//
// var adUnitID: String {
// return VPAdManager.applovin_appOpenAdUnitID
// }
//
// var isReady: Bool {
// if ALSdk.shared().isInitialized && appOpenAd.isReady {
// return true
// } else {
// return false
// }
// }
//
// func loadAd() {
//
// VPAdManager.initialize_applovinSdk { [weak self] in
// guard let self = self else { return }
// appOpenAd.delegate = self
// appOpenAd.load()
// }
// }
//
// func showAd() {
// guard isReady else { return }
//
// appOpenAd.show()
// }
//}
//
//
//extension VPApplovinAppOpenAd: MAAdDelegate {
// func didLoad(_ ad: MAAd) {
// self.delegate?.appOpenAdDidLoadFinish?(ad: self)
// }
//
// func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) {
// let nsError = NSError(domain: error.message, code: error.code.rawValue)
// self.delegate?.appOpenAd?(ad: self, didLoadFail: nsError)
// }
//
// func didDisplay(_ ad: MAAd) {
// self.delegate?.appOpenAdDidShow?(ad: self)
// }
//
// func didHide(_ ad: MAAd) {
// self.delegate?.appOpenAdDidDismiss?(ad: self)
// }
//
// func didClick(_ ad: MAAd) {
// self.delegate?.appOpenAdDidClick?(ad: self)
// }
//
// func didFail(toDisplay ad: MAAd, withError error: MAError) {
// let nsError = NSError(domain: error.message, code: error.code.rawValue)
// self.delegate?.appOpenAd?(ad: self, didDisplayFail: nsError)
// }
//
//
//
//}
//

View File

@ -0,0 +1,88 @@
////
//// VPApplovinBannerAd.swift
//// Veloria
////
//// Created by on 2025/7/17.
////
//
//import UIKit
//import AppLovinSDK
//
//class VPApplovinBannerAd: NSObject, VPBannerAd {
//
// let size = CGSize.init(width: UIScreen.width, height: 59)
//
// var delegate: (any VPBannerAdDelegate)?
//
// ///
// private var isLoaded = false
//
// private(set) lazy var _adView: MAAdView = {
// let view = MAAdView(adUnitIdentifier: adUnitID)
// view.frame = .init(x: 0, y: 0, width: size.width, height: size.height)
// view.delegate = self
// return view
// }()
//
// var adView: UIView {
// return _adView
// }
//
// var adPlatformKey: String {
// return VPAdPlatformKey.applovin.rawValue
// }
//
// var adUnitID: String {
// return VPAdManager.applovin_bannerAdUnitID
// }
//
// func loadAd() {
// _adView.loadAd()
// }
//}
//
////MARK: -------------- MAAdViewAdDelegate --------------
//extension VPApplovinBannerAd: MAAdViewAdDelegate {
// func didExpand(_ ad: MAAd) {
//
// }
//
// func didCollapse(_ ad: MAAd) {
//
// }
//
// func didLoad(_ ad: MAAd) {
// if !isLoaded {
// isLoaded = true
// self.delegate?.bannerAdDidLoadFinish?(bannerAd: self)
// }
// }
//
// func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) {
// if !isLoaded {
// isLoaded = true
// let nsError = NSError(domain: error.message, code: error.code.rawValue)
// self.delegate?.bannerAd?(bannerAd: self, didLoadFail: nsError)
// }
// }
//
// func didDisplay(_ ad: MAAd) {
// self.delegate?.bannerAdDidShow?(bannerAd: self)
// }
//
// func didHide(_ ad: MAAd) {
// self.delegate?.bannerAdDidDismiss?(bannerAd: self)
// }
//
// func didClick(_ ad: MAAd) {
// self.delegate?.bannerAdDidClick?(ad: self)
// }
//
// func didFail(toDisplay ad: MAAd, withError error: MAError) {
//
// }
//
//
//
//}
//

View File

@ -0,0 +1,131 @@
////
//// VPBannerAdManager.swift
//// Veloria
////
//// Created by on 2025/7/15.
////
//
//import UIKit
//#if canImport(AppLovinSDK)
//import AppLovinSDK
//#endif
//
//
//@objc protocol VPBannerAdDelegate: NSObjectProtocol {
// ///广
// @objc optional func bannerAd(bannerAd: VPBannerAd, didLoadFail error: Error)
// ///广
// @objc optional func bannerAdDidLoadFinish(bannerAd: VPBannerAd)
// ///广
// @objc optional func bannerAdDidShow(bannerAd: VPBannerAd)
// ///广
// @objc optional func bannerAdDidDismiss(bannerAd: VPBannerAd)
// ///广
// @objc optional func bannerAdDidClick(ad: VPBannerAd)
//}
//
//@objc protocol VPBannerAd: NSObjectProtocol {
//
// weak var delegate: VPBannerAdDelegate? { get set }
//
// var adView: UIView { get }
//
// var adPlatformKey: String { get }
//
// var adUnitID: String { get }
//
// func loadAd()
//
//}
//
//class VPBannerAdManager: NSObject {
//
// let size = CGSize.init(width: UIScreen.width, height: 59)
//
// var videoInfo: VPVideoInfoModel?
//
// private var adsDate: Date?
//
// private(set) lazy var bannerAd: VPBannerAd = {
// let ad = VPApplovinBannerAd()
// ad.delegate = self
// return ad
// }()
//
// var adView: UIView {
// return bannerAd.adView
// }
//
// deinit {
// NotificationCenter.default.removeObserver(self)
// }
//
// override init() {
// super.init()
// NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackgroundNotification), name: UIApplication.didEnterBackgroundNotification, object: nil)
//
// bannerAd.loadAd()
// }
//
//
//}
//
////MARK: -------------- VPBannerAdDelegate --------------
//extension VPBannerAdManager: VPBannerAdDelegate {
//
// ///广
// func bannerAd(bannerAd: VPBannerAd, didLoadFail error: Error) {
// requestStatAd(type: "load_failed", errorMsg: error.localizedDescription)
// }
// ///广
// func bannerAdDidLoadFinish(bannerAd: VPBannerAd) {
// adsDate = Date()
// requestStatAd(type: "start", errorMsg: nil)
// }
// ///广
// func bannerAdDidShow(bannerAd: VPBannerAd) {
//
// }
// ///广
// func bannerAdDidDismiss(bannerAd: VPBannerAd) {
// requestStatAd(type: "close", errorMsg: nil)
// }
// ///广
// func bannerAdDidClick(ad: VPBannerAd) {
// requestStatAd(type: "click", errorMsg: nil)
// }
//
//}
//
//extension VPBannerAdManager {
//
// func requestStatAd(type: String, errorMsg: String?) {
//
// var seconds = 0
// if let adsDate = self.adsDate {
// if type == "click" || type == "close" {
// seconds = Int(Date().timeIntervalSince(adsDate))
// }
// }
//
// if type == "close" && self.adsDate == nil { return }
//
// let model = VPStatAdModel()
// model.view_seconds = seconds
// model.type = type
// model.ads_id = bannerAd.adUnitID
// model.ad_platform_key = VPAdPlatformKey(rawValue: bannerAd.adPlatformKey)
// model.error_msg = errorMsg
// model.scene = .banner
// model.short_play_id = self.videoInfo?.short_play_id
// model.short_play_video_id = self.videoInfo?.short_play_video_id
//
// VPStatAPI.requestStatAd(model: model)
// }
//
//
// @objc private func didEnterBackgroundNotification() {
//
// self.requestStatAd(type: "Interrupt", errorMsg: nil)
// }
//}

View File

@ -0,0 +1,91 @@
////
//// VPApplovinRewardedAd.swift
//// Veloria
////
//// Created by on 2025/7/15.
////
//
//import UIKit
//#if canImport(AppLovinSDK)
//import AppLovinSDK
//#endif
//
//class VPApplovinRewardedAd: NSObject, VPRewardedAd {
//
//
//#if canImport(AppLovinSDK)
// private var rewardedAd: MARewardedAd?
//#endif
//
// weak var delegate: (any VPRewardedAdDelegate)?
//
// var adInfo: VPAdInfo? = nil
//
// var isReady: Bool {
//#if canImport(AppLovinSDK)
// return rewardedAd?.isReady ?? false
//#else
// return false
//#endif
// }
//
// func loadAd(adInfo: VPAdInfo) {
// self.adInfo = adInfo
//#if canImport(AppLovinSDK)
// rewardedAd = MARewardedAd.shared(withAdUnitIdentifier: adInfo.ads_id ?? "")
// rewardedAd?.delegate = self
// rewardedAd?.load()
//#endif
// }
//
// func showAd() {
//#if canImport(AppLovinSDK)
// if isReady {
// rewardedAd?.show()
// }
//#endif
// }
//
//
//}
//
//#if canImport(AppLovinSDK)
////MARK: -------------- MARewardedAdDelegate --------------
//extension VPApplovinRewardedAd: MARewardedAdDelegate {
//
// func didLoad(_ ad: MAAd) {
// self.delegate?.rewardedAdDidLoadFinish?(ad: self)
// }
//
// func didFailToLoadAd(forAdUnitIdentifier adUnitIdentifier: String, withError error: MAError) {
//
// let nsError = NSError(domain: error.message, code: error.code.rawValue)
// self.delegate?.rewardedAd?(ad: self, didLoadFail: nsError)
// }
//
// func didDisplay(_ ad: MAAd) {
// self.delegate?.rewardedAdDidShow?(ad: self)
// }
//
// func didHide(_ ad: MAAd) {
// self.delegate?.rewardedAdDidDismiss?(ad: self)
// }
//
// func didClick(_ ad: MAAd) {
// self.delegate?.rewardedAdDidClick?(ad: self)
// }
//
// func didFail(toDisplay ad: MAAd, withError error: MAError) {
// let nsError = NSError(domain: error.message, code: error.code.rawValue)
// self.delegate?.rewardedAd?(ad: self, didDisplayFail: nsError)
// }
//
// func didRewardUser(for ad: MAAd, with reward: MAReward) {
// if let adInfo = self.adInfo {
// self.delegate?.rewardedAd?(ad: self, userDidEarnReward: adInfo)
// }
// }
//}
//#endif
//
//

View File

@ -0,0 +1,101 @@
////
//// VPGoogleRewardedAd.swift
//// Veloria
////
//// Created by on 2025/7/14.
////
//
//import UIKit
//#if canImport(GoogleMobileAds)
//import GoogleMobileAds
//#endif
//
//class VPGoogleRewardedAd: NSObject, VPRewardedAd {
//
//
//
//#if canImport(GoogleMobileAds)
// private var rewardedAd: RewardedAd?
//#endif
//
// weak var delegate: VPRewardedAdDelegate?
//
// var adInfo: VPAdInfo?
//
// var isReady: Bool {
//#if canImport(GoogleMobileAds)
// if rewardedAd != nil {
// return true
// } else {
// return false
// }
//#else
// return false
//#endif
// }
//
// func loadAd(adInfo: VPAdInfo) {
//#if canImport(GoogleMobileAds)
// self.adInfo = adInfo
//#if DEBUG
// adInfo.ads_id = VPAdManager.google_rewardedAdUnitID
//#endif
// let adUnitID = adInfo.ads_id ?? ""
// let request = Request()
//
// RewardedAd.load(with: adUnitID, request: request) { [weak self] rewardedAd, error in
// guard let self = self else { return }
// if let error = error {
// self.delegate?.rewardedAd?(ad: self, didLoadFail: error)
// return
// }
// self.rewardedAd = rewardedAd
// self.rewardedAd?.fullScreenContentDelegate = self
// self.delegate?.rewardedAdDidLoadFinish?(ad: self)
// }
//#endif
// }
//
// func showAd() {
//#if canImport(GoogleMobileAds)
// guard let rewardedAd = rewardedAd else { return }
//
// rewardedAd.present(from: nil) { [weak self] in
// guard let self = self else { return }
// guard let adInfo = self.adInfo else { return }
// self.delegate?.rewardedAd?(ad: self, userDidEarnReward: adInfo)
// }
//#endif
// }
//
//
//
//}
//
////MARK: -------------- FullScreenContentDelegate --------------
//#if canImport(GoogleMobileAds)
//extension VPGoogleRewardedAd: FullScreenContentDelegate {
//
// /// Tells the delegate that the ad failed to present full screen content.
// func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
// self.delegate?.rewardedAd?(ad: self, didDisplayFail: error)
// }
//
// /// Tells the delegate that the ad will present full screen content.
// func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {
// print("====Ad will present full screen content.")
// self.delegate?.rewardedAdDidShow?(ad: self)
// }
//
// /// Tells the delegate that the ad dismissed full screen content.
// func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
// print("====Ad did dismiss full screen content.")
// self.delegate?.rewardedAdDidDismiss?(ad: self)
// }
//
// func adDidRecordClick(_ ad: any FullScreenPresentingAd) {
// self.delegate?.rewardedAdDidClick?(ad: self)
// }
//
//}
//#endif

View File

@ -0,0 +1,292 @@
////
//// VPRewardedAdManager.swift
//// Veloria
////
//// Created by on 2025/7/14.
////
//
//import UIKit
//
//
//@objc protocol VPRewardedAdManagerDelegate: NSObjectProtocol {
//
// ///
// @objc optional func rewardedAdManager(adManager: VPRewardedAdManager, userDidEarnReward adInfo: VPAdInfo)
// ///广
// @objc optional func rewardedAdManagerDidLoadFinish(adManager: VPRewardedAdManager)
// ///广
// @objc optional func rewardedAdManager(adManager: VPRewardedAdManager, didLoadFail error: Error)
// ///广
// @objc optional func rewardedAdManager(adManager: VPRewardedAdManager, didDisplayFail error: Error)
// ///广
// @objc optional func rewardedAdManagerDidShow(adManager: VPRewardedAdManager)
// ///广
// @objc optional func rewardedAdManager(adManager: VPRewardedAdManager, didDismiss adInfo: VPAdInfo)
// ///广
// @objc optional func rewardedAdManagerDidClick(adManager: VPRewardedAdManager)
// ///
// @objc optional func rewardedAdManager(adManager: VPRewardedAdManager, didOtherFail error: Error)
//
//}
//
//@objc protocol VPRewardedAdDelegate: NSObjectProtocol {
//
// ///
// @objc optional func rewardedAd(ad: VPRewardedAd, userDidEarnReward adInfo: VPAdInfo)
// ///广
// @objc optional func rewardedAd(ad: VPRewardedAd, didLoadFail error: Error)
// ///广
// @objc optional func rewardedAdDidLoadFinish(ad: VPRewardedAd)
// ///广
// @objc optional func rewardedAd(ad: VPRewardedAd, didDisplayFail error: Error)
// ///广
// @objc optional func rewardedAdDidShow(ad: VPRewardedAd)
// ///广
// @objc optional func rewardedAdDidDismiss(ad: VPRewardedAd)
// ///广
// @objc optional func rewardedAdDidClick(ad: VPRewardedAd)
//}
//
//@objc protocol VPRewardedAd: NSObjectProtocol {
//
// weak var delegate: VPRewardedAdDelegate? { get set }
// var adInfo: VPAdInfo? { get set }
// var isReady: Bool { get }
//
// func loadAd(adInfo: VPAdInfo)
// func showAd()
//
//}
//
//class VPRewardedAdManager: NSObject {
//
// static let manager = VPRewardedAdManager()
//
// private weak var delegate: VPRewardedAdManagerDelegate?
//
// ///广
// private var isLoadingAd = false
// ///广
// private(set) var isEnable = true
//
// private var adInfo: VPAdInfo?
//
// private var adsDate: Date?
// ///
// var statScene: VPStatAdModel.AdScene?
// var videoInfo: VPVideoInfoModel?
//
// var rewardedAd: VPRewardedAd? {
// didSet {
// rewardedAd?.delegate = self
// }
// }
//
// var isReady: Bool {
// return rewardedAd?.isReady ?? false
// }
//
// deinit {
// NotificationCenter.default.removeObserver(self)
// }
//
// override init() {
// super.init()
// NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackgroundNotification), name: UIApplication.didEnterBackgroundNotification, object: nil)
// }
//
// func loadAd() {
// if isReady || isLoadingAd { return }
//
// self.isLoadingAd = true
//
// self.getAdInfo { [weak self] adInfo in
// guard let self = self else { return }
//
// switch adInfo.platform_key {
// case .google:
// let ad = VPGoogleRewardedAd()
// self.rewardedAd = ad
// ad.loadAd(adInfo: adInfo)
//
// case .applovin:
// let ad = VPApplovinRewardedAd()
// self.rewardedAd = ad
// ad.loadAd(adInfo: adInfo)
//
// default:
// self.adInfo = nil
// break
// }
// }
//
// }
//
// func showAd(delegate: VPRewardedAdManagerDelegate? = nil) {
// if isEnable && !isLoadingAd && !isReady {//广广广
// loadAd()
// }
//
// if !isEnable || !isReady {
// VPToast.show(text: "veloria_no_ads_tip".localized)
// let error = NSError(domain: "veloria_no_ads_tip".localized, code: -1)
// self.delegate?.rewardedAdManager?(adManager: self, didOtherFail: error)
// return
// }
//
// guard let rewardedAd = self.rewardedAd else { return }
// self.delegate = delegate
//
// rewardedAd.showAd()
// }
//
// private func clean() {
// self.rewardedAd = nil
// self.statScene = nil
// self.videoInfo = nil
// self.delegate = nil
// }
//}
//
////MARK: -------------- VPRewardedAdDelegate --------------
//extension VPRewardedAdManager: VPRewardedAdDelegate {
//
// ///
// func rewardedAd(ad: VPRewardedAd, userDidEarnReward adInfo: VPAdInfo) {
// self.requestStatAd(type: "reward", errorMsg: nil)
// self.delegate?.rewardedAdManager?(adManager: self, userDidEarnReward: adInfo)
// }
// ///广
// func rewardedAd(ad: VPRewardedAd, didLoadFail error: Error) {
// isEnable = false
// isLoadingAd = false
//
// self.requestStatAd(type: "load_failed", errorMsg: error.localizedDescription)
// self.delegate?.rewardedAdManager?(adManager: self, didLoadFail: error)
//
// self.clean()
// }
// ///广
// func rewardedAdDidLoadFinish(ad: VPRewardedAd) {
// isLoadingAd = false
//
// self.delegate?.rewardedAdManagerDidLoadFinish?(adManager: self)
// }
// ///广
// func rewardedAd(ad: VPRewardedAd, didDisplayFail error: Error) {
//
// self.requestStatAd(type: "show_failed", errorMsg: error.localizedDescription)
// self.delegate?.rewardedAdManager?(adManager: self, didDisplayFail: error)
//
// self.isEnable = false
// self.clean()
//
// /*
// if self.adInfo?.platform_key != .google {
// let adInfo = VPAdInfo()
// adInfo.platform_key = .google
// adInfo.ads_id = VPAdManager.google_rewardedAdUnitID
//
// let ad = VPGoogleRewardedAd()
// self.rewardedAd = ad
// ad.loadAd(adInfo: adInfo)
//
// } else {
// self.isEnable = false
// }
// */
//
// }
// ///广
// func rewardedAdDidShow(ad: VPRewardedAd) {
// self.adsDate = Date()
//
// self.requestStatAd(type: "start", errorMsg: nil)
// self.delegate?.rewardedAdManagerDidShow?(adManager: self)
// }
// ///广
// func rewardedAdDidDismiss(ad: VPRewardedAd) {
//
//
// var seconds = 0
// if let adsDate = self.adsDate {
// seconds = Int(Date().timeIntervalSince(adsDate))
// }
//
//
// self.requestStatAd(type: "close", seconds: seconds, errorMsg: nil) { [weak self] in
// guard let self = self else { return }
// if let adInfo = ad.adInfo {
// self.delegate?.rewardedAdManager?(adManager: self, didDismiss: adInfo)
// }
//
// self.clean()
// //广
// self.loadAd()
// }
//
// }
//
// ///广
// func rewardedAdDidClick(ad: VPRewardedAd) {
//
// var seconds = 0
// if let adsDate = self.adsDate {
// seconds = Int(Date().timeIntervalSince(adsDate))
// }
// self.requestStatAd(type: "click", seconds: seconds, errorMsg: nil)
//
// self.delegate?.rewardedAdManagerDidClick?(adManager: self)
// }
//
//}
//
//
//extension VPRewardedAdManager {
// private func requestStatAd(type: String, seconds: Int = 0, errorMsg: String?, completer: (() -> Void)? = nil) {
// guard let adInfo = adInfo else { return }
//
// let model = VPStatAdModel()
// model.type = type
// model.ads_id = adInfo.ads_id
// model.view_seconds = seconds
// model.ad_platform_key = adInfo.platform_key
// model.error_msg = errorMsg
// model.scene = self.statScene
// model.short_play_id = self.videoInfo?.short_play_id
// model.short_play_video_id = self.videoInfo?.short_play_video_id
//
// VPStatAPI.requestStatAd(model: model) { finish in
// completer?()
// }
// }
//
// @objc private func didEnterBackgroundNotification() {
// if self.statScene == nil { return }
//
// self.requestStatAd(type: "Interrupt", seconds: 0, errorMsg: nil)
// }
//
//
// private func getAdInfo(completer: ((_ adInfo: VPAdInfo) -> Void)?) {
//
// if let adInfo = self.adInfo {
// completer?(adInfo)
// }
// VPAdAPI.requestShowAdInfo { [weak self] adInfo in
// guard let self = self else { return }
//
// if let adInfo = adInfo {
// self.adInfo = adInfo
// completer?(adInfo)
// } else {
// let adInfo = VPAdInfo.createNormalAdInfo()
// self.adInfo = adInfo
// completer?(adInfo)
// }
//
// }
// }
//}
//
//

View File

@ -0,0 +1,40 @@
//
// VPAdInfo.swift
// Veloria
//
// Created by on 2025/7/14.
//
import UIKit
import SmartCodable
enum VPAdPlatformKey: String, SmartCaseDefaultable {
case google = "google"
case applovin = "applovin"
}
struct VPAdDataModel: SmartCodable {
var ad: VPAdInfo?
}
class VPAdInfo:NSObject, SmartCodable {
required override init() {
super.init()
}
var id: String?
var platform_name: String?
var ads_id: String?
var status: String?
var platform_key: VPAdPlatformKey?
static func createNormalAdInfo() -> VPAdInfo {
var adInfo = VPAdInfo()
adInfo.platform_key = .applovin
adInfo.ads_id = VPAdManager.applovin_rewardedAdUnitID
return adInfo
}
}

View File

@ -0,0 +1,100 @@
//
// VPAdManager.swift
// Veloria
//
// Created by on 2025/7/14.
//
import UIKit
#if canImport(GoogleMobileAds)
import GoogleMobileAds
#endif
#if canImport(AppLovinSDK)
import AppLovinSDK
#endif
class VPAdManager: NSObject {
static var completer: (() -> Void)?
static func start() {
#if canImport(GoogleMobileAds)
MobileAds.shared.start()
#endif
initialize_applovinSdk()
}
///AppLovinSDK
static func initialize_applovinSdk(completer: (() -> Void)? = nil) {
if completer != nil {
self.completer = completer
}
#if canImport(AppLovinSDK)
if !ALSdk.shared().isInitialized {
//
let initConfig = ALSdkInitializationConfiguration(sdkKey: "XW2aulJv9urKD4MIIFT1xcSCuyTHaDZ9qUbDqygnTLS04GkdX7WMQJviGP5vDRWGsk4OJJIyLGRV3mbLqOWx0W") { builder in
builder.mediationProvider = ALMediationProviderMAX
//#if DEBUG
// builder.testDeviceAdvertisingIdentifiers = [JXUUID.idfa()]
//#endif
}
ALSdk.shared().initialize(with: initConfig) { sdkConfig in
// Start loading ads
self.completer?()
}
} else {
self.completer?()
}
#endif
}
}
//MARK: -------------- ID --------------
extension VPAdManager {
///广ID
static var google_rewardedAdUnitID: String {
#if DEBUG
return "ca-app-pub-3940256099942544/1712485313"
#else
return ""
#endif
}
///广ID
static var google_appOpenAdUnitID: String {
#if DEBUG
return "ca-app-pub-3940256099942544/5575463023"
#else
return ""
#endif
}
///广ID
static var google_bannerAdUnitID: String {
#if DEBUG
return "ca-app-pub-3940256099942544/2435281174"
#else
return ""
#endif
}
///广ID
static var applovin_rewardedAdUnitID: String {
return "39bf981e27728eae"
}
///广ID
static var applovin_appOpenAdUnitID: String {
return "d587df0fd75ff03b"
}
///广ID
static var applovin_bannerAdUnitID: String {
return "1eb6278843149711"
}
}

View File

@ -0,0 +1,33 @@
//
// VPStatAdModel.swift
// Veloria
//
// Created by on 2025/7/14.
//
import UIKit
import SmartCodable
class VPStatAdModel: NSObject, SmartCodable {
required override init() {
super.init()
}
enum AdScene: String, SmartCaseDefaultable {
case detail = "detail"
case me = "me"
case reward = "reward"
case splash = "splash"
case banner = "banner"
}
var type: String? //start click error click show_failed load_failed Interrupt(退) close
var ads_id: String?
var view_seconds: Int?
var ad_platform_key: VPAdPlatformKey?
var scene: AdScene? // splash reward banner detail me turntable
var short_play_id: String?
var short_play_video_id: String?
var error_msg: String?
}

View File

@ -0,0 +1,137 @@
//
// SRAdmobOpenAd.swift
// SynthReel
//
// Created by on 2025/12/11.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
#if canImport(GoogleMobileAds)
import GoogleMobileAds
#endif
class SRAdmobOpenAd: NSObject, SRAppOpenAd {
#if canImport(GoogleMobileAds)
private var appOpenAd: AppOpenAd?
#endif
weak var delegate: SRAppOpenAdDelegate?
var adInfo: VPAdInfo?
var isShowingAd: Bool = false
// SRAppOpenAd adPlatformKey
var adPlatformKey: String {
return "AdMob" // 广
}
var isReady: Bool {
#if canImport(GoogleMobileAds)
return appOpenAd != nil
#else
return false
#endif
}
// SRAppOpenAd adUnitID
var adUnitID: String {
return adInfo?.ads_id ?? ""
}
// 广
func loadAd() {
#if canImport(GoogleMobileAds)
guard let adInfo = self.adInfo else { return }
let adUnitID = adInfo.ads_id ?? ""
let request = Request()
AppOpenAd.load(with: adUnitID, request: request) { [weak self] appOpenAd, error in
guard let self = self else { return }
if let error = error {
self.delegate?.appOpenAd?(ad: self, didLoadFail: error)
return
}
self.appOpenAd = appOpenAd
appOpenAd?.fullScreenContentDelegate = self
if let error = error {
self.delegate?.appOpenAd?(ad: self, didLoadFail: error)
} else {
//
self.delegate?.appOpenAdDidLoadFinish?(ad: self)
}
}
#endif
}
// 广
func showAd() {
#if canImport(GoogleMobileAds)
guard let appOpenAd = appOpenAd else {
print("App open ad is not ready yet.")
return
}
if isShowingAd {
return print("App open ad is already showing.")
}
appOpenAd.present(from: nil)
isShowingAd = true
#endif
}
}
// MARK: - FullScreenContentDelegate
#if canImport(GoogleMobileAds)
extension SRAdmobOpenAd: FullScreenContentDelegate {
func adDidRecordImpression(_ ad: FullScreenPresentingAd) {
print("App open ad recorded an impression.")
delegate?.appOpenAdDidShow?(ad: self)
}
func adDidRecordClick(_ ad: FullScreenPresentingAd) {
print("App open ad recorded a click.")
delegate?.appOpenAdDidClick?(ad: self)
}
func adWillDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
print("App open ad will be dismissed.")
}
func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {
print("App open ad will be presented.")
delegate?.appOpenAdDidShow?(ad: self)
}
func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
print("App open ad was dismissed.")
//
delegate?.appOpenAdDidDismiss?(ad: self)
appOpenAd = nil
isShowingAd = false
// 广
loadAd()
}
func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
print("App open ad failed to present with error: \(error.localizedDescription)")
appOpenAd = nil
isShowingAd = false
delegate?.appOpenAd?(ad: self, didDisplayFail: error)
// 广
loadAd()
}
}
#endif

View File

@ -0,0 +1,227 @@
////
//// SROpenAdManager.swift
//// SynthReel
////
//// Created by on 2025/12/10.
//// Copyright © 2025 SR. All rights reserved.
////
//
import UIKit
import Foundation
import GoogleMobileAds
@objc protocol SRAppOpenAdManagerDelegate: NSObjectProtocol {
///广
@objc optional func appOpenAdManagerDidLoadFinish(adManager: SRAppOpenAdDelegate)
///广
@objc optional func appOpenAdManager(adManager: SRAppOpenAdDelegate, didLoadFail error: Error)
///广
@objc optional func appOpenAdManager(adManager: SRAppOpenAdDelegate, didDisplayFail error: Error)
///广
@objc optional func appOpenAdManagerDidShow(adManager: SRAppOpenAdDelegate)
///广
@objc optional func appOpenAdManagerDidDismiss(adManager: SRAppOpenAdDelegate)
///广
@objc optional func appOpenAdManagerDidClick(adManager: SRAppOpenAdDelegate)
///
@objc optional func appOpenAdManager(adManager: SRAppOpenAdDelegate, didOtherFail error: Error)
}
@objc protocol SRAppOpenAdDelegate: NSObjectProtocol {
///广
@objc optional func appOpenAd(ad: SRAppOpenAd, didLoadFail error: Error)
///广
@objc optional func appOpenAdDidLoadFinish(ad: SRAppOpenAd)
///广
@objc optional func appOpenAd(ad: SRAppOpenAd, didDisplayFail error: Error)
///广
@objc optional func appOpenAdDidShow(ad: SRAppOpenAd)
///广
@objc optional func appOpenAdDidDismiss(ad: SRAppOpenAd)
///广
@objc optional func appOpenAdDidClick(ad: SRAppOpenAd)
}
@objc protocol SRAppOpenAd: NSObjectProtocol {
weak var delegate: SRAppOpenAdDelegate? { get set }
var adPlatformKey: String { get }
var adUnitID: String { get }
var isReady: Bool { get }
func loadAd()
func showAd()
}
class SRAppOpenAdManager: NSObject {
static let manager = SRAppOpenAdManager()
weak var delegate: SRAppOpenAdManagerDelegate?
private(set) var appOpenAd: SRAppOpenAd? {
didSet {
oldValue?.delegate = nil
appOpenAd?.delegate = self
}
}
private var isLoading = false
private var isShowing = false
///
private var needAutoShow = false
private var timeOutTimer: Timer?
private var adsDate: Date?
deinit {
NotificationCenter.default.removeObserver(self)
}
override init() {
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackgroundNotification), name: UIApplication.didEnterBackgroundNotification, object: nil)
}
func showAdIfAvailable() {
if self.appOpenAd?.isReady == true {
self.showAd()
} else {
self.timeOutTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(handleTimeOutTimer), userInfo: nil, repeats: false)
self.needAutoShow = true
self.loadAd()
}
}
func loadAd() {
guard !isLoading else { return }
self.isLoading = true
appOpenAd = SRAdmobOpenAd()
appOpenAd?.loadAd()
}
func showAd() {
guard self.appOpenAd?.isReady == true else { return }
guard !self.isShowing else { return }
self.isShowing = true
self.appOpenAd?.showAd()
}
@objc private func handleTimeOutTimer() {
self.needAutoShow = false
cleanTimer()
let error = NSError(domain: "time-out", code: -1)
requestStatAd(type: "load_failed", errorMsg: error.localizedDescription)
self.delegate?.appOpenAdManager?(adManager: self, didOtherFail: error)
}
private func cleanTimer() {
self.timeOutTimer?.invalidate()
self.timeOutTimer = nil
}
private func clean() {
appOpenAd = nil
delegate = nil
isShowing = false
self.adsDate = nil
}
}
//MARK: -------------- VPAppOpenAdDelegate --------------
extension SRAppOpenAdManager: SRAppOpenAdDelegate {
///广
func appOpenAd(ad: SRAppOpenAd, didLoadFail error: Error) {
cleanTimer()
isLoading = false
requestStatAd(type: "load_failed", errorMsg: error.localizedDescription)
self.delegate?.appOpenAdManager?(adManager: self, didLoadFail: error)
clean()
}
///广
func appOpenAdDidLoadFinish(ad: SRAppOpenAd) {
cleanTimer()
self.isLoading = false
self.delegate?.appOpenAdManagerDidLoadFinish?(adManager: self)
if needAutoShow {
self.needAutoShow = false
self.showAd()
}
}
///广
func appOpenAd(ad: SRAppOpenAd, didDisplayFail error: Error) {
requestStatAd(type: "show_failed", errorMsg: error.localizedDescription)
self.delegate?.appOpenAdManager?(adManager: self, didDisplayFail: error)
clean()
}
///广
func appOpenAdDidShow(ad: SRAppOpenAd) {
adsDate = Date()
requestStatAd(type: "start", errorMsg: nil)
self.delegate?.appOpenAdManagerDidShow?(adManager: self)
}
///广
func appOpenAdDidDismiss(ad: SRAppOpenAd) {
requestStatAd(type: "close", errorMsg: nil)
self.delegate?.appOpenAdManagerDidDismiss?(adManager: self)
clean()
}
///广
func appOpenAdDidClick(ad: SRAppOpenAd) {
requestStatAd(type: "click", errorMsg: nil)
self.delegate?.appOpenAdManagerDidClick?(adManager: self)
}
}
extension SRAppOpenAdManager {
private func requestStatAd(type: String, errorMsg: String?) {
guard let appOpenAd = self.appOpenAd else { return }
var seconds = 0
if let adsDate = self.adsDate {
if type == "click" || type == "close" {
seconds = Int(Date().timeIntervalSince(adsDate))
}
}
let model = VPStatAdModel()
model.type = type
model.view_seconds = seconds
model.ads_id = appOpenAd.adUnitID
model.ad_platform_key = VPAdPlatformKey(rawValue: appOpenAd.adPlatformKey)
model.error_msg = errorMsg
model.scene = .splash
// VPStatAPI.requestStatAd(model: model)
}
@objc private func didEnterBackgroundNotification() {
guard self.isShowing else { return }
self.requestStatAd(type: "Interrupt", errorMsg: nil)
}
}

View File

@ -0,0 +1,101 @@
//
// SRAdManger.swift
// SynthReel
//
// Created by on 2025/12/11.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
#if canImport(GoogleMobileAds)
import GoogleMobileAds
#endif
#if canImport(AppLovinSDK)
import AppLovinSDK
#endif
class SRAdManger: NSObject {
static var completer: (() -> Void)?
static func start() {
#if canImport(GoogleMobileAds)
MobileAds.shared.start()
#endif
initialize_applovinSdk()
}
///AppLovinSDK
static func initialize_applovinSdk(completer: (() -> Void)? = nil) {
if completer != nil {
self.completer = completer
}
#if canImport(AppLovinSDK)
if !ALSdk.shared().isInitialized {
//
let initConfig = ALSdkInitializationConfiguration(sdkKey: "XW2aulJv9urKD4MIIFT1xcSCuyTHaDZ9qUbDqygnTLS04GkdX7WMQJviGP5vDRWGsk4OJJIyLGRV3mbLqOWx0W") { builder in
builder.mediationProvider = ALMediationProviderMAX
//#if DEBUG
// builder.testDeviceAdvertisingIdentifiers = [JXUUID.idfa()]
//#endif
}
ALSdk.shared().initialize(with: initConfig) { sdkConfig in
// Start loading ads
self.completer?()
}
} else {
self.completer?()
}
#endif
}
}
//MARK: -------------- ID --------------
extension SRAdManger {
///广ID
static var google_rewardedAdUnitID: String {
#if DEBUG
return "ca-app-pub-3940256099942544/1712485313"
#else
return "ca-app-pub-6275401933109862/2121481439"
#endif
}
///广ID
static var google_appOpenAdUnitID: String {
#if DEBUG
return "ca-app-pub-3940256099942544/5575463023"
#else
return "ca-app-pub-6275401933109862/7130265535"
#endif
}
///广ID
static var google_bannerAdUnitID: String {
#if DEBUG
return "ca-app-pub-3940256099942544/2435281174"
#else
return "ca-app-pub-6275401933109862/7971191024"
#endif
}
///广ID
static var applovin_rewardedAdUnitID: String {
return "3708ac6b6026e375"
}
// ///广ID
// static var applovin_appOpenAdUnitID: String {
// return "d587df0fd75ff03b"
// }
// ///广ID
// static var applovin_bannerAdUnitID: String {
// return "1eb6278843149711"
// }
}

View File

@ -2,7 +2,7 @@
// SRDefine.swift // SRDefine.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/12. // Created by on 2025/11/12.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
import UIKit import UIKit

View File

@ -2,7 +2,7 @@
// SRUserDefaultsKey.swift // SRUserDefaultsKey.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/12. // Created by on 2025/11/12.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
@ -12,3 +12,12 @@ let kSRAccountTokenDefaultsKey = "kSRAccountTokenDefaultsKey"
/// ///
let kSRUserInfoDefaultsKey = "kSRUserInfoDefaultsKey" let kSRUserInfoDefaultsKey = "kSRUserInfoDefaultsKey"
let kSRWaitRestoreIAPDefaultsKey = "kSRWaitRestoreIAPDefaultsKey"
let kSRApnsAlertDefaultsKey = "kSRApnsAlertDefaultsKey"
let kSRVersionUpdateAlertDefaultsKey = "kSRVersionUpdateAlertDefaultsKey"
let kSRHasBeenOpenedAPPDefaultsKey = "kSRHasBeenOpenedAPPDefaultsKey"

View File

@ -0,0 +1,24 @@
//
// Dictionary+SRAdd.swift
// SynthReel
//
// Created by on 2025/11/28.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
extension Dictionary {
func toJsonString() -> String? {
do {
let data = try JSONSerialization.data(withJSONObject: self)
let jsonStr = String(data: data, encoding: .utf8)
return jsonStr
} catch {
}
return nil
}
}

View File

@ -2,7 +2,7 @@
// NSNumber+SRAdd.swift // NSNumber+SRAdd.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/17. // Created by on 2025/11/17.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,17 +2,19 @@
// String+SRAdd.swift // String+SRAdd.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/13. // Created by on 2025/11/13.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
import UIKit import UIKit
import YYCategories import YYCategories
import SmartCodable
extension String { extension String {
var localized: String { var localized: String {
return String(localized: LocalizationValue(self)) let value = String(localized: LocalizationValue(self))
return value.replacingOccurrences(of: "<br>", with: "\n")
} }
func localizedReplace(text: String) -> String { func localizedReplace(text: String) -> String {
@ -24,3 +26,32 @@ extension String {
} }
} }
extension String: SmartCodable {
static func timeZone() -> String {
let timeZone = NSTimeZone.local as NSTimeZone
let timeZoneSecondsFromGMT = timeZone.secondsFromGMT / 3600
return String(format: "GMT+0%d:00", timeZoneSecondsFromGMT)
}
}
extension String {
///url
func urlQuryToDictionary() -> [String : Any] {
let array = self.components(separatedBy: "&")
var tempDic: [String : Any] = [:]
array.forEach {
if let strRange = $0.range(of: "=") {
var key: String = String($0.prefix(upTo: strRange.upperBound))
key.removeLast()
var value: String = String($0.suffix(from: strRange.upperBound))
value = value.removingPercentEncoding ?? value
tempDic[key] = value
}
}
return tempDic
}
}

View File

@ -2,7 +2,7 @@
// UIFont+SRAdd.swift // UIFont+SRAdd.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/13. // Created by on 2025/11/13.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// UINavigationBar+SRAdd.swift // UINavigationBar+SRAdd.swift
// SynthReel // SynthReel
// //
// Created by CSGY on 2025/11/25. // Created by on 2025/11/25.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// UIScreen+SRAdd.swift // UIScreen+SRAdd.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/13. // Created by on 2025/11/13.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// UIScrollView+SRAdd.swift // UIScrollView+SRAdd.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/17. // Created by on 2025/11/17.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// UIStackView+SRAdd.swift // UIStackView+SRAdd.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/15. // Created by on 2025/11/15.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// UIView+SRAdd.swift // UIView+SRAdd.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/17. // Created by on 2025/11/17.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// UserDefaults+SRAdd.swift // UserDefaults+SRAdd.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/12. // Created by on 2025/11/12.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -0,0 +1,26 @@
//
// SROpenAppModel.swift
// SynthReel
//
// Created by on 2025/12/9.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import SmartCodable
struct SROpenAppModel: SmartCodable {
enum Path: String, SmartCaseDefaultable {
case videoDetail = "detail"
///
case feedback = "feedback"
///
case promotion = "promotion"
}
var id: String?
var message_id: String?
var short_play_id: String?
var path: Path?
}

View File

@ -0,0 +1,39 @@
//
// SRVersionUpdateModel.swift
// SynthReel
//
// Created by on 2025/12/10.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import SmartCodable
struct SRVersionUpdateModel: SmartCodable {
var version_code: String?
var des: String?
var version_name: String?
var force: Bool?
func canUpdate() -> Bool {
guard let version = version_code else { return false }
let result = kSRAPPVersion.compare(version, options: .numeric)
if result == .orderedAscending {
return true
} else {
return false
}
}
static func mappingForKey() -> [SmartKeyTransformer]? {
return [
CodingKeys.des <--- ["description"]
]
}
}

View File

@ -2,7 +2,7 @@
// SRNetwork.swift // SRNetwork.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/12. // Created by on 2025/11/12.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
@ -20,7 +20,8 @@ class SRNetwork: NSObject {
static func request<T>(parameters: SRNetwork.Parameters, completion: ((_ response: SRNetwork.Response<T>) -> Void)?) { static func request<T>(parameters: SRNetwork.Parameters, completion: ((_ response: SRNetwork.Response<T>) -> Void)?) {
if SRAccountManager.manager.token == nil { if SRLogin.manager.token == nil {
print(SRLogin.manager.token ?? "123")
self.requestToken(completer: nil) self.requestToken(completer: nil)
} }
@ -48,11 +49,15 @@ class SRNetwork: NSObject {
static func _request<T>(parameters: SRNetwork.Parameters, completion: ((_ response: SRNetwork.Response<T>) -> Void)?) -> Cancellable { static func _request<T>(parameters: SRNetwork.Parameters, completion: ((_ response: SRNetwork.Response<T>) -> Void)?) -> Cancellable {
if parameters.isLoding { if parameters.isLoding {
SRHud.show() DispatchQueue.main.async {
SRHud.show()
}
} }
return provider.request(.request(parameters: parameters)) { result in return provider.request(.request(parameters: parameters)) { result in
if parameters.isLoding { if parameters.isLoding {
SRHud.dismiss() DispatchQueue.main.async {
SRHud.dismiss()
}
} }
guard let completion = completion else {return} guard let completion = completion else {return}
_resultDispose(parameters: parameters, result: result, completion: completion) _resultDispose(parameters: parameters, result: result, completion: completion)
@ -81,7 +86,7 @@ class SRNetwork: NSObject {
self.requestToken { token in self.requestToken { token in
if token != nil { if token != nil {
_Concurrency.Task { _Concurrency.Task {
await SRAccountManager.manager.updateUserInfo() await SRLogin.manager.requestUserToken(completer: nil)
} }
} }
} }
@ -164,7 +169,7 @@ class SRNetwork: NSObject {
extension SRNetwork { extension SRNetwork {
///token ///token
static func requestToken(completer: ((_ token: SRAccountToken?) -> Void)?) { static func requestToken(completer: ((_ token: SRTokenModel?) -> Void)?) {
guard self.tokenOperation == nil else { guard self.tokenOperation == nil else {
completer?(nil) completer?(nil)
return return
@ -173,11 +178,11 @@ extension SRNetwork {
self.tokenOperation = BlockOperation(block: { self.tokenOperation = BlockOperation(block: {
let semaphore = DispatchSemaphore(value: 0) let semaphore = DispatchSemaphore(value: 0)
let param = SRNetwork.Parameters(path: "/customer/register") let param = SRNetwork.Parameters(path: "/customer/register")
print("chongfu")
DispatchQueue.main.async { DispatchQueue.main.async {
SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRAccountToken>) in SRNetwork.request(parameters: param) { (response: SRNetwork.Response<SRTokenModel>) in
if let token = response.data { if let token = response.data {
SRAccountManager.manager.setAccountToken(token) SRLogin.manager.loginSuccessSaveToken(token)
} }
do { semaphore.signal() } do { semaphore.signal() }
self.tokenOperation = nil self.tokenOperation = nil
@ -189,5 +194,4 @@ extension SRNetwork {
operationQueue.addOperation(self.tokenOperation!) operationQueue.addOperation(self.tokenOperation!)
} }
} }

View File

@ -2,7 +2,7 @@
// SRNetworkModel.swift // SRNetworkModel.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/12. // Created by on 2025/11/12.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
@ -30,7 +30,7 @@ extension SRNetwork {
var data: T? var data: T?
var msg: String? var msg: String?
@SmartIgnored @IgnoredKey
var rawData: Any? var rawData: Any?
var isSuccess: Bool { var isSuccess: Bool {

View File

@ -2,7 +2,7 @@
// SRNetworkReachableManager.swift // SRNetworkReachableManager.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/12. // Created by on 2025/11/12.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRResponseCryptor.swift // SRResponseCryptor.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/12. // Created by on 2025/11/12.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRTargetType.swift // SRTargetType.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/12. // Created by on 2025/11/12.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
@ -56,7 +56,7 @@ extension SRTargetType: TargetType {
"device-id" : SRDeviceId.shared.id, //id "device-id" : SRDeviceId.shared.id, //id
"system-type" : "ios", "system-type" : "ios",
"model" : UIDevice.current.machineModelName ?? "", "model" : UIDevice.current.machineModelName ?? "",
"authorization" : SRAccountManager.manager.token?.token ?? "", "authorization" : SRLogin.manager.token?.token ?? "",
"device-gaid" : UIDevice.current.identifierForVendor?.uuidString ?? "", "device-gaid" : UIDevice.current.identifierForVendor?.uuidString ?? "",
"product-prefix" : "SynthReel" "product-prefix" : "SynthReel"
] ]

View File

@ -2,16 +2,27 @@
// SRUrlPath.swift // SRUrlPath.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/12. // Created by on 2025/11/12.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
let SRBaseURL = "https://api-hbqinjiu.hbqinjiu.com" let SRBaseURL = "https://api-synthreeltv.synthreeltv.com"
let SRUrlPathPrefix = "/eon" let SRUrlPathPrefix = "/th"
let SRWebBaseURL = "https://www.hbqinjiu.com" let SRWebBaseURL = "https://www.synthreeltv.com"
let SRCampaignWebURL = "https://campaign.hbqinjiu.com" let SRCampaignWebURL = "https://campaign.synthreeltv.com"
//@"https://api-univicos.univicos.com/uni";
///
let kSRFeedBackHomeWebUrl = SRCampaignWebURL + "/pages/leave/index"
///
let kSRFeedBackListWebUrl = SRCampaignWebURL + "/pages/leave/list"
///
let kSRFeedBackDetailWebUrl = SRCampaignWebURL + "/pages/leave/detail"
///
let kSRLogoutWebUrl = SRCampaignWebURL + "/pages/setting/logout"
let kSRRewardWebUrl = SRCampaignWebURL + "/pages/reward/theme4"
/* /*
SynthReel SynthReel
admin-api https://admin-api-synthreeltv.synthreeltv.com admin-api https://admin-api-synthreeltv.synthreeltv.com

View File

@ -2,11 +2,12 @@
// SRCollectionView.swift // SRCollectionView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/14. // Created by on 2025/11/14.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
import UIKit import UIKit
import LYEmptyView
class SRCollectionView: UICollectionView { class SRCollectionView: UICollectionView {

View File

@ -2,7 +2,7 @@
// SRGradientView.swift // SRGradientView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/15. // Created by on 2025/11/15.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -0,0 +1,45 @@
//
// SRGradientbutton.swift
// SynthReel
//
// Created by on 2025/11/27.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRGradientbutton: UIButton {
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
var fa_gradientLayer: CAGradientLayer {
return self.layer as! CAGradientLayer
}
var fa_colors: [CGColor]? {
didSet {
fa_gradientLayer.colors = fa_colors
}
}
var fa_startPoint: CGPoint = .zero {
didSet {
fa_gradientLayer.startPoint = fa_startPoint
}
}
var fa_endPoint: CGPoint = .zero {
didSet {
fa_gradientLayer.endPoint = fa_endPoint
}
}
var fa_locations: [NSNumber] = [] {
didSet {
fa_gradientLayer.locations = fa_locations
}
}
}

View File

@ -2,7 +2,7 @@
// SRImageView.swift // SRImageView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/15. // Created by on 2025/11/15.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRLabel.swift // SRLabel.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/14. // Created by on 2025/11/14.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRNavgationTitleView.swift // SRNavgationTitleView.swift
// SynthReel // SynthReel
// //
// Created by CSGY on 2025/11/25. // Created by on 2025/11/25.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
@ -26,7 +26,7 @@ class SRNavgationTitleView: UIView {
private let titleLabel: UILabel = { private let titleLabel: UILabel = {
let label = UILabel() let label = UILabel()
label.font = .font(ofSize: 18, weight: .bold) label.font = .font(ofSize: 18, weight: .bold).withBoldItalic()
label.textAlignment = .center label.textAlignment = .center
label.textColor = .white label.textColor = .white
return label return label

View File

@ -2,7 +2,7 @@
// SRPanModalContentView.swift // SRPanModalContentView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/18. // Created by on 2025/11/18.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
@ -12,16 +12,20 @@ import SnapKit
class SRPanModalContentView: HWPanModalContentView { class SRPanModalContentView: HWPanModalContentView {
var mainScrollView: UIScrollView?
var contentHeight = UIScreen.height * (2 / 3) var contentHeight = UIScreen.height * (2 / 3)
var bgImage: UIImage? = UIImage(named: "pan_bg_image_01")
///UI contentSize ///UI contentSize
func setNeedsLayoutUpdate() { func setNeedsLayoutUpdate() {
self.panModalSetNeedsLayoutUpdate() self.panModalSetNeedsLayoutUpdate()
} }
lazy var bgImageView: UIImageView = { lazy var bgImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "pan_bg_image_01")) let imageView = UIImageView(image: bgImage)
return imageView return imageView
}() }()
@ -35,15 +39,28 @@ class SRPanModalContentView: HWPanModalContentView {
} }
} }
//
func updateBackgroundImage(_ img: UIImage?) {
self.bgImage = img
self.bgImageView.image = img
}
//
func updateContentHeight(_ height: CGFloat) {
self.contentHeight = height
self.panModalSetNeedsLayoutUpdate()
}
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
//MARK: HWPanModalPresentable //MARK: HWPanModalPresentable
override func panScrollable() -> UIScrollView? { override func panScrollable() -> UIScrollView? {
return nil return mainScrollView
} }
override func longFormHeight() -> PanModalHeight { override func longFormHeight() -> PanModalHeight {
return PanModalHeightMake(.content, contentHeight) return PanModalHeightMake(.content, contentHeight)
} }

View File

@ -2,7 +2,7 @@
// SRScrollView.swift // SRScrollView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/15. // Created by on 2025/11/15.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRTableView.swift // SRTableView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/18. // Created by on 2025/11/18.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRTableViewCell.swift // SRTableViewCell.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/18. // Created by on 2025/11/18.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -0,0 +1,53 @@
//
// SRAppOpenAdViewController.swift
// SynthReel
//
// Created by on 2025/12/11.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRAppOpenAdViewController: SRViewController {
var didEndBlock: (() -> Void)?
private(set) lazy var lanuchVC: UIViewController? = {
let vc = SRTool.lanuchViewController
return vc
}()
override func viewDidLoad() {
super.viewDidLoad()
if let vc = lanuchVC {
addChild(vc)
view.addSubview(vc.view)
}
let manager = SRAppOpenAdManager.manager
manager.delegate = self
manager.showAdIfAvailable()
}
}
//MARK: -------------- VPAppOpenAdManagerDelegate --------------
extension SRAppOpenAdViewController: SRAppOpenAdManagerDelegate {
func appOpenAdManagerDidDismiss(adManager: SRAppOpenAdDelegate) {
self.didEndBlock?()
}
func appOpenAdManager(adManager: SRAppOpenAdDelegate, didLoadFail error: any Error) {
self.didEndBlock?()
}
func appOpenAdManager(adManager: SRAppOpenAdDelegate, didDisplayFail error: any Error) {
self.didEndBlock?()
}
func appOpenAdManager(adManager: SRAppOpenAdDelegate, didOtherFail error: any Error) {
self.didEndBlock?()
}
}

View File

@ -0,0 +1,52 @@
//
// SRAppStartViewController.swift
// SynthReel
//
// Created by on 2025/12/9.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRAppStartViewController: SRViewController {
var openAppBlock: (() -> Void)?
private lazy var lanuchVC: UIViewController? = {
let vc = SRTool.lanuchViewController
return vc
}()
private lazy var startButton: UIButton = {
let button = SRGradientbutton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
self.openAppBlock?()
}))
button.setBackgroundImage(.startButton, for: .normal)
button.setTitle("start", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = .font(ofSize:16, weight: .semibold)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
if let vc = lanuchVC {
addChild(vc)
view.addSubview(vc.view)
}
view.addSubview(startButton)
startButton.snp.makeConstraints { make in
make.left.right.equalToSuperview().inset(48)
make.height.equalTo(50)
make.bottom.equalToSuperview().offset(-(UIScreen.safeBottom + 110))
}
}
}

View File

@ -2,7 +2,7 @@
// SRNavigationController.swift // SRNavigationController.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/13. // Created by on 2025/11/13.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRTabBarController.swift // SRTabBarController.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/13. // Created by on 2025/11/13.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
@ -32,6 +32,9 @@ class SRTabBarController: ESTabBarController {
self.tabBar.standardAppearance = appearance self.tabBar.standardAppearance = appearance
self.tabBar.scrollEdgeAppearance = appearance self.tabBar.scrollEdgeAppearance = appearance
self.tabBar.isTranslucent = false self.tabBar.isTranslucent = false
// SRTool.checkUpdates()
} }

View File

@ -2,7 +2,7 @@
// SRViewController.swift // SRViewController.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/13. // Created by on 2025/11/13.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -0,0 +1,83 @@
//
// SRAppWebViewController.swift
// SynthReel
//
// Created by on 2025/11/28.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRAppWebViewController: SRBaseWebViewController {
var id: String?
private var receiveDataCount = 0
var theme: String? = "theme_16"
override func viewDidLoad() {
super.viewDidLoad()
self.autoTitle = false
if webUrl == kSRFeedBackListWebUrl {
self.title = "synthreel_feedback_history".localized
} else if webUrl == kSRFeedBackHomeWebUrl {
self.title = "synthreel_feedback".localized
} else if webUrl == kSRFeedBackDetailWebUrl {
self.title = "synthreel_feedback_detail".localized
} else if webUrl == kSRLogoutWebUrl {
self.title = "synthreel_account_deletion".localized
}else if webUrl == kSRRewardWebUrl {
self.title = "Rewards".localized
}
}
override func fa_webViewDidFinishLoad(_ webView: SRWebView) {
super.fa_webViewDidFinishLoad(webView)
receiveDataCount = 0
receiveDataFromNative()
}
}
extension SRAppWebViewController {
func receiveDataFromNative() {
receiveDataCount += 1
if receiveDataCount > 10 { return }
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
guard let self = self else { return }
var dic = [
"token" : SRLogin.manager.token?.token ?? "",
"time_zone" : String.timeZone(),
// "lang" : FALanguageManager.manager.currentLanguageKey,
"lang" : "en",
"type" : "ios",
"device-id" : SRDeviceId.shared.id
]
if let theme = theme {
dic["theme"] = theme
}
if let id = id {
dic["id"] = id
}
if let json = dic.toJsonString() {
let js = "receiveDataFromNative(\(json))"
self.webView.evaluateJavaScript(js) { [weak self] _, error in
guard let self = self else { return }
if error != nil {
self.receiveDataFromNative()
}
}
}
}
}
}

View File

@ -0,0 +1,112 @@
//
// SRBaseWebViewController + Script.swift
// SynthReel
//
// Created by on 2025/11/28.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import WebKit
import ZLPhotoBrowser
import SmartCodable
///APP
let kSRWebMessageAPP = "js2app"
///
let kSRWebMessageOpenFeedbackList = "openFeedbackList"
///
let kSRWebMessageOpenFeedbackDetail = "openFeedbackDetail"
///
let kSRWebMessageOpenPhotoPicker = "openPhotoPicker"
///
let kSRWebMessageAccountDeletionFinish = "accountLogout"
extension SRBaseWebViewController {
func fa_webViewUserContentController(didReceive message: WKScriptMessage) {
let name = message.name
let body = message.body
switch name {
case kSRWebMessageOpenFeedbackList:
let vc = SRAppWebViewController()
vc.webUrl = kSRFeedBackListWebUrl
self.navigationController?.pushViewController(vc, animated: true)
case kSRWebMessageOpenFeedbackDetail:
guard let body = body as? [String : Any] else { return }
guard let id = body["id"] as? Int else { return }
let vc = SRAppWebViewController()
vc.id = "\(id)"
vc.webUrl = kSRFeedBackDetailWebUrl
self.navigationController?.pushViewController(vc, animated: true)
case kSRWebMessageOpenPhotoPicker:
openPhotoPicker()
case kSRWebMessageAPP:
guard let body = message.body as? [String : Any] else { return }
guard let model = SRWebMessageModel.deserialize(from: body) else { return }
let type = model.type
let data = model.data
if type == "login" {
// let view = FALoginView()
// view.present(in: nil)
} else if type == "open_notify" {
// openNotify()
} else if type == "watch_video" {
let vc = SRDetailPlayerViewController()
vc.shortId = data?.short_play_id
// vc.activityId = data?.activity_id
self.navigationController?.pushViewController(vc, animated: true)
} else {
guard let urlStr = data?.link else { return }
guard let url = URL(string: urlStr) else { return }
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}
case kSRWebMessageAccountDeletionFinish:
self.navigationController?.popToRootViewController(animated: true)
NotificationCenter.default.post(name: SRLogin.loginStatusChangeNotification, object: nil)
default:
break
}
}
///
private func openPhotoPicker() {
ZLPhotoConfiguration.default().allowSelectOriginal = false
ZLPhotoConfiguration.default().maxSelectCount = 1
ZLPhotoConfiguration.default().allowEditImage = false
ZLPhotoConfiguration.default().allowSelectVideo = false
ZLPhotoConfiguration.default().allowSelectGif = false
ZLPhotoConfiguration.default().allowTakePhotoInLibrary = false
let picker = ZLPhotoPicker()
picker.selectImageBlock = { [weak self] (results, _) in
guard let self = self else { return }
guard let image = results.first?.image else { return }
guard let imageData = image.jpegData(compressionQuality: 0.8) else { return }
let imageDataStr = imageData.base64EncodedString(options: .endLineWithCarriageReturn)
let js = "uploadConvertImage('\(imageDataStr)')"
self.webView.evaluateJavaScript(js)
}
picker.showPhotoLibrary(sender: self)
}
}

View File

@ -0,0 +1,114 @@
//
// SRBaseWebViewController.swift
// SynthReel
//
// Created by on 2025/11/28.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import WebKit
class SRBaseWebViewController: SRViewController {
var webUrl: String?
///
var autoTitle = true
var needAutoRefresh = true
private(set) lazy var webView: SRWebView = {
let controller = WKUserContentController()
let config = WKWebViewConfiguration()
config.userContentController = controller
config.preferences.javaScriptEnabled = true
/** JS */
config.preferences.javaScriptCanOpenWindowsAutomatically = true
let webView = SRWebView(frame: self.view.bounds, configuration: config)
webView.delegate = self
return webView
}()
override func viewDidLoad() {
super.viewDidLoad()
// self.edgesForExtendedLayout = []
fa_setupLayout()
if let url = webUrl {
self.load(webUrl: url)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
// self.fa_setNavigationStyle()
}
func load(webUrl: String) {
let str: String = webUrl
guard let url = URL(string: str) else { return }
let request = URLRequest(url: url, timeoutInterval: 30)
self.webView.load(request)
}
func reload() {
self.webView.reload()
}
}
extension SRBaseWebViewController {
private func fa_setupLayout() {
self.view.addSubview(webView)
self.webView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(0)
make.right.equalToSuperview().offset(0)
make.bottom.equalToSuperview().offset(0)
make.top.equalToSuperview().offset(UIScreen.navBarHeight)
}
}
}
//MARK: -------------- VPWebViewDelegate --------------
extension SRBaseWebViewController: SRWebViewDelegate {
func fa_webView(_ webView: SRWebView, shouldStartLoadWith navigationAction: WKNavigationAction) -> Bool {
self.webView.isHidden = false
return true
}
func fa_webViewDidStartLoad(_ webView: SRWebView) {
SRHud.show(containerView: self.view)
}
func fa_webView(webView: SRWebView, didChangeTitle title: String) {
if autoTitle {
self.title = title
}
}
func fa_webViewDidFinishLoad(_ webView: SRWebView) {
self.webView.isHidden = false
SRHud.dismiss()
}
func fa_webView(_ webView: SRWebView, didFailLoadWithError error: any Error) {
SRHud.dismiss()
}
func fa_userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
self.fa_webViewUserContentController(didReceive: message)
}
}

View File

@ -0,0 +1,26 @@
//
// SRWebMessageModel.swift
// SynthReel
//
// Created by on 2025/11/28.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import SmartCodable
struct SRWebMessageModel: SmartCodable {
var type: String?
var data: SRWebMessageData?
}
struct SRWebMessageData: SmartCodable {
var activity_id: String?
var short_play_id: String?
var link: String?
}

View File

@ -0,0 +1,153 @@
//
// SRWebView.swift
// SynthReel
//
// Created by on 2025/11/28.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
@preconcurrency import WebKit
import YYText
@objc protocol SRWebViewDelegate: NSObjectProtocol {
@objc optional func fa_webView(_ webView: SRWebView, shouldStartLoadWith navigationAction: WKNavigationAction) -> Bool
@objc optional func fa_webViewDidStartLoad(_ webView: SRWebView)
@objc optional func fa_webViewDidFinishLoad(_ webView: SRWebView)
@objc optional func fa_webView(_ webView: SRWebView, didFailLoadWithError error: Error)
///
@objc optional func fa_webView(webView: SRWebView, didChangeProgress progress: CGFloat)
///
@objc optional func fa_webView(webView: SRWebView, didChangeTitle title: String)
///web
@objc optional func fa_userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)
}
class SRWebView: WKWebView {
weak var delegate: SRWebViewDelegate?
private(set) var scriptMessageHandlerArray: [String] = [
kSRWebMessageAPP,
kSRWebMessageOpenFeedbackList,
kSRWebMessageOpenFeedbackDetail,
kSRWebMessageOpenPhotoPicker,
kSRWebMessageAccountDeletionFinish,
]
deinit {
self.removeObserver(self, forKeyPath: "estimatedProgress")
self.removeObserver(self, forKeyPath: "title")
}
override init(frame: CGRect, configuration: WKWebViewConfiguration) {
super.init(frame: frame, configuration: configuration)
addScriptMessageHandler()
_setupInit()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func _setupInit() {
self.isOpaque = false
self.navigationDelegate = self
self.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
self.addObserver(self, forKeyPath: "title", options: .new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if object as? SRWebView == self {
if keyPath == "estimatedProgress", let progress = change?[NSKeyValueChangeKey.newKey] as? CGFloat {
self.delegate?.fa_webView?(webView: self, didChangeProgress: progress)
} else if keyPath == "title", let title = change?[NSKeyValueChangeKey.newKey] as? String {
self.delegate?.fa_webView?(webView: self, didChangeTitle: title)
}
}
}
func load(urlStr: String) {
guard let url = URL(string: urlStr) else { return }
let request = URLRequest(url: url, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 30)
self.load(request)
}
func removeScriptMessageHandler() {
self.scriptMessageHandlerArray.forEach{
configuration.userContentController.removeScriptMessageHandler(forName: $0)
}
}
func addScriptMessageHandler() {
self.scriptMessageHandlerArray.forEach{
configuration.userContentController.add(YYTextWeakProxy(target: self) as! WKScriptMessageHandler, name: $0)
}
}
}
//MARK:-------------- WKNavigationDelegate --------------
extension SRWebView: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
decisionHandler(.allow);
}
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url,
url.scheme != "http",
url.scheme != "https"
{
UIApplication.shared.open(url)
decisionHandler(.cancel)
return
}
if let result = self.delegate?.fa_webView?(self, shouldStartLoadWith: navigationAction) {
if result {
decisionHandler(.allow)
} else {
decisionHandler(.cancel)
}
} else {
decisionHandler(.allow)
}
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
self.delegate?.fa_webViewDidStartLoad?(self)
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.delegate?.fa_webViewDidFinishLoad?(self)
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
self.delegate?.fa_webView?(self, didFailLoadWithError: error)
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
self.delegate?.fa_webView?(self, didFailLoadWithError: error)
}
}
//MARK:-------------- WKScriptMessageHandler --------------
extension SRWebView: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
self.delegate?.fa_userContentController?(userContentController, didReceive: message)
}
}

View File

@ -0,0 +1,14 @@
//
// SRCoinPackCanReceiveModel.swift
// SynthReel
//
// Created by on 2025/12/9.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import SmartCodable
struct SRCoinPackCanReceiveModel: SmartCodable {
var coins: Int?
}

View File

@ -0,0 +1,26 @@
//
// SRCoinsPackModel.swift
// SynthReel
//
// Created by on 2025/11/28.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import SmartCodable
struct SRCoinsPackModel: 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: [SRCoinsPackReceiveModel]?
}

View File

@ -0,0 +1,26 @@
//
// SRCoinsPackReceiveModel.swift
// SynthReel
//
// Created by on 2025/11/28.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import SmartCodable
struct SRCoinsPackReceiveModel: 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

@ -0,0 +1,246 @@
//
// SRCoinPackController.swift
// SynthReel
//
// Created by on 2025/11/28.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinPackController: SRViewController {
private var model: SRCoinsPackModel? {
didSet {
headerView.model = model
claimView.dataArr = model?.receive_list ?? []
}
}
private var payDataModel : SRPayDateModel? {
didSet {
var arr: [SRPayItem] = []
payDataModel?.list_coins?.forEach {
if $0.buy_type == .subCoins {
arr.append($0)
}
}
self.buyView.dataArr = arr
}
}
private var payDataRequest: SRPayDataRequest?
lazy var topImageview = UIImageView.init(image: .topCoinPack)
private lazy var bgIconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "查看图片 25"))
return imageView
}()
private lazy var scrollView: SRScrollView = {
let scrollView = SRScrollView()
return scrollView
}()
private lazy var headerView: SRCoinPackHeaderView = {
let view = SRCoinPackHeaderView()
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: SRCoinsPackClaimListView = {
let view = SRCoinsPackClaimListView()
view.clickClaimButton = { [weak self] id in
guard let self = self else { return }
self.requestReceiveCoins(id)
}
return view
}()
private lazy var buyView: SRCoinsPackBuyView = {
let view = SRCoinsPackBuyView()
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 = .white.withAlphaComponent(0.5)
label.text = "coins_pack_tips_title".localized
return label
}()
private weak var recommandView: UIView?
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 = .white.withAlphaComponent(0.5)
label.attributedText = att
label.numberOfLines = 0
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
()
self.navigationItem.title = "My Refills".localized
payDataModel = SRIapManager.manager.payDateModel
sr_setui()
requestCoinsPackData()
updateLayout()
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
private func updateLayout() {
stackView.sr_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 SRCoinPackController {
func sr_setui() {
view.addSubview(topImageview)
topImageview.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
}
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 SRCoinPackController {
private func requestCoinsPackData() {
Task {
let model = await SRStoreAPI.requestCoinsPackData()
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 = SRPayDataRequest()
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?) {
Task {
let sucess = await SRStoreAPI.requestReceiveCoinsPackCoins(id: id)
self.requestCoinsPackData()
if sucess {
self.showVideoRecommand()
}
}
}
///
private func showVideoRecommand() {
guard self.recommandView == nil else { return }
Task {
let (model, _, _) = await SRShortApi.requestRecommand()
guard let model = model else { return }
guard self.recommandView == nil else { return }
let view = SRDetailRecommendview()
view.dataArr = model
view.didSelectedVideo = { [weak self] model in
guard let self = self else { return }
let vc = SRDetailPlayerViewController()
vc.shortId = model.short_play_id
self.navigationController?.pushViewController(vc, animated: true)
}
view.show(in: SRTool.keyWindow)
self.recommandView = view
}
}
}

View File

@ -0,0 +1,82 @@
//
// SRCoinPackConfirmItem1View.swift
// SynthReel
//
// Created by on 2025/12/4.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinPackConfirmItem1View: SRCoinPackConfirmItemView {
override init(frame: CGRect) {
super.init(frame: frame)
lazy var bgIconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "getBg"))
return imageView
}()
titleLabel.text = "synthreel_bonus_you_get".localized
let view1 = getItemView(image: UIImage(named: "dailyIocn"), text: "synthreel_weekly_refill_package".localized)
let view2 = getItemView(image: UIImage(named: "rewardicon"), text: "synthreel_dailu_bonuses".localized)
addSubview(bgIconImageView)
addSubview(view1)
addSubview(view2)
bgIconImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
view1.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.top.equalTo(titleLabel.snp.bottom).offset(8)
make.height.equalTo(83)
make.bottom.equalToSuperview().offset(-16)
}
view2.snp.makeConstraints { make in
make.left.equalTo(view1.snp.right).offset(12)
make.right.equalToSuperview().offset(-12)
make.width.height.top.equalTo(view1)
}
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func getItemView(image: UIImage?, text: String) -> UIView {
let view = UIImageView()
view.image = .itemBg
let imageView = UIImageView(image: image)
imageView.setContentHuggingPriority(.required, for: .horizontal)
imageView.setContentCompressionResistancePriority(.required, for: .horizontal)
let label = UILabel()
label.numberOfLines = 0
label.font = .font(ofSize: 12, weight: .init(800))
label.textColor = .white
label.text = text
label.textAlignment = .center
view.addSubview(imageView)
view.addSubview(label)
imageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(5)
}
label.snp.makeConstraints { make in
make.left.right.equalToSuperview().inset(10)
make.top.equalTo(imageView.snp.bottom).offset(10)
}
return view
}
}

View File

@ -0,0 +1,134 @@
//
// SRCoinPackConfirmItem2View.swift
// SynthReel
//
// Created by on 2025/12/4.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinPackConfirmItem2View: SRCoinPackConfirmItemView {
var list: [String]? {
didSet {
self.stackView.sr_removeAllArrangedSubview()
list?.forEach {
let view = SRCoinPackConfirmItem2View.ItemView()
view.text = $0
self.stackView.addArrangedSubview(view)
}
}
}
private lazy var bgIconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "receiveBg"))
return imageView
}()
private lazy var lineView: UIView = {
let view = UIView()
view.backgroundColor = .white.withAlphaComponent(0.1)
return view
}()
private lazy var stackView: UIStackView = {
let view = UIStackView()
view.axis = .vertical
view.spacing = 24
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.titleLabel.text = "synthreel_coin_bag_buy_tip_title".localized
fa_setupLayout()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension SRCoinPackConfirmItem2View {
private func fa_setupLayout() {
addSubview(bgIconImageView)
addSubview(lineView)
addSubview(stackView)
bgIconImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
lineView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(12)
make.centerX.equalToSuperview()
make.top.equalTo(titleLabel.snp.bottom).offset(16)
make.height.equalTo(1)
}
stackView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.top.equalTo(lineView.snp.bottom).offset(16)
make.bottom.equalToSuperview().offset(-16)
}
}
}
extension SRCoinPackConfirmItem2View {
class ItemView: UIView {
var text: String? {
didSet {
textLabel.text = text
}
}
private lazy var iconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "coins_icon_06"))
return imageView
}()
private lazy var textLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 14, weight: .regular)
label.textColor = .white
label.numberOfLines = 0
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(iconImageView)
addSubview(textLabel)
iconImageView.snp.makeConstraints { make in
make.left.equalToSuperview()
make.top.equalToSuperview()
}
textLabel.snp.makeConstraints { make in
make.left.equalTo(iconImageView.snp.right).offset(12)
make.right.lessThanOrEqualToSuperview()
make.top.equalToSuperview()
make.bottom.equalToSuperview()
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}

View File

@ -0,0 +1,41 @@
//
// SRCoinPackConfirmItemView.swift
// SynthReel
//
// Created by on 2025/12/4.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinPackConfirmItemView: UIView {
lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 18, weight: .bold)
label.textColor = .white
label.textAlignment = .center
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .black
layer.cornerRadius = 8
layer.masksToBounds = true
addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.right.lessThanOrEqualToSuperview().offset(-16)
make.top.equalToSuperview().offset(16)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -0,0 +1,207 @@
//
// SRCoinPackConfirmView.swift
// SynthReel
//
// Created by on 2025/12/4.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
import HWPanModal
class SRCoinPackConfirmView: SRPanModalContentView {
var buyFinishHandle: (() -> Void)?
var shortPlayId: String?
var videoId: String?
var model: SRPayItem? {
didSet {
item2View.list = model?.ext_info?.sub_coins_txt_list
var price = model?.price ?? ""
if model?.discount_type == 1, let introductoryPrice = model?.introductionaryOffer {
price = introductoryPrice.price.stringValue
} else if model?.discount_type == 2, let discount = model?.promotionalOffers?.first {
price = discount.price.stringValue
}
priceLabel.text = "\(model?.currency ?? "")\(price)/\(model?.getTimeString() ?? "")"
}
}
// private lazy var bgImageView: UIImageView = {
// let imageView = UIImageView(image: UIImage(named: ""))
// return imageView
// }()
private lazy var bgIconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: ""))
return imageView
}()
private lazy var titleLabel: SRLabel = {
let label = SRLabel()
label.font = .font(ofSize: 24, weight: .init(700))
label.textColors = [UIColor._4_CFFD_4.cgColor, UIColor._51_D_4_FF.cgColor]
label.textStartPoint = .init(x: 0.5, y: 0)
label.textEndPoint = .init(x: 0.5, y: 1)
label.text = "synthreel_what_you_get".localized
return label
}()
private lazy var closeButton: UIButton = {
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
Task {
await self.dismiss(animated: true)
}
}))
button.setImage(UIImage(named: "Close"), for: .normal)
return button
}()
private lazy var scrollView: SRScrollView = {
let scrollView = SRScrollView()
scrollView.addObserver(self, forKeyPath: "contentSize", context: nil)
return scrollView
}()
private lazy var item1View: SRCoinPackConfirmItem1View = {
let view = SRCoinPackConfirmItem1View()
return view
}()
private lazy var item2View: SRCoinPackConfirmItem2View = {
let view = SRCoinPackConfirmItem2View()
return view
}()
private lazy var priceLabel: SRLabel = {
let label = SRLabel()
label.font = .font(ofSize: 18, weight: .bold)
label.textColors = [UIColor.srBlue.cgColor, UIColor.srBlue.cgColor]
label.textStartPoint = .init(x: 0, y: 0.5)
label.textEndPoint = .init(x: 1, y: 0.5)
return label
}()
private lazy var continueButton: UIButton = {
let button = UIButton(type: .custom, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
guard let model = self.model else { return }
SRIapManager.manager.start(model: model, shortPlayId: self.shortPlayId, videoId: self.videoId) { [weak self] finish in
guard let self = self else { return }
if finish {
self.buyFinishHandle?()
Task {
await self.dismiss(animated: true)
}
}
}
}))
button.setTitle("synthreel_continue".localized, for: .normal)
button.setTitleColor(.black, for: .normal)
button.titleLabel?.font = .font(ofSize: 14, weight: .bold)
button.setBackgroundImage(.continueBG, for: .normal)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .clear
sr_setupLayout()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "contentSize" {
self.panModalSetNeedsLayoutUpdate()
}
}
override func allowsTapBackgroundToDismiss() -> Bool {
return false
}
override func cornerRadius() -> CGFloat {
return 0
}
override func longFormHeight() -> PanModalHeight {
let height = 96 + scrollView.contentSize.height + 92 + 10
return PanModalHeightMake(.content, height + UIScreen.safeBottom)
}
}
extension SRCoinPackConfirmView {
private func sr_setupLayout() {
addSubview(bgImageView)
bgImageView.addSubview(bgIconImageView)
addSubview(titleLabel)
addSubview(closeButton)
addSubview(scrollView)
scrollView.addSubview(item1View)
scrollView.addSubview(item2View)
addSubview(priceLabel)
addSubview(continueButton)
bgImageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
bgIconImageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview()
}
titleLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(55)
}
closeButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-16)
make.top.equalToSuperview().offset(24)
}
scrollView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.top.equalToSuperview().offset(96)
make.bottom.equalToSuperview().offset(-(92 + UIScreen.safeBottom + 10))
}
item1View.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.top.equalToSuperview()
}
item2View.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.centerX.equalToSuperview()
make.top.equalTo(item1View.snp.bottom).offset(12)
make.bottom.equalToSuperview()
}
priceLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.centerY.equalTo(scrollView.snp.bottom).offset(22)
}
continueButton.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.left.equalToSuperview().offset(16)
make.top.equalTo(scrollView.snp.bottom).offset(44)
make.height.equalTo(48)
}
}
}

View File

@ -0,0 +1,328 @@
//
// SRCoinPackHeaderView.swift
// SynthReel
//
// Created by on 2025/11/28.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinPackHeaderView: UIView {
var clickClaimButton: (() -> Void)?
var model: SRCoinsPackModel? {
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: UILabel = {
let view = UILabel()
view.font = .font(ofSize: 14, weight: .regular)
view.textColor = .white
view.text = "synthreel_rewards_overview".localized.uppercased()
return view
}()
private lazy var coinsView1: CoinsView = {
let view = CoinsView()
view.title = "synthreel_weekly_total".localized
view.coins = 0
return view
}()
private lazy var coinsView2: CoinsView = {
let view = CoinsView()
view.title = "synthreel_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 = .white.withAlphaComponent(0.5)
label.text = "synthreel_active_refills".localized + ": "
return label
}()
private lazy var activeCountLabel: SRLabel = {
let label = SRLabel()
label.font = .font(ofSize: 12, weight: .medium)
label.textColors = [UIColor._4_CFFD_4.cgColor, UIColor._51_D_4_FF.cgColor]
label.textStartPoint = .init(x: 0.5, y: 0)
label.textEndPoint = .init(x: 0.5, y: 1)
label.text = "0"
return label
}()
private lazy var claimButton: UIButton = {
let button = UIButton(type: .custom)
// 使
let updateHandler: UIButton.ConfigurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
var config = UIButton.Configuration.plain()
if button.isEnabled {
//
config.background.image = .claimAllHiglit
config.background.imageContentMode = .scaleToFill
//
let coinImage = UIImage(named: "smallCoins")!
let coinText = NSTextAttachment(image: coinImage)
coinText.bounds = CGRect(x: 0, y: -2.5, width: 13, height: 13)
let coinAtt = AttributedString(NSAttributedString(attachment: coinText))
let textAtt = AttributedString("synthreel_claim_all".localized + " ", attributes: AttributeContainer([
.font: UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor: UIColor.white
]))
let countAtt = AttributedString(" \(self.model?.receive_coins ?? 0)", attributes: AttributeContainer([
.font: UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor: UIColor.white
]))
config.attributedTitle = textAtt + coinAtt + countAtt
} else {
//
config.background.image = .claimAllNomarl
config.background.imageContentMode = .scaleToFill
config.attributedTitle = AttributedString("synthreel_get_refill_button_text".localized, attributes: AttributeContainer([
.font: UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor: UIColor.white.withAlphaComponent(0.5)
]))
}
//
config.contentInsets = NSDirectionalEdgeInsets(top: 12, leading: 20, bottom: 12, trailing: 20)
//
button.configuration = config
}
//
button.configurationUpdateHandler = updateHandler
//
button.addAction(UIAction(handler: { [weak self] _ in
self?.clickClaimButton?()
}), for: .touchUpInside)
//
button.isEnabled = false
button.configurationUpdateHandler?(button) //
return button
}()
// private lazy var claimButton: UIButton = {
// let config = UIButton.Configuration.plain()
// let button = UIButton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in
// self?.clickClaimButton?()
// }))
// button.isEnabled = false
// button.configurationUpdateHandler = { [weak self] button in
// guard let self = self else { return }
// guard let button = button as? UIButton else { return }
// if button.isEnabled {
//// button.fa_colors = [UIColor._53_A_2_F_1.cgColor, UIColor.C_5_DDF_5.cgColor]
// button.setBackgroundImage(.claimAllHiglit, for: .normal)
// let coinImage = UIImage(named: "smallCoins")!
// 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("synthreel_claim_all".localized + " ", attributes: AttributeContainer([
// .font : UIFont.font(ofSize: 14, weight: .bold),
// .foregroundColor : UIColor.white
// ]))
//
// let countAtt = AttributedString(" \(self.model?.receive_coins ?? 0)".localized, attributes: AttributeContainer([
// .font : UIFont.font(ofSize: 14, weight: .bold),
// .foregroundColor : UIColor.white
// ]))
//
// 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.setBackgroundImage(.claimAllNomarl, for: .normal)
// button.configuration?.attributedTitle = AttributedString("synthreel_get_refill_button_text".localized, attributes: AttributeContainer([
// .font : UIFont.font(ofSize: 14, weight: .bold),
// .foregroundColor : UIColor.white.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 SRCoinPackHeaderView {
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 SRCoinPackHeaderView {
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 = .white.withAlphaComponent(0.5)
return label
}()
private lazy var iconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "smallCoins"))
return imageView
}()
private lazy var coinsLabel: SRLabel = {
let label = SRLabel()
label.font = .font(ofSize: 18, weight: .bold)
label.textColors = [UIColor.srBlue.cgColor, UIColor.srBlue.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,124 @@
//
// SRCoinsPackAlert.swift
// SynthReel
//
// Created by on 2025/12/9.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinsPackAlert: SRBaseAlert {
var claimButtonClick: (() -> Void)?
var coinsCount: Int? {
didSet {
}
}
private lazy var imageView = UIImageView(image: UIImage(named: "coinalertBg"))
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 18, weight: .bold)
label.textColor = .white
label.textAlignment = .center
label.numberOfLines = 0
label.text = "coins_pack_alert_title".localized
return label
}()
private lazy var moreCoinimageView = UIImageView(image: UIImage(named: "moreCoins"))
private lazy var coinLabel: SRLabel = {
let label = SRLabel()
label.textColors = [UIColor.F_1_FF_2_F.cgColor, UIColor.srBlue.cgColor]
label.textStartPoint = .init(x: 0, y: 0.5)
label.textEndPoint = .init(x: 1, y: 0.5)
label.font = .font(ofSize: 20, weight: .semibold)
return label
}()
private lazy var claimButton: UIButton = {
let button = UIButton(type: .custom)
button.setTitle("synthreel_claim_now".localized, for: .normal)
button.setTitleColor(.black, for: .normal)
button.titleLabel?.font = .font(ofSize: 16, weight: .semibold)
button.setBackgroundImage(.cliamNowBg, for: .normal)
button.addTarget(self, action: #selector(claimButtonAciton), for: .touchUpInside)
return button
}()
private lazy var laterButton: UIButton = {
let button = UIButton(type: .custom)
button.setTitle("synthreel_open_notification_later".localized, for: .normal)
button.setTitleColor(.FFFFFF_40, for: .normal)
button.titleLabel?.font = .font(ofSize: 14, weight: .regular)
button.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func claimButtonAciton () {
self.claimButtonClick?()
}
}
extension SRCoinsPackAlert {
private func fa_setupLayout() {
contentView.addSubview(imageView)
imageView.addSubview(titleLabel)
imageView.addSubview(moreCoinimageView)
imageView.addSubview(coinLabel)
imageView.addSubview(claimButton)
imageView.addSubview(laterButton)
imageView.isUserInteractionEnabled = true
imageView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
titleLabel.snp.makeConstraints { make in
make.left.equalTo(17)
make.right.lessThanOrEqualToSuperview().offset(-110)
make.top.equalTo(15)
}
moreCoinimageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(titleLabel.snp.bottom).offset(35)
}
coinLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(moreCoinimageView.snp.bottom).offset(2)
}
claimButton.snp.makeConstraints { make in
make.top.equalTo(coinLabel.snp.bottom).offset(35)
make.left.right.equalToSuperview().inset(17)
make.height.equalTo(48)
}
laterButton.snp.makeConstraints { make in
make.left.right.height.equalTo(claimButton)
make.top.equalTo(claimButton.snp.bottom).offset(1)
make.bottom.equalToSuperview().offset(-13)
}
}
}

View File

@ -0,0 +1,114 @@
//
// SRCoinsPackBuyView.swift
// SynthReel
//
// Created by on 2025/12/4.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinsPackBuyView: UIView {
var buyFinishHandle: (() -> Void)?
var dataArr: [SRPayItem] = []
private lazy var titleView: UILabel = {
let view = UILabel()
view.textColor = .white
view.font = .font(ofSize: 14, weight: .regular)
view.text = "synthreel_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: SRCollectionView = {
let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.addObserver(self, forKeyPath: "contentSize", context: nil)
collectionView.register(SRPackCoinViewCell.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 SRCoinsPackBuyView {
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 SRCoinsPackBuyView: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRPackCoinViewCell
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 = SRCoinPackConfirmView()
view.model = model
view.buyFinishHandle = { [weak self] in
guard let self = self else { return }
Task {
await SRLogin.manager.requestUserInfo(completer: nil)
self.buyFinishHandle?()
}
}
view.present(in: nil)
}
}

View File

@ -0,0 +1,264 @@
//
// SRCoinsPackClaimListCell.swift
// SynthReel
//
// Created by on 2025/12/4.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinsPackClaimListCell: UICollectionViewCell {
var clickClaimButton: ((_ id: String?) -> Void)?
var model: SRCoinsPackReceiveModel? {
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 = .white
titleAtt.yy_font = .font(ofSize: 14, weight: .bold)
let day = "synthreel_d".localized
let dayAtt = NSMutableAttributedString(string: " (\(day) \(model?.day_text ?? ""))")
dayAtt.yy_color = .white
dayAtt.yy_font = .font(ofSize: 14, weight: .regular)
titleAtt.append(dayAtt)
titleLabel.attributedText = titleAtt
}
}
private lazy var bgView: UIImageView = {
let view = UIImageView()
view.image = .claimCellbg
view.contentMode = .scaleToFill
view.isUserInteractionEnabled = true
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 = "synthreel_total_reward".localized
return view
}()
private lazy var coinsView2: CoinsView = {
let view = CoinsView()
view.title = "synthreel_remaining".localized
return view
}()
private lazy var claimButton: UIButton = {
var config = UIButton.Configuration.plain()
config.titleAlignment = .center
config.background.image = .coinStackBg
let button = UIButton(configuration: config, primaryAction: UIAction(handler: { [weak self] _ in
guard let self = self else { return }
self.clickClaimButton?(self.model?.id)
}))
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
if button.isEnabled {
button.configuration?.background.image = UIImage(named: "coinStackBg")
let coinImage = UIImage(named: "bigCoins")!
let coinText = NSTextAttachment(image: coinImage)
coinText.bounds = .init(x: 0, y: -2.5, width: 13, height: 13)
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("synthreel_claim".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor.white
]))
button.configuration?.attributedSubtitle = coinAtt + countAtt
} else {
button.configuration?.background.image = UIImage(named: "claimd_button_bg")
button.configuration?.attributedTitle = AttributedString("synthreel_claimd".localized, attributes: AttributeContainer([
.font : UIFont.font(ofSize: 14, weight: .bold),
.foregroundColor : UIColor.FFFFFF_40
]))
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 SRCoinsPackClaimListCell {
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(-14)
}
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.bottom.equalTo(-14)
make.height.equalTo(48)
make.width.equalTo(120)
}
}
}
extension SRCoinsPackClaimListCell {
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 = .white
return label
}()
private lazy var iconImageView = UIImageView(image: UIImage(named: "bigCoins"))
private lazy var coinsLabel: UILabel = {
let label = SRLabel()
label.font = .font(ofSize: 14, weight: .bold)
label.textColor = .white
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 @@
//
// SRCoinsPackClaimListView.swift
// SynthReel
//
// Created by on 2025/12/4.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRCoinsPackClaimListView: UIView {
var clickClaimButton: ((_ id: String?) -> Void)?
var dataArr: [SRCoinsPackReceiveModel] = [] {
didSet {
collectionView.reloadData()
}
}
private lazy var titleView: UILabel = {
let view = UILabel()
view.textColor = .white
view.font = .font(ofSize: 14, weight: .regular)
view.text = "synthreel_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: SRCollectionView = {
let collectionView = SRCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.addObserver(self, forKeyPath: "contentSize", context: nil)
collectionView.register(SRCoinsPackClaimListCell.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 SRCoinsPackClaimListView {
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 SRCoinsPackClaimListView: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SRCoinsPackClaimListCell
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

@ -2,7 +2,7 @@
// SRCategoryModel.swift // SRCategoryModel.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/14. // Created by on 2025/11/14.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomeModuleItem.swift // SRHomeModuleItem.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/14. // Created by on 2025/11/14.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //
@ -22,6 +22,7 @@ class SRHomeModuleItem: NSObject, SmartCodable {
case bingeWorthy = "week_highest_recommend" case bingeWorthy = "week_highest_recommend"
case viralHits = "highest_payment_hot_video" case viralHits = "highest_payment_hot_video"
case premiereNow = "new_recommand" case premiereNow = "new_recommand"
case catetoryList = "category_navigation"
} }

View File

@ -2,7 +2,7 @@
// SRHomeBannerCell.swift // SRHomeBannerCell.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/15. // Created by on 2025/11/15.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomeBannerMiniCell.swift // SRHomeBannerMiniCell.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/17. // Created by on 2025/11/17.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomeBannerView.swift // SRHomeBannerView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/15. // Created by on 2025/11/15.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomeBingeWorthyCell.swift // SRHomeBingeWorthyCell.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/17. // Created by on 2025/11/17.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomeBingeWorthyView.swift // SRHomeBingeWorthyView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/17. // Created by on 2025/11/17.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomeChildCell.swift // SRHomeChildCell.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/14. // Created by on 2025/11/14.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -0,0 +1,50 @@
//
// SRHomeCoinsPackButton.swift
// SynthReel
//
// Created by on 2025/12/9.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRHomeCoinsPackButton: UIControl {
private lazy var bgImageView = UIImageView(image: UIImage(named: "浮窗"))
private lazy var textBgImageView = UIImageView(image: UIImage(named: "Rectangle 1098"))
private lazy var textLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 10, weight: .init(900)).withBoldItalic()
label.textColor = .white
label.text = "synthreel_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

@ -2,7 +2,7 @@
// SRHomeHeaderView.swift // SRHomeHeaderView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/15. // Created by on 2025/11/15.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -0,0 +1,143 @@
//
// SRHomeHistoryBottomView.swift
// SynthReel
//
// Created by on 2025/11/27.
// Copyright © 2025 SR. All rights reserved.
//
import UIKit
class SRHomeHistoryBottomView: UIView {
var model: SRShortModel? {
didSet {
coverImageView.sr_setImage(model?.image_url)
titleLabel.text = model?.name
let text = String(format: "EP. %@ / EP. %d", model?.current_episode ?? "0", model?.episode_total ?? 0)
setEpisodeText(text)
}
}
private lazy var coverImageView: SRImageView = {
let imageView = SRImageView()
imageView.layer.cornerRadius = 21
imageView.layer.masksToBounds = true
return imageView
}()
private lazy var bgView: UIImageView = {
let view = UIImageView()
view.image = .recordBottomBg
return view
}()
private lazy var playView: UIImageView = {
let imageView = UIImageView(image: .recordBottomArrow)
imageView.setContentHuggingPriority(.required, for: .horizontal)
imageView.setContentCompressionResistancePriority(.required, for: .horizontal)
return imageView
}()
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 12, weight: .medium)
label.textColor = .white
return label
}()
private lazy var epLabel: UILabel = {
let label = UILabel()
label.font = .font(ofSize: 10, weight: .regular)
label.textColor = .white
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
fa_setupLayout()
let tap = UITapGestureRecognizer(target: self, action: #selector(handleBg))
addGestureRecognizer(tap)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func handleBg() {
let vc = SRDetailPlayerViewController()
vc.shortId = self.model?.short_play_id
self.viewController?.navigationController?.pushViewController(vc, animated: true)
}
func setEpisodeText(_ text: String) {
let full = text as NSString
let parts = text.components(separatedBy: " / ")
guard parts.count == 2 else {
epLabel.text = text
return
}
let front = parts[0] + " "
let frontLen = (front as NSString).length
let fullLen = full.length
let attr = NSMutableAttributedString(string: text)
//
attr.yy_setColor(.srBlue, range: NSRange(location: 0, length: frontLen))
attr.yy_setFont(.systemFont(ofSize: 12), range: NSRange(location: 0, length: frontLen))
//
attr.yy_setColor(UIColor.white, range: NSRange(location: frontLen, length: fullLen - frontLen))
attr.yy_setFont(.systemFont(ofSize: 12), range: NSRange(location: frontLen, length: fullLen - frontLen))
epLabel.attributedText = attr
}
}
extension SRHomeHistoryBottomView {
private func fa_setupLayout() {
addSubview(bgView)
addSubview(coverImageView)
bgView.addSubview(playView)
bgView.addSubview(titleLabel)
bgView.addSubview(epLabel)
bgView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
make.top.equalToSuperview().offset(1)
make.height.equalTo(57)
}
coverImageView.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.left.equalToSuperview().offset(25)
make.width.equalTo(42)
make.height.equalTo(42)
}
playView.snp.makeConstraints { make in
make.centerY.equalToSuperview()
make.right.equalToSuperview().offset(-28)
}
titleLabel.snp.makeConstraints { make in
make.left.equalTo(coverImageView.snp.right).offset(10)
make.top.equalToSuperview().offset(12)
make.right.lessThanOrEqualTo(playView.snp.left).offset(-10)
}
epLabel.snp.makeConstraints { make in
make.left.equalTo(titleLabel)
make.bottom.equalToSuperview().offset(-12)
}
}
}

View File

@ -2,7 +2,7 @@
// SRHomeHotView.swift // SRHomeHotView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/17. // Created by on 2025/11/17.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomeMenuCell.swift // SRHomeMenuCell.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/14. // Created by on 2025/11/14.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomeModuleView.swift // SRHomeModuleView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/15. // Created by on 2025/11/15.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomePremiereNowView.swift // SRHomePremiereNowView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/17. // Created by on 2025/11/17.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomeTopChartsContentView.swift // SRHomeTopChartsContentView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/17. // Created by on 2025/11/17.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomeTopChartsView.swift // SRHomeTopChartsView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/17. // Created by on 2025/11/17.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomeViralHitsCell.swift // SRHomeViralHitsCell.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/17. // Created by on 2025/11/17.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomeViralHitsView.swift // SRHomeViralHitsView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/17. // Created by on 2025/11/17.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomeYouLikeCell.swift // SRHomeYouLikeCell.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/15. // Created by on 2025/11/15.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHomeYouLikeView.swift // SRHomeYouLikeView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/15. // Created by on 2025/11/15.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHotSearchCell.swift // SRHotSearchCell.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/18. // Created by on 2025/11/18.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRHotSearchView.swift // SRHotSearchView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/18. // Created by on 2025/11/18.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRSearchHomeView.swift // SRSearchHomeView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/18. // Created by on 2025/11/18.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRSearchRecordCell.swift // SRSearchRecordCell.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/18. // Created by on 2025/11/18.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

View File

@ -2,7 +2,7 @@
// SRSearchRecordView.swift // SRSearchRecordView.swift
// SynthReel // SynthReel
// //
// Created by on 2025/11/18. // Created by on 2025/11/18.
// Copyright © 2025 SR. All rights reserved. // Copyright © 2025 SR. All rights reserved.
// //

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