Integrate DailyNotificationPlugin with notification UI to enable native notifications on iOS/Android while maintaining web push for web/PWA. - Add platform detection to PushNotificationPermission component - Implement native notification flow via NotificationService - Hide push server setting on native platforms (not needed) - Add time conversion (AM/PM to 24-hour) for native plugin - Add comprehensive documentation Breaking Changes: None (backward compatible)
8.0 KiB
Notification Permissions & Rollover Handling
Date: 2026-01-23
Purpose: Answers to questions about permission requests and rollover handling
Question 1: Where does the notification permission request happen?
Permission Request Flow
The permission request flows through multiple layers:
User clicks "Turn on Daily Message"
↓
PushNotificationPermission.vue
↓ (line 715)
service.requestPermissions()
↓
NotificationService.getInstance()
↓ (platform detection)
NativeNotificationService.requestPermissions()
↓ (line 53)
DailyNotification.requestPermissions()
↓
Plugin Native Code
↓
┌─────────────────────┬─────────────────────┐
│ iOS Platform │ Android Platform │
├─────────────────────┼─────────────────────┤
│ UNUserNotification │ ActivityCompat │
│ Center.current() │ .requestPermissions()│
│ .requestAuthorization│ │
│ (options: [.alert, │ (POST_NOTIFICATIONS) │
│ .sound, .badge]) │ │
└─────────────────────┴─────────────────────┘
↓
Native OS Permission Dialog
↓
User grants/denies
↓
Result returned to app
Code Locations
1. UI Entry Point (src/components/PushNotificationPermission.vue):
// Line 715
const granted = await service.requestPermissions();
2. Service Layer (src/services/notifications/NativeNotificationService.ts):
// Lines 49-68
async requestPermissions(): Promise<boolean> {
const result = await DailyNotification.requestPermissions();
return result.allPermissionsGranted;
}
3. Plugin Registration (src/plugins/DailyNotificationPlugin.ts):
// Line 30-36
const DailyNotification = registerPlugin<DailyNotificationPluginType>(
"DailyNotification"
);
4. iOS Native Implementation (node_modules/@timesafari/daily-notification-plugin/ios/Plugin/DailyNotificationScheduler.swift):
// Lines 113-115
func requestPermissions() async -> Bool {
let granted = try await notificationCenter.requestAuthorization(
options: [.alert, .sound, .badge]
)
return granted
}
5. Android Native Implementation (node_modules/@timesafari/daily-notification-plugin/android/src/main/java/com/timesafari/dailynotification/PermissionManager.java):
// Line 87
ActivityCompat.requestPermissions(
activity,
new String[]{Manifest.permission.POST_NOTIFICATIONS},
REQUEST_CODE
);
Platform-Specific Details
iOS
- API Used:
UNUserNotificationCenter.requestAuthorization() - Options Requested:
.alert,.sound,.badge - Dialog: System-native iOS permission dialog
- Location: First time user enables notifications
- Result: Returns
trueif granted,falseif denied
Android
- API Used:
ActivityCompat.requestPermissions() - Permission:
POST_NOTIFICATIONS(Android 13+) - Dialog: System-native Android permission dialog
- Location: First time user enables notifications
- Result: Returns
trueif granted,falseif denied - Note: Android 12 and below don't require runtime permission (declared in manifest)
When Permission Request Happens
The permission request is triggered when:
- User opens the notification setup dialog (
PushNotificationPermission.vue) - User clicks "Turn on Daily Message" button
- App detects native platform (
isNativePlatform === true) turnOnNativeNotifications()method is calledservice.requestPermissions()is called (line 715)
Important: The permission dialog only appears once per app installation. After that:
- If granted: Future calls to
requestPermissions()returntrueimmediately - If denied: User must manually enable in system settings
Question 2: Does the plugin handle rollovers automatically?
✅ Yes - Rollover Handling is Automatic
The plugin automatically handles rollovers in multiple scenarios:
1. Initial Scheduling (Time Has Passed Today)
Location: ios/Plugin/DailyNotificationScheduler.swift (lines 326-329)
// If time has passed today, schedule for tomorrow
if scheduledDate <= now {
scheduledDate = calendar.date(byAdding: .day, value: 1, to: scheduledDate) ?? scheduledDate
}
Behavior:
- If user schedules a notification for 9:00 AM but it's already 10:00 AM today
- Plugin automatically schedules it for 9:00 AM tomorrow
- No manual intervention needed
2. Daily Rollover (After Notification Fires)
Location: ios/Plugin/DailyNotificationScheduler.swift (lines 437-609)
The plugin has a scheduleNextNotification() function that:
- Automatically schedules the next day's notification after current one fires
- Handles 24-hour rollovers with DST (Daylight Saving Time) awareness
- Prevents duplicate rollovers with state tracking
Key Function: calculateNextScheduledTime() (lines 397-435)
// Add 24 hours (handles DST transitions automatically)
guard let nextDate = calendar.date(byAdding: .hour, value: 24, to: currentDate) else {
// Fallback to simple 24-hour addition
return currentScheduledTime + (24 * 60 * 60 * 1000)
}
Features:
- ✅ DST-safe: Uses Calendar API to handle daylight saving transitions
- ✅ Automatic: No manual scheduling needed
- ✅ Persistent: Survives app restarts and device reboots
- ✅ Duplicate prevention: Tracks rollover state to prevent duplicates
3. Rollover State Tracking
Location: ios/Plugin/DailyNotificationStorage.swift (lines 161-195)
The plugin tracks rollover state to prevent duplicate scheduling:
// Check if rollover was processed recently (< 1 hour ago)
if let lastTime = lastRolloverTime,
(currentTime - lastTime) < (60 * 60 * 1000) {
// Skip - already processed
return false
}
Purpose: Prevents multiple rollover attempts if notification fires multiple times
4. Android Rollover Handling
Android implementation also handles rollovers:
- Uses
AlarmManagerwithsetRepeating()or schedules next alarm after current fires - Handles timezone changes and DST transitions
- Persists across device reboots via
BootReceiver
Rollover Scenarios Handled
| Scenario | Handled? | How |
|---|---|---|
| Time passed today | ✅ Yes | Schedules for tomorrow automatically |
| Daily rollover | ✅ Yes | Schedules next day after notification fires |
| DST transitions | ✅ Yes | Uses Calendar API for DST-aware calculations |
| Device reboot | ✅ Yes | BootReceiver restores schedules |
| App restart | ✅ Yes | Schedules persist in database |
| Duplicate prevention | ✅ Yes | State tracking prevents duplicate rollovers |
Verification
You can verify rollover handling by:
-
Check iOS logs for rollover messages:
DNP-ROLLOVER: START id=... current_time=... scheduled_time=... DNP-ROLLOVER: CALC_NEXT current=... next=... diff_hours=24.00 -
Test scenario: Schedule notification for a time that's already passed today
- Expected: Notification scheduled for tomorrow at same time
-
Test scenario: Wait for notification to fire
- Expected: Next day's notification automatically scheduled
Summary
✅ Permission Request: Happens in native plugin code via platform-specific APIs:
- iOS:
UNUserNotificationCenter.requestAuthorization() - Android:
ActivityCompat.requestPermissions()
✅ Rollover Handling: Fully automatic:
- Initial scheduling: If time passed, schedules for tomorrow
- Daily rollover: Automatically schedules next day after notification fires
- DST handling: Calendar-aware calculations
- Duplicate prevention: State tracking prevents issues
- Persistence: Survives app restarts and device reboots
No manual intervention needed - the plugin handles all rollover scenarios automatically!
Last Updated: 2026-01-23