115 lines
4.6 KiB
Swift
115 lines
4.6 KiB
Swift
import Foundation
|
|
import Observation
|
|
|
|
struct CodexSystemAccountPromotionUserFacingError: Error, Equatable {
|
|
let title: String
|
|
let message: String
|
|
}
|
|
|
|
@MainActor
|
|
@Observable
|
|
final 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)
|
|
}
|
|
}
|