mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-10-11 12:26:41 +00:00
160 lines
4.6 KiB
Swift
160 lines
4.6 KiB
Swift
import Foundation
|
|
import SwiftUI
|
|
import Sparkle
|
|
|
|
class UpdateViewModel: ObservableObject {
|
|
@Published var state: UpdateState = .idle
|
|
|
|
/// The text to display for the current update state.
|
|
/// Returns an empty string for idle state, progress percentages for downloading/extracting,
|
|
/// or descriptive text for other states.
|
|
var text: String {
|
|
switch state {
|
|
case .idle:
|
|
return ""
|
|
case .permissionRequest:
|
|
return "Update Permission"
|
|
case .checking:
|
|
return "Checking for Updates…"
|
|
case .updateAvailable(let update):
|
|
return "Update Available: \(update.appcastItem.displayVersionString)"
|
|
case .downloading(let download):
|
|
if let expectedLength = download.expectedLength, expectedLength > 0 {
|
|
let progress = Double(download.progress) / Double(expectedLength)
|
|
return String(format: "Downloading: %.0f%%", progress * 100)
|
|
}
|
|
return "Downloading…"
|
|
case .extracting(let extracting):
|
|
return String(format: "Preparing: %.0f%%", extracting.progress * 100)
|
|
case .readyToInstall:
|
|
return "Install Update"
|
|
case .installing:
|
|
return "Installing…"
|
|
case .notFound:
|
|
return "No Updates Available"
|
|
case .error(let err):
|
|
return err.error.localizedDescription
|
|
}
|
|
}
|
|
|
|
/// The SF Symbol icon name for the current update state.
|
|
/// Returns nil for idle, downloading, and extracting states.
|
|
var iconName: String? {
|
|
switch state {
|
|
case .idle:
|
|
return nil
|
|
case .permissionRequest:
|
|
return "questionmark.circle"
|
|
case .checking:
|
|
return "arrow.triangle.2.circlepath"
|
|
case .updateAvailable:
|
|
return "arrow.down.circle.fill"
|
|
case .downloading, .extracting:
|
|
return nil
|
|
case .readyToInstall:
|
|
return "checkmark.circle.fill"
|
|
case .installing:
|
|
return "gear"
|
|
case .notFound:
|
|
return "info.circle"
|
|
case .error:
|
|
return "exclamationmark.triangle.fill"
|
|
}
|
|
}
|
|
|
|
/// The color to apply to the icon for the current update state.
|
|
var iconColor: Color {
|
|
switch state {
|
|
case .idle:
|
|
return .secondary
|
|
case .permissionRequest, .checking:
|
|
return .secondary
|
|
case .updateAvailable, .readyToInstall:
|
|
return .accentColor
|
|
case .downloading, .extracting, .installing:
|
|
return .secondary
|
|
case .notFound:
|
|
return .secondary
|
|
case .error:
|
|
return .orange
|
|
}
|
|
}
|
|
|
|
/// The background color for the update pill.
|
|
var backgroundColor: Color {
|
|
switch state {
|
|
case .updateAvailable:
|
|
return .accentColor
|
|
case .readyToInstall:
|
|
return Color(nsColor: NSColor.systemGreen.blended(withFraction: 0.3, of: .black) ?? .systemGreen)
|
|
case .error:
|
|
return .orange.opacity(0.2)
|
|
default:
|
|
return Color(nsColor: .controlBackgroundColor)
|
|
}
|
|
}
|
|
|
|
/// The foreground (text) color for the update pill.
|
|
var foregroundColor: Color {
|
|
switch state {
|
|
case .updateAvailable, .readyToInstall:
|
|
return .white
|
|
case .error:
|
|
return .orange
|
|
default:
|
|
return .primary
|
|
}
|
|
}
|
|
}
|
|
|
|
enum UpdateState {
|
|
case idle
|
|
case permissionRequest(PermissionRequest)
|
|
case checking(Checking)
|
|
case updateAvailable(UpdateAvailable)
|
|
case notFound
|
|
case error(Error)
|
|
case downloading(Downloading)
|
|
case extracting(Extracting)
|
|
case readyToInstall(ReadyToInstall)
|
|
case installing
|
|
|
|
var isIdle: Bool {
|
|
if case .idle = self { return true }
|
|
return false
|
|
}
|
|
|
|
struct PermissionRequest {
|
|
let request: SPUUpdatePermissionRequest
|
|
let reply: @Sendable (SUUpdatePermissionResponse) -> Void
|
|
}
|
|
|
|
struct Checking {
|
|
let cancel: () -> Void
|
|
}
|
|
|
|
struct UpdateAvailable {
|
|
let appcastItem: SUAppcastItem
|
|
let reply: @Sendable (SPUUserUpdateChoice) -> Void
|
|
}
|
|
|
|
struct Error {
|
|
let error: any Swift.Error
|
|
let retry: () -> Void
|
|
}
|
|
|
|
struct Downloading {
|
|
let cancel: () -> Void
|
|
let expectedLength: UInt64?
|
|
let progress: UInt64
|
|
}
|
|
|
|
struct Extracting {
|
|
let progress: Double
|
|
}
|
|
|
|
struct ReadyToInstall {
|
|
let reply: @Sendable (SPUUserUpdateChoice) -> Void
|
|
}
|
|
}
|