aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Hotline/macOS/AboutView.swift105
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)
}
}