feat: Add notification types to enable opening specific URLs upon interaction.

This commit is contained in:
Paweł Orzech 2026-01-18 00:23:38 +00:00
parent 1ce929bffa
commit 4f8772f986
No known key found for this signature in database
2 changed files with 88 additions and 20 deletions

View file

@ -1,10 +1,46 @@
import Foundation import Foundation
import UserNotifications import UserNotifications
import AppKit
class NotificationManager { enum NotificationType: String {
case drugReady
case medicalReady
case boosterReady
case landed
case chainExpiring
case released
case energy
case nerve
case happy
case life
var url: URL {
switch self {
case .drugReady, .medicalReady, .boosterReady:
return URL(string: "https://www.torn.com/item.php")!
case .landed:
return URL(string: "https://www.torn.com/page.php?sid=ItemMarket")!
case .chainExpiring:
return URL(string: "https://www.torn.com/factions.php?step=your#/tab=wars")!
case .released:
return URL(string: "https://www.torn.com/")!
case .energy, .happy:
return URL(string: "https://www.torn.com/gym.php")!
case .nerve:
return URL(string: "https://www.torn.com/crimes.php")!
case .life:
return URL(string: "https://www.torn.com/hospitalview.php")!
}
}
}
class NotificationManager: NSObject, UNUserNotificationCenterDelegate {
static let shared = NotificationManager() static let shared = NotificationManager()
private init() {} private override init() {
super.init()
UNUserNotificationCenter.current().delegate = self
}
func requestPermission() async { func requestPermission() async {
do { do {
@ -18,11 +54,12 @@ class NotificationManager {
} }
} }
func send(title: String, body: String) { func send(title: String, body: String, type: NotificationType) {
let content = UNMutableNotificationContent() let content = UNMutableNotificationContent()
content.title = title content.title = title
content.body = body content.body = body
content.sound = .default content.sound = .default
content.categoryIdentifier = type.rawValue
let request = UNNotificationRequest( let request = UNNotificationRequest(
identifier: UUID().uuidString, identifier: UUID().uuidString,
@ -36,4 +73,26 @@ class NotificationManager {
} }
} }
} }
// MARK: - UNUserNotificationCenterDelegate
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
let categoryIdentifier = response.notification.request.content.categoryIdentifier
if let type = NotificationType(rawValue: categoryIdentifier) {
NSWorkspace.shared.open(type.url)
}
completionHandler()
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
completionHandler([.banner, .sound])
}
} }

View file

@ -501,31 +501,31 @@ class AppState: ObservableObject {
if let prevCD = previousCooldowns, let currentCD = newData.cooldowns { if let prevCD = previousCooldowns, let currentCD = newData.cooldowns {
if prevCD.drug > 0 && currentCD.drug == 0 { if prevCD.drug > 0 && currentCD.drug == 0 {
NotificationManager.shared.send(title: "Drug Ready! 💊", body: "Drug cooldown has ended") NotificationManager.shared.send(title: "Drug Ready! 💊", body: "Drug cooldown has ended", type: .drugReady)
} }
if prevCD.medical > 0 && currentCD.medical == 0 { if prevCD.medical > 0 && currentCD.medical == 0 {
NotificationManager.shared.send(title: "Medical Ready! 🏥", body: "Medical cooldown has ended") NotificationManager.shared.send(title: "Medical Ready! 🏥", body: "Medical cooldown has ended", type: .medicalReady)
} }
if prevCD.booster > 0 && currentCD.booster == 0 { if prevCD.booster > 0 && currentCD.booster == 0 {
NotificationManager.shared.send(title: "Booster Ready! 🚀", body: "Booster cooldown has ended") NotificationManager.shared.send(title: "Booster Ready! 🚀", body: "Booster cooldown has ended", type: .boosterReady)
} }
} }
if let prevTravel = previousTravel, let currentTravel = newData.travel { if let prevTravel = previousTravel, let currentTravel = newData.travel {
if prevTravel.isTraveling && !currentTravel.isTraveling { if prevTravel.isTraveling && !currentTravel.isTraveling {
NotificationManager.shared.send(title: "Landed! ✈️", body: "You have arrived in \(currentTravel.destination ?? "destination")") NotificationManager.shared.send(title: "Landed! ✈️", body: "You have arrived in \(currentTravel.destination ?? "destination")", type: .landed)
} }
} }
if let chain = newData.chain, chain.isActive { if let chain = newData.chain, chain.isActive {
if chain.timeoutRemaining < 60 && chain.timeoutRemaining > 0 { if chain.timeoutRemaining < 60 && chain.timeoutRemaining > 0 {
NotificationManager.shared.send(title: "Chain Expiring! ⚠️", body: "Chain timeout in \(chain.timeoutRemaining) seconds!") NotificationManager.shared.send(title: "Chain Expiring! ⚠️", body: "Chain timeout in \(chain.timeoutRemaining) seconds!", type: .chainExpiring)
} }
} }
if let prevStatus = previousStatus, let currentStatus = newData.status { if let prevStatus = previousStatus, let currentStatus = newData.status {
if !prevStatus.isOkay && currentStatus.isOkay { if !prevStatus.isOkay && currentStatus.isOkay {
NotificationManager.shared.send(title: "Released! 🎉", body: "You are now free") NotificationManager.shared.send(title: "Released! 🎉", body: "You are now free", type: .released)
} }
} }
} }
@ -539,13 +539,22 @@ class AppState: ObservableObject {
if prevPct < threshold && currentPct >= threshold { if prevPct < threshold && currentPct >= threshold {
let title: String let title: String
let notificationType: NotificationType
switch barType { switch barType {
case .energy: title = "Energy \(rule.threshold)%! ⚡️" case .energy:
case .nerve: title = "Nerve \(rule.threshold)%! 💪" title = "Energy \(rule.threshold)%! ⚡️"
case .happy: title = "Happy \(rule.threshold)%! 😊" notificationType = .energy
case .life: title = "Life \(rule.threshold)%! ❤️" case .nerve:
title = "Nerve \(rule.threshold)%! 💪"
notificationType = .nerve
case .happy:
title = "Happy \(rule.threshold)%! 😊"
notificationType = .happy
case .life:
title = "Life \(rule.threshold)%! ❤️"
notificationType = .life
} }
NotificationManager.shared.send(title: title, body: "\(barType.rawValue) is now at \(currentBar.current)/\(currentBar.maximum)") NotificationManager.shared.send(title: title, body: "\(barType.rawValue) is now at \(currentBar.current)/\(currentBar.maximum)", type: notificationType)
if let sound = NotificationSound(rawValue: rule.soundName) { if let sound = NotificationSound(rawValue: rule.soundName) {
SoundManager.shared.play(sound) SoundManager.shared.play(sound)