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 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()
private init() {}
private override init() {
super.init()
UNUserNotificationCenter.current().delegate = self
}
func requestPermission() async {
do {
@ -18,11 +54,12 @@ class NotificationManager {
}
}
func send(title: String, body: String) {
func send(title: String, body: String, type: NotificationType) {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
content.categoryIdentifier = type.rawValue
let request = UNNotificationRequest(
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 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 {
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 {
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 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 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 !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 {
let title: String
let notificationType: NotificationType
switch barType {
case .energy: title = "Energy \(rule.threshold)%! ⚡️"
case .nerve: title = "Nerve \(rule.threshold)%! 💪"
case .happy: title = "Happy \(rule.threshold)%! 😊"
case .life: title = "Life \(rule.threshold)%! ❤️"
case .energy:
title = "Energy \(rule.threshold)%! ⚡️"
notificationType = .energy
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) {
SoundManager.shared.play(sound)