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:
@@ -101,6 +101,13 @@ dependencies {
|
||||
implementation project(':capacitor-android')
|
||||
implementation project(':capacitor-community-sqlite')
|
||||
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"
|
||||
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
||||
|
||||
@@ -52,6 +52,25 @@
|
||||
</provider>
|
||||
</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 -->
|
||||
|
||||
<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.CAMERA" />
|
||||
<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>
|
||||
|
||||
@@ -67,6 +67,9 @@ public class MainActivity extends BridgeActivity {
|
||||
// Register SharedImage plugin
|
||||
registerPlugin(SharedImagePlugin.class);
|
||||
|
||||
// Register DailyNotification plugin
|
||||
registerPlugin(com.timesafari.dailynotification.DailyNotificationPlugin.class);
|
||||
|
||||
// Initialize SQLite
|
||||
//registerPlugin(SQLite.class);
|
||||
|
||||
|
||||
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.
|
||||
341
doc/daily-notification-plugin-checklist.md
Normal file
341
doc/daily-notification-plugin-checklist.md
Normal 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) 🚀
|
||||
193
doc/daily-notification-plugin-integration-summary.md
Normal file
193
doc/daily-notification-plugin-integration-summary.md
Normal 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) 🚀
|
||||
237
doc/daily-notification-plugin-integration.md
Normal file
237
doc/daily-notification-plugin-integration.md
Normal 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
|
||||
@@ -58,5 +58,17 @@
|
||||
</array>
|
||||
</dict>
|
||||
</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>
|
||||
</plist>
|
||||
|
||||
38
src/plugins/DailyNotificationPlugin.ts
Normal file
38
src/plugins/DailyNotificationPlugin.ts
Normal 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 };
|
||||
197
src/services/notifications/NativeNotificationService.ts
Normal file
197
src/services/notifications/NativeNotificationService.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
220
src/services/notifications/NotificationService.ts
Normal file
220
src/services/notifications/NotificationService.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
212
src/services/notifications/WebPushNotificationService.ts
Normal file
212
src/services/notifications/WebPushNotificationService.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
25
src/services/notifications/index.ts
Normal file
25
src/services/notifications/index.ts
Normal 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";
|
||||
Reference in New Issue
Block a user