diff --git a/Xcodes/AppleAPI/Sources/AppleAPI/Client.swift b/Xcodes/AppleAPI/Sources/AppleAPI/Client.swift index c8e28101..ff4eb9e3 100644 --- a/Xcodes/AppleAPI/Sources/AppleAPI/Client.swift +++ b/Xcodes/AppleAPI/Sources/AppleAPI/Client.swift @@ -182,6 +182,7 @@ public class Client { // MARK: - Types public enum AuthenticationState: Equatable { + case checking case unauthenticated case waitingForSecondFactor(TwoFactorOption, AuthOptionsResponse, AppleSessionData) case authenticated diff --git a/Xcodes/Backend/AppState.swift b/Xcodes/Backend/AppState.swift index 39c57a87..eebaef9c 100644 --- a/Xcodes/Backend/AppState.swift +++ b/Xcodes/Backend/AppState.swift @@ -13,7 +13,8 @@ class AppState: ObservableObject { // MARK: - Published Properties - @Published var authenticationState: AuthenticationState = .unauthenticated + @Published var authenticationState: AuthenticationState = .checking + var isCheckingAuthentication: Bool { authenticationState == .checking } @Published var availableXcodes: [AvailableXcode] = [] { willSet { if newValue.count > availableXcodes.count && availableXcodes.count != 0 { @@ -134,6 +135,21 @@ class AppState: ObservableObject { checkIfHelperIsInstalled() setupAutoInstallTimer() setupDefaults() + + validateSession() + .receive(on: DispatchQueue.main) + .sink( + receiveCompletion: { [weak self] result in + switch result { + case .finished: + self?.authenticationState = .authenticated + case .failure: + self?.authenticationState = .unauthenticated + } + }, + receiveValue: { _ in } + ) + .store(in: &cancellables) } func setupDefaults() { @@ -286,7 +302,7 @@ class AppState: ObservableObject { self.authError = error case .finished: switch self.authenticationState { - case .authenticated, .unauthenticated, .notAppleDeveloper: + case .authenticated, .unauthenticated, .notAppleDeveloper, .checking: self.presentedSheet = nil case let .waitingForSecondFactor(option, authOptions, sessionData): self.handleTwoFactorOption(option, authOptions: authOptions, serviceKey: sessionData.serviceKey, sessionID: sessionData.sessionID, scnt: sessionData.scnt) @@ -396,7 +412,7 @@ class AppState: ObservableObject { self.$authenticationState .filter { state in switch state { - case .authenticated, .unauthenticated, .notAppleDeveloper: return true + case .authenticated, .unauthenticated, .notAppleDeveloper, .checking: return true case .waitingForSecondFactor: return false } } diff --git a/Xcodes/Frontend/XcodeList/MainToolbar.swift b/Xcodes/Frontend/XcodeList/MainToolbar.swift index dcff73ab..3675dcaa 100644 --- a/Xcodes/Frontend/XcodeList/MainToolbar.swift +++ b/Xcodes/Frontend/XcodeList/MainToolbar.swift @@ -14,9 +14,20 @@ struct MainToolbarModifier: ViewModifier { private var toolbar: some ToolbarContent { ToolbarItemGroup(placement: .status) { - Button(action: { appState.presentedSheet = .signIn }, label: { - Label("Login", systemImage: "person.circle") - }) + ProgressButton( + isInProgress: appState.isCheckingAuthentication, + action: { appState.presentedSheet = .signIn } + ) { + switch appState.authenticationState { + case .authenticated: + Label("Login", systemImage: "person.circle") + case .unauthenticated, .notAppleDeveloper, .checking, .waitingForSecondFactor: + Label("Login", systemImage: "person.circle") + .foregroundColor(Color.red) + Text("Signed out") // TODO: Localization + .foregroundColor(Color.red) + } + } .help("LoginDescription") ProgressButton(