diff options
| author | Dustin Mierau <dustin@mierau.me> | 2024-05-05 22:34:34 -0700 |
|---|---|---|
| committer | Dustin Mierau <dustin@mierau.me> | 2024-05-05 22:34:34 -0700 |
| commit | a74ceae0348bb04fc0b289dc7226934726ce9ece (patch) | |
| tree | 171e270633954beae08c5c1c6518dbf8f6f4be9c /Hotline | |
| parent | 3feb709522b38f1aa5ddedc5e1aee28597375aab (diff) | |
Add version check/download UI in about box.
Diffstat (limited to 'Hotline')
| -rw-r--r-- | Hotline/macOS/AboutView.swift | 105 |
1 files changed, 94 insertions, 11 deletions
diff --git a/Hotline/macOS/AboutView.swift b/Hotline/macOS/AboutView.swift index 378385a..acf79b5 100644 --- a/Hotline/macOS/AboutView.swift +++ b/Hotline/macOS/AboutView.swift @@ -1,38 +1,121 @@ import SwiftUI +enum VersionCheckState { + case needToCheck + case checking + case upToDate + case updateAvailable(version: String) +} + struct AboutView: View { @Environment(\.openURL) private var openURL + @State private var versionCheck: VersionCheckState = .needToCheck + @State private var downloadURL: String = "https://github.com/mierau/hotline/releases/latest" + var body: some View { - VStack(alignment: .center) { + VStack(alignment: .center, spacing: 0) { Spacer() + Image("About Hotline") .padding(.top, 32) + Text("Hotline") .font(.title) .fontWeight(.semibold) .padding(.top, 16) + .foregroundColor(.white) + let appDetails = getAppVersionAndBuild() - Text("\(appDetails.version)b\(appDetails.build)") + Text("\(String(format: "%.1f", appDetails.version))b\(appDetails.build)") .fontWeight(.semibold) + .foregroundColor(.white) .opacity(0.6) - Button("Download Latest") { - if let url = URL(string: "https://github.com/mierau/hotline/releases/latest") { - openURL(url) + + HStack(alignment: .center) { + switch versionCheck { + case .needToCheck: + Button("Check for Updates") { + Task { + await checkForUpdate() + } + } + .controlSize(.regular) + case .checking: + HStack(spacing: 8) { + ProgressView() + .controlSize(.small) + .tint(.white) + Text("Checking for updates...") + .fontWeight(.semibold) + .foregroundStyle(.white) + } + case .upToDate: + Label("Hotline is up to date.", systemImage: "checkmark.circle.fill") + .foregroundStyle(.white) + .fontWeight(.semibold) + .tint(.white) + .onTapGesture { + versionCheck = .needToCheck + } + case .updateAvailable(let version): + Button("Download Latest \(version)") { + if let url = URL(string: downloadURL) { + openURL(url) + } + } + .controlSize(.regular) } } - .controlSize(.large) - .padding(.top, 16) + .frame(height: 40) + .padding(.top, 8) + Spacer() } .frame(width: 300, height: 400) .ignoresSafeArea() } - func getAppVersionAndBuild() -> (version: String, build: String) { - let infoDictionary = Bundle.main.infoDictionary - let version = infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown" - let build = infoDictionary?["CFBundleVersion"] as? String ?? "Unknown" + func checkForUpdate() async { + let appDetails = getAppVersionAndBuild() + + self.versionCheck = .checking + + do { + let url = URL(string: "https://api.github.com/repos/mierau/hotline/releases/latest")! + let (data, _) = try await URLSession.shared.data(from: url) + let versionExpression = /^([0-9\.]+)beta([0-9]+)/ + + if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], + let tagName = json["tag_name"] as? String, + let assets = json["assets"] as? [[String: Any]], + let firstAsset = assets.first, + let assetDownloadURL = firstAsset["browser_download_url"] as? String, + let versionMatches = try? versionExpression.wholeMatch(in: tagName) { + if let versionNumber = Double(versionMatches.1), + let buildNumber = Int(versionMatches.2), + versionNumber > appDetails.version || buildNumber > appDetails.build { + let versionString = "\(versionMatches.1)b\(versionMatches.2)" + self.versionCheck = .updateAvailable(version: versionString) + downloadURL = assetDownloadURL + } + else { + self.versionCheck = .upToDate + } + } + else { + self.versionCheck = .needToCheck + } + } + catch { + self.versionCheck = .needToCheck + } + } + + func getAppVersionAndBuild() -> (version: Double, build: Int) { + let infoDictionary = Bundle.main.infoDictionary! + let version = Double(infoDictionary["CFBundleShortVersionString"]! as! String)! + let build = Int(infoDictionary["CFBundleVersion"]! as! String)! return (version, build) } } |