diff --git a/Calendr/Assets/Generated/Strings.generated.swift b/Calendr/Assets/Generated/Strings.generated.swift index d4b119c9..21ec8029 100644 --- a/Calendr/Assets/Generated/Strings.generated.swift +++ b/Calendr/Assets/Generated/Strings.generated.swift @@ -10,6 +10,10 @@ import Foundation // swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length // swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces internal enum Strings { + /// New version available: %@ + internal static func newVersion(_ p1: Any) -> String { + return Strings.tr("Localizable", "new_version", String(describing: p1), fallback: "New version available: %@") + } /// Localizable.strings /// Calendr /// diff --git a/Calendr/Assets/de.lproj/Localizable.strings b/Calendr/Assets/de.lproj/Localizable.strings index 89a94cab..d20a7693 100644 --- a/Calendr/Assets/de.lproj/Localizable.strings +++ b/Calendr/Assets/de.lproj/Localizable.strings @@ -7,6 +7,7 @@ "quit" = "Programm beenden"; "search" = "Suchen"; +"new_version" = "Neue Version verfügbar: %@"; "settings.title" = "Einstellungen"; "settings.tab.general" = "Allgemein"; diff --git a/Calendr/Assets/en.lproj/Localizable.strings b/Calendr/Assets/en.lproj/Localizable.strings index 1f9045cd..1b50ba3f 100644 --- a/Calendr/Assets/en.lproj/Localizable.strings +++ b/Calendr/Assets/en.lproj/Localizable.strings @@ -7,6 +7,7 @@ "quit" = "Quit"; "search" = "Search"; +"new_version" = "New version available: %@"; "settings.title" = "Preferences"; "settings.tab.general" = "General"; diff --git a/Calendr/Assets/es.lproj/Localizable.strings b/Calendr/Assets/es.lproj/Localizable.strings index 502e1050..180285ec 100644 --- a/Calendr/Assets/es.lproj/Localizable.strings +++ b/Calendr/Assets/es.lproj/Localizable.strings @@ -7,6 +7,7 @@ "quit" = "Salir"; "search" = "Buscar"; +"new_version" = "Nueva versión disponible: %@"; "settings.title" = "Preferencias"; "settings.tab.general" = "General"; diff --git a/Calendr/Assets/fr.lproj/Localizable.strings b/Calendr/Assets/fr.lproj/Localizable.strings index 2433e2fc..1f21fbd0 100644 --- a/Calendr/Assets/fr.lproj/Localizable.strings +++ b/Calendr/Assets/fr.lproj/Localizable.strings @@ -7,6 +7,7 @@ "quit" = "Quitter"; "search" = "Rechercher"; +"new_version" = "Nouvelle version disponible: %@"; "settings.title" = "Préférences"; "settings.tab.general" = "Général"; diff --git a/Calendr/Assets/it.lproj/Localizable.strings b/Calendr/Assets/it.lproj/Localizable.strings index 5251c000..64d5e7ee 100644 --- a/Calendr/Assets/it.lproj/Localizable.strings +++ b/Calendr/Assets/it.lproj/Localizable.strings @@ -7,6 +7,7 @@ "quit" = "Esci"; "search" = "Cerca"; +"new_version" = "Nuova versione disponibile: %@"; "settings.title" = "Preferenze"; "settings.tab.general" = "Generali"; diff --git a/Calendr/Assets/pt.lproj/Localizable.strings b/Calendr/Assets/pt.lproj/Localizable.strings index 7fb93514..eb9d852a 100644 --- a/Calendr/Assets/pt.lproj/Localizable.strings +++ b/Calendr/Assets/pt.lproj/Localizable.strings @@ -7,6 +7,7 @@ "quit" = "Sair"; "search" = "Buscar"; +"new_version" = "Nova versão disponível: %@"; "settings.title" = "Preferências"; "settings.tab.general" = "Geral"; diff --git a/Calendr/Config/Calendr.entitlements b/Calendr/Config/Calendr.entitlements index d9120b5a..498d6171 100644 --- a/Calendr/Config/Calendr.entitlements +++ b/Calendr/Config/Calendr.entitlements @@ -6,6 +6,8 @@ com.apple.security.files.user-selected.read-only + com.apple.security.network.client + com.apple.security.personal-information.calendars com.apple.security.temporary-exception.apple-events diff --git a/Calendr/Settings/AboutViewController.swift b/Calendr/Settings/AboutViewController.swift index 435c2d00..3ef80f8f 100644 --- a/Calendr/Settings/AboutViewController.swift +++ b/Calendr/Settings/AboutViewController.swift @@ -6,11 +6,14 @@ // import Cocoa +import UserNotifications -class AboutViewController: NSViewController { +class AboutViewController: NSViewController, UNUserNotificationCenterDelegate { private let quitButton: NSButton private let linkView: NSTextView + private let newVersionButton: NSButton + private let appVersion = "v\(BuildConfig.appVersion)" init() { quitButton = NSButton(title: Strings.quit, target: NSApp, action: #selector(NSApp.terminate)) @@ -26,23 +29,38 @@ class AboutViewController: NSViewController { linkView.alignment = .center linkView.height(equalTo: 15) + newVersionButton = NSButton() + super.init(nibName: nil, bundle: nil) + newVersionButton.target = self + newVersionButton.action = #selector(openReleasePage) + newVersionButton.refusesFirstResponder = true + newVersionButton.bezelStyle = .roundRect + setUpAccessibility() + setUpReleaseCheck() } override func loadView() { + if let version = UserDefaults.standard.string(forKey: Prefs.lastCheckedVersion), version != appVersion { + showNewVersionButton(Strings.newVersion(version)) + } else { + newVersionButton.isHidden = true + } + view = NSStackView(views: [ Label(text: "Calendr", font: .systemFont(ofSize: 16, weight: .semibold), align: .center), .spacer(height: 0), - Label(text: "v\(BuildConfig.appVersion)", font: .systemFont(ofSize: 13), align: .center), + Label(text: appVersion, font: .systemFont(ofSize: 13), align: .center), Label(text: "\(BuildConfig.date) - \(BuildConfig.time)", color: .secondaryLabelColor, align: .center), .spacer(height: 4), Label(text: #"¯\_(ツ)_/¯"#, font: .systemFont(ofSize: 16), align: .center), .spacer(height: 4), Label(text: "© 2020 - \(BuildConfig.date.suffix(4)) Carlos Enumo", align: .center), linkView, + newVersionButton, .spacer(height: 4), quitButton ]) @@ -50,7 +68,7 @@ class AboutViewController: NSViewController { .with(orientation: .vertical) } - func setUpAccessibility() { + private func setUpAccessibility() { guard BuildConfig.isUITesting else { return } @@ -62,6 +80,84 @@ class AboutViewController: NSViewController { quitButton.setAccessibilityIdentifier(Accessibility.Settings.About.quitBtn) } + private func setUpReleaseCheck() { + UNUserNotificationCenter.current().delegate = self + UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { (granted, error) in + guard granted else { return } + self.checkRelease() + DispatchQueue.main.async { + Timer.scheduledTimer( + withTimeInterval: 60 * 60, + repeats: true + ) { _ in + DispatchQueue.global().async { + self.checkRelease() + } + } + } + } + } + + private func showNewVersionButton(_ title: String) { + newVersionButton.title = title + newVersionButton.isHidden = false + } + + private func checkRelease() { + do { + let url = "https://api.github.com/repos/pakerwreah/Calendr/releases/latest" + let data = try Data(contentsOf: URL(string: url)!) + guard + let json = try JSONSerialization.jsonObject(with: data) as? [String: Any], + let version = json["name"] as? String + else { return } + + guard version != appVersion else { + DispatchQueue.main.async { + self.newVersionButton.isHidden = true + } + UserDefaults.standard.set(version, forKey: Prefs.lastCheckedVersion) + return + } + + guard version != UserDefaults.standard.string(forKey: Prefs.lastCheckedVersion) else { return } + + let message = Strings.newVersion(version) + + DispatchQueue.main.async { + self.showNewVersionButton(message) + } + + let content = UNMutableNotificationContent() + content.title = message + content.sound = .default + + UNUserNotificationCenter.current().add( + UNNotificationRequest( + identifier: UUID().uuidString, + content: content, + trigger: nil + ) + ) { error in + if let error { + print(error.localizedDescription) + return + } + UserDefaults.standard.set(version, forKey: Prefs.lastCheckedVersion) + } + } catch { + print(error.localizedDescription) + } + } + + @objc private func openReleasePage() { + NSWorkspace.shared.open(URL(string: "https://github.com/pakerwreah/Calendr/releases/latest")!) + } + + func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { + openReleasePage() + } + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/Calendr/Settings/Prefs+UserDefaults.swift b/Calendr/Settings/Prefs+UserDefaults.swift index 45231812..6e2eea09 100644 --- a/Calendr/Settings/Prefs+UserDefaults.swift +++ b/Calendr/Settings/Prefs+UserDefaults.swift @@ -26,6 +26,7 @@ enum Prefs { static let transparencyLevel = "transparency_level" static let calendarScaling = "calendar_scaling" static let highlightedWeekdays = "highlighted_weekdays" + static let lastCheckedVersion = "last_checked_version" } extension UserDefaults {