853 lines
26 KiB
Markdown
853 lines
26 KiB
Markdown
# Daily Notification Plugin
|
||
|
||
## Overview
|
||
|
||
The Daily Notification Plugin is a Capacitor plugin that provides daily notification functionality following local-first principles across Android, iOS, and Electron platforms.
|
||
|
||
This is to support apps that allow users to own their data. This approach is in contrast to standard server-managed notifications; they have the advantage of trustworthy delivery, but they have the following downsides:
|
||
|
||
* Users must store their search terms and notification preferences on the server.
|
||
|
||
* Users are not able to move their notifications elsewhere, and cannot take control of their notifications with their own apps.
|
||
|
||
* Peer-to-peer network scenarios are not supported.
|
||
|
||
There are two types of notifications supported:
|
||
|
||
* Periodic static reminder messages
|
||
|
||
* Periodic API requests, then notifying the user if there is new content
|
||
|
||
|
||
|
||
## 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 (someday)
|
||
- ❌ **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**
|
||
|
||
Stand-alone tests are found in the test-apps directory.
|
||
- The daily-notification-test (that includes Vue) has worked but is not tested extensively.
|
||
|
||
### ✅ **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:
|
||
|
||
- **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
|
||
|
||
### **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!
|
||
|
||
## 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<string, string>;
|
||
});
|
||
```
|
||
|
||
#### `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;
|
||
});
|
||
```
|
||
|
||
If `contentFetch` omits `timeout`, `retryAttempts`, or `retryDelay`, Android applies defaults when scheduling fetch work (currently 30000 ms, 3 attempts, 1000 ms between attempts; see `FetchWorker`).
|
||
|
||
If `userNotification` omits optional fields (`title`, `body`, `sound`, `vibration`, `priority`), Android parses them as omitted; scheduling uses the same defaults as `NotifyReceiver` / `DualScheduleHelper` (e.g. sound and vibration default to on, priority to `normal` where applicable).
|
||
|
||
**Android (dual prefetch timing & cache):** Prefetch work is scheduled with a delay to the next `contentFetch.schedule` instant (best-effort under Doze/OEM). Fetched content is stored in a **scoped** cache row (`dual`) so it is not overwritten by the daily reminder fetch (`daily`). With no `contentFetch.url`, the host app’s **`NativeNotificationContentFetcher`** is used when registered.
|
||
|
||
### 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<string, string>;
|
||
});
|
||
```
|
||
|
||
#### `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
|
||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||
|
||
<!-- NotifyReceiver for AlarmManager-based notifications -->
|
||
<!-- REQUIRED: Without this, alarms fire but notifications won't display -->
|
||
<receiver android:name="org.timesafari.dailynotification.NotifyReceiver"
|
||
android:enabled="true"
|
||
android:exported="false">
|
||
</receiver>
|
||
|
||
<receiver android:name="org.timesafari.dailynotification.BootReceiver"
|
||
android:enabled="true"
|
||
android:exported="false">
|
||
<intent-filter>
|
||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||
</intent-filter>
|
||
</receiver>
|
||
```
|
||
|
||
**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
|
||
<key>UIBackgroundModes</key>
|
||
<array>
|
||
<string>background-app-refresh</string>
|
||
<string>background-processing</string>
|
||
</array>
|
||
|
||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||
<array>
|
||
<string>org.timesafari.dailynotification.content-fetch</string>
|
||
<string>org.timesafari.dailynotification.notification-delivery</string>
|
||
</array>
|
||
```
|
||
|
||
#### 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
|
||
|
||
## 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
|