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
313 lines
9.8 KiB
Markdown
313 lines
9.8 KiB
Markdown
# 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.
|