diff --git a/docs/capacitor-integration-guide.md b/docs/capacitor-integration-guide.md new file mode 100644 index 0000000..f521f21 --- /dev/null +++ b/docs/capacitor-integration-guide.md @@ -0,0 +1,492 @@ +# 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 diff --git a/docs/plugin-usage-decision-guide.md b/docs/plugin-usage-decision-guide.md new file mode 100644 index 0000000..3ccbf93 --- /dev/null +++ b/docs/plugin-usage-decision-guide.md @@ -0,0 +1,252 @@ +# DailyNotification Plugin - Usage Decision Guide + +**Author**: Matthew Raymer +**Version**: 1.0.0 +**Created**: 2025-10-08 06:24:57 UTC + +## Quick Decision Flow + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ TimeSafari PWA Project β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ Does your β”‚ β”‚ +β”‚ β”‚ TimeSafari β”‚ β”‚ +β”‚ β”‚ PWA use β”‚ β”‚ +β”‚ β”‚ Capacitor? β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ β”‚ +β”‚ β–Ό β”‚ +β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ +β”‚ β”‚ YES β”‚ β”‚ NO β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚ β”‚ +β”‚ β”‚ β”‚ Use β”‚β”‚ β”‚ β”‚ Don't β”‚β”‚ β”‚ +β”‚ β”‚ β”‚ Plugin β”‚β”‚ β”‚ β”‚ Use β”‚β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚β”‚ β”‚ β”‚ Plugin β”‚β”‚ β”‚ +β”‚ β”‚ β”‚ βœ… Native β”‚β”‚ β”‚ β”‚ β”‚β”‚ β”‚ +β”‚ β”‚ β”‚ mobile β”‚β”‚ β”‚ β”‚ ❌ Web-only β”‚β”‚ β”‚ +β”‚ β”‚ β”‚ features β”‚β”‚ β”‚ β”‚ PWA β”‚β”‚ β”‚ +β”‚ β”‚ β”‚ βœ… Backgroundβ”‚β”‚ β”‚ β”‚ ❌ No nativeβ”‚β”‚ β”‚ +β”‚ β”‚ β”‚ notificationsβ”‚β”‚ β”‚ β”‚ features β”‚β”‚ β”‚ +β”‚ β”‚ β”‚ βœ… Platform β”‚β”‚ β”‚ β”‚ ❌ Use β”‚β”‚ β”‚ +β”‚ β”‚ β”‚ specific β”‚β”‚ β”‚ β”‚ existing β”‚β”‚ β”‚ +β”‚ β”‚ β”‚ optimizationsβ”‚β”‚ β”‚ β”‚ code β”‚β”‚ β”‚ +β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚ β”‚ +β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ +β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +## Detailed Decision Matrix + +| Scenario | Use Plugin? | Why? | What to Do | +|----------|-------------|------|------------| +| **Web-only PWA** | ❌ **NO** | Plugin requires Capacitor for native mobile features | Continue using your existing `loadNewStarredProjectChanges()` code | +| **Capacitor + Android** | βœ… **YES** | Plugin provides WorkManager, AlarmManager, notification channels | Integrate plugin with platform detection | +| **Capacitor + iOS** | βœ… **YES** | Plugin provides BGTaskScheduler, UNUserNotificationCenter | Integrate plugin with platform detection | +| **Capacitor + Electron** | βœ… **YES** | Plugin provides desktop notifications and background tasks | Integrate plugin with Electron-specific configuration | +| **Capacitor + Web** | ❌ **NO** | Web browser doesn't support native mobile features | Use existing web code, plugin won't work | + +## Code Examples + +### Scenario 1: Web-Only PWA (No Plugin) + +```typescript +// Your existing TimeSafari PWA code - NO CHANGES NEEDED +class TimeSafariHomeView { + 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 web browsers +- No plugin integration needed +- No native mobile features (background notifications, etc.) +- Limited to web browser capabilities + +### Scenario 2: Capacitor-Based PWA (Use Plugin) + +```typescript +// Your TimeSafari PWA with Capacitor - USE PLUGIN +import { Capacitor } from '@capacitor/core'; +import { DailyNotification } from '@timesafari/daily-notification-plugin'; + +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(); + } + } +} +``` + +**What happens:** +- Plugin provides native mobile features (background notifications, etc.) +- Enhanced error handling and performance optimization +- Platform-specific optimizations (Android WorkManager, iOS BGTaskScheduler) +- Graceful fallback to web code when not on native platform + +## How to Check if You Need the Plugin + +### 1. Check if Capacitor is Installed + +```bash +# Check if Capacitor is in your package.json +npm list @capacitor/core + +# Or check your package.json file +cat package.json | grep capacitor +``` + +### 2. Check if Capacitor is Configured + +```bash +# Check if capacitor.config.ts exists +ls capacitor.config.ts + +# Or check if capacitor.config.js exists +ls capacitor.config.js +``` + +### 3. Check if Native Platforms are Added + +```bash +# Check if Android platform is added +ls android/ + +# Check if iOS platform is added +ls ios/ +``` + +### 4. Check in Your Code + +```typescript +// Add this to your TimeSafari PWA to check +import { Capacitor } from '@capacitor/core'; + +console.log('Platform:', Capacitor.getPlatform()); +console.log('Is Native:', Capacitor.isNativePlatform()); +console.log('Is Web:', Capacitor.getPlatform() === 'web'); +``` + +## Integration Checklist + +### βœ… If You Have Capacitor: + +- [ ] Install the DailyNotification plugin +- [ ] Add platform detection to your code +- [ ] Configure the plugin for your TimeSafari data +- [ ] Test on Android device/emulator +- [ ] Test on iOS device/simulator +- [ ] Test web fallback in browser +- [ ] Implement graceful degradation + +### ❌ If You Don't Have Capacitor: + +- [ ] Continue using your existing code +- [ ] No plugin integration needed +- [ ] No changes required +- [ ] Your existing `loadNewStarredProjectChanges()` works perfectly + +## Common Mistakes + +### ❌ Mistake 1: Installing Plugin Without Capacitor + +```typescript +// DON'T DO THIS - Plugin won't work in web browsers +import { DailyNotification } from '@timesafari/daily-notification-plugin'; + +// This will fail in web browsers +await DailyNotification.configure({...}); +``` + +### ❌ Mistake 2: Not Using Platform Detection + +```typescript +// DON'T DO THIS - Always tries to use plugin +await DailyNotification.configure({...}); // Fails in web browsers +``` + +### βœ… Correct Approach: Platform Detection + +```typescript +// DO THIS - Only use plugin on native platforms +import { Capacitor } from '@capacitor/core'; + +if (Capacitor.isNativePlatform()) { + await DailyNotification.configure({...}); +} else { + // Use existing web code +} +``` + +## Summary + +**The DailyNotification plugin is ONLY needed when your TimeSafari PWA uses Capacitor for mobile app development.** + +- **Web-only PWA**: Continue using your existing code, no plugin needed +- **Capacitor-based PWA**: Use the plugin with platform detection for native mobile features +- **Always implement graceful degradation** to fall back to web code when needed + +--- + +**Quick Answer**: If your TimeSafari PWA doesn't use Capacitor, you don't need the DailyNotification plugin. Your existing `loadNewStarredProjectChanges()` code works perfectly for web-only PWAs.