Initial commit

This commit is contained in:
2026-06-22 11:28:01 +08:00
commit ace9c94359
84 changed files with 8899 additions and 0 deletions

View File

@ -0,0 +1,125 @@
//
// RootView.swift
// suixinkan
//
// Created by Codex on 2026/6/18.
//
import SwiftUI
/// App
struct RootView: View {
@State private var appSession = AppSession()
@State private var accountContext = AccountContext()
@State private var permissionContext = PermissionContext()
@State private var scenicSpotContext = ScenicSpotContext()
@State private var appRouter = AppRouter()
@State private var toastCenter = ToastCenter()
@State private var apiClient: APIClient
@State private var authAPI: AuthAPI
@State private var profileAPI: ProfileAPI
@State private var accountContextAPI: AccountContextAPI
@State private var authSessionCoordinator: AuthSessionCoordinator
@State private var sessionBootstrapper: SessionBootstrapper
/// API token provider
init() {
let apiClient = APIClient()
let tokenStore = SessionTokenStore()
let snapshotStore = AccountSnapshotStore()
_apiClient = State(initialValue: apiClient)
_authAPI = State(initialValue: AuthAPI(client: apiClient))
_profileAPI = State(initialValue: ProfileAPI(client: apiClient))
_accountContextAPI = State(initialValue: AccountContextAPI(client: apiClient))
_authSessionCoordinator = State(
initialValue: AuthSessionCoordinator(
tokenStore: tokenStore,
snapshotStore: snapshotStore,
preferencesStore: AppPreferencesStore()
)
)
_sessionBootstrapper = State(
initialValue: SessionBootstrapper(
tokenStore: tokenStore,
snapshotStore: snapshotStore
)
)
}
var body: some View {
rootContent
.globalToastOverlay()
.environment(appSession)
.environment(accountContext)
.environment(permissionContext)
.environment(scenicSpotContext)
.environment(appRouter)
.environment(toastCenter)
.environment(apiClient)
.environment(authAPI)
.environment(profileAPI)
.environment(accountContextAPI)
.environment(authSessionCoordinator)
.task {
apiClient.bindAuthTokenProvider { appSession.token }
await sessionBootstrapper.restore(
appSession: appSession,
accountContext: accountContext,
permissionContext: permissionContext,
profileAPI: profileAPI,
accountContextAPI: accountContextAPI,
toastCenter: toastCenter
)
}
.task(id: scenicSpotTaskID) {
guard appSession.isLoggedIn else {
scenicSpotContext.reset()
return
}
await scenicSpotContext.reload(
scenicId: accountContext.currentScenic?.id,
api: accountContextAPI
)
}
.onChange(of: appSession.phase) { _, phase in
guard phase == .loggedOut else { return }
accountContext.reset()
permissionContext.reset()
scenicSpotContext.reset()
appRouter.reset()
toastCenter.dismiss()
}
}
@ViewBuilder
private var rootContent: some View {
switch appSession.phase {
case .loggedOut:
LoginView()
case .restoring:
ProgressView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(.systemBackground))
case .loggedIn:
MainTabsView()
}
}
///
private var scenicSpotTaskID: String {
let phaseText: String
switch appSession.phase {
case .loggedOut:
phaseText = "loggedOut"
case .restoring:
phaseText = "restoring"
case .loggedIn:
phaseText = "loggedIn"
}
return "\(phaseText)-\(accountContext.currentScenic?.id ?? 0)"
}
}
#Preview {
RootView()
}