summaryrefslogtreecommitdiff
path: root/macos/Sources/Features/Update/UpdateController.swift
diff options
context:
space:
mode:
Diffstat (limited to 'macos/Sources/Features/Update/UpdateController.swift')
-rw-r--r--macos/Sources/Features/Update/UpdateController.swift34
1 files changed, 34 insertions, 0 deletions
diff --git a/macos/Sources/Features/Update/UpdateController.swift b/macos/Sources/Features/Update/UpdateController.swift
index 446b82ebc..aa875567c 100644
--- a/macos/Sources/Features/Update/UpdateController.swift
+++ b/macos/Sources/Features/Update/UpdateController.swift
@@ -1,5 +1,6 @@
import Sparkle
import Cocoa
+import Combine
/// Standard controller for managing Sparkle updates in Ghostty.
///
@@ -10,6 +11,7 @@ class UpdateController {
private(set) var updater: SPUUpdater
private let userDriver: UpdateDriver
private let updaterDelegate = UpdaterDelegate()
+ private var installCancellable: AnyCancellable?
var viewModel: UpdateViewModel {
userDriver.viewModel
@@ -29,6 +31,10 @@ class UpdateController {
)
}
+ deinit {
+ installCancellable?.cancel()
+ }
+
/// Start the updater.
///
/// This must be called before the updater can check for updates. If starting fails,
@@ -50,6 +56,34 @@ class UpdateController {
}
}
+ /// Force install the current update. As long as we're in some "update available" state this will
+ /// trigger all the steps necessary to complete the update.
+ func installUpdate() {
+ // Must be in an installable state
+ guard viewModel.state.isInstallable else { return }
+
+ // If we're already force installing then do nothing.
+ guard installCancellable == nil else { return }
+
+ // Setup a combine listener to listen for state changes and to always
+ // confirm them. If we go to a non-installable state, cancel the listener.
+ // The sink runs immediately with the current state, so we don't need to
+ // manually confirm the first state.
+ installCancellable = viewModel.$state.sink { [weak self] state in
+ guard let self else { return }
+
+ // If we move to a non-installable state (error, idle, etc.) then we
+ // stop force installing.
+ guard state.isInstallable else {
+ self.installCancellable = nil
+ return
+ }
+
+ // Continue the `yes` chain!
+ state.confirm()
+ }
+ }
+
/// Check for updates.
///
/// This is typically connected to a menu item action.