feat(settings): Installation section
Server version + up-to-date/behind indicator via installation.info.
This commit is contained in:
@@ -44,6 +44,9 @@ struct SettingsView: View {
|
||||
@State private var apiKeys: [OutlineAPIKey] = []
|
||||
@State private var isLoadingApiKeys = false
|
||||
@State private var apiKeysErrorMessage: String?
|
||||
@State private var installationInfo: OutlineInstallationInfo?
|
||||
@State private var isLoadingInstallationInfo = false
|
||||
@State private var installationInfoErrorMessage: String?
|
||||
|
||||
/// Full Local Sync and cache-clearing both need a real connection to be
|
||||
/// safe — clearing while offline (or letting Full Local Sync think it
|
||||
@@ -87,6 +90,7 @@ struct SettingsView: View {
|
||||
case .notifications: notificationsDetail
|
||||
case .passkeys: passkeysDetail
|
||||
case .apiAccess: apiAccessDetail
|
||||
case .installation: installationDetail
|
||||
case .offlineSync: offlineSyncDetail
|
||||
case .advanced: advancedDetail
|
||||
case .about: aboutDetail
|
||||
@@ -820,6 +824,55 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Installation
|
||||
|
||||
private var installationDetail: some View {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
sectionHeader
|
||||
|
||||
if isLoadingInstallationInfo && installationInfo == nil {
|
||||
ProgressView().controlSize(.small)
|
||||
} else if let installationInfoErrorMessage {
|
||||
Text(installationInfoErrorMessage)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.red)
|
||||
} else if let installationInfo {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
labeledRow("Version", installationInfo.version)
|
||||
Divider()
|
||||
if installationInfo.versionsBehind > 0 {
|
||||
labeledRow("Latest Version", installationInfo.latestVersion)
|
||||
Divider()
|
||||
Label(
|
||||
"\(installationInfo.versionsBehind) version\(installationInfo.versionsBehind == 1 ? "" : "s") behind",
|
||||
systemImage: "exclamationmark.triangle.fill"
|
||||
)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.orange)
|
||||
} else {
|
||||
Label("Up to date", systemImage: "checkmark.circle.fill")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.green)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: 420, alignment: .leading)
|
||||
}
|
||||
}
|
||||
.task { await refreshInstallationInfo() }
|
||||
}
|
||||
|
||||
private func refreshInstallationInfo() async {
|
||||
guard let apiClient = session.apiClient else { return }
|
||||
isLoadingInstallationInfo = true
|
||||
defer { isLoadingInstallationInfo = false }
|
||||
do {
|
||||
installationInfo = try await apiClient.installationInfo()
|
||||
installationInfoErrorMessage = nil
|
||||
} catch {
|
||||
installationInfoErrorMessage = outlineErrorMessage(error, fallback: "Couldn't load installation info.")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Offline & Sync
|
||||
|
||||
private var offlineSyncDetail: some View {
|
||||
|
||||
Reference in New Issue
Block a user