import SwiftUI struct GateView: View { @EnvironmentObject private var router: Router @State private var isNavigated = false @State private var isLoading = true @State private var errorMessage: String? = nil var body: some View { ZStack { Color.themeTextWhiteSecondary.ignoresSafeArea() if isLoading { VStack(spacing: 16) { ProgressView() Text("Loading...") .font(Typography.font(for: .body)) .foregroundColor(.themeTextMessage) } } else if let error = errorMessage { // Briefly inform the user, then auto-navigate to avatar page VStack(spacing: 12) { Text("Network error, going to Avatar setup...") .font(Typography.font(for: .subtitle, family: .quicksandBold)) .foregroundColor(.themeTextMessageMain) Text(error) .font(.caption) .foregroundColor(.themeTextMessage) .multilineTextAlignment(.center) .padding(.horizontal) } } else { // Should not be seen because we auto-route when done EmptyView() } } .onAppear(perform: queryAndRoute) .navigationBarBackButtonHidden(true) } private func queryAndRoute() { guard !isNavigated else { return } isLoading = true errorMessage = nil print("[Gate] Start queryAndRoute") NetworkService.shared.get( path: "/blind_boxs/query", parameters: nil ) { (result: Result, NetworkError>) in print("Query result: \(result)") DispatchQueue.main.async { isLoading = false switch result { case .success(let response): let list = response.data ?? [] print("[Gate] Success, list count: \(list.count)") let lowered = list.map { ($0.boxType).lowercased() } let hasFirst = lowered.contains(where: { $0 == "first" }) let hasSecond = lowered.contains(where: { $0 == "second" }) let hasRetrieval = lowered.contains(where: { $0 == "retrievalgeneration" }) print("hasFirst: \(hasFirst), hasSecond: \(hasSecond), hasRetrieval: \(hasRetrieval)") // Routing rules (updated): // - If has 'second' OR 'retrievalgeneration' -> normal home // - Else if only 'first' exists -> second onboarding page // - Else -> avatar upload page if hasSecond || hasRetrieval { print("[Gate] Route -> BlindBox .all") navigateOnce(to: .blindBox(mediaType: .all)) } else if hasFirst { print("[Gate] Route -> SecondOnboardingView") navigateOnce(to: .secondOnboarding) } else { print("[Gate] Route -> UserInfo (no first/second/retrieval)") navigateOnce(to: .userInfo) } // Defensive fallback: if for any reason not navigated, push avatar after small delay DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { if !isNavigated { print("[Gate] Fallback routing -> UserInfo") navigateOnce(to: .userInfo) } } case .failure(let error): // Show an error briefly, then route to avatar page automatically errorMessage = error.localizedDescription print("Error: \(error.localizedDescription)") DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) { navigateOnce(to: .userInfo) } } } } } private func navigateOnce(to destination: AppRoute) { guard !isNavigated else { return } isNavigated = true router.navigate(to: destination) } } #Preview { GateView().environmentObject(Router.shared) }