jsdw_ios/QuickLocation/Section/PopupWindow/PopupQueueManager.swift

200 lines
6.2 KiB
Swift

//
// PopupQueueManager.swift
// QuickLocation
//
// Created by on 2026/7/3.
//
import UIKit
class PopupQueueManager {
private weak var sourceVC: UIViewController?
private var currentStep = 0
private var stepGeneration = 0
private var countdownTimer: Timer?
private var isPaused = false
init() {
NotificationCenter.default.addObserver(self,
selector: #selector(handlePause),
name: .pausePopupQueue,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(handleResume),
name: .resumePopupQueue,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(handleInvalidate),
name: .invalidatePopupQueue,
object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
func start(from vc: UIViewController) {
sourceVC = vc
currentStep = 0
executeNextStep()
}
@objc private func handlePause() {
countdownTimer?.invalidate()
countdownTimer = nil
isPaused = true
}
@objc private func handleResume() {
guard isPaused else { return }
isPaused = false
executeNextStep()
}
@objc private func handleInvalidate() {
countdownTimer?.invalidate()
countdownTimer = nil
isPaused = true
currentStep = 3
}
private func executeNextStep() {
guard AppContextManager.shared.systemConfig?.isIntercept == false else { return }
switch currentStep {
case 0:
tryStartPromotionalActivities()
case 1:
tryStartSearchPopup()
case 2:
tryStartRegisterOrVipPopup()
default:
break
}
}
// MARK: - Step 0: PromotionalActivities
private func tryStartPromotionalActivities() {
guard let config = AppContextManager.shared.systemConfig else {
skipToNext()
return
}
guard config.popupConfig?.pay_pop == true,
AppContextManager.shared.vip == 1,
let timeConfig = config.timeConfig else {
skipToNext()
return
}
let gen = stepGeneration
startCountdown(seconds: timeConfig.pay_pop_time) { [weak self] in
guard let self = self, self.currentStep == 0, self.stepGeneration == gen else { return }
let vc = PromotionalActivitiesVC()
vc.onDismiss = { [weak self] in
self?.skipToNext()
}
self.push(vc)
}
}
// MARK: - Step 1: Search PopupWindow
private func tryStartSearchPopup() {
guard let config = AppContextManager.shared.systemConfig else {
skipToNext()
return
}
guard config.popupConfig?.search_phone == true,
let timeConfig = config.timeConfig else {
skipToNext()
return
}
let gen = stepGeneration
startCountdown(seconds: timeConfig.search_phone) {
guard self.currentStep == 1, self.stepGeneration == gen else { return }
PopupWindow.show(popupType: .search) {
self.skipToNext()
}
}
}
// MARK: - Step 2: Register or VIP PopupWindow (mutually exclusive)
private func tryStartRegisterOrVipPopup() {
guard let config = AppContextManager.shared.systemConfig,
let timeConfig = config.timeConfig else {
return
}
if AppContextManager.shared.isGuest {
let gen = stepGeneration
startCountdown(seconds: timeConfig.signup_tip) {
guard self.currentStep == 2, self.stepGeneration == gen else { return }
PopupWindow.show(popupType: .register) {
// done
}
}
} else if AppContextManager.shared.vip == 1 {
let gen = stepGeneration
startCountdown(seconds: timeConfig.buy_vip_tip) {
guard self.currentStep == 2, self.stepGeneration == gen else { return }
PopupWindow.show(popupType: .vip) {
// done
}
}
}
}
// MARK: - Helpers
private func skipToNext() {
stepGeneration += 1
currentStep += 1
executeNextStep()
}
private func startCountdown(seconds: Int, onExpired: @escaping () -> Void) {
guard seconds > 0 else {
onExpired()
return
}
countdownTimer?.invalidate()
var remaining = seconds
countdownTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
remaining -= 1
if remaining <= 0 {
timer.invalidate()
self?.countdownTimer = nil
onExpired()
}
}
}
private func push(_ vc: UIViewController) {
let topVC = topViewController()
guard let nav = topVC?.navigationController else { return }
if nav.viewControllers.contains(where: { type(of: $0) == type(of: vc) }) {
return
}
nav.pushViewController(vc, animated: true)
}
private func topViewController() -> UIViewController? {
guard let rootVC = kKeyWindow?.rootViewController else { return nil }
return topViewController(from: rootVC)
}
private func topViewController(from root: UIViewController) -> UIViewController {
if let presented = root.presentedViewController {
return topViewController(from: presented)
}
if let nav = root as? UINavigationController, let visible = nav.visibleViewController {
return topViewController(from: visible)
}
if let tab = root as? UITabBarController, let selected = tab.selectedViewController {
return topViewController(from: selected)
}
return root
}
}