mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-10-16 23:06:20 +00:00
macOS: Move update view model over to App scope
This commit is contained in:
@@ -102,6 +102,9 @@ class AppDelegate: NSObject,
|
|||||||
let updaterController: SPUStandardUpdaterController
|
let updaterController: SPUStandardUpdaterController
|
||||||
let updaterDelegate: UpdaterDelegate = UpdaterDelegate()
|
let updaterDelegate: UpdaterDelegate = UpdaterDelegate()
|
||||||
|
|
||||||
|
/// Update view model for UI display
|
||||||
|
@Published private(set) var updateUIModel = UpdateViewModel()
|
||||||
|
|
||||||
/// The elapsed time since the process was started
|
/// The elapsed time since the process was started
|
||||||
var timeSinceLaunch: TimeInterval {
|
var timeSinceLaunch: TimeInterval {
|
||||||
return ProcessInfo.processInfo.systemUptime - applicationLaunchTime
|
return ProcessInfo.processInfo.systemUptime - applicationLaunchTime
|
||||||
@@ -1008,24 +1011,16 @@ class AppDelegate: NSObject,
|
|||||||
// Demo mode: simulate update check instead of real Sparkle check
|
// Demo mode: simulate update check instead of real Sparkle check
|
||||||
// TODO: Replace with real updaterController.checkForUpdates(sender) when SPUUserDriver is implemented
|
// TODO: Replace with real updaterController.checkForUpdates(sender) when SPUUserDriver is implemented
|
||||||
|
|
||||||
guard let terminalWindow = NSApp.keyWindow as? TerminalWindow else {
|
|
||||||
// Fallback to real update check if no terminal window
|
|
||||||
updaterController.checkForUpdates(sender)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let model = terminalWindow.updateUIModel
|
|
||||||
|
|
||||||
// Simulate the full update check flow
|
// Simulate the full update check flow
|
||||||
model.state = .checking
|
updateUIModel.state = .checking
|
||||||
model.progress = nil
|
updateUIModel.progress = nil
|
||||||
model.details = nil
|
updateUIModel.details = nil
|
||||||
model.error = nil
|
updateUIModel.error = nil
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
|
||||||
// Simulate finding an update
|
// Simulate finding an update
|
||||||
model.state = .updateAvailable
|
self.updateUIModel.state = .updateAvailable
|
||||||
model.details = .init(
|
self.updateUIModel.details = .init(
|
||||||
version: "1.2.0",
|
version: "1.2.0",
|
||||||
build: "demo",
|
build: "demo",
|
||||||
size: "42 MB",
|
size: "42 MB",
|
||||||
|
@@ -17,7 +17,6 @@ class TerminalWindow: NSWindow {
|
|||||||
|
|
||||||
/// Update notification UI in titlebar
|
/// Update notification UI in titlebar
|
||||||
private let updateAccessory = NSTitlebarAccessoryViewController()
|
private let updateAccessory = NSTitlebarAccessoryViewController()
|
||||||
private(set) var updateUIModel = UpdateViewModel()
|
|
||||||
|
|
||||||
/// The configuration derived from the Ghostty config so we don't need to rely on references.
|
/// The configuration derived from the Ghostty config so we don't need to rely on references.
|
||||||
private(set) var derivedConfig: DerivedConfig = .init()
|
private(set) var derivedConfig: DerivedConfig = .init()
|
||||||
@@ -94,7 +93,7 @@ class TerminalWindow: NSWindow {
|
|||||||
updateAccessory.layoutAttribute = .right
|
updateAccessory.layoutAttribute = .right
|
||||||
updateAccessory.view = NSHostingView(rootView: UpdateAccessoryView(
|
updateAccessory.view = NSHostingView(rootView: UpdateAccessoryView(
|
||||||
viewModel: viewModel,
|
viewModel: viewModel,
|
||||||
model: updateUIModel,
|
model: appDelegate.updateUIModel,
|
||||||
actions: createUpdateActions()
|
actions: createUpdateActions()
|
||||||
))
|
))
|
||||||
addTitlebarAccessoryViewController(updateAccessory)
|
addTitlebarAccessoryViewController(updateAccessory)
|
||||||
@@ -457,48 +456,60 @@ class TerminalWindow: NSWindow {
|
|||||||
// MARK: Update UI
|
// MARK: Update UI
|
||||||
|
|
||||||
private func createUpdateActions() -> UpdateUIActions {
|
private func createUpdateActions() -> UpdateUIActions {
|
||||||
UpdateUIActions(
|
guard let appDelegate = NSApp.delegate as? AppDelegate else {
|
||||||
allowAutoChecks: { [weak self] in
|
return UpdateUIActions(
|
||||||
|
allowAutoChecks: {},
|
||||||
|
denyAutoChecks: {},
|
||||||
|
cancel: {},
|
||||||
|
install: {},
|
||||||
|
remindLater: {},
|
||||||
|
skipThisVersion: {},
|
||||||
|
showReleaseNotes: {},
|
||||||
|
retry: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return UpdateUIActions(
|
||||||
|
allowAutoChecks: {
|
||||||
print("Demo: Allow auto checks")
|
print("Demo: Allow auto checks")
|
||||||
self?.updateUIModel.state = .idle
|
appDelegate.updateUIModel.state = .idle
|
||||||
},
|
},
|
||||||
denyAutoChecks: { [weak self] in
|
denyAutoChecks: {
|
||||||
print("Demo: Deny auto checks")
|
print("Demo: Deny auto checks")
|
||||||
self?.updateUIModel.state = .idle
|
appDelegate.updateUIModel.state = .idle
|
||||||
},
|
},
|
||||||
cancel: { [weak self] in
|
cancel: {
|
||||||
print("Demo: Cancel")
|
print("Demo: Cancel")
|
||||||
self?.updateUIModel.state = .idle
|
appDelegate.updateUIModel.state = .idle
|
||||||
},
|
},
|
||||||
install: { [weak self] in
|
install: {
|
||||||
guard let self else { return }
|
|
||||||
print("Demo: Install - simulating download and install flow")
|
print("Demo: Install - simulating download and install flow")
|
||||||
|
|
||||||
// Start downloading
|
// Start downloading
|
||||||
self.updateUIModel.state = .downloading
|
appDelegate.updateUIModel.state = .downloading
|
||||||
self.updateUIModel.progress = 0.0
|
appDelegate.updateUIModel.progress = 0.0
|
||||||
|
|
||||||
// Simulate download progress
|
// Simulate download progress
|
||||||
for i in 1...10 {
|
for i in 1...10 {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i) * 0.3) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i) * 0.3) {
|
||||||
self.updateUIModel.progress = Double(i) / 10.0
|
appDelegate.updateUIModel.progress = Double(i) / 10.0
|
||||||
|
|
||||||
if i == 10 {
|
if i == 10 {
|
||||||
// Move to extraction
|
// Move to extraction
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
self.updateUIModel.state = .extracting
|
appDelegate.updateUIModel.state = .extracting
|
||||||
self.updateUIModel.progress = 0.0
|
appDelegate.updateUIModel.progress = 0.0
|
||||||
|
|
||||||
// Simulate extraction progress
|
// Simulate extraction progress
|
||||||
for j in 1...5 {
|
for j in 1...5 {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + Double(j) * 0.3) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + Double(j) * 0.3) {
|
||||||
self.updateUIModel.progress = Double(j) / 5.0
|
appDelegate.updateUIModel.progress = Double(j) / 5.0
|
||||||
|
|
||||||
if j == 5 {
|
if j == 5 {
|
||||||
// Move to ready to install
|
// Move to ready to install
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
self.updateUIModel.state = .readyToInstall
|
appDelegate.updateUIModel.state = .readyToInstall
|
||||||
self.updateUIModel.progress = nil
|
appDelegate.updateUIModel.progress = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -508,29 +519,28 @@ class TerminalWindow: NSWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
remindLater: { [weak self] in
|
remindLater: {
|
||||||
print("Demo: Remind later")
|
print("Demo: Remind later")
|
||||||
self?.updateUIModel.state = .idle
|
appDelegate.updateUIModel.state = .idle
|
||||||
},
|
},
|
||||||
skipThisVersion: { [weak self] in
|
skipThisVersion: {
|
||||||
print("Demo: Skip version")
|
print("Demo: Skip version")
|
||||||
self?.updateUIModel.state = .idle
|
appDelegate.updateUIModel.state = .idle
|
||||||
},
|
},
|
||||||
showReleaseNotes: { [weak self] in
|
showReleaseNotes: {
|
||||||
print("Demo: Show release notes")
|
print("Demo: Show release notes")
|
||||||
guard let url = URL(string: "https://github.com/ghostty-org/ghostty/releases") else { return }
|
guard let url = URL(string: "https://github.com/ghostty-org/ghostty/releases") else { return }
|
||||||
NSWorkspace.shared.open(url)
|
NSWorkspace.shared.open(url)
|
||||||
},
|
},
|
||||||
retry: { [weak self] in
|
retry: {
|
||||||
guard let self else { return }
|
|
||||||
print("Demo: Retry - simulating update check")
|
print("Demo: Retry - simulating update check")
|
||||||
self.updateUIModel.state = .checking
|
appDelegate.updateUIModel.state = .checking
|
||||||
self.updateUIModel.progress = nil
|
appDelegate.updateUIModel.progress = nil
|
||||||
self.updateUIModel.error = nil
|
appDelegate.updateUIModel.error = nil
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
|
||||||
self.updateUIModel.state = .updateAvailable
|
appDelegate.updateUIModel.state = .updateAvailable
|
||||||
self.updateUIModel.details = .init(
|
appDelegate.updateUIModel.details = .init(
|
||||||
version: "1.2.0",
|
version: "1.2.0",
|
||||||
build: "demo",
|
build: "demo",
|
||||||
size: "42 MB",
|
size: "42 MB",
|
||||||
|
Reference in New Issue
Block a user