Initial commit: suixinkan_ios UIKit rewrite project.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-26 14:33:31 +08:00
commit 9edf993432
297 changed files with 47151 additions and 0 deletions

View File

@ -0,0 +1,138 @@
//
// 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
}
}