feat: integrate daily notification plugin for native iOS/Android

Add native notification support via @timesafari/daily-notification-plugin
while maintaining existing Web Push for web/PWA builds. Platform detection
automatically selects the appropriate notification system at runtime.

Key Changes:
- Created NotificationService abstraction layer with unified API
- Implemented NativeNotificationService for iOS/Android
- Stubbed WebPushNotificationService for future web integration
- Registered DailyNotificationPlugin in Capacitor plugin system

Android Configuration:
- Added notification permissions (POST_NOTIFICATIONS, SCHEDULE_EXACT_ALARM, etc.)
- Registered DailyNotificationReceiver for alarm-based notifications
- Registered BootReceiver to restore schedules after device restart
- Added Room, WorkManager, and Coroutines dependencies
- Registered plugin in MainActivity.java

iOS Configuration:
- Added UIBackgroundModes (fetch, processing) to Info.plist
- Configured BGTaskSchedulerPermittedIdentifiers
- Added NSUserNotificationAlertStyle

Documentation:
- Created comprehensive integration guide
- Added architecture overview with diagrams
- Created implementation checklist
- Documented platform-specific behavior

Manual Steps Required:
- iOS: Enable Background Modes capability in Xcode
- iOS: Run `pod install` to install CapacitorDailyNotification pod
- Run `npx cap sync` to sync native projects

Platform Support:
- iOS: Native UNUserNotificationCenter (requires Xcode setup)
- Android: Native NotificationManager with AlarmManager
- Web/PWA: Existing Web Push (coexists, not yet wired to service)
- Electron: Ready (uses native implementation)

Status: Phase 1 complete - infrastructure ready for UI integration
Next: Update PushNotificationPermission.vue to use NotificationService
This commit is contained in:
Jose Olarte III
2026-01-21 22:22:48 +08:00
parent 679c4d6456
commit 14ffcb5434
13 changed files with 1823 additions and 0 deletions

View File

@@ -101,6 +101,13 @@ dependencies {
implementation project(':capacitor-android') implementation project(':capacitor-android')
implementation project(':capacitor-community-sqlite') implementation project(':capacitor-community-sqlite')
implementation "androidx.biometric:biometric:1.2.0-alpha05" implementation "androidx.biometric:biometric:1.2.0-alpha05"
// Daily Notification Plugin dependencies
implementation "androidx.room:room-runtime:2.6.1"
implementation "androidx.work:work-runtime-ktx:2.9.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
annotationProcessor "androidx.room:room-compiler:2.6.1"
testImplementation "junit:junit:$junitVersion" testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"

View File

@@ -52,6 +52,25 @@
</provider> </provider>
</application> </application>
<!-- Daily Notification Plugin Receivers -->
<!-- ⚠️ CRITICAL: NotifyReceiver is required for alarm-based notifications -->
<receiver
android:name="com.timesafari.dailynotification.DailyNotificationReceiver"
android:enabled="true"
android:exported="false">
</receiver>
<!-- Boot receiver to restore notification schedules after device restart -->
<receiver
android:name="com.timesafari.dailynotification.BootReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
<!-- Permissions --> <!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
@@ -59,4 +78,11 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="true" /> <uses-feature android:name="android.hardware.camera" android:required="true" />
<!-- Daily Notification Plugin Permissions -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.USE_EXACT_ALARM" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
</manifest> </manifest>

View File

@@ -67,6 +67,9 @@ public class MainActivity extends BridgeActivity {
// Register SharedImage plugin // Register SharedImage plugin
registerPlugin(SharedImagePlugin.class); registerPlugin(SharedImagePlugin.class);
// Register DailyNotification plugin
registerPlugin(com.timesafari.dailynotification.DailyNotificationPlugin.class);
// Initialize SQLite // Initialize SQLite
//registerPlugin(SQLite.class); //registerPlugin(SQLite.class);

View File

@@ -0,0 +1,312 @@
# Daily Notification Plugin - Architecture Overview
## System Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Vue Components │
│ (PushNotificationPermission.vue, AccountViewView.vue, etc.) │
└───────────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ NotificationService (Factory) │
│ - Platform detection via Capacitor API │
│ - Singleton pattern │
│ - Returns appropriate implementation │
└───────────────────────────┬─────────────────────────────────────┘
┌───────────┴────────────┐
▼ ▼
┌───────────────────────────┐ ┌────────────────────────────┐
│ NativeNotificationService │ │ WebPushNotificationService │
│ │ │ │
│ iOS/Android │ │ Web/PWA │
│ - UNUserNotificationCenter│ │ - Web Push API │
│ - NotificationManager │ │ - Service Workers │
│ - AlarmManager │ │ - VAPID keys │
│ - Background tasks │ │ - Push server │
└─────────────┬─────────────┘ └────────────┬───────────────┘
│ │
▼ ▼
┌─────────────────────────┐ ┌──────────────────────────────┐
│ DailyNotificationPlugin│ │ Existing Web Push Logic │
│ (Capacitor Plugin) │ │ (PushNotificationPermission)│
│ │ │ │
│ - Native iOS code │ │ - Service worker │
│ - Native Android code │ │ - VAPID subscription │
│ - SQLite storage │ │ - Push server integration │
└─────────────────────────┘ └──────────────────────────────┘
```
## Platform Decision Flow
```
User Action: Schedule Notification
NotificationService.getInstance()
├──> Check: Capacitor.isNativePlatform()
┌────┴─────┐
│ │
YES NO
│ │
▼ ▼
Native Web/PWA
Service Service
│ │
▼ ▼
Plugin Web Push
```
## Data Flow Example: Scheduling a Notification
### Native Platform (iOS/Android)
```
1. User clicks "Enable Notifications"
2. PushNotificationPermission.vue
└─> NotificationService.getInstance()
└─> Returns NativeNotificationService (detected iOS/Android)
└─> nativeService.requestPermissions()
└─> DailyNotification.requestPermissions() [Capacitor Plugin]
└─> Native code requests OS permissions
└─> Returns: { granted: true/false }
3. User sets time & message
4. nativeService.scheduleDailyNotification({ time: '09:00', ... })
└─> DailyNotification.scheduleDailyReminder({ ... })
└─> Native code:
- Stores in SQLite
- Schedules AlarmManager (Android) or UNNotificationRequest (iOS)
- Returns: success/failure
5. At 9:00 AM:
- Android: AlarmManager triggers → DailyNotificationReceiver
- iOS: UNUserNotificationCenter triggers notification
- Notification appears even if app is closed
```
### Web Platform
```
1. User clicks "Enable Notifications"
2. PushNotificationPermission.vue
└─> NotificationService.getInstance()
└─> Returns WebPushNotificationService (detected web)
└─> webService.requestPermissions()
└─> Notification.requestPermission() [Browser API]
└─> Returns: 'granted'/'denied'/'default'
3. User sets time & message
4. webService.scheduleDailyNotification({ ... })
└─> [TODO] Subscribe to push service with VAPID
└─> Send subscription to server with schedule time
└─> Server sends push at scheduled time
└─> Service worker receives → shows notification
```
## File Organization
```
src/
├── plugins/
│ └── DailyNotificationPlugin.ts [Plugin registration]
├── services/
│ └── notifications/
│ ├── index.ts [Barrel export]
│ ├── NotificationService.ts [Factory + Interface]
│ ├── NativeNotificationService.ts [iOS/Android impl]
│ └── WebPushNotificationService.ts [Web impl stub]
├── components/
│ └── PushNotificationPermission.vue [UI - to be updated]
└── views/
└── AccountViewView.vue [Settings UI]
```
## Key Design Decisions
### 1. **Unified Interface**
- Single `NotificationServiceInterface` for all platforms
- Consistent API regardless of underlying implementation
- Type-safe across TypeScript codebase
### 2. **Runtime Platform Detection**
- No build-time configuration needed
- Same code bundle for all platforms
- Factory pattern selects implementation automatically
### 3. **Coexistence Strategy**
- Web Push and Native run on different platforms
- No conflicts - mutually exclusive at runtime
- Allows gradual migration and testing
### 4. **Singleton Pattern**
- One service instance per app lifecycle
- Efficient resource usage
- Consistent state management
## Permission Flow
### Android
```
App Launch
Check if POST_NOTIFICATIONS granted (API 33+)
├─> YES: Ready to schedule
└─> NO: Request runtime permission
Show system dialog
User grants/denies
Schedule notifications (if granted)
```
### iOS
```
App Launch
Check notification authorization status
├─> authorized: Ready to schedule
├─> notDetermined: Request permission
│ ↓
│ Show system dialog
│ ↓
│ User grants/denies
└─> denied: Guide user to Settings
```
### Web
```
App Load
Check Notification.permission
├─> "granted": Ready to subscribe
├─> "default": Request permission
│ ↓
│ Show browser prompt
│ ↓
│ User grants/denies
└─> "denied": Cannot show notifications
```
## Error Handling Strategy
```typescript
// All methods return promises with success/failure
try {
const granted = await service.requestPermissions();
if (granted) {
const success = await service.scheduleDailyNotification({...});
if (success) {
// Show success message
} else {
// Show scheduling error
}
} else {
// Show permission denied message
}
} catch (error) {
// Log error and show generic error message
logger.error('Notification error:', error);
}
```
## Background Execution
### Native (iOS/Android)
- ✅ Full background support
- ✅ Survives app termination
- ✅ Survives device reboot (with BootReceiver)
- ✅ Exact alarm scheduling
- ✅ Works offline
### Web/PWA
- ⚠️ Limited background support
- ⚠️ Requires active service worker
- ⚠️ Browser/OS dependent
- ❌ Needs network for delivery
- ⚠️ iOS: Only on Home Screen PWAs (16.4+)
## Storage
### Native
```
DailyNotificationPlugin
SQLite Database (Room/Core Data)
Stores:
- Schedule configurations
- Content cache
- Delivery history
- Callback registrations
```
### Web
```
Web Push
IndexedDB (via Dexie)
Stores:
- Settings (notifyingNewActivityTime, etc.)
- Push subscription info
- VAPID keys
```
## Testing Strategy
### Unit Testing
- Mock `Capacitor.isNativePlatform()` to test both paths
- Test factory returns correct implementation
- Test each service implementation independently
### Integration Testing
- Test on actual devices (iOS/Android)
- Test in browsers (Chrome, Safari, Firefox)
- Verify notification delivery
- Test permission flows
### E2E Testing
- Schedule notification → Wait → Verify delivery
- Test app restart scenarios
- Test device reboot scenarios
- Test permission denial recovery
---
**Key Takeaway**: The architecture provides a clean separation between platforms while maintaining a unified API for Vue components. Platform detection happens automatically at runtime, and the appropriate notification system is used transparently.

View File

@@ -0,0 +1,341 @@
# Daily Notification Plugin - Integration Checklist
**Integration Date**: 2026-01-21
**Plugin Version**: 1.0.11
**Status**: Phase 1 Complete ✅
---
## Phase 1: Infrastructure Setup ✅ COMPLETE
### Code Files
- [x] Created `src/plugins/DailyNotificationPlugin.ts`
- [x] Created `src/services/notifications/NotificationService.ts`
- [x] Created `src/services/notifications/NativeNotificationService.ts`
- [x] Created `src/services/notifications/WebPushNotificationService.ts`
- [x] Created `src/services/notifications/index.ts`
### Android Configuration
- [x] Added permissions to `AndroidManifest.xml`:
- [x] `POST_NOTIFICATIONS`
- [x] `SCHEDULE_EXACT_ALARM`
- [x] `USE_EXACT_ALARM`
- [x] `RECEIVE_BOOT_COMPLETED`
- [x] `WAKE_LOCK`
- [x] Registered receivers in `AndroidManifest.xml`:
- [x] `DailyNotificationReceiver`
- [x] `BootReceiver`
- [x] Added dependencies to `build.gradle`:
- [x] Room (`androidx.room:room-runtime:2.6.1`)
- [x] WorkManager (`androidx.work:work-runtime-ktx:2.9.0`)
- [x] Coroutines (`org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3`)
- [x] Room Compiler (`androidx.room:room-compiler:2.6.1`)
- [x] Registered plugin in `MainActivity.java`
### iOS Configuration
- [x] Added to `Info.plist`:
- [x] `UIBackgroundModes` (fetch, processing)
- [x] `BGTaskSchedulerPermittedIdentifiers`
- [x] `NSUserNotificationAlertStyle`
- [ ] ⚠️ **MANUAL STEP**: Xcode capabilities (see Phase 5)
### Documentation
- [x] Created `doc/daily-notification-plugin-integration.md`
- [x] Created `doc/daily-notification-plugin-integration-summary.md`
- [x] Created `doc/daily-notification-plugin-architecture.md`
- [x] Created this checklist
---
## Phase 2: UI Integration ⏳ TODO
### Update Components
- [ ] Modify `PushNotificationPermission.vue`:
- [ ] Import `NotificationService`
- [ ] Replace direct web push calls with service methods
- [ ] Add platform-aware messaging
- [ ] Test permission flow
- [ ] Test notification scheduling
### Update Views
- [ ] Update `AccountViewView.vue`:
- [ ] Use `NotificationService` for status checks
- [ ] Add platform indicator
- [ ] Test settings display
### Settings Integration
- [ ] Verify settings save/load correctly:
- [ ] `notifyingNewActivityTime` for native
- [ ] `notifyingReminderMessage` for native
- [ ] `notifyingReminderTime` for native
- [ ] Existing web push settings preserved
---
## Phase 3: Web Push Integration ⏳ TODO
### Wire WebPushNotificationService
- [ ] Extract subscription logic from `PushNotificationPermission.vue`
- [ ] Implement `scheduleDailyNotification()` method
- [ ] Implement `cancelDailyNotification()` method
- [ ] Implement `getStatus()` method
- [ ] Test web platform notification flow
### Server Integration
- [ ] Verify web push server endpoints still work
- [ ] Test subscription/unsubscription
- [ ] Test scheduled message delivery
---
## Phase 4: Testing ⏳ TODO
### Desktop Development
- [ ] Code compiles without errors
- [ ] ESLint passes
- [ ] TypeScript types are correct
- [ ] Platform detection works in browser console
### Android Emulator
- [ ] App builds successfully
- [ ] Plugin loads without errors
- [ ] Can open app and navigate
- [ ] No JavaScript console errors
### Android Device (Real)
- [ ] Request permissions dialog appears
- [ ] Permissions can be granted
- [ ] Schedule notification succeeds
- [ ] Notification appears at scheduled time
- [ ] Notification survives app close
- [ ] Notification survives device reboot
- [ ] Notification can be cancelled
### iOS Simulator
- [ ] App builds successfully
- [ ] Plugin loads without errors
- [ ] Can open app and navigate
- [ ] No JavaScript console errors
### iOS Device (Real)
- [ ] Request permissions dialog appears
- [ ] Permissions can be granted
- [ ] Schedule notification succeeds
- [ ] Notification appears at scheduled time
- [ ] Background fetch works
- [ ] Notification survives app close
- [ ] Notification can be cancelled
### Web Browser
- [ ] Existing web push still works
- [ ] No JavaScript errors
- [ ] Platform detection selects web service
- [ ] Permission flow works
- [ ] Subscription works
---
## Phase 5: iOS Xcode Setup ⚠️ MANUAL REQUIRED
### Open Xcode Project
```bash
cd ios
open App/App.xcodeproj
```
### Configure Capabilities
- [ ] Select "App" target in project navigator
- [ ] Go to "Signing & Capabilities" tab
- [ ] Click "+ Capability" button
- [ ] Add "Background Modes":
- [ ] Enable "Background fetch"
- [ ] Enable "Background processing"
- [ ] Click "+ Capability" button again
- [ ] Add "Push Notifications" (if using remote notifications)
### Install CocoaPods
```bash
cd ios
pod install
cd ..
```
- [ ] Run `pod install` successfully
- [ ] Verify `CapacitorDailyNotification` pod is installed
### Verify Configuration
- [ ] Build succeeds in Xcode
- [ ] No capability warnings
- [ ] No pod errors
- [ ] Can run on simulator
---
## Phase 6: Build & Deploy ⏳ TODO
### Sync Capacitor
```bash
npx cap sync
```
- [ ] Sync completes without errors
- [ ] Plugin files copied to native projects
### Build Android
```bash
npm run build:android:debug
```
- [ ] Build succeeds
- [ ] APK/AAB generated
- [ ] Can install on device/emulator
### Build iOS
```bash
npm run build:ios:debug
```
- [ ] Build succeeds
- [ ] IPA generated (if release)
- [ ] Can install on device/simulator
### Test Production Builds
- [ ] Android release build works
- [ ] iOS release build works
- [ ] Notifications work in production
---
## Troubleshooting Checklist
### Android Issues
#### Notifications Not Appearing
- [ ] Verified `DailyNotificationReceiver` is in AndroidManifest.xml
- [ ] Checked logcat for errors: `adb logcat | grep DailyNotification`
- [ ] Verified permissions granted in app settings
- [ ] Checked "Exact alarms" permission (Android 12+)
- [ ] Verified notification channel is created
#### Build Errors
- [ ] Verified all dependencies in build.gradle
- [ ] Ran `./gradlew clean` and rebuilt
- [ ] Verified Kotlin version compatibility
- [ ] Checked for conflicting dependencies
### iOS Issues
#### Notifications Not Appearing
- [ ] Verified Background Modes enabled in Xcode
- [ ] Checked Xcode console for errors
- [ ] Verified permissions granted in Settings app
- [ ] Tested on real device (not just simulator)
- [ ] Checked BGTaskScheduler identifiers match Info.plist
#### Build Errors
- [ ] Ran `pod install` successfully
- [ ] Verified deployment target is iOS 13.0+
- [ ] Checked for pod conflicts
- [ ] Cleaned build folder (Xcode → Product → Clean Build Folder)
### Web Issues
#### Web Push Not Working
- [ ] Verified service worker is registered
- [ ] Checked browser console for errors
- [ ] Verified VAPID keys are correct
- [ ] Tested in supported browser (Chrome 42+, Firefox)
- [ ] Checked push server is running
#### Permission Issues
- [ ] Verified permissions not blocked in browser
- [ ] Checked site settings in browser
- [ ] Verified HTTPS connection (required for web push)
---
## Verification Commands
### Check Plugin is Installed
```bash
npm list @timesafari/daily-notification-plugin
```
### Check Capacitor Sync
```bash
npx cap ls
```
### Check Android Build
```bash
cd android
./gradlew clean
./gradlew assembleDebug
```
### Check iOS Build
```bash
cd ios
pod install
xcodebuild -workspace App/App.xcworkspace -scheme App -configuration Debug build
```
### Check TypeScript
```bash
npm run type-check
```
### Check Linting
```bash
npm run lint
```
---
## Next Immediate Actions
1. **Run Capacitor Sync**:
```bash
npx cap sync
```
2. **For iOS Development**:
```bash
cd ios
open App/App.xcodeproj
# Enable Background Modes capability
pod install
cd ..
```
3. **Test on Emulator/Simulator**:
```bash
npm run build:android:debug # For Android
npm run build:ios:debug # For iOS
```
4. **Update UI Components**:
- Start with `PushNotificationPermission.vue`
- Import and use `NotificationService`
---
## Success Criteria
- [x] **Phase 1**: All files created and configurations applied
- [ ] **Phase 2**: Components use NotificationService
- [ ] **Phase 3**: Web push integrated with service
- [ ] **Phase 4**: All tests pass on all platforms
- [ ] **Phase 5**: iOS capabilities configured in Xcode
- [ ] **Phase 6**: Production builds work on real devices
---
## Questions or Issues?
See documentation:
- Full guide: `doc/daily-notification-plugin-integration.md`
- Architecture: `doc/daily-notification-plugin-architecture.md`
- Summary: `doc/daily-notification-plugin-integration-summary.md`
Plugin docs: `node_modules/@timesafari/daily-notification-plugin/README.md`
---
**Current Status**: Ready for Phase 2 (UI Integration) 🚀

View File

@@ -0,0 +1,193 @@
# Daily Notification Plugin Integration - Summary
**Date**: 2026-01-21
**Status**: ✅ Phase 1 Complete
**Next Phase**: UI Integration
---
## What Was Completed
### ✅ Plugin Infrastructure
1. **Plugin Registration**: `src/plugins/DailyNotificationPlugin.ts`
- Capacitor plugin registered with full TypeScript types
- Native-only (iOS/Android)
2. **Service Abstraction**: `src/services/notifications/`
- `NotificationService.ts` - Platform detection & factory
- `NativeNotificationService.ts` - Native implementation
- `WebPushNotificationService.ts` - Web stub (for future)
- `index.ts` - Barrel export
3. **Android Configuration**:
- ✅ Permissions added to `AndroidManifest.xml`
- ✅ Receivers registered (DailyNotificationReceiver, BootReceiver)
- ✅ Dependencies added to `build.gradle` (Room, WorkManager, Coroutines)
- ✅ Plugin registered in `MainActivity.java`
4. **iOS Configuration**:
- ✅ Background modes added to `Info.plist`
- ✅ BGTaskScheduler identifiers configured
- ⚠️ **Requires manual Xcode setup** (capabilities)
5. **Documentation**: `doc/daily-notification-plugin-integration.md`
---
## Platform Support
| Platform | Notification System | Status |
|----------|---------------------|--------|
| **iOS** | Native (UNUserNotificationCenter) | ✅ Configured |
| **Android** | Native (NotificationManager + AlarmManager) | ✅ Configured |
| **Web/PWA** | Web Push (existing) | 🔄 Coexists, not yet wired |
| **Electron** | Native (via Capacitor) | ✅ Ready |
**Key Feature**: Both systems coexist using runtime platform detection.
---
## Quick Start Usage
```typescript
import { NotificationService } from '@/services/notifications';
// Automatically uses native on iOS/Android, web push on web
const service = NotificationService.getInstance();
// Request permissions
const granted = await service.requestPermissions();
if (granted) {
// Schedule daily notification at 9 AM
await service.scheduleDailyNotification({
time: '09:00',
title: 'Daily Check-In',
body: 'Time to check your TimeSafari activity'
});
}
// Check status
const status = await service.getStatus();
console.log('Notifications enabled:', status.enabled);
```
---
## Next Steps
### Immediate (Phase 2)
1. **Update UI Components**:
- Modify `PushNotificationPermission.vue` to use `NotificationService`
- Add platform-aware messaging
- Test on simulator/emulator
2. **iOS Xcode Setup** (Required):
```bash
cd ios
open App/App.xcodeproj
```
- Enable "Background Modes" capability
- Enable "Push Notifications" capability
- Run `pod install`
### Short-term (Phase 3)
3. **Wire Web Push**: Connect `WebPushNotificationService` to existing web push logic
4. **Test on Devices**: Real iOS and Android devices
5. **Update Settings**: Ensure notification preferences save correctly
---
## Build & Sync
```bash
# Sync native projects with web code
npx cap sync
# Build for Android
npm run build:android:debug
# Build for iOS (after Xcode setup)
cd ios && pod install && cd ..
npm run build:ios:debug
```
---
## Important Notes
### ⚠️ Critical Requirements
**Android**:
- `DailyNotificationReceiver` must be in AndroidManifest.xml (✅ done)
- Runtime permissions needed for Android 13+ (API 33+)
- Exact alarm permission for Android 12+ (API 31+)
**iOS**:
- Background Modes capability must be enabled in Xcode (⚠️ manual)
- BGTaskScheduler identifiers must match Info.plist (✅ done)
- Test on real device (simulators have limitations)
**Web**:
- Existing Web Push continues to work unchanged
- No conflicts - platform detection ensures correct system
---
## Files Created/Modified
### Created (8 files)
- `src/plugins/DailyNotificationPlugin.ts`
- `src/services/notifications/NotificationService.ts`
- `src/services/notifications/NativeNotificationService.ts`
- `src/services/notifications/WebPushNotificationService.ts`
- `src/services/notifications/index.ts`
- `doc/daily-notification-plugin-integration.md`
- `doc/daily-notification-plugin-integration-summary.md`
### Modified (4 files)
- `android/app/src/main/AndroidManifest.xml` - Permissions + Receivers
- `android/app/build.gradle` - Dependencies
- `android/app/src/main/java/app/timesafari/MainActivity.java` - Plugin registration
- `ios/App/App/Info.plist` - Background modes + BGTaskScheduler
---
## Testing Checklist
### Before Device Testing
- [ ] Code compiles without errors
- [ ] Platform detection logic verified
- [ ] Service factory creates correct implementation
### Android Device
- [ ] Request permissions (Android 13+)
- [ ] Schedule notification
- [ ] Notification appears at scheduled time
- [ ] Notification survives app close
- [ ] Notification survives device reboot
### iOS Device
- [ ] Xcode capabilities enabled
- [ ] Request permissions
- [ ] Schedule notification
- [ ] Notification appears at scheduled time
- [ ] Background fetch works
- [ ] Notification survives app close
### Web/PWA
- [ ] Existing web push still works
- [ ] No errors in console
- [ ] Platform detection selects web implementation
---
## Questions?
See full documentation: `doc/daily-notification-plugin-integration.md`
Plugin README: `node_modules/@timesafari/daily-notification-plugin/README.md`
---
**Status**: Ready for Phase 2 (UI Integration) 🚀

View File

@@ -0,0 +1,237 @@
# Daily Notification Plugin Integration
**Date**: 2026-01-21
**Status**: ✅ Phase 1 Complete - Native Infrastructure
**Integration Type**: Native + Web Coexistence
## Overview
The Daily Notification Plugin has been integrated to provide native notification functionality for iOS and Android while maintaining existing Web Push for web/PWA builds. The integration uses platform detection to automatically select the appropriate notification system at runtime.
## What Was Implemented
### 1. **Plugin Registration** ✅
- **File**: `src/plugins/DailyNotificationPlugin.ts`
- Registered Capacitor plugin with proper TypeScript types
- Native-only (no web implementation)
### 2. **Service Abstraction Layer** ✅
Created unified notification service with platform-specific implementations:
- **`NotificationService.ts`**: Factory that selects implementation based on platform
- **`NativeNotificationService.ts`**: Wraps DailyNotificationPlugin for iOS/Android
- **`WebPushNotificationService.ts`**: Stub for future Web Push integration
**Location**: `src/services/notifications/`
**Key Features**:
- Unified interface (`NotificationServiceInterface`)
- Automatic platform detection via `Capacitor.isNativePlatform()`
- Type-safe implementation
- Singleton pattern for efficiency
### 3. **Android Configuration** ✅
**Modified Files**:
- `android/app/src/main/AndroidManifest.xml`
- `android/app/build.gradle`
- `android/app/src/main/java/app/timesafari/MainActivity.java`
**Changes**:
- ✅ Added notification permissions (POST_NOTIFICATIONS, SCHEDULE_EXACT_ALARM, etc.)
- ✅ Registered `DailyNotificationReceiver` (critical for alarm delivery)
- ✅ Registered `BootReceiver` (restores schedules after device restart)
- ✅ Added Room, WorkManager, and Coroutines dependencies
- ✅ Registered plugin in MainActivity
### 4. **iOS Configuration** ✅
**Modified Files**:
- `ios/App/App/Info.plist`
**Changes**:
- ✅ Added `UIBackgroundModes` (fetch, processing)
- ✅ Added `BGTaskSchedulerPermittedIdentifiers` for background tasks
- ✅ Added `NSUserNotificationAlertStyle` for alert-style notifications
**Still Required** (Manual in Xcode):
- ⚠️ Enable "Background Modes" capability in Xcode
- Background fetch
- Background processing
- ⚠️ Enable "Push Notifications" capability (if using remote notifications)
## Platform Behavior
| Platform | Implementation | Status |
|----------|---------------|--------|
| **iOS** | DailyNotificationPlugin (native) | ✅ Configured |
| **Android** | DailyNotificationPlugin (native) | ✅ Configured |
| **Web/PWA** | Web Push (existing) | 🔄 Not yet wired up |
| **Electron** | Would use native | ✅ Ready |
## Usage Example
```typescript
import { NotificationService } from '@/services/notifications/NotificationService';
// Get the appropriate service for current platform
const notificationService = NotificationService.getInstance();
// Check platform
console.log('Platform:', NotificationService.getPlatform());
console.log('Is native:', NotificationService.isNative());
// Request permissions
const granted = await notificationService.requestPermissions();
if (granted) {
// Schedule daily notification
await notificationService.scheduleDailyNotification({
time: '09:00',
title: 'Daily Check-In',
body: 'Time to check your TimeSafari activity',
priority: 'normal'
});
}
// Check status
const status = await notificationService.getStatus();
console.log('Enabled:', status.enabled);
console.log('Time:', status.scheduledTime);
```
## Next Steps
### Phase 2: UI Integration
- [ ] Update `PushNotificationPermission.vue` to use `NotificationService`
- [ ] Add platform-aware UI messaging
- [ ] Update settings storage to work with both systems
- [ ] Test notification scheduling UI
### Phase 3: Web Push Integration
- [ ] Wire `WebPushNotificationService` to existing PushNotificationPermission logic
- [ ] Extract web push subscription code into service methods
- [ ] Test web platform notification flow
### Phase 4: Testing & Polish
- [ ] Test on real iOS device
- [ ] Test on real Android device (API 23+, API 33+)
- [ ] Test permission flows
- [ ] Test notification delivery
- [ ] Test app restart/reboot scenarios
- [ ] Verify background notification delivery
### Phase 5: Xcode Configuration (iOS Only)
- [ ] Open `ios/App/App.xcodeproj` in Xcode
- [ ] Select App target → Signing & Capabilities
- [ ] Click "+ Capability" → Add "Background Modes"
- Enable "Background fetch"
- Enable "Background processing"
- [ ] Click "+ Capability" → Add "Push Notifications" (if using remote)
- [ ] Run `pod install` in `ios/` directory
- [ ] Build and test on device
## Build Commands
### Sync Capacitor
```bash
npx cap sync
# or
npx cap sync android
npx cap sync ios
```
### Build Android
```bash
npm run build:android
# or
npm run build:android:debug
```
### Build iOS
```bash
npm run build:ios
# or after Xcode setup:
cd ios && pod install && cd ..
npm run build:ios:debug
```
## Important Notes
### Android
- **Critical**: `DailyNotificationReceiver` must be in AndroidManifest.xml
- Android 12+ (API 31+) requires `SCHEDULE_EXACT_ALARM` permission
- Android 13+ (API 33+) requires runtime `POST_NOTIFICATIONS` permission
- BootReceiver restores schedules after device restart
### iOS
- **Critical**: Background modes must be enabled in Xcode capabilities
- iOS 13.0+ supported (already compatible with your deployment target)
- Background tasks use `BGTaskScheduler`
- User must grant notification permissions in Settings
### Web
- Existing Web Push continues to work
- No conflicts with native implementation
- Platform detection ensures correct system is used
## Files Modified
### Created
- `src/plugins/DailyNotificationPlugin.ts`
- `src/services/notifications/NotificationService.ts`
- `src/services/notifications/NativeNotificationService.ts`
- `src/services/notifications/WebPushNotificationService.ts`
### Modified
- `android/app/src/main/AndroidManifest.xml`
- `android/app/build.gradle`
- `android/app/src/main/java/app/timesafari/MainActivity.java`
- `ios/App/App/Info.plist`
## Troubleshooting
### Android: Notifications Not Appearing
1. Check that `DailyNotificationReceiver` is registered in AndroidManifest.xml
2. Verify permissions are requested at runtime (Android 13+)
3. Check that notification channel is created
4. Enable "Exact alarms" in app settings (Android 12+)
### iOS: Background Tasks Not Running
1. Ensure Background Modes capability is enabled in Xcode
2. Check that BGTaskScheduler identifiers match Info.plist
3. Test on real device (simulator has limitations)
4. Check iOS Settings → Notifications → TimeSafari
### Permission Issues
1. Request permissions before scheduling: `requestPermissions()`
2. Check permission status: `checkPermissions()`
3. Guide users to system settings if denied
## Plugin Documentation
For complete plugin documentation, see:
- Plugin README: `node_modules/@timesafari/daily-notification-plugin/README.md`
- Plugin version: 1.0.11
- Repository: https://gitea.anomalistdesign.com/trent_larson/daily-notification-plugin
## Testing Checklist
- [ ] Android: Notification appears at scheduled time
- [ ] Android: Notification survives app close
- [ ] Android: Notification survives device reboot
- [ ] iOS: Notification appears at scheduled time
- [ ] iOS: Background fetch works
- [ ] iOS: Notification survives app close
- [ ] Web: Existing web push still works
- [ ] Platform detection works correctly
- [ ] Permission requests work on all platforms
- [ ] Status retrieval works correctly
## Current Status
**Phase 1 Complete**: Native infrastructure configured
🔄 **Phase 2 In Progress**: Ready for UI integration
**Phase 3 Pending**: Web Push service integration
**Phase 4 Pending**: Testing and validation
**Phase 5 Pending**: Xcode capabilities setup

View File

@@ -58,5 +58,17 @@
</array> </array>
</dict> </dict>
</array> </array>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
</array>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.timesafari.dailynotification.content-fetch</string>
<string>com.timesafari.dailynotification.notification-delivery</string>
</array>
<key>NSUserNotificationAlertStyle</key>
<string>alert</string>
</dict> </dict>
</plist> </plist>

View File

@@ -0,0 +1,38 @@
/**
* Daily Notification Capacitor Plugin
* Provides native notification functionality for iOS and Android
*
* @see https://gitea.anomalistdesign.com/trent_larson/daily-notification-plugin
*/
import { registerPlugin } from "@capacitor/core";
/**
* Type definitions for the Daily Notification Plugin
* Re-exported from the plugin package
*/
export type {
DailyNotificationPlugin,
DailyReminderOptions,
DailyReminderInfo,
PermissionStatusResult,
NotificationStatus,
} from "@timesafari/daily-notification-plugin";
// Import the plugin type
import type { DailyNotificationPlugin as DailyNotificationPluginType } from "@timesafari/daily-notification-plugin";
/**
* Register the Daily Notification plugin
* This plugin is only available on native platforms (iOS/Android)
* No web implementation - use Web Push for web/PWA builds
*/
const DailyNotification = registerPlugin<DailyNotificationPluginType>(
"DailyNotification",
{
// No web implementation - native platforms only
// Web builds will use the existing Web Push system
},
);
export { DailyNotification };

View File

@@ -0,0 +1,197 @@
/**
* Native Notification Service
*
* Implementation of notification service using the DailyNotificationPlugin
* for native iOS and Android platforms. Provides full native notification
* capabilities including background delivery, exact alarm scheduling, and
* offline operation.
*
* @author Matthew Raymer
* @version 1.0.0
* @since 2026-01-21
*/
import { DailyNotification } from "@/plugins/DailyNotificationPlugin";
import type {
NotificationServiceInterface,
DailyNotificationOptions,
NotificationStatus,
PermissionStatus,
} from "./NotificationService";
import { logger } from "@/utils/logger";
/**
* Native notification implementation using DailyNotificationPlugin
* Used for iOS and Android builds
*
* Features:
* - Full background notification support
* - Survives app restarts and device reboots
* - Exact alarm scheduling (Android 12+)
* - No network dependency for delivery
* - Native OS notification UI
*/
export class NativeNotificationService implements NotificationServiceInterface {
private readonly reminderId = "timesafari_daily_reminder";
private readonly platformName = "native";
/**
* Native notifications are always supported on iOS/Android
*/
isSupported(): boolean {
return true;
}
/**
* Request notification permissions from the OS
* Shows native permission dialog on first call
*/
async requestPermissions(): Promise<boolean> {
try {
logger.debug("[NativeNotificationService] Requesting permissions");
const result = await DailyNotification.requestPermissions();
logger.debug("[NativeNotificationService] Permission result:", {
notificationsEnabled: result.notificationsEnabled,
allPermissionsGranted: result.allPermissionsGranted,
});
return result.allPermissionsGranted;
} catch (error) {
logger.error(
"[NativeNotificationService] Permission request failed:",
error,
);
return false;
}
}
/**
* Check current permission status without prompting
*/
async checkPermissions(): Promise<PermissionStatus> {
try {
const status = await DailyNotification.checkPermissionStatus();
return {
granted: status.allPermissionsGranted,
details: {
notifications: status.notificationsEnabled,
exactAlarm: status.exactAlarmEnabled,
backgroundRefresh: true, // Native always has background capability
},
};
} catch (error) {
logger.error(
"[NativeNotificationService] Permission check failed:",
error,
);
return {
granted: false,
details: {
notifications: false,
exactAlarm: false,
backgroundRefresh: false,
},
};
}
}
/**
* Schedule a daily notification using native alarms
*/
async scheduleDailyNotification(
options: DailyNotificationOptions,
): Promise<boolean> {
try {
logger.info(
"[NativeNotificationService] Scheduling daily notification:",
{
time: options.time,
title: options.title,
},
);
await DailyNotification.scheduleDailyReminder({
id: this.reminderId,
title: options.title,
body: options.body,
time: options.time, // HH:mm format
repeatDaily: true,
sound: true,
vibration: true,
priority: options.priority || "normal",
});
logger.info(
"[NativeNotificationService] Daily notification scheduled successfully",
);
return true;
} catch (error) {
logger.error("[NativeNotificationService] Schedule failed:", error);
return false;
}
}
/**
* Cancel the daily notification
*/
async cancelDailyNotification(): Promise<void> {
try {
logger.info("[NativeNotificationService] Cancelling daily notification");
await DailyNotification.cancelDailyReminder(this.reminderId);
logger.info(
"[NativeNotificationService] Daily notification cancelled successfully",
);
} catch (error) {
logger.error("[NativeNotificationService] Cancel failed:", error);
// Don't throw - cancellation failures are non-critical
}
}
/**
* Get current notification status from the plugin
*/
async getStatus(): Promise<NotificationStatus> {
try {
const reminders = await DailyNotification.getScheduledReminders();
const reminder = reminders.reminders.find(
(r) => r.id === this.reminderId,
);
if (reminder) {
logger.debug("[NativeNotificationService] Found active reminder:", {
time: reminder.time,
isScheduled: reminder.isScheduled,
});
return {
enabled: reminder.isScheduled,
scheduledTime: reminder.time,
message: reminder.body,
notificationType: "native",
};
}
logger.debug("[NativeNotificationService] No active reminder found");
return {
enabled: false,
notificationType: "native",
};
} catch (error) {
logger.error("[NativeNotificationService] Get status failed:", error);
return {
enabled: false,
notificationType: "native",
};
}
}
/**
* Get platform identifier
*/
getPlatformName(): string {
return this.platformName;
}
}

View File

@@ -0,0 +1,220 @@
/**
* Unified Notification Service
*
* Provides a platform-agnostic interface for managing notifications across
* web and native platforms. Automatically delegates to the appropriate
* implementation based on the runtime platform:
*
* - Native platforms (iOS/Android): Uses DailyNotificationPlugin
* - Web/PWA: Uses Web Push with service workers and VAPID
*
* @author Matthew Raymer
* @version 1.0.0
* @since 2026-01-21
*/
import { Capacitor } from "@capacitor/core";
import { NativeNotificationService } from "./NativeNotificationService";
import { WebPushNotificationService } from "./WebPushNotificationService";
/**
* Options for scheduling a daily notification
*/
export interface DailyNotificationOptions {
/**
* Time to send notification in HH:mm format (24-hour)
* Example: "09:00" for 9 AM, "17:30" for 5:30 PM
*/
time: string;
/**
* Notification title
*/
title: string;
/**
* Notification body/message
*/
body: string;
/**
* Optional notification priority
* @default 'normal'
*/
priority?: "low" | "normal" | "high";
}
/**
* Current notification status
*/
export interface NotificationStatus {
/**
* Whether notifications are currently enabled
*/
enabled: boolean;
/**
* Scheduled time in HH:mm format, if any
*/
scheduledTime?: string;
/**
* Current notification message, if any
*/
message?: string;
/**
* Platform-specific notification type identifier
*/
notificationType?: string;
}
/**
* Permission status result
*/
export interface PermissionStatus {
/**
* Whether notification permissions are granted
*/
granted: boolean;
/**
* Additional platform-specific permission details
*/
details?: {
notifications?: boolean;
exactAlarm?: boolean;
backgroundRefresh?: boolean;
};
}
/**
* Unified notification service interface
* All platform-specific implementations must conform to this interface
*/
export interface NotificationServiceInterface {
/**
* Check if notifications are supported on current platform
* @returns true if notifications are supported
*/
isSupported(): boolean;
/**
* Request notification permissions from the user
* Shows system permission dialog on first call
* @returns Promise that resolves to true if permissions granted
*/
requestPermissions(): Promise<boolean>;
/**
* Check current notification permission status
* @returns Promise with permission status
*/
checkPermissions(): Promise<PermissionStatus>;
/**
* Schedule a daily notification/reminder
* @param options Notification configuration
* @returns Promise that resolves to true if scheduling succeeded
*/
scheduleDailyNotification(
options: DailyNotificationOptions,
): Promise<boolean>;
/**
* Cancel all daily notifications
* @returns Promise that resolves when cancellation is complete
*/
cancelDailyNotification(): Promise<void>;
/**
* Get current notification status
* @returns Promise with current notification state
*/
getStatus(): Promise<NotificationStatus>;
/**
* Get the platform name for this service
* @returns Platform identifier (e.g., 'native', 'web')
*/
getPlatformName(): string;
}
/**
* Notification Service Factory
*
* Singleton factory that creates and manages the appropriate notification
* service implementation based on the current platform.
*
* @example
* ```typescript
* const notificationService = NotificationService.getInstance();
* const granted = await notificationService.requestPermissions();
* if (granted) {
* await notificationService.scheduleDailyNotification({
* time: '09:00',
* title: 'Daily Check-In',
* body: 'Time to check your TimeSafari activity'
* });
* }
* ```
*/
export class NotificationService {
private static instance: NotificationServiceInterface | null = null;
private static instanceCreated = false;
/**
* Get the singleton notification service instance
* Creates the appropriate platform-specific implementation on first call
*
* @returns NotificationServiceInterface implementation for current platform
*/
static getInstance(): NotificationServiceInterface {
if (!this.instance) {
const platform = Capacitor.getPlatform();
if (!this.instanceCreated) {
// Log initialization for debugging (only happens once per app lifecycle)
// eslint-disable-next-line no-console
console.log(
`[NotificationService] Creating ${this.isNative() ? "native" : "web"} notification service for platform: ${platform}`,
);
this.instanceCreated = true;
}
if (this.isNative()) {
// iOS/Android: Use native plugin
this.instance = new NativeNotificationService();
} else {
// Web/PWA: Use web push
this.instance = new WebPushNotificationService();
}
}
return this.instance;
}
/**
* Check if running on a native platform (iOS/Android)
* @returns true if running on iOS or Android
*/
static isNative(): boolean {
return Capacitor.isNativePlatform();
}
/**
* Get the current platform name
* @returns Platform identifier: 'ios', 'android', 'web', 'electron'
*/
static getPlatform(): string {
return Capacitor.getPlatform();
}
/**
* Reset the singleton instance (for testing purposes)
* @internal
*/
static reset(): void {
this.instance = null;
this.instanceCreated = false;
}
}

View File

@@ -0,0 +1,212 @@
/**
* Web Push Notification Service
*
* Implementation of notification service using Web Push API with service workers
* for web/PWA builds. This is a wrapper around the existing PushNotificationPermission
* component logic, providing a consistent interface with the native notification service.
*
* @author Matthew Raymer
* @version 1.0.0
* @since 2026-01-21
*/
import type {
NotificationServiceInterface,
DailyNotificationOptions,
NotificationStatus,
PermissionStatus,
} from "./NotificationService";
import { logger } from "@/utils/logger";
/**
* Web Push notification implementation for web/PWA builds
*
* Features:
* - Uses Web Push API with VAPID authentication
* - Service worker-based message delivery
* - Requires server-side push infrastructure
* - Limited background support (browser-dependent)
*
* Limitations:
* - Requires active service worker
* - iOS: Limited to Home Screen PWAs on iOS 16.4+
* - Android: Works in Chrome 42+
* - Requires network connection for delivery
*
* @note This is currently a stub implementation that will be fully integrated
* with the existing PushNotificationPermission.vue component logic in a
* future update. For now, it provides the interface structure.
*/
export class WebPushNotificationService
implements NotificationServiceInterface
{
private readonly platformName = "web";
/**
* Check if Web Push is supported in the current browser
*/
isSupported(): boolean {
const supported =
"serviceWorker" in navigator &&
"PushManager" in window &&
"Notification" in window;
if (!supported) {
logger.warn(
"[WebPushNotificationService] Web Push is not supported in this browser",
);
}
return supported;
}
/**
* Request notification permissions via browser API
*/
async requestPermissions(): Promise<boolean> {
try {
if (!this.isSupported()) {
logger.error("[WebPushNotificationService] Web Push not supported");
return false;
}
logger.debug(
"[WebPushNotificationService] Requesting browser notification permissions",
);
const permission = await Notification.requestPermission();
const granted = permission === "granted";
logger.debug(
"[WebPushNotificationService] Permission result:",
permission,
);
return granted;
} catch (error) {
logger.error(
"[WebPushNotificationService] Permission request failed:",
error,
);
return false;
}
}
/**
* Check current browser notification permission status
*/
async checkPermissions(): Promise<PermissionStatus> {
if (!this.isSupported()) {
return {
granted: false,
details: {
notifications: false,
},
};
}
const granted = window.Notification?.permission === "granted";
return {
granted,
details: {
notifications: granted,
},
};
}
/**
* Schedule a daily notification via Web Push
*
* @note This is a stub implementation. The actual scheduling will be
* integrated with the existing PushNotificationPermission.vue component
* and web push server infrastructure.
*
* @param options Notification configuration
* @returns Promise that resolves to true if scheduling succeeded
*/
async scheduleDailyNotification(
options: DailyNotificationOptions,
): Promise<boolean> {
try {
logger.info("[WebPushNotificationService] Schedule requested:", {
time: options.time,
title: options.title,
});
// TODO: Integrate with existing PushNotificationPermission.vue logic
// This will involve:
// 1. Subscribing to push service with VAPID key
// 2. Sending subscription to server with schedule time
// 3. Server will send push messages at scheduled time
logger.warn(
"[WebPushNotificationService] Scheduling not yet implemented - use PushNotificationPermission.vue component directly",
);
return false;
} catch (error) {
logger.error("[WebPushNotificationService] Schedule failed:", error);
return false;
}
}
/**
* Cancel daily web push notifications
*
* @note This is a stub implementation. Will be integrated with existing
* web push unsubscribe logic.
*/
async cancelDailyNotification(): Promise<void> {
try {
logger.info("[WebPushNotificationService] Cancel requested");
// TODO: Integrate with existing unsubscribe logic from App.vue
// This will involve:
// 1. Getting current service worker subscription
// 2. Calling subscription.unsubscribe()
// 3. Notifying server to stop sending push messages
logger.warn(
"[WebPushNotificationService] Cancellation not yet implemented - use existing App.vue logic",
);
} catch (error) {
logger.error("[WebPushNotificationService] Cancel failed:", error);
}
}
/**
* Get current web push notification status
*
* @note This is a stub implementation. Will be integrated with existing
* settings retrieval logic.
*/
async getStatus(): Promise<NotificationStatus> {
try {
// TODO: Integrate with existing settings from database
// Check settings.notifyingNewActivityTime and settings.notifyingReminderTime
logger.debug(
"[WebPushNotificationService] Status check - stub implementation",
);
return {
enabled: false,
notificationType: "web-push",
};
} catch (error) {
logger.error("[WebPushNotificationService] Get status failed:", error);
return {
enabled: false,
notificationType: "web-push",
};
}
}
/**
* Get platform identifier
*/
getPlatformName(): string {
return this.platformName;
}
}

View File

@@ -0,0 +1,25 @@
/**
* Notification Services Barrel Export
*
* Central export point for all notification-related services.
* Use this instead of importing from individual files.
*
* @example
* ```typescript
* import { NotificationService } from '@/services/notifications';
*
* const service = NotificationService.getInstance();
* await service.scheduleDailyNotification({ ... });
* ```
*/
export { NotificationService } from "./NotificationService";
export { NativeNotificationService } from "./NativeNotificationService";
export { WebPushNotificationService } from "./WebPushNotificationService";
export type {
NotificationServiceInterface,
DailyNotificationOptions,
NotificationStatus,
PermissionStatus,
} from "./NotificationService";