13 KiB
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)
// 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)
// 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
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
// 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)
# 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
// 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
# 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)
// 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)
// 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)
// 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
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
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
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
{
"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
# .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
// 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
# Test web-only version
npm run dev:web
# Test in browser - should use existing code
2. Native Testing
# 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
// 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:
- Check if your TimeSafari PWA uses Capacitor
- If yes, integrate the plugin with platform detection
- If no, continue using your existing web-only code
- Test both web and native implementations