mirror of
https://github.com/ParrotXray/mac-wallpaperengine.git
synced 2026-08-24 19:00:27 +09:00
Feat/auto update (#2)
* feat: add auto-update check against GitHub Releases * fix: Pause/Resume menu item not updating after click * fix: resolve UUID collision for UpdateService.swift in project file
This commit is contained in:
parent
11fa407029
commit
b778ce3a21
@ -57,6 +57,7 @@
|
||||
BB0000000000000000000003 /* ObjCExceptionCatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = BB0000000000000000000002 /* ObjCExceptionCatcher.m */; };
|
||||
EE0000000000000000000001 /* WELogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0000000000000000000002 /* WELogger.swift */; };
|
||||
CC0000000000000000000001 /* WallpaperDirectory.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0000000000000000000002 /* WallpaperDirectory.swift */; };
|
||||
CC000000000000000000000D /* UpdateService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC000000000000000000000E /* UpdateService.swift */; };
|
||||
DD0000000000000000000001 /* WallpaperWindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0000000000000000000002 /* WallpaperWindowManager.swift */; };
|
||||
CC0000000000000000000003 /* ZipImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0000000000000000000004 /* ZipImporter.swift */; };
|
||||
CC0000000000000000000005 /* SteamCmdService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0000000000000000000006 /* SteamCmdService.swift */; };
|
||||
@ -129,6 +130,7 @@
|
||||
BB0000000000000000000002 /* ObjCExceptionCatcher.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ObjCExceptionCatcher.m; sourceTree = "<group>"; };
|
||||
EE0000000000000000000002 /* WELogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WELogger.swift; sourceTree = "<group>"; };
|
||||
CC0000000000000000000002 /* WallpaperDirectory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WallpaperDirectory.swift; sourceTree = "<group>"; };
|
||||
CC000000000000000000000E /* UpdateService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateService.swift; sourceTree = "<group>"; };
|
||||
DD0000000000000000000002 /* WallpaperWindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WallpaperWindowManager.swift; sourceTree = "<group>"; };
|
||||
CC0000000000000000000004 /* ZipImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZipImporter.swift; sourceTree = "<group>"; };
|
||||
CC0000000000000000000006 /* SteamCmdService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SteamCmdService.swift; sourceTree = "<group>"; };
|
||||
@ -220,6 +222,7 @@
|
||||
BB0000000000000000000002 /* ObjCExceptionCatcher.m */,
|
||||
EE0000000000000000000002 /* WELogger.swift */,
|
||||
CC0000000000000000000002 /* WallpaperDirectory.swift */,
|
||||
CC000000000000000000000E /* UpdateService.swift */,
|
||||
DD0000000000000000000002 /* WallpaperWindowManager.swift */,
|
||||
CC0000000000000000000004 /* ZipImporter.swift */,
|
||||
CC0000000000000000000006 /* SteamCmdService.swift */,
|
||||
@ -470,6 +473,7 @@
|
||||
BB0000000000000000000003 /* ObjCExceptionCatcher.m in Sources */,
|
||||
EE0000000000000000000001 /* WELogger.swift in Sources */,
|
||||
CC0000000000000000000001 /* WallpaperDirectory.swift in Sources */,
|
||||
CC000000000000000000000D /* UpdateService.swift in Sources */,
|
||||
DD0000000000000000000001 /* WallpaperWindowManager.swift in Sources */,
|
||||
CC0000000000000000000003 /* ZipImporter.swift in Sources */,
|
||||
CC0000000000000000000005 /* SteamCmdService.swift in Sources */,
|
||||
@ -705,4 +709,4 @@
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 5EC47E6E2A2DB2E600120CCC /* Project object */;
|
||||
}
|
||||
}
|
||||
@ -49,7 +49,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
|
||||
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
wallpaperWindowManager.showAll()
|
||||
|
||||
UpdateService.shared.checkForUpdates()
|
||||
}
|
||||
|
||||
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
|
||||
|
||||
@ -26,6 +26,10 @@ extension AppDelegate {
|
||||
openMainWindow()
|
||||
}
|
||||
|
||||
@objc func checkForUpdates() {
|
||||
UpdateService.shared.checkForUpdates(userInitiated: true)
|
||||
}
|
||||
|
||||
@objc func openSupportWebpage() {
|
||||
NSWorkspace.shared.open(URL(string: "https://github.com/haren724/open-wallpaper-engine-mac/wiki")!)
|
||||
}
|
||||
@ -86,6 +90,11 @@ extension AppDelegate {
|
||||
|
||||
.separator(),
|
||||
|
||||
.init(title: String(localized: "Check for Updates"),
|
||||
systemImage: "arrow.down.circle",
|
||||
action: #selector(checkForUpdates),
|
||||
keyEquivalent: "u"),
|
||||
|
||||
.init(title: String(localized: "Support & FAQ"),
|
||||
systemImage: "person.fill.questionmark",
|
||||
action: #selector(openSupportWebpage),
|
||||
@ -145,10 +154,10 @@ extension AppDelegate {
|
||||
.sink { [weak self] rate in
|
||||
guard let menu = self?.statusItem.menu else { return }
|
||||
let isPaused = rate == 0
|
||||
if let idx = menu.items.firstIndex(where: { $0.title == "Pause" || $0.title == "Resume" }) {
|
||||
if let idx = menu.items.firstIndex(where: { $0.title == String(localized: "Pause") || $0.title == String(localized: "Resume") }) {
|
||||
menu.items[idx] = isPaused
|
||||
? .init(title: "Resume", systemImage: "play.fill", action: #selector(AppDelegate.shared.resume), keyEquivalent: "p")
|
||||
: .init(title: "Pause", systemImage: "pause.fill", action: #selector(AppDelegate.shared.pause), keyEquivalent: "p")
|
||||
? .init(title: String(localized: "Resume"), systemImage: "play.fill", action: #selector(AppDelegate.shared.resume), keyEquivalent: "p")
|
||||
: .init(title: String(localized: "Pause"), systemImage: "pause.fill", action: #selector(AppDelegate.shared.pause), keyEquivalent: "p")
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
@ -163,4 +172,4 @@ extension AppDelegate: NSMenuDelegate {
|
||||
recentItem.submenu = buildRecentWallpapersMenu()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
82
Open Wallpaper Engine/Services/UpdateService.swift
Normal file
82
Open Wallpaper Engine/Services/UpdateService.swift
Normal file
@ -0,0 +1,82 @@
|
||||
import Foundation
|
||||
import AppKit
|
||||
|
||||
class UpdateService {
|
||||
static let shared = UpdateService()
|
||||
|
||||
private let releasesURL = "https://api.github.com/repos/parrotxray/mac-wallpaperengine/releases/latest"
|
||||
|
||||
private var currentVersion: String {
|
||||
Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.0"
|
||||
}
|
||||
|
||||
func checkForUpdates(userInitiated: Bool = false) {
|
||||
guard let url = URL(string: releasesURL) else { return }
|
||||
var request = URLRequest(url: url)
|
||||
request.setValue("application/vnd.github+json", forHTTPHeaderField: "Accept")
|
||||
|
||||
URLSession.shared.dataTask(with: request) { [weak self] data, _, error in
|
||||
guard let self,
|
||||
let data,
|
||||
error == nil,
|
||||
let release = try? JSONDecoder().decode(GHRelease.self, from: data) else {
|
||||
if userInitiated {
|
||||
DispatchQueue.main.async { self?.showUpToDateAlert() }
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let latestVersion = release.tagName.trimmingCharacters(in: CharacterSet(charactersIn: "v"))
|
||||
|
||||
if self.isNewer(latestVersion, than: self.currentVersion) {
|
||||
DispatchQueue.main.async { self.showUpdateAlert(release: release) }
|
||||
} else if userInitiated {
|
||||
DispatchQueue.main.async { self.showUpToDateAlert() }
|
||||
}
|
||||
}.resume()
|
||||
}
|
||||
|
||||
private func isNewer(_ latest: String, than current: String) -> Bool {
|
||||
func baseParts(_ v: String) -> [Int] {
|
||||
let base = v.components(separatedBy: "-").first ?? v
|
||||
return base.split(separator: ".").compactMap { Int($0) }
|
||||
}
|
||||
let latestParts = baseParts(latest)
|
||||
let currentParts = baseParts(current)
|
||||
let count = max(latestParts.count, currentParts.count)
|
||||
for i in 0..<count {
|
||||
let l = i < latestParts.count ? latestParts[i] : 0
|
||||
let c = i < currentParts.count ? currentParts[i] : 0
|
||||
if l != c { return l > c }
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private func showUpdateAlert(release: GHRelease) {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = String(localized: "Update Available")
|
||||
alert.informativeText = String(localized: "Version \(release.tagName) is available. You have \(currentVersion).")
|
||||
alert.addButton(withTitle: String(localized: "Download"))
|
||||
alert.addButton(withTitle: String(localized: "Later"))
|
||||
if alert.runModal() == .alertFirstButtonReturn, let url = URL(string: release.htmlURL) {
|
||||
NSWorkspace.shared.open(url)
|
||||
}
|
||||
}
|
||||
|
||||
private func showUpToDateAlert() {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = String(localized: "You're up to date")
|
||||
alert.informativeText = String(localized: "Version \(currentVersion) is the latest.")
|
||||
alert.runModal()
|
||||
}
|
||||
}
|
||||
|
||||
private struct GHRelease: Decodable {
|
||||
let tagName: String
|
||||
let htmlURL: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case tagName = "tag_name"
|
||||
case htmlURL = "html_url"
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user