114 lines · 4.6 KB · Swift
RawDownload
import Foundationimport Observation struct CodexSystemAccountPromotionUserFacingError: Error, Equatable {    let title: String    let message: String} @MainActor@Observablefinal class CodexAccountPromotionCoordinator {    let service: CodexAccountPromotionService    weak var managedAccountCoordinator: ManagedCodexAccountCoordinator?    private(set) var isAuthenticatingLiveAccount = false    private(set) var isPromotingSystemAccount = false    private(set) var userFacingError: CodexSystemAccountPromotionUserFacingError?     init(        service: CodexAccountPromotionService,        managedAccountCoordinator: ManagedCodexAccountCoordinator? = nil)    {        self.service = service        self.managedAccountCoordinator = managedAccountCoordinator    }     convenience init(        settingsStore: SettingsStore,        usageStore: UsageStore,        managedAccountCoordinator: ManagedCodexAccountCoordinator? = nil)    {        self.init(            service: CodexAccountPromotionService(settingsStore: settingsStore, usageStore: usageStore),            managedAccountCoordinator: managedAccountCoordinator)    }     func promote(managedAccountID: UUID)        async -> Result<CodexAccountPromotionResult, CodexSystemAccountPromotionUserFacingError>    {        self.userFacingError = nil         guard !self.isInteractionBlocked() else {            let error = Self.interactionBlockedError()            self.userFacingError = error            return .failure(error)        }         self.isPromotingSystemAccount = true        defer { self.isPromotingSystemAccount = false }         do {            let result = try await self.service.promoteManagedAccount(id: managedAccountID)            return .success(result)        } catch {            let mapped = Self.mapUserFacingError(error)            self.userFacingError = mapped            return .failure(mapped)        }    }     func clearError() {        self.userFacingError = nil    }     func setLiveReauthenticationInProgress(_ isInProgress: Bool) {        self.isAuthenticatingLiveAccount = isInProgress    }     func isInteractionBlocked() -> Bool {        self.isPromotingSystemAccount ||            self.isAuthenticatingLiveAccount ||            self.managedAccountCoordinator?.hasConflictingManagedAccountOperationInFlight == true    }     private static func interactionBlockedError() -> CodexSystemAccountPromotionUserFacingError {        CodexSystemAccountPromotionUserFacingError(            title: L("Could not switch system account"),            message: L("Finish the current managed account change before switching the system account."))    }     static func mapUserFacingError(_ error: Error) -> CodexSystemAccountPromotionUserFacingError {        let title = L("Could not switch system account")         if let error = error as? CodexAccountPromotionError {            let message = switch error {            case .targetManagedAccountNotFound:                L("That account is no longer available in CodexBar. Refresh the account list and try again.")            case .targetManagedAccountAuthMissing:                L("CodexBar could not find saved auth for that account. Re-authenticate it and try again.")            case .targetManagedAccountAuthUnreadable:                L("CodexBar could not read saved auth for that account. Re-authenticate it and try again.")            case .liveAccountUnreadable:                L("CodexBar could not read the current system account on this Mac.")            case .liveAccountMissingIdentityForPreservation:                L("CodexBar could not safely preserve the current system account before switching.")            case .liveAccountAPIKeyOnlyUnsupported:                L("CodexBar can't replace a system account that is signed in with an API key only setup.")            case .displacedLiveManagedAccountConflict:                L(                    "CodexBar found another managed account that already uses the current system account. " +                        "Resolve the duplicate account before switching.")            case .displacedLiveImportFailed:                L("CodexBar could not save the current system account before switching.")            case .managedStoreCommitFailed:                L("CodexBar could not update managed account storage.")            case .liveAuthSwapFailed:                L("CodexBar could not replace the live Codex auth on this Mac.")            }             return CodexSystemAccountPromotionUserFacingError(title: title, message: message)        }         return CodexSystemAccountPromotionUserFacingError(title: title, message: error.localizedDescription)    }}