Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
b104bc3212
commit
07f75bd766
@ -2386,6 +2386,10 @@
|
||||
"comment" : "A hint that appears when a user taps on a day cell to explain that it will navigate to more details about that day.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Test Notification" : {
|
||||
"comment" : "Title of a test notification used to verify notification delivery.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Thank someone" : {
|
||||
"comment" : "Habit title for a ritual preset focused on gratitude practice.",
|
||||
"isCommentAutoGenerated" : true
|
||||
@ -2409,6 +2413,10 @@
|
||||
"comment" : "A description displayed when a day has no habit completion data.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"This is a test notification to verify delivery." : {
|
||||
"comment" : "Title and body of a test notification used to verify notification delivery.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"This ritual is not currently active" : {
|
||||
"comment" : "A message displayed when a user views a past ritual and it is not currently active.",
|
||||
"isCommentAutoGenerated" : true
|
||||
@ -2506,6 +2514,10 @@
|
||||
"comment" : "Accessibility label for a trend direction indicating an increase.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Trigger Test Notification (5s)" : {
|
||||
"comment" : "Title of a settings option that triggers a test local notification.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"Try a different search term" : {
|
||||
"comment" : "A description text displayed below the \"No icons found\" message in the icon picker sheet.",
|
||||
"isCommentAutoGenerated" : true
|
||||
|
||||
@ -48,7 +48,7 @@ enum ReminderSlot: String, CaseIterable {
|
||||
/// Only schedules reminders for time slots that have active rituals.
|
||||
@MainActor
|
||||
@Observable
|
||||
final class ReminderScheduler {
|
||||
final class ReminderScheduler: NSObject, UNUserNotificationCenterDelegate {
|
||||
|
||||
private(set) var authorizationStatus: UNAuthorizationStatus = .notDetermined
|
||||
private(set) var scheduledSlots: Set<ReminderSlot> = []
|
||||
@ -69,10 +69,33 @@ final class ReminderScheduler {
|
||||
/// The rituals to base reminders on - set this from RitualStore
|
||||
private var activeRituals: [Ritual] = []
|
||||
|
||||
init() {
|
||||
override init() {
|
||||
super.init()
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
Task { await refreshAuthorizationStatus() }
|
||||
}
|
||||
|
||||
// MARK: - UNUserNotificationCenterDelegate
|
||||
|
||||
func userNotificationCenter(
|
||||
_ center: UNUserNotificationCenter,
|
||||
willPresent notification: UNNotification,
|
||||
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
|
||||
) {
|
||||
// Show the notification even when the app is in the foreground
|
||||
completionHandler([.banner, .list, .sound, .badge])
|
||||
}
|
||||
|
||||
func userNotificationCenter(
|
||||
_ center: UNUserNotificationCenter,
|
||||
didReceive response: UNNotificationResponse,
|
||||
withCompletionHandler completionHandler: @escaping () -> Void
|
||||
) {
|
||||
// Clear badge when user interacts with notification
|
||||
clearBadge()
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
// MARK: - Public API
|
||||
|
||||
/// Updates the scheduled reminders based on the current active rituals.
|
||||
@ -116,6 +139,28 @@ final class ReminderScheduler {
|
||||
UNUserNotificationCenter.current().setBadgeCount(0) { _ in }
|
||||
}
|
||||
|
||||
/// Schedules a test notification to appear in 5 seconds.
|
||||
func scheduleTestNotification() {
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = String(localized: "Test Notification")
|
||||
content.body = String(localized: "This is a test notification to verify delivery.")
|
||||
content.sound = .default
|
||||
content.badge = 1
|
||||
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)
|
||||
let request = UNNotificationRequest(
|
||||
identifier: "rituals.test.notification",
|
||||
content: content,
|
||||
trigger: trigger
|
||||
)
|
||||
|
||||
UNUserNotificationCenter.current().add(request) { error in
|
||||
if let error = error {
|
||||
print("Failed to schedule test notification: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Refreshes authorization status and reschedules if enabled.
|
||||
func refreshStatus() async {
|
||||
await refreshAuthorizationStatus()
|
||||
|
||||
@ -72,6 +72,7 @@ struct RootView: View {
|
||||
.background(AppSurface.primary.ignoresSafeArea())
|
||||
.onChange(of: scenePhase) { _, newPhase in
|
||||
if newPhase == .active {
|
||||
store.reminderScheduler.clearBadge()
|
||||
refreshCurrentTab()
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,6 +154,14 @@ struct SettingsView: View {
|
||||
) {
|
||||
ritualStore.clearAllCompletions()
|
||||
}
|
||||
|
||||
SettingsRow(
|
||||
systemImage: "bell.badge",
|
||||
title: String(localized: "Trigger Test Notification (5s)"),
|
||||
iconColor: AppStatus.info
|
||||
) {
|
||||
ritualStore.reminderScheduler.scheduleTestNotification()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -6,6 +6,96 @@
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "83.5x83.5",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"size" : "1024x1024",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user