summaryrefslogtreecommitdiff
path: root/macos/Sources/Features
diff options
context:
space:
mode:
authorMitchell Hashimoto <m@mitchellh.com>2025-10-08 21:29:14 -0700
committerMitchell Hashimoto <m@mitchellh.com>2025-10-08 21:41:18 -0700
commitbce49a08438b39bbc699348d8308e724f6334f75 (patch)
treeec8f7c758ad168415069a1c5c0bdde426734a846 /macos/Sources/Features
parentb4ab1cc1edd273577449c9ced8f713c4293134b7 (diff)
macos: hook up our new update controller
Diffstat (limited to 'macos/Sources/Features')
-rw-r--r--macos/Sources/Features/Update/UpdateController.swift55
-rw-r--r--macos/Sources/Features/Update/UpdateDriver.swift20
2 files changed, 69 insertions, 6 deletions
diff --git a/macos/Sources/Features/Update/UpdateController.swift b/macos/Sources/Features/Update/UpdateController.swift
new file mode 100644
index 000000000..47e6c8def
--- /dev/null
+++ b/macos/Sources/Features/Update/UpdateController.swift
@@ -0,0 +1,55 @@
+import Sparkle
+import Cocoa
+
+/// Standard controller for managing Sparkle updates in Ghostty.
+///
+/// This controller wraps SPUStandardUpdaterController to provide a simpler interface
+/// for managing updates with Ghostty's custom driver and delegate. It handles
+/// initialization, starting the updater, and provides the check for updates action.
+class UpdateController {
+ private(set) var updater: SPUUpdater
+ private let userDriver: UpdateDriver
+ private let updaterDelegate = UpdaterDelegate()
+
+ var viewModel: UpdateViewModel {
+ userDriver.viewModel
+ }
+
+ /// Initialize a new update controller.
+ init() {
+ let hostBundle = Bundle.main
+ self.userDriver = UpdateDriver(viewModel: .init())
+ self.updater = SPUUpdater(
+ hostBundle: hostBundle,
+ applicationBundle: hostBundle,
+ userDriver: userDriver,
+ delegate: updaterDelegate
+ )
+ }
+
+ /// Start the updater.
+ ///
+ /// This must be called before the updater can check for updates. If starting fails,
+ /// an error alert will be shown after a short delay.
+ func startUpdater() {
+ try? updater.start()
+ }
+
+ /// Check for updates.
+ ///
+ /// This is typically connected to a menu item action.
+ @objc func checkForUpdates() {
+ updater.checkForUpdates()
+ }
+
+ /// Validate the check for updates menu item.
+ ///
+ /// - Parameter item: The menu item to validate
+ /// - Returns: Whether the menu item should be enabled
+ func validateMenuItem(_ item: NSMenuItem) -> Bool {
+ if item.action == #selector(checkForUpdates) {
+ return updater.canCheckForUpdates
+ }
+ return true
+ }
+}
diff --git a/macos/Sources/Features/Update/UpdateDriver.swift b/macos/Sources/Features/Update/UpdateDriver.swift
index 6627559e8..70f9341a6 100644
--- a/macos/Sources/Features/Update/UpdateDriver.swift
+++ b/macos/Sources/Features/Update/UpdateDriver.swift
@@ -1,13 +1,12 @@
+import Cocoa
import Sparkle
/// Implement the SPUUserDriver to modify our UpdateViewModel for custom presentation.
class UpdateDriver: NSObject, SPUUserDriver {
let viewModel: UpdateViewModel
- let retryHandler: () -> Void
- init(viewModel: UpdateViewModel, retryHandler: @escaping () -> Void) {
+ init(viewModel: UpdateViewModel) {
self.viewModel = viewModel
- self.retryHandler = retryHandler
super.init()
}
@@ -38,9 +37,18 @@ class UpdateDriver: NSObject, SPUUserDriver {
}
func showUpdaterError(_ error: any Error, acknowledgement: @escaping () -> Void) {
- viewModel.state = .error(.init(error: error, retry: retryHandler, dismiss: { [weak viewModel] in
- viewModel?.state = .idle
- }))
+ viewModel.state = .error(.init(
+ error: error,
+ retry: {
+ guard let delegate = NSApp.delegate as? AppDelegate else {
+ return
+ }
+
+ // TODO fill this in
+ },
+ dismiss: { [weak viewModel] in
+ viewModel?.state = .idle
+ }))
}
func showDownloadInitiated(cancellation: @escaping () -> Void) {