139 lines
4.2 KiB
Swift
139 lines
4.2 KiB
Swift
//
|
||
// RootViewController.swift
|
||
// suixinkan
|
||
//
|
||
|
||
import SnapKit
|
||
import UIKit
|
||
|
||
@MainActor
|
||
/// 应用根 ViewController,根据登录阶段切换子页面并挂载全局 Overlay。
|
||
final class RootViewController: UIViewController {
|
||
|
||
private let services: AppServices
|
||
private var currentChild: UIViewController?
|
||
private let restoringView = UIView()
|
||
|
||
init(services: AppServices = .shared) {
|
||
self.services = services
|
||
super.init(nibName: nil, bundle: nil)
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
override func viewDidLoad() {
|
||
super.viewDidLoad()
|
||
view.backgroundColor = .systemBackground
|
||
restoringView.backgroundColor = .systemBackground
|
||
|
||
services.appSession.onChange = { [weak self] in
|
||
self?.handleSessionPhaseChange()
|
||
}
|
||
|
||
mountChild(for: services.appSession.phase)
|
||
bootstrapSession()
|
||
configurePushNotifications()
|
||
}
|
||
|
||
override func viewDidAppear(_ animated: Bool) {
|
||
super.viewDidAppear(animated)
|
||
attachGlobalOverlays()
|
||
}
|
||
|
||
private func bootstrapSession() {
|
||
Task {
|
||
await services.globalLoading.withLoading {
|
||
await services.sessionBootstrapper.restore(
|
||
appSession: services.appSession,
|
||
accountContext: services.accountContext,
|
||
permissionContext: services.permissionContext,
|
||
profileAPI: services.profileAPI,
|
||
accountContextAPI: services.accountContextAPI,
|
||
toastCenter: services.toastCenter
|
||
)
|
||
}
|
||
|
||
if services.appSession.isLoggedIn, !AppUITestLaunchState.isRunningUITests {
|
||
PushNotificationManager.shared.requestAuthorizationAndRegister()
|
||
}
|
||
}
|
||
}
|
||
|
||
private func configurePushNotifications() {
|
||
services.configurePushNotifications()
|
||
}
|
||
|
||
private func attachGlobalOverlays() {
|
||
guard let window = view.window else { return }
|
||
services.toastCenter.attachToWindow(window)
|
||
services.globalLoading.attachToWindow(window)
|
||
}
|
||
|
||
private func handleSessionPhaseChange() {
|
||
mountChild(for: services.appSession.phase)
|
||
|
||
switch services.appSession.phase {
|
||
case .loggedIn:
|
||
if !AppUITestLaunchState.isRunningUITests {
|
||
PushNotificationManager.shared.requestAuthorizationAndRegister()
|
||
}
|
||
Task { await reloadScenicSpotContextIfNeeded() }
|
||
case .loggedOut:
|
||
services.accountContext.reset()
|
||
services.permissionContext.reset()
|
||
services.scenicSpotContext.reset()
|
||
services.appRouter.reset()
|
||
services.toastCenter.dismiss()
|
||
services.scenicQueueRuntime.stop()
|
||
case .restoring:
|
||
break
|
||
}
|
||
}
|
||
|
||
private func reloadScenicSpotContextIfNeeded() async {
|
||
guard services.appSession.isLoggedIn else {
|
||
services.scenicSpotContext.reset()
|
||
return
|
||
}
|
||
await services.scenicSpotContext.reload(
|
||
scenicId: services.accountContext.currentScenic?.id,
|
||
api: services.accountContextAPI
|
||
)
|
||
}
|
||
|
||
private func mountChild(for phase: AuthPhase) {
|
||
let nextChild: UIViewController
|
||
switch phase {
|
||
case .loggedOut:
|
||
nextChild = LoginViewController(services: services)
|
||
case .restoring:
|
||
nextChild = restoringViewController()
|
||
case .loggedIn:
|
||
nextChild = MainTabBarController(services: services)
|
||
}
|
||
|
||
guard currentChild !== nextChild else { return }
|
||
|
||
currentChild?.willMove(toParent: nil)
|
||
currentChild?.view.removeFromSuperview()
|
||
currentChild?.removeFromParent()
|
||
|
||
addChild(nextChild)
|
||
view.addSubview(nextChild.view)
|
||
nextChild.view.snp.makeConstraints { make in
|
||
make.edges.equalToSuperview()
|
||
}
|
||
nextChild.didMove(toParent: self)
|
||
currentChild = nextChild
|
||
}
|
||
|
||
private func restoringViewController() -> UIViewController {
|
||
let controller = UIViewController()
|
||
controller.view.backgroundColor = .systemBackground
|
||
return controller
|
||
}
|
||
}
|