Add Lottie-based global loading overlay across key flows.

Introduce GlobalLoadingCenter with reference counting, wire it through RootView and major auth/order/profile screens, and add the loading animation asset plus unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-23 11:25:56 +08:00
parent 1d1eb46ed8
commit 7fd964fe19
17 changed files with 514 additions and 139 deletions

View File

@ -18,6 +18,7 @@ struct AccountSwitchView: View {
@Environment(AccountContextAPI.self) private var accountContextAPI
@Environment(AuthSessionCoordinator.self) private var authSessionCoordinator
@Environment(ToastCenter.self) private var toastCenter
@Environment(\.globalLoading) private var globalLoading
@Environment(\.dismiss) private var dismiss
@State private var viewModel = AccountSwitchViewModel()
@ -71,15 +72,9 @@ struct AccountSwitchView: View {
Button {
Task { await confirmSelection() }
} label: {
HStack(spacing: 8) {
if viewModel.switching {
ProgressView()
.tint(.white)
}
Text("确认切换")
.font(.system(size: 16, weight: .semibold))
}
.foregroundStyle(.white)
Text("确认切换")
.font(.system(size: 16, weight: .semibold))
.foregroundStyle(.white)
.frame(maxWidth: .infinity)
.frame(height: 50)
.background(canConfirm ? AppDesign.primary : Color(hex: 0xC9CED6), in: RoundedRectangle(cornerRadius: 12))
@ -187,7 +182,9 @@ struct AccountSwitchView: View {
///
private func loadAccounts(force: Bool = false) async {
do {
try await viewModel.load(api: profileAPI, force: force, currentAccountId: currentAccountId)
try await globalLoading.withOptionalLoading(!force && viewModel.accounts.isEmpty, message: "加载中...") {
try await viewModel.load(api: profileAPI, force: force, currentAccountId: currentAccountId)
}
} catch is CancellationError {
return
} catch {
@ -204,18 +201,20 @@ struct AccountSwitchView: View {
}
do {
let response = try await viewModel.switchAccount(account, api: authAPI)
let username = nonEmpty(accountContext.profile?.phone) ?? nonEmpty(account.phone) ?? ""
try await authSessionCoordinator.completeLogin(
with: response,
username: username,
privacyAgreementAccepted: authSessionCoordinator.loginPreferences().privacyAgreementAccepted,
appSession: appSession,
accountContext: accountContext,
permissionContext: permissionContext,
profileAPI: profileAPI,
accountContextAPI: accountContextAPI
)
try await globalLoading.withLoading(message: "账号切换中...") {
let response = try await viewModel.switchAccount(account, api: authAPI)
let username = nonEmpty(accountContext.profile?.phone) ?? nonEmpty(account.phone) ?? ""
try await authSessionCoordinator.completeLogin(
with: response,
username: username,
privacyAgreementAccepted: authSessionCoordinator.loginPreferences().privacyAgreementAccepted,
appSession: appSession,
accountContext: accountContext,
permissionContext: permissionContext,
profileAPI: profileAPI,
accountContextAPI: accountContextAPI
)
}
appRouter.reset()
toastCenter.show("账号已切换")
dismiss()