mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-30 20:49:23 +00:00
feat: inline Sparkle update checks to avoid unnecessary modals
This commit is contained in:
@@ -1,16 +1,58 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Sparkle
|
import Sparkle
|
||||||
|
|
||||||
final class SparkleUpdater: ObservableObject {
|
final class SparkleUpdater: NSObject, ObservableObject, SPUUpdaterDelegate {
|
||||||
let controller: SPUStandardUpdaterController
|
private var _controller: SPUStandardUpdaterController?
|
||||||
init(controller: SPUStandardUpdaterController) {
|
var controller: SPUStandardUpdaterController { _controller! }
|
||||||
self.controller = controller
|
|
||||||
|
@Published var isChecking = false
|
||||||
|
@Published var updateStatus: String?
|
||||||
|
@Published var foundUpdateItem: SUAppcastItem?
|
||||||
|
|
||||||
|
override init() {
|
||||||
|
super.init()
|
||||||
|
self._controller = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: self, userDriverDelegate: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkForUpdates() {
|
||||||
|
isChecking = true
|
||||||
|
updateStatus = "Checking for updates..."
|
||||||
|
foundUpdateItem = nil
|
||||||
|
controller.updater.checkForUpdatesInBackground()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updater(_ updater: SPUUpdater, didFindValidUpdate item: SUAppcastItem) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.isChecking = false
|
||||||
|
self.foundUpdateItem = item
|
||||||
|
self.updateStatus = "Update available: Version \(item.displayVersionString)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updaterDidNotFindUpdate(_ updater: SPUUpdater, error: Error) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.isChecking = false
|
||||||
|
let version = updater.hostBundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? ""
|
||||||
|
let build = updater.hostBundle.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? ""
|
||||||
|
self.updateStatus = "You're up to date! (Version \(version) (\(build)))"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updater(_ updater: SPUUpdater, didAbortWithError error: Error) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.isChecking = false
|
||||||
|
let nsError = error as NSError
|
||||||
|
if nsError.domain == "SUSparkleErrorDomain" && nsError.code == 1002 {
|
||||||
|
// SUNoUpdateError, handled by updaterDidNotFindUpdate
|
||||||
|
} else {
|
||||||
|
self.updateStatus = "Update check failed: \(error.localizedDescription)"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct FirelinkApp: App {
|
struct FirelinkApp: App {
|
||||||
private let updaterController: SPUStandardUpdaterController
|
|
||||||
@StateObject private var sparkleUpdater: SparkleUpdater
|
@StateObject private var sparkleUpdater: SparkleUpdater
|
||||||
|
|
||||||
@StateObject private var settings: AppSettings
|
@StateObject private var settings: AppSettings
|
||||||
@@ -22,10 +64,7 @@ struct FirelinkApp: App {
|
|||||||
private let extensionServer: LocalExtensionServer?
|
private let extensionServer: LocalExtensionServer?
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
// Initialize Sparkle updater
|
self._sparkleUpdater = StateObject(wrappedValue: SparkleUpdater())
|
||||||
let updaterController = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil)
|
|
||||||
self.updaterController = updaterController
|
|
||||||
self._sparkleUpdater = StateObject(wrappedValue: SparkleUpdater(controller: updaterController))
|
|
||||||
|
|
||||||
let settings = AppSettings()
|
let settings = AppSettings()
|
||||||
let controller = DownloadController(settings: settings)
|
let controller = DownloadController(settings: settings)
|
||||||
|
|||||||
@@ -41,12 +41,31 @@ struct AboutSettingsPane: View {
|
|||||||
|
|
||||||
Section("Updates") {
|
Section("Updates") {
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
|
if let status = sparkleUpdater.updateStatus {
|
||||||
|
HStack {
|
||||||
|
if sparkleUpdater.isChecking {
|
||||||
|
ProgressView()
|
||||||
|
.controlSize(.small)
|
||||||
|
} else if sparkleUpdater.foundUpdateItem != nil {
|
||||||
|
Image(systemName: "exclamationmark.circle.fill")
|
||||||
|
.foregroundStyle(.orange)
|
||||||
|
} else {
|
||||||
|
Image(systemName: "checkmark.circle.fill")
|
||||||
|
.foregroundStyle(.green)
|
||||||
|
}
|
||||||
|
Text(status)
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
Button {
|
Button {
|
||||||
sparkleUpdater.controller.checkForUpdates(nil)
|
sparkleUpdater.checkForUpdates()
|
||||||
} label: {
|
} label: {
|
||||||
Label("Check for Updates", systemImage: "arrow.clockwise")
|
Label("Check for Updates", systemImage: "arrow.clockwise")
|
||||||
}
|
}
|
||||||
|
.disabled(sparkleUpdater.isChecking)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
NSWorkspace.shared.open(projectURL.appendingPathComponent("releases"))
|
NSWorkspace.shared.open(projectURL.appendingPathComponent("releases"))
|
||||||
|
|||||||
Reference in New Issue
Block a user