# DailyNotification Plugin - Capacitor Integration Guide **Author**: Matthew Raymer **Version**: 1.0.0 **Created**: 2025-10-08 06:24:57 UTC ## Overview The DailyNotification plugin is **specifically designed for Capacitor-based mobile applications**. It provides native mobile functionality that is not available in web-only PWAs. This guide explains when and how to use the plugin in your TimeSafari PWA. ## When to Use the Plugin ### โœ… Use the Plugin When: - **Capacitor is installed** in your TimeSafari PWA project - **Building for mobile platforms** (Android, iOS) - **Need native mobile features** like: - Background notifications - Native notification scheduling - Background task execution - Platform-specific notification channels - Battery optimization handling - Exact alarm permissions ### โŒ Don't Use the Plugin When: - **Web-only PWA** (no Capacitor) - **Desktop-only Electron app** (though Electron support is available) - **Server-side rendering** (SSR) - **Static site generation** (SSG) ## Integration Scenarios ### Scenario 1: Web-Only PWA (No Plugin Needed) ```typescript // In your web-only TimeSafari PWA // You continue using your existing code as-is private async loadNewStarredProjectChanges() { if (this.activeDid && this.starredPlanHandleIds.length > 0) { try { const starredProjectChanges = await getStarredProjectsWithChanges( this.axios, this.apiServer, this.activeDid, this.starredPlanHandleIds, this.lastAckedStarredPlanChangesJwtId, ); this.numNewStarredProjectChanges = starredProjectChanges.data.length; this.newStarredProjectChangesHitLimit = starredProjectChanges.hitLimit; } catch (error) { console.warn("[HomeView] Failed to load starred project changes:", error); this.numNewStarredProjectChanges = 0; this.newStarredProjectChangesHitLimit = false; } } } ``` **What happens:** - Your existing code works perfectly in the browser - No plugin integration needed - No native mobile features (background notifications, etc.) - Limited to web browser capabilities ### Scenario 2: Capacitor-Based PWA (Plugin Recommended) ```typescript // In your Capacitor-based TimeSafari PWA import { DailyNotification } from '@timesafari/daily-notification-plugin'; import { Capacitor } from '@capacitor/core'; class TimeSafariHomeView { async initialize() { // Check if running in Capacitor if (Capacitor.isNativePlatform()) { // Initialize plugin for native mobile features await this.setupDailyNotification(); } else { // Use existing web-only code console.log('Running in web browser - using existing code'); } } async setupDailyNotification() { // Only runs on native platforms (Android, iOS) await DailyNotification.configure({ timesafariConfig: { activeDid: this.activeDid, endpoints: { projectsLastUpdated: `${this.apiServer}/api/v2/report/plansLastUpdatedBetween` }, starredProjectsConfig: { enabled: true, starredPlanHandleIds: this.starredPlanHandleIds, lastAckedJwtId: this.lastAckedStarredPlanChangesJwtId, fetchInterval: '0 8 * * *' } }, networkConfig: { httpClient: this.axios, baseURL: this.apiServer, timeout: 30000 } }); } async loadNewStarredProjectChanges() { if (Capacitor.isNativePlatform()) { // Use plugin-enhanced method on native platforms await this.loadNewStarredProjectChangesNative(); } else { // Use existing web method in browser await this.loadNewStarredProjectChangesWeb(); } } async loadNewStarredProjectChangesNative() { // Plugin-enhanced method with native features const starredProjectChanges = await this.integrationService.getStarredProjectsWithChanges( this.activeDid, this.starredPlanHandleIds, this.lastAckedStarredPlanChangesJwtId ); this.numNewStarredProjectChanges = starredProjectChanges.data.length; this.newStarredProjectChangesHitLimit = starredProjectChanges.hitLimit; } async loadNewStarredProjectChangesWeb() { // Existing web-only method const starredProjectChanges = await getStarredProjectsWithChanges( this.axios, this.apiServer, this.activeDid, this.starredPlanHandleIds, this.lastAckedStarredPlanChangesJwtId, ); this.numNewStarredProjectChanges = starredProjectChanges.data.length; this.newStarredProjectChangesHitLimit = starredProjectChanges.hitLimit; } } ``` ## Platform Detection ### Check if Capacitor is Available ```typescript import { Capacitor } from '@capacitor/core'; // Check if running in Capacitor if (Capacitor.isNativePlatform()) { // Use plugin for native mobile features await this.setupDailyNotification(); } else { // Use existing web-only code console.log('Running in web browser'); } // Check specific platform if (Capacitor.getPlatform() === 'android') { // Android-specific configuration } else if (Capacitor.getPlatform() === 'ios') { // iOS-specific configuration } else { // Web browser } ``` ### Conditional Plugin Loading ```typescript // Only load plugin when needed let DailyNotification: any = null; let TimeSafariIntegrationService: any = null; if (Capacitor.isNativePlatform()) { // Dynamically import plugin only on native platforms const plugin = await import('@timesafari/daily-notification-plugin'); DailyNotification = plugin.DailyNotification; TimeSafariIntegrationService = plugin.TimeSafariIntegrationService; } // Use plugin conditionally if (DailyNotification) { await DailyNotification.configure({...}); } else { // Use existing web-only code } ``` ## Capacitor Configuration ### 1. Install Capacitor (if not already installed) ```bash # Install Capacitor npm install @capacitor/core @capacitor/cli # Initialize Capacitor npx cap init # Add platforms npx cap add android npx cap add ios ``` ### 2. Configure Capacitor for TimeSafari PWA ```typescript // capacitor.config.ts import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'app.timesafari', appName: 'TimeSafari', webDir: 'dist', plugins: { DailyNotification: { // Plugin configuration storage: 'tiered', ttlSeconds: 1800, enableETagSupport: true, enableErrorHandling: true, enablePerformanceOptimization: true } } }; export default config; ``` ### 3. Build and Sync ```bash # Build your PWA npm run build # Sync with Capacitor npx cap sync # Open in IDE npx cap open android npx cap open ios ``` ## Platform-Specific Features ### Android Features (Only with Plugin) ```typescript // Android-specific features if (Capacitor.getPlatform() === 'android') { // WorkManager for background tasks // AlarmManager for exact notifications // Notification channels // Battery optimization handling // Exact alarm permissions } ``` ### iOS Features (Only with Plugin) ```typescript // iOS-specific features if (Capacitor.getPlatform() === 'ios') { // BGTaskScheduler for background tasks // UNUserNotificationCenter for notifications // Background App Refresh // Provisional authorization // Notification categories } ``` ### Web Features (Existing Code) ```typescript // Web browser features if (!Capacitor.isNativePlatform()) { // Service Worker (if implemented) // Web Notifications API // Local Storage // IndexedDB // Your existing web-only code } ``` ## Integration Strategy ### 1. Progressive Enhancement ```typescript class TimeSafariHomeView { async initialize() { // Always initialize with existing web code await this.initializeWeb(); // Enhance with plugin if on native platform if (Capacitor.isNativePlatform()) { await this.initializeNative(); } } async initializeWeb() { // Your existing web-only initialization console.log('Initialized web version'); } async initializeNative() { // Plugin-enhanced initialization await this.setupDailyNotification(); console.log('Initialized native version with plugin'); } } ``` ### 2. Feature Detection ```typescript class TimeSafariHomeView { private isNative = Capacitor.isNativePlatform(); private hasPlugin = false; async initialize() { if (this.isNative) { try { await this.setupDailyNotification(); this.hasPlugin = true; console.log('Plugin initialized successfully'); } catch (error) { console.warn('Plugin initialization failed, falling back to web mode'); this.hasPlugin = false; } } } async loadNewStarredProjectChanges() { if (this.hasPlugin) { // Use plugin-enhanced method await this.loadNewStarredProjectChangesNative(); } else { // Use existing web method await this.loadNewStarredProjectChangesWeb(); } } } ``` ### 3. Graceful Degradation ```typescript class TimeSafariHomeView { async loadNewStarredProjectChanges() { try { if (this.hasPlugin) { // Try plugin-enhanced method first await this.loadNewStarredProjectChangesNative(); } else { // Fall back to web method await this.loadNewStarredProjectChangesWeb(); } } catch (error) { // If plugin fails, fall back to web method console.warn('Plugin method failed, falling back to web method'); await this.loadNewStarredProjectChangesWeb(); } } } ``` ## Build Configuration ### 1. Package.json Scripts ```json { "scripts": { "build": "vite build", "build:web": "vite build", "build:android": "vite build && npx cap sync android", "build:ios": "vite build && npx cap sync ios", "dev:web": "vite", "dev:android": "vite && npx cap run android", "dev:ios": "vite && npx cap run ios" } } ``` ### 2. Environment Variables ```bash # .env VITE_CAPACITOR_ENABLED=true VITE_PLUGIN_ENABLED=true # .env.web (web-only build) VITE_CAPACITOR_ENABLED=false VITE_PLUGIN_ENABLED=false ``` ### 3. Conditional Imports ```typescript // Only import plugin when needed const loadPlugin = async () => { if (import.meta.env.VITE_PLUGIN_ENABLED === 'true') { return await import('@timesafari/daily-notification-plugin'); } return null; }; const plugin = await loadPlugin(); if (plugin) { await plugin.DailyNotification.configure({...}); } ``` ## Testing Strategy ### 1. Web Testing ```bash # Test web-only version npm run dev:web # Test in browser - should use existing code ``` ### 2. Native Testing ```bash # Test Android version npm run dev:android # Test on Android device/emulator - should use plugin # Test iOS version npm run dev:ios # Test on iOS device/simulator - should use plugin ``` ### 3. Cross-Platform Testing ```typescript // Test both implementations const testBothImplementations = async () => { // Test web implementation const webResult = await this.loadNewStarredProjectChangesWeb(); // Test native implementation (if available) if (this.hasPlugin) { const nativeResult = await this.loadNewStarredProjectChangesNative(); // Compare results console.log('Web result:', webResult); console.log('Native result:', nativeResult); } }; ``` ## Common Questions ### Q: Do I need to install the plugin for web-only PWAs? **A:** No, the plugin is only needed for Capacitor-based mobile apps. Web-only PWAs continue using your existing code. ### Q: What happens if I install the plugin but don't use Capacitor? **A:** The plugin will not work in web browsers. You should use platform detection to only initialize the plugin on native platforms. ### Q: Can I use the plugin in Electron? **A:** Yes, the plugin supports Electron, but you need to configure it specifically for Electron. ### Q: How do I handle different platforms? **A:** Use `Capacitor.getPlatform()` to detect the platform and configure the plugin accordingly. ### Q: What if the plugin fails to initialize? **A:** Implement graceful degradation to fall back to your existing web-only code. ## Conclusion The DailyNotification plugin is **only needed when using Capacitor** for mobile app development. For web-only PWAs, you continue using your existing TimeSafari code. The plugin provides native mobile features like background notifications, exact alarm scheduling, and platform-specific optimizations that are not available in web browsers. **Key Points:** - โœ… **Use plugin with Capacitor** for mobile apps (Android, iOS) - โŒ **Don't use plugin** for web-only PWAs - ๐Ÿ”„ **Use platform detection** to conditionally load the plugin - ๐Ÿ›ก๏ธ **Implement graceful degradation** to fall back to web code - ๐Ÿงช **Test both implementations** to ensure compatibility --- **Next Steps:** 1. Check if your TimeSafari PWA uses Capacitor 2. If yes, integrate the plugin with platform detection 3. If no, continue using your existing web-only code 4. Test both web and native implementations