Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
6322f1cbd3
commit
bf489878dd
@ -11,6 +11,12 @@ import SwiftUI
|
||||
@main
|
||||
struct TheNoiseClockApp: App {
|
||||
|
||||
// MARK: - Initialization
|
||||
init() {
|
||||
// Initialize notification delegate to handle snooze actions
|
||||
_ = NotificationDelegate.shared
|
||||
}
|
||||
|
||||
// MARK: - Body
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
|
||||
@ -70,7 +70,7 @@ class FocusModeService {
|
||||
UNNotificationAction(
|
||||
identifier: "SNOOZE_ACTION",
|
||||
title: "Snooze",
|
||||
options: [.foreground]
|
||||
options: []
|
||||
),
|
||||
UNNotificationAction(
|
||||
identifier: "STOP_ACTION",
|
||||
|
||||
179
TheNoiseClock/Services/NotificationDelegate.swift
Normal file
179
TheNoiseClock/Services/NotificationDelegate.swift
Normal file
@ -0,0 +1,179 @@
|
||||
//
|
||||
// NotificationDelegate.swift
|
||||
// TheNoiseClock
|
||||
//
|
||||
// Created by Matt Bruce on 9/8/25.
|
||||
//
|
||||
|
||||
import UserNotifications
|
||||
import Foundation
|
||||
|
||||
/// Delegate to handle notification actions (snooze, stop, etc.)
|
||||
class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate {
|
||||
|
||||
// MARK: - Singleton
|
||||
static let shared = NotificationDelegate()
|
||||
|
||||
// MARK: - Properties
|
||||
private var alarmService: AlarmService?
|
||||
|
||||
// MARK: - Initialization
|
||||
private override init() {
|
||||
super.init()
|
||||
setupNotificationCenter()
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
private func setupNotificationCenter() {
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
print("🔔 Notification delegate configured")
|
||||
}
|
||||
|
||||
/// Set the alarm service instance (called from AlarmViewModel)
|
||||
func setAlarmService(_ service: AlarmService) {
|
||||
self.alarmService = service
|
||||
}
|
||||
|
||||
// MARK: - UNUserNotificationCenterDelegate
|
||||
|
||||
/// Handle notification actions when app is in foreground
|
||||
func userNotificationCenter(
|
||||
_ center: UNUserNotificationCenter,
|
||||
didReceive response: UNNotificationResponse,
|
||||
withCompletionHandler completionHandler: @escaping () -> Void
|
||||
) {
|
||||
let actionIdentifier = response.actionIdentifier
|
||||
let notification = response.notification
|
||||
let userInfo = notification.request.content.userInfo
|
||||
|
||||
print("🔔 Notification action received: \(actionIdentifier)")
|
||||
|
||||
switch actionIdentifier {
|
||||
case "SNOOZE_ACTION":
|
||||
handleSnoozeAction(userInfo: userInfo)
|
||||
case "STOP_ACTION":
|
||||
handleStopAction(userInfo: userInfo)
|
||||
case UNNotificationDefaultActionIdentifier:
|
||||
// User tapped the notification itself
|
||||
handleNotificationTap(userInfo: userInfo)
|
||||
default:
|
||||
print("🔔 Unknown action: \(actionIdentifier)")
|
||||
}
|
||||
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
/// Handle notifications when app is in foreground
|
||||
func userNotificationCenter(
|
||||
_ center: UNUserNotificationCenter,
|
||||
willPresent notification: UNNotification,
|
||||
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
|
||||
) {
|
||||
// Show notification even when app is in foreground
|
||||
completionHandler([.banner, .sound, .badge])
|
||||
}
|
||||
|
||||
// MARK: - Action Handlers
|
||||
|
||||
private func handleSnoozeAction(userInfo: [AnyHashable: Any]) {
|
||||
guard let alarmIdString = userInfo["alarmId"] as? String,
|
||||
let alarmId = UUID(uuidString: alarmIdString),
|
||||
let alarmService = self.alarmService,
|
||||
let alarm = alarmService.getAlarm(id: alarmId) else {
|
||||
print("❌ Could not find alarm for snooze action")
|
||||
return
|
||||
}
|
||||
|
||||
print("🔔 Snoozing alarm: \(alarm.label) for \(alarm.snoozeDuration) minutes")
|
||||
|
||||
// Calculate snooze time (current time + snooze duration)
|
||||
let snoozeTime = Date().addingTimeInterval(TimeInterval(alarm.snoozeDuration * 60))
|
||||
print("🔔 Snooze time: \(snoozeTime)")
|
||||
print("🔔 Current time: \(Date())")
|
||||
|
||||
// Create a temporary alarm for the snooze
|
||||
let snoozeAlarm = Alarm(
|
||||
id: UUID(), // New ID for snooze alarm
|
||||
time: snoozeTime,
|
||||
isEnabled: true,
|
||||
soundName: alarm.soundName,
|
||||
label: "\(alarm.label) (Snoozed)",
|
||||
notificationMessage: "Snoozed: \(alarm.notificationMessage)",
|
||||
snoozeDuration: alarm.snoozeDuration,
|
||||
isVibrationEnabled: alarm.isVibrationEnabled,
|
||||
isLightFlashEnabled: alarm.isLightFlashEnabled,
|
||||
volume: alarm.volume
|
||||
)
|
||||
|
||||
// Schedule the snooze notification
|
||||
Task {
|
||||
await scheduleSnoozeNotification(snoozeAlarm, userInfo: userInfo)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleStopAction(userInfo: [AnyHashable: Any]) {
|
||||
guard let alarmIdString = userInfo["alarmId"] as? String,
|
||||
let alarmId = UUID(uuidString: alarmIdString) else {
|
||||
print("❌ Could not find alarm ID for stop action")
|
||||
return
|
||||
}
|
||||
|
||||
print("🔔 Stopping alarm: \(alarmId)")
|
||||
|
||||
// Cancel any pending notifications for this alarm
|
||||
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [alarmIdString])
|
||||
|
||||
// If this was a snooze alarm, we don't want to disable the original alarm
|
||||
// Just cancel the current notification
|
||||
}
|
||||
|
||||
private func handleNotificationTap(userInfo: [AnyHashable: Any]) {
|
||||
guard let alarmIdString = userInfo["alarmId"] as? String,
|
||||
let alarmId = UUID(uuidString: alarmIdString) else {
|
||||
print("❌ Could not find alarm ID for notification tap")
|
||||
return
|
||||
}
|
||||
|
||||
print("🔔 Notification tapped for alarm: \(alarmId)")
|
||||
|
||||
// For now, just log the tap. In the future, this could open the alarm details
|
||||
// or perform some other action when the user taps the notification
|
||||
}
|
||||
|
||||
// MARK: - Private Methods
|
||||
|
||||
private func scheduleSnoozeNotification(_ snoozeAlarm: Alarm, userInfo: [AnyHashable: Any]) async {
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = snoozeAlarm.label
|
||||
content.body = snoozeAlarm.notificationMessage
|
||||
content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: snoozeAlarm.soundName))
|
||||
content.categoryIdentifier = "ALARM_CATEGORY"
|
||||
content.userInfo = [
|
||||
"alarmId": snoozeAlarm.id.uuidString,
|
||||
"soundName": snoozeAlarm.soundName,
|
||||
"isSnooze": true,
|
||||
"originalAlarmId": userInfo["alarmId"] as? String ?? ""
|
||||
]
|
||||
|
||||
// Create trigger for snooze time
|
||||
let trigger = UNTimeIntervalNotificationTrigger(
|
||||
timeInterval: snoozeAlarm.time.timeIntervalSinceNow,
|
||||
repeats: false
|
||||
)
|
||||
|
||||
// Create request
|
||||
let request = UNNotificationRequest(
|
||||
identifier: snoozeAlarm.id.uuidString,
|
||||
content: content,
|
||||
trigger: trigger
|
||||
)
|
||||
|
||||
// Schedule notification
|
||||
do {
|
||||
try await UNUserNotificationCenter.current().add(request)
|
||||
print("🔔 Snooze notification scheduled for \(snoozeAlarm.time)")
|
||||
} catch {
|
||||
print("❌ Error scheduling snooze notification: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -29,6 +29,9 @@ class AlarmViewModel {
|
||||
notificationService: NotificationService = NotificationService()) {
|
||||
self.alarmService = alarmService
|
||||
self.notificationService = notificationService
|
||||
|
||||
// Register alarm service with notification delegate for snooze handling
|
||||
NotificationDelegate.shared.setAlarmService(alarmService)
|
||||
}
|
||||
|
||||
// MARK: - Public Interface
|
||||
|
||||
Loading…
Reference in New Issue
Block a user