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:
312
doc/daily-notification-plugin-architecture.md
Normal file
312
doc/daily-notification-plugin-architecture.md
Normal 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.
|
||||
Reference in New Issue
Block a user