# Daily Notification Plugin **Author**: Matthew Raymer **Version**: 1.2.0 (see `package.json` for source of truth) **Created**: 2025-09-22 09:22:32 UTC **Last Updated**: 2025-12-23 UTC ## Overview The Daily Notification Plugin is a comprehensive Capacitor plugin that provides enterprise-grade daily notification functionality across Android, iOS, and Electron platforms. It features dual scheduling, callback support, TTL-at-fire logic, and comprehensive observability. ## Quick Start **New to the plugin?** Start here: 1. **[Installation & Setup](./doc/GETTING_STARTED.md)** โ€” Installation, platform setup, and basic usage 2. **[Quick Start Guide](./doc/examples/QUICK_START.md)** โ€” Minimal working example 3. **[Common Patterns](./doc/examples/COMMON_PATTERNS.md)** โ€” Common integration patterns 4. **[Troubleshooting](./doc/TROUBLESHOOTING.md)** โ€” Common issues and solutions For complete documentation, see the [Documentation Index](./doc/00-INDEX.md). ### ๐ŸŽฏ **Native-First Architecture** The plugin has been optimized for **native-first deployment** with the following key improvements: **Platform Support:** - โœ… **Android**: WorkManager + AlarmManager + SQLite - โœ… **iOS**: BGTaskScheduler + UNUserNotificationCenter + Core Data - โœ… **Electron**: Desktop notifications + SQLite/LocalStorage - โŒ **Web (PWA)**: Removed for native-first focus **Key Benefits:** - **Simplified Architecture**: Focused on mobile and desktop platforms - **Better Performance**: Optimized for native platform capabilities - **Reduced Complexity**: Fewer platform-specific code paths - **Cleaner Codebase**: Removed unused web-specific code (~90 lines) ## Implementation Status ### **Overview** Dec 17 - test-apps - android has been seen to work - ios is being developed (Jose) - after ios, will work on daily-notification-test (that includes Vue) - need to test with real data in the API ### โœ… **Phase 2 Complete - Production Ready** | Component | Status | Implementation | |-----------|--------|----------------| | **Android Core** | โœ… Complete | WorkManager + AlarmManager + SQLite | | **iOS Parity** | โœ… Complete | BGTaskScheduler + UNUserNotificationCenter | | **Web Service Worker** | โŒ Removed | Web support dropped for native-first architecture | | **Callback Registry** | โœ… Complete | Circuit breaker + retry logic | | **Observability** | โœ… Complete | Structured logging + health monitoring | | **Documentation** | โœ… Complete | Migration guides + enterprise examples | **All platforms are fully implemented with complete feature parity and enterprise-grade functionality.** ## Behavioral Contracts ### Guaranteed Behaviors The plugin guarantees the following behaviors: - **Monotonic Watermark**: Watermark values are strictly monotonic (never decrease) - **Idempotency**: Operations with the same idempotency key are safe to retry - **TTL Semantics**: Content with expired TTL is not delivered - **Schedule Persistence**: Schedules persist across app restarts - **Recovery**: Missed notifications are recovered on app launch (best-effort) ### Best-Effort Behaviors The following behaviors are best-effort and may vary by platform: - **Delivery in Doze Mode**: Android Doze mode may delay notifications - **Background Fetch Timing**: Exact timing depends on OS scheduling - **Battery Optimization**: May be affected by device battery optimization settings ### ๐Ÿงช **Testing & Quality** - **Test Coverage**: 58 tests across 4 test suites โœ… - **Build Status**: TypeScript compilation and Rollup bundling โœ… - **Code Quality**: ESLint and Prettier compliance โœ… - **Cross-Platform**: Unified API surface across all platforms โœ… ## Features ### ๐Ÿš€ **Core Features** - **Dual Scheduling**: Separate content fetch and user notification scheduling - **TTL-at-Fire Logic**: Content validity checking at notification time - **Callback System**: HTTP, local, and queue callback support - **Circuit Breaker Pattern**: Automatic failure detection and recovery - **Static Daily Reminders**: Simple daily notifications without network content - **Cross-Platform**: Android, iOS, and Electron implementations ### ๐Ÿ“ฑ **Platform Support** - **Android**: WorkManager + AlarmManager + SQLite (Room) - **iOS**: BGTaskScheduler + UNUserNotificationCenter + Core Data - **Web**: โŒ Removed (native-first architecture) ### ๐Ÿ”ง **Enterprise Features** - **Observability**: Structured logging with event codes - **Health Monitoring**: Comprehensive status and performance metrics - **Error Handling**: Exponential backoff and retry logic - **Security**: Encrypted storage and secure callback handling - **Database Access**: Full TypeScript interfaces for plugin database access - See [`doc/architecture/DATABASE_INTERFACES.md`](doc/architecture/DATABASE_INTERFACES.md) for complete API reference - See [doc/00-INDEX.md](doc/00-INDEX.md) for complete documentation index - Plugin owns its SQLite database - access via Capacitor interfaces - Supports schedules, content cache, callbacks, history, and configuration ### โฐ **Static Daily Reminders** - **No Network Required**: Completely offline reminder notifications - **Simple Scheduling**: Easy daily reminder setup with HH:mm time format - **Rich Customization**: Customizable title, body, sound, vibration, and priority - **Persistent Storage**: Survives app restarts and device reboots - **Cross-Platform**: Consistent API across Android, iOS, and Electron - **Management**: Full CRUD operations for reminder management ## Installation ```bash npm install @timesafari/daily-notification-plugin ``` Or install from Git repository: ```bash npm install git+https://gitea.anomalistdesign.com/trent_larson/daily-notification-plugin.git ``` The plugin follows the standard Capacitor Android structure - no additional path configuration needed! ## Documentation **๐Ÿ“š Complete Documentation Index**: See [doc/00-INDEX.md](./doc/00-INDEX.md) for organized access to all documentation. ## Quick Integration **New to the plugin?** Start with the [Quick Integration Guide](./doc/integration/QUICK_START.md) for step-by-step setup instructions. The quick guide covers: - Installation and setup - AndroidManifest.xml configuration (โš ๏ธ **Critical**: NotifyReceiver registration) - iOS configuration - Basic usage examples - Troubleshooting common issues **For AI Agents**: See [AI Integration Guide](./doc/ai/AI_INTEGRATION_GUIDE.md) for explicit, machine-readable instructions with verification steps, error handling, and decision trees. ## Quick Start ### Basic Usage ```typescript import { DailyNotification } from '@timesafari/daily-notification-plugin'; // Schedule a daily notification await DailyNotification.scheduleDailyNotification({ title: 'Daily Update', body: 'Your daily content is ready', schedule: '0 9 * * *' // 9 AM daily }); ``` ### Enhanced Usage (Recommended) ```typescript import { DailyNotification, DualScheduleConfiguration } from '@timesafari/daily-notification-plugin'; // Configure dual scheduling const config: DualScheduleConfiguration = { contentFetch: { schedule: '0 8 * * *', // Fetch at 8 AM ttlSeconds: 3600, // 1 hour TTL source: 'api', url: 'https://api.example.com/daily-content' }, userNotification: { schedule: '0 9 * * *', // Notify at 9 AM title: 'Daily Update', body: 'Your daily content is ready', actions: [ { id: 'view', title: 'View' }, { id: 'dismiss', title: 'Dismiss' } ] } }; await DailyNotification.scheduleDualNotification(config); ``` ### Callback Integration ```typescript // Register analytics callback await DailyNotification.registerCallback('analytics', { kind: 'http', target: 'https://analytics.example.com/events', headers: { 'Authorization': 'Bearer your-token', 'Content-Type': 'application/json' } }); // Register local callback await DailyNotification.registerCallback('database', { kind: 'local', target: 'saveToDatabase' }); function saveToDatabase(event: CallbackEvent) { console.log('Saving to database:', event); // Your database save logic here } ``` ### Static Daily Reminders For simple daily reminders that don't require network content: ```typescript import { DailyNotification } from '@timesafari/daily-notification-plugin'; // Schedule a simple daily reminder await DailyNotification.scheduleDailyReminder({ id: 'morning_checkin', title: 'Good Morning!', body: 'Time to check your TimeSafari community updates', time: '09:00', // HH:mm format sound: true, vibration: true, priority: 'normal', repeatDaily: true }); // Get all scheduled reminders const result = await DailyNotification.getScheduledReminders(); console.log('Scheduled reminders:', result.reminders); // Update an existing reminder await DailyNotification.updateDailyReminder('morning_checkin', { title: 'Updated Morning Reminder', time: '08:30' }); // Cancel a reminder await DailyNotification.cancelDailyReminder('morning_checkin'); ``` **Key Benefits of Static Reminders:** - โœ… **No network dependency** - works completely offline - โœ… **No content cache required** - direct notification display - โœ… **Simple API** - easy to use for basic reminder functionality - โœ… **Persistent** - survives app restarts and device reboots ## API Reference ### Core Methods #### `scheduleDailyNotification(options)` Schedule a basic daily notification (backward compatible). ```typescript await DailyNotification.scheduleDailyNotification({ title: string; body: string; schedule: string; // Cron expression actions?: NotificationAction[]; }); ``` #### `scheduleContentFetch(config)` Schedule content fetching separately. ```typescript await DailyNotification.scheduleContentFetch({ schedule: string; // Cron expression ttlSeconds: number; // Time-to-live in seconds source: string; // Content source identifier url?: string; // API endpoint URL headers?: Record; }); ``` #### `scheduleUserNotification(config)` Schedule user notifications separately. ```typescript await DailyNotification.scheduleUserNotification({ schedule: string; // Cron expression title: string; // Notification title body: string; // Notification body actions?: NotificationAction[]; }); ``` #### `scheduleDualNotification(config)` Schedule both content fetch and user notification. ```typescript await DailyNotification.scheduleDualNotification({ contentFetch: ContentFetchConfig; userNotification: UserNotificationConfig; }); ``` ### Callback Methods #### `registerCallback(name, config)` Register a callback function. ```typescript await DailyNotification.registerCallback('callback-name', { kind: 'http' | 'local' | 'queue'; target: string; // URL or function name headers?: Record; }); ``` #### `unregisterCallback(name)` Remove a registered callback. ```typescript await DailyNotification.unregisterCallback('callback-name'); ``` #### `getRegisteredCallbacks()` Get list of registered callbacks. ```typescript const callbacks = await DailyNotification.getRegisteredCallbacks(); // Returns: string[] ``` ### Status Methods #### `getDualScheduleStatus()` Get comprehensive status information. ```typescript const status = await DailyNotification.getDualScheduleStatus(); // Returns: { // nextRuns: number[]; // lastOutcomes: string[]; // cacheAgeMs: number | null; // staleArmed: boolean; // queueDepth: number; // circuitBreakers: CircuitBreakerStatus; // performance: PerformanceMetrics; // } ``` ### Android Diagnostic Methods #### `isAlarmScheduled(options)` Check if an alarm is scheduled for a specific trigger time. Useful for debugging and verification. ```typescript const result = await DailyNotification.isAlarmScheduled({ triggerAtMillis: 1762421400000 // Unix timestamp in milliseconds }); console.log(`Alarm scheduled: ${result.scheduled}`); ``` #### `getNextAlarmTime()` Get the next scheduled alarm time from AlarmManager. Requires Android 5.0+ (API 21+). ```typescript const result = await DailyNotification.getNextAlarmTime(); if (result.scheduled) { const nextAlarm = new Date(result.triggerAtMillis); console.log(`Next alarm: ${nextAlarm.toLocaleString()}`); } ``` #### `testAlarm(options?)` Schedule a test alarm that fires in a few seconds. Useful for verifying alarm delivery works correctly. ```typescript // Schedule test alarm for 10 seconds from now const result = await DailyNotification.testAlarm({ secondsFromNow: 10 }); console.log(`Test alarm scheduled for ${result.secondsFromNow} seconds`); console.log(`Will fire at: ${new Date(result.triggerAtMillis).toLocaleString()}`); ``` ### Quick Smoke Test For immediate validation of plugin functionality: - **Android**: [Manual Smoke Test - Android](./doc/testing/MANUAL_SMOKE_TEST.md#android-platform-testing) - **iOS**: [Manual Smoke Test - iOS](./doc/testing/MANUAL_SMOKE_TEST.md#ios-platform-testing) - **Electron**: [Manual Smoke Test - Electron](./doc/testing/MANUAL_SMOKE_TEST.md#electron-platform-testing) ### Manual Smoke Test Documentation Complete testing procedures: [doc/testing/MANUAL_SMOKE_TEST.md](./doc/testing/MANUAL_SMOKE_TEST.md) ## Compatibility Matrix ### Capacitor Versions | Plugin Version | Capacitor Version | Status | Notes | |----------------|-------------------|--------|-------| | 1.0.0+ | 6.2.1+ | โœ… **Recommended** | Latest stable, full feature support | | 1.0.0+ | 6.0.0 - 6.2.0 | โœ… **Supported** | Full feature support | | 1.0.0+ | 5.7.8 | โš ๏ธ **Legacy** | Deprecated, upgrade recommended | ### Platform Requirements ### Android Requirements - **Minimum SDK**: 23 (Android 6.0) - **Target SDK**: 35 (Android 15) - **Exact Alarm Permission**: Required for Android 12+ (SCHEDULE_EXACT_ALARM) - **Notification Permission**: Required for Android 13+ (POST_NOTIFICATIONS) - **Dependencies**: Room 2.6.1+, WorkManager 2.9.0+ ### iOS - **Minimum Version**: iOS 13.0 - **Background Modes**: Background App Refresh, Background Processing - **Permissions**: Notification permissions required - **Dependencies**: Core Data, BGTaskScheduler ### Electron ### Electron Requirements - **Minimum Version**: Electron 20+ - **Desktop Notifications**: Native desktop notification APIs - **Storage**: SQLite or LocalStorage fallback - **Permissions**: Desktop notification permissions ## Capacitor Compatibility Matrix | Plugin Version | Capacitor Version | Android | iOS | Electron | Status | |----------------|-------------------|---------|-----|----------|--------| | 2.2.x | 6.2.x | โœ… | โœ… | โœ… | **Current** | | 2.1.x | 6.1.x | โœ… | โœ… | โœ… | Supported | | 2.0.x | 6.0.x | โœ… | โœ… | โœ… | Supported | | 1.x.x | 5.x.x | โš ๏ธ | โš ๏ธ | โŒ | Deprecated | ### Installation Guide **For TimeSafari PWA Integration:** ```bash # Install the plugin npm install @timesafari/daily-notification-plugin # For workspace development (recommended) npm install --save-dev @timesafari/daily-notification-plugin ``` **Workspace Linking (Development):** ```bash # Link plugin for local development npm link @timesafari/daily-notification-plugin # Or use pnpm workspace pnpm add @timesafari/daily-notification-plugin --filter timesafari-app ``` **Capacitor Integration:** ```typescript // In your Capacitor app import { DailyNotification } from '@timesafari/daily-notification-plugin'; // Initialize the plugin await DailyNotification.configure({ storage: 'tiered', ttlSeconds: 1800, enableETagSupport: true }); ``` ### Static Daily Reminder Methods #### `scheduleDailyReminder(options)` Schedule a simple daily reminder without network content. ```typescript await DailyNotification.scheduleDailyReminder({ id: string; // Unique reminder identifier title: string; // Notification title body: string; // Notification body time: string; // Time in HH:mm format (e.g., "09:00") sound?: boolean; // Enable sound (default: true) vibration?: boolean; // Enable vibration (default: true) priority?: 'low' | 'normal' | 'high'; // Priority level (default: 'normal') repeatDaily?: boolean; // Repeat daily (default: true) timezone?: string; // Optional timezone }); ``` #### `cancelDailyReminder(reminderId)` Cancel a scheduled daily reminder. ```typescript await DailyNotification.cancelDailyReminder('morning_checkin'); ``` #### `getScheduledReminders()` Get all scheduled reminders. ```typescript const result = await DailyNotification.getScheduledReminders(); console.log('Reminders:', result.reminders); ``` #### `updateDailyReminder(reminderId, options)` Update an existing daily reminder. ```typescript await DailyNotification.updateDailyReminder('morning_checkin', { title: 'Updated Title', time: '08:30', priority: 'high' }); ``` ## Configuration ### Android Configuration #### AndroidManifest.xml **โš ๏ธ CRITICAL**: The `NotifyReceiver` registration is **required** for alarm-based notifications to work. Without it, alarms will fire but notifications won't be displayed. ```xml ``` **Note**: The `NotifyReceiver` must be registered in your app's `AndroidManifest.xml`, not just in the plugin's manifest. If notifications aren't appearing even though alarms are scheduled, check that `NotifyReceiver` is properly registered. #### build.gradle ```gradle 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" } ``` ### iOS Configuration #### Info.plist ```xml UIBackgroundModes background-app-refresh background-processing BGTaskSchedulerPermittedIdentifiers org.timesafari.dailynotification.content-fetch org.timesafari.dailynotification.notification-delivery ``` #### Capabilities 1. Enable "Background Modes" capability 2. Enable "Background App Refresh" 3. Enable "Background Processing" ### Web Configuration #### Service Worker Registration ```typescript if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js') .then(registration => { console.log('Service Worker registered:', registration); }) .catch(error => { console.error('Service Worker registration failed:', error); }); } ``` #### Push Notification Setup ```typescript const permission = await Notification.requestPermission(); if (permission === 'granted') { const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: 'your-vapid-public-key' }); await fetch('/api/push-subscription', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(subscription) }); } ``` ## Testing ### Unit Tests ```bash npm test ``` ### Integration Tests ```typescript import { DailyNotification } from '@timesafari/daily-notification-plugin'; describe('Integration Tests', () => { test('dual scheduling workflow', async () => { const config = { contentFetch: { schedule: '0 8 * * *', ttlSeconds: 3600 }, userNotification: { schedule: '0 9 * * *', title: 'Test' } }; await DailyNotification.scheduleDualNotification(config); const status = await DailyNotification.getDualScheduleStatus(); expect(status.nextRuns.length).toBe(2); }); }); ``` ## Enterprise Integration ### Analytics Integration ```typescript // Google Analytics 4 const ga4Callback = new GoogleAnalyticsCallback('G-XXXXXXXXXX', 'your-api-secret'); await ga4Callback.register(); // Mixpanel const mixpanelCallback = new MixpanelCallback('your-project-token'); await mixpanelCallback.register(); ``` ### CRM Integration ```typescript // Salesforce const salesforceCallback = new SalesforceCallback('your-access-token', 'your-instance-url'); await salesforceCallback.register(); // HubSpot const hubspotCallback = new HubSpotCallback('your-api-key'); await hubspotCallback.register(); ``` ### Monitoring Integration ```typescript // Datadog const datadogCallback = new DatadogCallback('your-api-key', 'your-app-key'); await datadogCallback.register(); // New Relic const newrelicCallback = new NewRelicCallback('your-license-key'); await newrelicCallback.register(); ``` ## Troubleshooting ### Common Issues #### Android - **Permission Denied**: Ensure all required permissions are declared - **WorkManager Not Running**: Check battery optimization settings - **Database Errors**: Verify Room database schema migration #### iOS - **Background Tasks Not Running**: Check Background App Refresh settings - **Core Data Errors**: Verify Core Data model compatibility - **Notification Permissions**: Request notification permissions #### Web - **Service Worker Not Registering**: Ensure HTTPS and proper file paths - **Push Notifications Not Working**: Verify VAPID keys and server setup - **Web Support**: Web platform support was removed for native-first architecture ### Debug Commands ```typescript // Get comprehensive status const status = await DailyNotification.getDualScheduleStatus(); console.log('Status:', status); // Check registered callbacks const callbacks = await DailyNotification.getRegisteredCallbacks(); console.log('Callbacks:', callbacks); ``` ## Performance Considerations ### Memory Usage - **Android**: Room database with connection pooling - **iOS**: Core Data with lightweight contexts - **Web**: โŒ Removed (native-first architecture) ### Battery Optimization - **Android**: WorkManager with battery-aware constraints - **iOS**: BGTaskScheduler with system-managed execution - **Web**: Service Worker with efficient background sync ### Network Usage - **Circuit Breaker**: Prevents excessive retry attempts - **TTL-at-Fire**: Reduces unnecessary network calls - **Exponential Backoff**: Intelligent retry scheduling ## Security Considerations ### Permissions - **Minimal Permissions**: Only request necessary permissions - **Runtime Checks**: Verify permissions before operations - **Graceful Degradation**: Handle permission denials gracefully ### Data Protection - **Local Storage**: Encrypted local storage on all platforms - **Network Security**: HTTPS-only for all network operations - **Callback Security**: Validate callback URLs and headers ### Privacy - **No Personal Data**: Plugin doesn't collect personal information - **Local Processing**: All processing happens locally - **User Control**: Users can disable notifications and callbacks ## Contributing ### Development Setup ```bash git clone https://gitea.anomalistdesign.com/trent_larson/daily-notification-plugin.git cd daily-notification-plugin npm install npm run build npm test ``` ### Code Standards - **TypeScript**: Strict type checking enabled - **ESLint**: Code quality and consistency - **Prettier**: Code formatting - **Jest**: Comprehensive testing ### Pull Request Process 1. Fork the repository 2. Create a feature branch 3. Make your changes 4. Add tests for new functionality 5. Ensure all tests pass 6. Submit a pull request ## License MIT License - see [LICENSE](LICENSE) file for details. ## Support ### Documentation **๐Ÿ“š [Complete Documentation Index](./doc/00-INDEX.md)** - Central hub for all project documentation **Key Documentation:** - **Integration**: [Integration Guide](./doc/integration/INTEGRATION_GUIDE.md) - Complete integration instructions - **Platform Guides**: - [iOS Platform Docs](./doc/platform/ios/) - iOS implementation, migration, and troubleshooting - [Android Platform Docs](./doc/platform/android/) - Android implementation and directives - **Testing**: [Testing Documentation](./doc/testing/) - Comprehensive testing guides and procedures - **Alarms**: [Alarm System Docs](./doc/alarms/) - Alarm system documentation - **Database Interfaces**: [`doc/architecture/DATABASE_INTERFACES.md`](doc/architecture/DATABASE_INTERFACES.md) - Complete guide to accessing plugin database from TypeScript/webview - **Database Implementation**: [`doc/DATABASE_INTERFACES_IMPLEMENTATION.md`](doc/DATABASE_INTERFACES_IMPLEMENTATION.md) - Implementation summary and status - **Database Consolidation Plan**: [`doc/platform/android/DATABASE_CONSOLIDATION_PLAN.md`](doc/platform/android/DATABASE_CONSOLIDATION_PLAN.md) - Database schema consolidation roadmap - **Building Guide**: [BUILDING.md](BUILDING.md) - Comprehensive build instructions and troubleshooting - **Design & Research**: [Design Documentation](./doc/design/) - Design research and implementation guides - **Archive**: [Legacy Documentation](./doc/archive/2025-legacy-doc/) - Historical documentation preserved for reference ### Community - **GitHub Issues**: Report bugs and request features - **Discussions**: Ask questions and share solutions - **Contributing**: Submit pull requests and improvements ### Enterprise Support - **Custom Implementations**: Tailored solutions for enterprise needs - **Integration Support**: Help with complex integrations - **Performance Optimization**: Custom performance tuning --- **Version**: 2.0.0 **Last Updated**: 2025-09-22 09:22:32 UTC **Status**: Phase 2 Complete - Production Ready **Author**: Matthew Raymer