diff --git a/Outpost/Features/About/AboutView.swift b/Outpost/Features/About/AboutView.swift index 6f4c53b..93b5afd 100644 --- a/Outpost/Features/About/AboutView.swift +++ b/Outpost/Features/About/AboutView.swift @@ -14,17 +14,7 @@ struct AboutInfoView: View { Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String ?? "Outpost" } - /// Bumped alongside `MARKETING_VERSION` in the Xcode project — kept out - /// of the bundle version itself since `CFBundleShortVersionString` is - /// expected to stay a plain dotted-numeric string, not `0.0.1-ALPHA`. - private let releaseStage = "ALPHA" - - var versionString: String { - let shortVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "0.0.1" - let buildNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "1" - let stageSuffix = releaseStage.isEmpty ? "" : "-\(releaseStage)" - return "Version \(shortVersion)\(stageSuffix) (\(buildNumber))" - } + var versionString: String { OutpostVersion.fullVersionString } private var copyrightYear: String { String(Calendar.current.component(.year, from: Date())) diff --git a/Outpost/Features/Account/SettingsSidebarList.swift b/Outpost/Features/Account/SettingsSidebarList.swift index d855429..48b7fbf 100644 --- a/Outpost/Features/Account/SettingsSidebarList.swift +++ b/Outpost/Features/Account/SettingsSidebarList.swift @@ -20,10 +20,13 @@ struct SettingsSidebarList: View { let onDone: () -> Void @Environment(SessionStore.self) private var session + @AppStorage(CachingOutlineAPIClient.offlineModeDefaultsKey) private var isOfflineModeEnabled = false @State private var outlineVersion: String? - private var outpostVersion: String { - Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "0.0.1" + /// Mirrors `SettingsView`'s own check — a real dropped connection or + /// the manual Offline Mode toggle both mean there's no server to ask. + private var isEffectivelyOnline: Bool { + session.networkMonitor.isOnline && !isOfflineModeEnabled } var body: some View { @@ -67,14 +70,20 @@ struct SettingsSidebarList: View { .frame(maxWidth: .infinity) .padding(12) } - .task { await refreshOutlineVersion() } + // Keyed to connectivity, not a one-shot `.task {}` — reconnecting + // (or turning the manual Offline Mode toggle back off) re-fires + // this automatically instead of leaving the footer stuck on + // whatever it last knew, or blank, until Settings is reopened. + .task(id: isEffectivelyOnline) { await refreshOutlineVersion() } } private var versionFooter: some View { VStack(alignment: .leading, spacing: 2) { - Text("Outpost \(outpostVersion)") + Text("Outpost \(OutpostVersion.displayString)") if let outlineVersion { Text("Outline \(outlineVersion)") + } else if !isEffectivelyOnline { + Text("Outline — offline") } } .font(.caption2) @@ -85,7 +94,7 @@ struct SettingsSidebarList: View { } private func refreshOutlineVersion() async { - guard let apiClient = session.apiClient else { return } + guard isEffectivelyOnline, let apiClient = session.apiClient else { return } outlineVersion = try? await apiClient.installationInfo().version } } diff --git a/Outpost/Support/OutpostVersion.swift b/Outpost/Support/OutpostVersion.swift new file mode 100644 index 0000000..85aee99 --- /dev/null +++ b/Outpost/Support/OutpostVersion.swift @@ -0,0 +1,31 @@ +import Foundation + +/// Single source of truth for how Outpost's own version is formatted — +/// used by both the About page and the Settings sidebar footer, so they +/// can't drift out of sync the way `AboutInfoView` was already written to +/// avoid for its own two call sites. +enum OutpostVersion { + /// Bumped alongside `MARKETING_VERSION` in the Xcode project — kept out + /// of the bundle version itself since `CFBundleShortVersionString` is + /// expected to stay a plain dotted-numeric string, not `0.0.1-ALPHA`. + static let releaseStage = "ALPHA" + + static var shortVersion: String { + Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "0.0.1" + } + + static var buildNumber: String { + Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? "1" + } + + /// e.g. `"0.0.3-ALPHA"` — for compact display (sidebar footer). + static var displayString: String { + let stageSuffix = releaseStage.isEmpty ? "" : "-\(releaseStage)" + return "\(shortVersion)\(stageSuffix)" + } + + /// e.g. `"Version 0.0.3-ALPHA (1)"` — for the About page. + static var fullVersionString: String { + "Version \(displayString) (\(buildNumber))" + } +}