推送权限提示

This commit is contained in:
zeng 2025-05-09 16:31:18 +08:00
parent e0e727573a
commit 34beab7b6b
26 changed files with 402 additions and 35 deletions

View File

@ -256,7 +256,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.thimratv.app; PRODUCT_BUNDLE_IDENTIFIER = com.thimratv.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -301,7 +301,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.thimratv.app; PRODUCT_BUNDLE_IDENTIFIER = com.thimratv.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";

View File

@ -25,7 +25,7 @@ extension AppDelegate {
#if DEBUG #if DEBUG
self.showApnsAlert() self.showApnsAlert()
#else #else
if date.sp_isToday { if !date.sp_isToday {
self.showApnsAlert() self.showApnsAlert()
} }
#endif #endif
@ -38,14 +38,17 @@ extension AppDelegate {
} }
private func showApnsAlert() { private func showApnsAlert() {
let alert = UIAlertController(title: nil, message: "kAlertMessage_03".localized, preferredStyle: .alert) let view = SPApnsAlertView()
alert.addAction(UIAlertAction(title: "Affirm".localized, style: .default, handler: { _ in view.show()
SPAPPTool.openApnsSetting()
}))
alert.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel))
// let alert = UIAlertController(title: nil, message: "kAlertMessage_03".localized, preferredStyle: .alert)
SPAPPTool.topViewController()?.present(alert, animated: true) // alert.addAction(UIAlertAction(title: "Affirm".localized, style: .default, handler: { _ in
// SPAPPTool.openApnsSetting()
// }))
// alert.addAction(UIAlertAction(title: "Cancel".localized, style: .cancel))
//
//
// SPAPPTool.topViewController()?.present(alert, animated: true)
} }
} }

View File

@ -46,6 +46,8 @@ extension SceneDelegate {
} }
private func _handleOpenAppMessage(webpageURL: URL?) { private func _handleOpenAppMessage(webpageURL: URL?) {
if !SPAPPTool.isAppOpen { return }
//URL //URL
var statUrlStr: String? var statUrlStr: String?
var data: [String : Any]? var data: [String : Any]?

View File

@ -20,6 +20,8 @@ extension AppDelegate {
#endif #endif
registAdjust() registAdjust()
///
MJRefreshConfig.default.languageCode = "en"
} }

View File

@ -18,13 +18,23 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
let tabBarController = SPTabBarController() let tabBarController = SPTabBarController()
SPAPPTool.mainTabBarController = tabBarController SPAPPTool.mainTabBarController = tabBarController
let hasOpenApp = UserDefaults.standard.object(forKey: kSPHasBeenOpenedAPPDefaultsKey) as? Bool
window = UIWindow(windowScene: windowScene) window = UIWindow(windowScene: windowScene)
window?.rootViewController = tabBarController if hasOpenApp == true {
SPAPPTool.isAppOpen = true
window?.rootViewController = tabBarController
} else {
SPAPPTool.isAppOpen = false
let vc = SPGuideViewController()
vc.openAppBlock = {
self.handleOpenApp()
}
window?.rootViewController = vc
}
window?.makeKeyAndVisible() window?.makeKeyAndVisible()
// if let webpageURL = session.stateRestorationActivity?.webpageURL {
// handleOpenAppMessage(webpageURL: webpageURL)
// }
//线 //线
timer = Timer.scheduledTimer(timeInterval: 60 * 10, target: self, selector: #selector(handleOnLine), userInfo: nil, repeats: true) timer = Timer.scheduledTimer(timeInterval: 60 * 10, target: self, selector: #selector(handleOnLine), userInfo: nil, repeats: true)
@ -41,12 +51,14 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// Called when the scene has moved from an inactive state to an active state. // Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
handleOpenAppMessage(webpageURL: nil) handleOpenAppMessage(webpageURL: nil)
SPStatAPI.requestEnterApp()
} }
func sceneWillResignActive(_ scene: UIScene) { func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state. // Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call). // This may occur due to temporary interruptions (ex. an incoming phone call).
SPStatAPI.requestEnterApp()
SPStatAPI.requestLeaveApp()
} }
func sceneWillEnterForeground(_ scene: UIScene) { func sceneWillEnterForeground(_ scene: UIScene) {
@ -54,13 +66,13 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// Use this method to undo the changes made on entering the background. // Use this method to undo the changes made on entering the background.
} }
func sceneDidEnterBackground(_ scene: UIScene) { func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background. // Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information // Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state. // to restore the scene back to its current state.
SPStatAPI.requestLeaveApp()
} }
@ -68,7 +80,16 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
extension SceneDelegate { extension SceneDelegate {
///线
@objc private func handleOnLine() { @objc private func handleOnLine() {
SPStatAPI.requestStatOnLine() SPStatAPI.requestStatOnLine()
} }
///app
@objc private func handleOpenApp() {
window?.rootViewController = SPAPPTool.mainTabBarController!
window?.makeKeyAndVisible()
handleOpenAppMessage(webpageURL: nil)
}
} }

View File

@ -6,9 +6,16 @@
// //
import UIKit import UIKit
import WebKit
class SPTabBarController: UITabBarController { class SPTabBarController: UITabBarController {
private lazy var tempWebView: SPWebView = {
let view = SPWebView(frame: .init(x: 1000, y: 1000, width: 100, height: 100), configuration: WKWebViewConfiguration())
view.load(urlStr: "https://www.apple.com")
return view
}()
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
self.view.backgroundColor = .backgroundColor() self.view.backgroundColor = .backgroundColor()
@ -25,6 +32,9 @@ class SPTabBarController: UITabBarController {
self.viewControllers = [nav1, nav2, nav3, nav4, nav5] self.viewControllers = [nav1, nav2, nav3, nav4, nav5]
// view.addSubview(tempWebView)
} }

View File

@ -7,6 +7,9 @@
import UIKit import UIKit
///APP
let kSPHasBeenOpenedAPPDefaultsKey = "kSPHasBeenOpenedAPPDefaultsKey"
/// ///
let kSPLoginTokenDefaultsKey = "kSPLoginTokenDefaultsKey" let kSPLoginTokenDefaultsKey = "kSPLoginTokenDefaultsKey"
/// ///

View File

@ -396,5 +396,9 @@ extension UIColor {
static func color967A7A(alpha: CGFloat = 1) -> UIColor { static func color967A7A(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0x967A7A, alpha: alpha) return color(hex: 0x967A7A, alpha: alpha)
} }
static func colorA8B8C3(alpha: CGFloat = 1) -> UIColor {
return color(hex: 0xA8B8C3, alpha: alpha)
}
} }

View File

@ -137,7 +137,7 @@ class SPNetwork: NSObject {
var res = SPNetworkResponse<T>() var res = SPNetworkResponse<T>()
res.code = -1 res.code = -1
if parameters.isToast { if parameters.isToast {
SPToast.show(text: "Error".localized) SPToast.show(text: "kToastMessage_03".localized)
} }
completion?(res) completion?(res)
break break

View File

@ -0,0 +1,160 @@
//
// SPApnsAlertView.swift
// MoviaBox
//
// Created by on 2025/5/9.
//
import UIKit
class SPApnsAlertView: UIView {
//MARK: UI
private lazy var contentView: UIView = {
let view = UIView()
return view
}()
private lazy var bgView: UIView = {
let imageView = UIImageView(image: UIImage(named: "alert_bg_image_01"))
imageView.isUserInteractionEnabled = true
return imageView
}()
private lazy var iconImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "message_icon_01"))
return imageView
}()
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = .fontMedium(ofSize: 20)
label.textColor = .colorFFFFFF()
label.text = "kAlertMessage_03".localized
label.textAlignment = .center
label.numberOfLines = 0
return label
}()
private lazy var textLabel: UILabel = {
let label = UILabel()
label.textColor = .colorA8B8C3()
label.font = .fontRegular(ofSize: 14)
label.text = "kAlertMessage_04".localized
label.textAlignment = .center
label.numberOfLines = 0
return label
}()
private lazy var laterButton: UIButton = {
let button = UIButton(type: .custom)
button.setTitle("Later".localized, for: .normal)
button.titleLabel?.font = .fontMedium(ofSize: 16)
button.setTitleColor(.colorFF3232(), for: .normal)
button.addTarget(self, action: #selector(handleLaterButton), for: .touchUpInside)
return button
}()
private lazy var allowButton: UIButton = {
let button = UIButton(type: .custom)
button.setTitle("Allow".localized, for: .normal)
button.titleLabel?.font = .fontMedium(ofSize: 16)
button.setTitleColor(.colorFFFFFF(), for: .normal)
button.layer.cornerRadius = 21
button.layer.masksToBounds = true
button.backgroundColor = .colorFF3232()
button.addTarget(self, action: #selector(handleAllowButton), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .color000000(alpha: 0.8)
_setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func show() {
guard let view = SPAPPTool.getKeyWindow() else { return }
view.addSubview(self)
self.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
@objc private func handleLaterButton() {
self.removeFromSuperview()
}
@objc private func handleAllowButton() {
self.removeFromSuperview()
SPAPPTool.openApnsSetting()
}
}
extension SPApnsAlertView {
private func _setupUI() {
addSubview(contentView)
contentView.addSubview(bgView)
bgView.addSubview(iconImageView)
bgView.addSubview(titleLabel)
bgView.addSubview(textLabel)
bgView.addSubview(laterButton)
bgView.addSubview(allowButton)
contentView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.centerY.equalToSuperview()
make.left.equalToSuperview().offset(26)
}
bgView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
// make.height.equalTo(284)
make.bottom.equalToSuperview()
}
iconImageView.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalToSuperview().offset(9)
}
titleLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(iconImageView.snp.bottom).offset(15)
make.right.lessThanOrEqualToSuperview().offset(-30)
}
textLabel.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.top.equalTo(titleLabel.snp.bottom).offset(19)
make.right.lessThanOrEqualToSuperview().offset(-30)
}
laterButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(33)
make.top.equalTo(textLabel.snp.bottom).offset(28)
make.height.equalTo(42)
}
allowButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-33)
make.left.equalTo(laterButton.snp.right).offset(13)
make.width.height.top.equalTo(laterButton)
make.bottom.equalToSuperview().offset(-32)
}
}
}

View File

@ -13,7 +13,26 @@ class SPCampaignWebViewController: SPWebViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
autoTitle = false
if urlStr == SPFeedBackListWebUrl {
self.title = "Feedback History".localized
} else if urlStr == SPFeedBackHomeWebUrl {
self.title = "Feedback".localized
} else if urlStr == SPFeedBackDetailWebUrl {
self.title = "Feedback Details".localized
} else if urlStr == SPRewardsWebUrl {
self.title = "Rewards".localized
}
self.webView.scrollView.sp_addRefreshHeader { [weak self] in
self?.handleHeaderRefresh(nil)
}
}
override func handleHeaderRefresh(_ completer: (() -> Void)?) {
self.reload()
self.webView.scrollView.sp_endHeaderRefreshing()
} }

View File

@ -12,6 +12,9 @@ class SPWebViewController: SPViewController {
var urlStr: String? var urlStr: String?
///
var autoTitle = true
private(set) lazy var webView: SPWebView = { private(set) lazy var webView: SPWebView = {
let controller = WKUserContentController() let controller = WKUserContentController()
@ -74,7 +77,9 @@ extension SPWebViewController {
extension SPWebViewController: SPWebViewDelegate { extension SPWebViewController: SPWebViewDelegate {
func webView(webView: SPWebView, didChangeTitle title: String) { func webView(webView: SPWebView, didChangeTitle title: String) {
self.title = title if autoTitle {
self.title = title
}
} }
func webView(_ webView: SPWebView, didFailLoadWithError error: any Error) { func webView(_ webView: SPWebView, didFailLoadWithError error: any Error) {
@ -82,7 +87,7 @@ extension SPWebViewController: SPWebViewDelegate {
} }
func webViewDidFinishLoad(_ webView: SPWebView) { func webViewDidFinishLoad(_ webView: SPWebView) {
spLog(message: "网页加载完成")
} }

View File

@ -98,6 +98,7 @@ class SPAllShortViewController: SPViewController {
let collectionView = SPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) let collectionView = SPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self collectionView.delegate = self
collectionView.dataSource = self collectionView.dataSource = self
collectionView.showNormalEmpty()
collectionView.contentInset = .init(top: 10, left: 0, bottom: 10, right: 0) collectionView.contentInset = .init(top: 10, left: 0, bottom: 10, right: 0)
collectionView.sp_addRefreshHeader(insetTop: collectionView.contentInset.top) { [weak self] in collectionView.sp_addRefreshHeader(insetTop: collectionView.contentInset.top) { [weak self] in
self?.handleHeaderRefresh(nil) self?.handleHeaderRefresh(nil)

View File

@ -0,0 +1,73 @@
//
// SPGuideViewController.swift
// MoviaBox
//
// Created by on 2025/5/9.
//
import UIKit
class SPGuideViewController: SPViewController {
var openAppBlock: (() -> Void)?
private lazy var lanuchVC: UIViewController = {
let vc = SPAPPTool.getLanuchViewController()
return vc
}()
private lazy var button: UIButton = {
let button = JXButton(type: .custom)
button.setTitle("Open".localized, for: .normal)
button.setTitleColor(.colorFFFFFF(), for: .normal)
button.jx_font = .fontRegular(ofSize: 18)
button.leftAndRightMargin = 33
button.layer.cornerRadius = 17
button.layer.masksToBounds = true
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.colorFFFFFF().cgColor
button.addTarget(self, action: #selector(handleButton), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
_setupUI()
}
@objc private func handleButton() {
SPAPPTool.isAppOpen = true
UserDefaults.standard.set(true, forKey: kSPHasBeenOpenedAPPDefaultsKey)
self.openAppBlock?()
NotificationCenter.default.post(name: SPGuideViewController.didOpenAppNotification, object: nil, userInfo: nil)
}
}
extension SPGuideViewController {
private func _setupUI() {
addChild(lanuchVC)
view.addSubview(lanuchVC.view)
view.addSubview(button)
button.snp.makeConstraints { make in
make.centerX.equalToSuperview()
make.height.equalTo(34)
make.bottom.equalToSuperview().offset(-(kSPTabbarSafeBottomMargin + 100))
}
}
}
extension SPGuideViewController {
///app
@objc static let didOpenAppNotification = NSNotification.Name(rawValue: "SPGuideViewController.didOpenAppNotification")
}

View File

@ -76,7 +76,7 @@ class SPPlayHistoryCell: SPCollectionViewCell {
let isCollect = !(self.model?.is_collect ?? false) let isCollect = !(self.model?.is_collect ?? false)
guard let shortPlayId = self.model?.short_play_id else { return } guard let shortPlayId = self.model?.short_play_id else { return }
SPVideoAPI.requestCollectShort(isCollect: isCollect, shortPlayId: shortPlayId, videoId: nil) { SPVideoAPI.requestCollectShort(isCollect: isCollect, shortPlayId: shortPlayId, videoId: self.model?.short_play_video_id) {
} }
} }

View File

@ -24,6 +24,7 @@ class SPShortModel: SPModel, SmartCodable {
var search_click_total: String? var search_click_total: String?
var short_id: String? var short_id: String?
var short_play_id: String? var short_play_id: String?
var short_play_video_id: String?
var tag_type: String? var tag_type: String?
var video_info: SPVideoInfoModel? var video_info: SPVideoInfoModel?
var watch_total: Int? var watch_total: Int?

View File

@ -9,6 +9,13 @@ import UIKit
class SPRewardsViewController: SPCampaignWebViewController { class SPRewardsViewController: SPCampaignWebViewController {
deinit {
NotificationCenter.default.removeObserver(self)
}
private var isFirst = true
override func viewDidLoad() { override func viewDidLoad() {
self.urlStr = SPRewardsWebUrl self.urlStr = SPRewardsWebUrl
@ -16,17 +23,18 @@ class SPRewardsViewController: SPCampaignWebViewController {
NotificationCenter.default.addObserver(self, selector: #selector(loginStateDidChangeNotification), name: SPLoginManager.loginStateDidChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(loginStateDidChangeNotification), name: SPLoginManager.loginStateDidChangeNotification, object: nil)
self.webView.scrollView.sp_addRefreshHeader { [weak self] in }
self?.handleHeaderRefresh(nil)
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !isFirst {
self.reload()
} else {
isFirst = false
} }
} }
override func handleHeaderRefresh(_ completer: (() -> Void)?) {
self.reload()
self.webView.scrollView.sp_endHeaderRefreshing()
}
} }

View File

@ -64,7 +64,7 @@ class SPStoreViewController: SPViewController {
let range3 = text.ocString().range(of: text3) let range3 = text.ocString().range(of: text3)
let string = NSMutableAttributedString(string: text) let string = NSMutableAttributedString(string: text)
string.color = tipTextLabel.textColor string.color = .colorFFFFFF()
string.font = tipTextLabel.font string.font = tipTextLabel.font
string.setTextHighlight(range1, color: nil, backgroundColor: nil) { [weak self] _, _, _, _ in string.setTextHighlight(range1, color: nil, backgroundColor: nil) { [weak self] _, _, _, _ in

View File

@ -9,6 +9,9 @@ import UIKit
class SPAPPTool: NSObject { class SPAPPTool: NSObject {
///app
static var isAppOpen = true
static var mainTabBarController: SPTabBarController? static var mainTabBarController: SPTabBarController?
static func getAppDelegate() -> AppDelegate? { static func getAppDelegate() -> AppDelegate? {
@ -18,8 +21,8 @@ class SPAPPTool: NSObject {
/// ///
static func getLanuchViewController() -> UIViewController { static func getLanuchViewController() -> UIViewController {
let storyboard = UIStoryboard(name: "LaunchScreen", bundle: nil) let storyboard = UIStoryboard(name: "LaunchScreen", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "LaunchScreen") let vc = storyboard.instantiateInitialViewController()
return vc return vc!
} }
/// ///

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 KiB

View File

@ -93,18 +93,26 @@
"Unlock the previous episode" = "Unlock the previous episode"; "Unlock the previous episode" = "Unlock the previous episode";
"Purchase Single Episode" = "Purchase Single Episode"; "Purchase Single Episode" = "Purchase Single Episode";
"Retry" = "Retry"; "Retry" = "Retry";
"Open" = "Open";
"Later" = "Later";
"Allow" = "Allow";
"Feedback History" = "Feedback History";
"Feedback Details" = "Feedback Details";
///没有可恢复购买 ///没有可恢复购买
"kToastMessage_01" = "There are no recoverable in-app purchases."; "kToastMessage_01" = "There are no recoverable in-app purchases.";
///还有未完成的内购 ///还有未完成的内购
"kToastMessage_02" = "You still have unfinished in-app purchases. Please restore them first."; "kToastMessage_02" = "You still have unfinished in-app purchases. Please restore them first.";
///网络错误提示
"kToastMessage_03" = "The service is abnormal. Check the network.";
///请购买上一集提示 ///请购买上一集提示
"kAlertMessage_01" = "The previous episode of this series has not been unlocked yet. Please unlock the previous episode first."; "kAlertMessage_01" = "The previous episode of this series has not been unlocked yet. Please unlock the previous episode first.";
///没有找到视频提示 ///没有找到视频提示
"kAlertMessage_02" = "Purchase failed, please try again later!"; "kAlertMessage_02" = "Purchase failed, please try again later!";
///推送消息提示 ///推送消息提示
"kAlertMessage_03" = "Go and enable push messages?"; "kAlertMessage_03" = "Enable Notifications";
"kAlertMessage_04" = "Stay informed with popular recommendations and latest updates!";
"kLoginAgreementText" = "By continuing, you agree to the User Agreement and Privacy Policy"; "kLoginAgreementText" = "By continuing, you agree to the User Agreement and Privacy Policy";