diff --git a/Sources/Shotdeck/UpdateChecker.swift b/Sources/Shotdeck/UpdateChecker.swift index 18ff2d0..5b705cb 100644 --- a/Sources/Shotdeck/UpdateChecker.swift +++ b/Sources/Shotdeck/UpdateChecker.swift @@ -2,6 +2,7 @@ import AppKit import CryptoKit import Foundation import Security +import ShotdeckCore /// Built-in updater. Checks an appcast, stages a verified payload, and installs /// only when the user clicks the menu row — never automatically. @@ -31,6 +32,10 @@ final class UpdateChecker { private var repeatingTimer: Timer? private var firstCheckTask: Task? private var stagingDirectory: URL? + /// Snapshot-only override for previousVersion; when snapshotUsesPreviousVersionOverride is true, + /// this value (including nil) is returned instead of checking the file system. + var snapshotPreviousVersionOverride: String? + var snapshotUsesPreviousVersionOverride: Bool = false init() { let config = URLSessionConfiguration.ephemeral @@ -85,7 +90,12 @@ final class UpdateChecker { guard Self.isNewer(appcast.version, than: Self.currentVersion()) else { clearOffer() - statusMessage = manual ? "Redline \(Self.currentVersion()) is up to date." : nil + if manual { + let timestamp = DubaiTime.checkTime(lastCheckedAt ?? Date()) + statusMessage = "Redline \(Self.currentVersion()) is up to date, checked \(timestamp)" + } else { + statusMessage = nil + } onChecked?() return } @@ -93,7 +103,7 @@ final class UpdateChecker { do { try await downloadAndStage(appcast) availableUpdate = (version: appcast.version, notes: appcast.notes ?? "") - statusMessage = nil + statusMessage = manual ? "Update to \(appcast.version) is ready" : nil } catch UpdateCheckError.checksumMismatch { discardStaging() availableUpdate = nil @@ -226,8 +236,11 @@ final class UpdateChecker { } /// The version recorded in `Redline.app.previous`'s Info.plist, or nil when no - /// rollback copy exists. + /// rollback copy exists. Respects the snapshot-only override for panel rendering. func previousVersion(target: URL = UpdateChecker.defaultInstallTarget) -> String? { + if snapshotUsesPreviousVersionOverride { + return snapshotPreviousVersionOverride + } let previousURL = target.deletingLastPathComponent().appendingPathComponent("Redline.app.previous") let plistURL = previousURL.appendingPathComponent("Contents/Info.plist") guard let plist = NSDictionary(contentsOf: plistURL) as? [String: Any] else { return nil }