From 09661a520f39eca623f13c24da12cd084601ec01 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 7 Oct 2025 05:19:09 +0000 Subject: [PATCH] refactor: remove dead code and unused files - Remove duplicate web implementation (src/web.ts - 1,129 lines) - Remove unused DailyNotification wrapper class (src/daily-notification.ts - 288 lines) - Remove unused callback registry (src/callback-registry.ts - 413 lines) - Remove unused example files (5 files, ~1,500 lines) - Remove unused TypeScript modules (moved to test-apps/shared/typescript/) - Remove unused interfaces (ContentHandler, ScheduleOptions) - Remove outdated documentation files (VERIFICATION_*, GLOSSARY, etc.) - Update import paths in test apps to use moved TypeScript modules - Clean up README and USAGE.md references to deleted files Total cleanup: ~3,330+ lines of dead code removed Files deleted: 20 files Files modified: 6 files (import path updates and documentation cleanup) This significantly reduces codebase complexity and maintenance burden. --- CRITICAL_IMPROVEMENTS.md | 349 ----- IMPROVEMENT_SUMMARY.md | 214 --- PROJECT_ASSESSMENT.md | 232 --- README.md | 7 +- USAGE.md | 7 +- doc/GLOSSARY.md | 31 - doc/VERIFICATION_CHECKLIST.md | 354 ----- doc/VERIFICATION_REPORT.md | 473 ------ doc/enterprise-callback-examples.md | 1083 -------------- examples/advanced-usage.ts | 259 ---- examples/enterprise-usage.ts | 262 ---- examples/phase1-2-ttl-enforcement.ts | 173 --- examples/phase1-3-rolling-window.ts | 224 --- examples/phase1-sqlite-usage.ts | 121 -- examples/phase2-1-ios-background-tasks.ts | 285 ---- examples/phase2-2-android-fallback.ts | 321 ---- examples/phase3-1-etag-support.ts | 317 ---- examples/phase3-2-advanced-error-handling.ts | 423 ------ examples/phase3-3-performance-optimization.ts | 413 ------ examples/static-daily-reminders.ts | 342 ----- examples/ui-integration-examples.ts | 1304 ----------------- examples/usage.ts | 165 --- src/callback-registry.ts | 413 ------ src/daily-notification.ts | 288 ---- src/definitions.ts | 22 +- src/index.ts | 1 - src/web.ts | 1128 -------------- test-apps/android-test/src/index.ts | 6 +- test-apps/electron-test/src/index.ts | 6 +- test-apps/ios-test/src/index.ts | 6 +- .../shared}/typescript/EndorserAPIClient.ts | 0 .../shared}/typescript/SecurityManager.ts | 0 .../TimeSafariNotificationManager.ts | 0 33 files changed, 16 insertions(+), 9213 deletions(-) delete mode 100644 CRITICAL_IMPROVEMENTS.md delete mode 100644 IMPROVEMENT_SUMMARY.md delete mode 100644 PROJECT_ASSESSMENT.md delete mode 100644 doc/GLOSSARY.md delete mode 100644 doc/VERIFICATION_CHECKLIST.md delete mode 100644 doc/VERIFICATION_REPORT.md delete mode 100644 doc/enterprise-callback-examples.md delete mode 100644 examples/advanced-usage.ts delete mode 100644 examples/enterprise-usage.ts delete mode 100644 examples/phase1-2-ttl-enforcement.ts delete mode 100644 examples/phase1-3-rolling-window.ts delete mode 100644 examples/phase1-sqlite-usage.ts delete mode 100644 examples/phase2-1-ios-background-tasks.ts delete mode 100644 examples/phase2-2-android-fallback.ts delete mode 100644 examples/phase3-1-etag-support.ts delete mode 100644 examples/phase3-2-advanced-error-handling.ts delete mode 100644 examples/phase3-3-performance-optimization.ts delete mode 100644 examples/static-daily-reminders.ts delete mode 100644 examples/ui-integration-examples.ts delete mode 100644 examples/usage.ts delete mode 100644 src/callback-registry.ts delete mode 100644 src/daily-notification.ts delete mode 100644 src/web.ts rename {src => test-apps/shared}/typescript/EndorserAPIClient.ts (100%) rename {src => test-apps/shared}/typescript/SecurityManager.ts (100%) rename {src => test-apps/shared}/typescript/TimeSafariNotificationManager.ts (100%) diff --git a/CRITICAL_IMPROVEMENTS.md b/CRITICAL_IMPROVEMENTS.md deleted file mode 100644 index b506a98..0000000 --- a/CRITICAL_IMPROVEMENTS.md +++ /dev/null @@ -1,349 +0,0 @@ -# Critical Improvements for Daily Notification Plugin - -## Immediate Action Items (Next 48 Hours) - -### 1. Restore Android Implementation - -**Priority**: CRITICAL -**Effort**: 8-12 hours - -The Android implementation was completely removed and needs to be recreated: - -```java -// Required files to recreate: -android/app/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.java -android/app/src/main/java/com/timesafari/dailynotification/DailyNotificationReceiver.java -android/app/src/main/java/com/timesafari/dailynotification/DailyNotificationLogger.java -android/app/src/main/java/com/timesafari/dailynotification/DailyNotificationConstants.java -android/app/src/main/java/com/timesafari/dailynotification/DailyNotificationConfig.java -android/app/src/main/java/com/timesafari/dailynotification/BatteryOptimizationSettings.java -android/app/src/main/java/com/timesafari/dailynotification/MaintenanceWorker.java -android/app/src/main/java/com/timesafari/dailynotification/MaintenanceReceiver.java -``` - -**Key Features to Implement**: - -- Notification scheduling with AlarmManager -- Battery optimization handling -- Background task management -- Permission handling -- Error logging and reporting - -### 2. Fix Test Suite - -**Priority**: HIGH -**Effort**: 4-6 hours - -All test files need to be updated to match current interfaces: - -- `tests/daily-notification.test.ts` ✅ Fixed -- `tests/enterprise-scenarios.test.ts` - Remove non-existent methods -- `tests/edge-cases.test.ts` - Update interface references -- `tests/advanced-scenarios.test.ts` - Fix mock implementations - -**Required Changes**: - -- Remove references to `checkPermissions` method -- Update `NotificationOptions` interface usage -- Fix timestamp types (string vs number) -- Implement proper mock objects - -### 3. Complete Interface Definitions - -**Priority**: HIGH -**Effort**: 2-3 hours - -Add missing properties and methods to interfaces: - -```typescript -// Add to NotificationOptions -export interface NotificationOptions { - // ... existing properties - retryCount?: number; - retryInterval?: number; - cacheDuration?: number; - headers?: Record; - offlineFallback?: boolean; - contentHandler?: (response: Response) => Promise<{ - title: string; - body: string; - data?: any; - }>; -} - -// Add to DailyNotificationPlugin -export interface DailyNotificationPlugin { - // ... existing methods - checkPermissions(): Promise; - requestPermissions(): Promise; -} -``` - -## Week 1 Improvements - -### 4. Enhanced Error Handling - -**Priority**: HIGH -**Effort**: 6-8 hours - -Implement comprehensive error handling: - -```typescript -// Create custom error types -export class DailyNotificationError extends Error { - constructor( - message: string, - public code: string, - public details?: any - ) { - super(message); - this.name = 'DailyNotificationError'; - } -} - -export class NetworkError extends DailyNotificationError { - constructor(message: string, public statusCode?: number) { - super(message, 'NETWORK_ERROR', { statusCode }); - this.name = 'NetworkError'; - } -} - -export class PermissionError extends DailyNotificationError { - constructor(message: string) { - super(message, 'PERMISSION_ERROR'); - this.name = 'PermissionError'; - } -} -``` - -### 5. Structured Logging - -**Priority**: MEDIUM -**Effort**: 4-6 hours - -Implement comprehensive logging system: - -```typescript -export enum LogLevel { - DEBUG = 0, - INFO = 1, - WARN = 2, - ERROR = 3 -} - -export interface Logger { - debug(message: string, context?: any): void; - info(message: string, context?: any): void; - warn(message: string, context?: any): void; - error(message: string, error?: Error, context?: any): void; -} -``` - -### 6. Validation Utilities - -**Priority**: MEDIUM -**Effort**: 3-4 hours - -Create comprehensive validation utilities: - -```typescript -export class ValidationUtils { - static isValidUrl(url: string): boolean; - static isValidTime(time: string): boolean; - static isValidTimezone(timezone: string): boolean; - static isValidPriority(priority: string): boolean; - static validateNotificationOptions(options: NotificationOptions): void; -} -``` - -## Week 2 Improvements - -### 7. Retry Mechanisms - -**Priority**: MEDIUM -**Effort**: 6-8 hours - -Implement exponential backoff retry logic: - -```typescript -export interface RetryConfig { - maxAttempts: number; - baseDelay: number; - maxDelay: number; - backoffMultiplier: number; -} - -export class RetryManager { - async executeWithRetry( - operation: () => Promise, - config: RetryConfig - ): Promise; -} -``` - -### 8. Performance Monitoring - -**Priority**: MEDIUM -**Effort**: 4-6 hours - -Add performance tracking: - -```typescript -export interface PerformanceMetrics { - notificationDeliveryTime: number; - schedulingLatency: number; - errorRate: number; - successRate: number; -} - -export class PerformanceMonitor { - trackNotificationDelivery(): void; - trackSchedulingLatency(): void; - getMetrics(): PerformanceMetrics; -} -``` - -## Security Improvements - -### 9. Input Validation - -**Priority**: HIGH -**Effort**: 3-4 hours - -Implement comprehensive input validation: - -```typescript -export class SecurityValidator { - static sanitizeUrl(url: string): string; - static validateHeaders(headers: Record): void; - static validateContent(content: string): void; - static checkForXSS(content: string): boolean; -} -``` - -### 10. Secure Storage - -**Priority**: MEDIUM -**Effort**: 4-6 hours - -Implement secure storage for sensitive data: - -```typescript -export interface SecureStorage { - set(key: string, value: string): Promise; - get(key: string): Promise; - remove(key: string): Promise; - clear(): Promise; -} -``` - -## Testing Improvements - -### 11. Integration Tests - -**Priority**: HIGH -**Effort**: 8-10 hours - -Create comprehensive integration tests: - -```typescript -describe('Integration Tests', () => { - it('should handle full notification lifecycle', async () => { - // Test complete workflow - }); - - it('should handle network failures gracefully', async () => { - // Test error scenarios - }); - - it('should respect battery optimization settings', async () => { - // Test platform-specific features - }); -}); -``` - -### 12. Performance Tests - -**Priority**: MEDIUM -**Effort**: 4-6 hours - -Add performance benchmarking: - -```typescript -describe('Performance Tests', () => { - it('should schedule notifications within 100ms', async () => { - // Performance benchmark - }); - - it('should handle 1000 concurrent notifications', async () => { - // Stress test - }); -}); -``` - -## Documentation Improvements - -### 13. API Documentation - -**Priority**: MEDIUM -**Effort**: 6-8 hours - -Generate comprehensive API documentation: - -- JSDoc comments for all public methods -- TypeScript declaration files -- Usage examples for each method -- Troubleshooting guides -- Migration guides - -### 14. Example Applications - -**Priority**: MEDIUM -**Effort**: 4-6 hours - -Create complete example applications: - -- Basic notification app -- Advanced features demo -- Enterprise usage example -- Performance optimization example - -## Success Criteria - -### Code Quality - -- [ ] 100% test coverage -- [ ] Zero TypeScript errors -- [ ] All linting rules passing -- [ ] Performance benchmarks met - -### Functionality - -- [ ] All platforms working -- [ ] Feature parity across platforms -- [ ] Proper error handling -- [ ] Comprehensive logging - -### Security - -- [ ] Input validation implemented -- [ ] Secure storage working -- [ ] No security vulnerabilities -- [ ] Audit logging in place - -### Documentation - -- [ ] API documentation complete -- [ ] Examples working -- [ ] Troubleshooting guides -- [ ] Migration guides available - -## Timeline Summary - -- **Days 1-2**: Critical fixes (Android implementation, test fixes) -- **Week 1**: Core improvements (error handling, logging, validation) -- **Week 2**: Advanced features (retry mechanisms, performance monitoring) -- **Week 3**: Security and testing improvements -- **Week 4**: Documentation and examples - -This timeline will bring the project to production readiness with all critical issues resolved and advanced features implemented. diff --git a/IMPROVEMENT_SUMMARY.md b/IMPROVEMENT_SUMMARY.md deleted file mode 100644 index b868fba..0000000 --- a/IMPROVEMENT_SUMMARY.md +++ /dev/null @@ -1,214 +0,0 @@ -# Daily Notification Plugin - Improvement Summary - -## What Was Accomplished ✅ - -### 1. Fixed Critical Build Issues - -- **TypeScript Compilation**: Resolved all TypeScript compilation errors -- **Interface Definitions**: Updated and completed interface definitions to match implementation -- **Build System**: Fixed Rollup configuration to use CommonJS syntax -- **Module Resolution**: Resolved import/export issues across all files - -### 2. Updated Core Files - -- **src/definitions.ts**: Enhanced with complete interface definitions -- **src/web/index.ts**: Fixed web implementation with proper method signatures -- **src/web.ts**: Updated web plugin implementation -- **src/daily-notification.ts**: Fixed validation logic and removed unused imports -- **rollup.config.js**: Converted to CommonJS syntax for compatibility - -### 3. Test Improvements - -- **tests/daily-notification.test.ts**: Updated to match current interfaces -- **Jest Configuration**: Removed duplicate configuration files -- **Test Structure**: Aligned test expectations with actual implementation - -### 4. Documentation - -- **PROJECT_ASSESSMENT.md**: Comprehensive project analysis -- **CRITICAL_IMPROVEMENTS.md**: Detailed improvement roadmap -- **IMPROVEMENT_SUMMARY.md**: This summary document - -## Current Project Status - -### ✅ Working Components - -- TypeScript compilation and build system -- Web platform implementation (basic) -- iOS platform implementation (Swift-based) -- Core interface definitions -- Basic test structure -- Documentation framework - -### ❌ Critical Missing Components - -- **Android Implementation**: Completely missing (was deleted) -- **Test Suite**: Most tests still failing due to interface mismatches -- **Advanced Features**: Retry logic, error handling, performance monitoring -- **Security Features**: Input validation, secure storage -- **Production Features**: Analytics, A/B testing, enterprise features - -## Immediate Next Steps (Priority Order) - -### 1. Restore Android Implementation (CRITICAL) - -**Estimated Time**: 8-12 hours -**Files Needed**: - -``` -android/app/src/main/java/com/timesafari/dailynotification/ -├── DailyNotificationPlugin.java -├── DailyNotificationReceiver.java -├── DailyNotificationLogger.java -├── DailyNotificationConstants.java -├── DailyNotificationConfig.java -├── BatteryOptimizationSettings.java -├── MaintenanceWorker.java -└── MaintenanceReceiver.java -``` - -### 2. Fix Remaining Test Files (HIGH) - -**Estimated Time**: 4-6 hours -**Files to Update**: - -- `tests/enterprise-scenarios.test.ts` -- `tests/edge-cases.test.ts` -- `tests/advanced-scenarios.test.ts` - -### 3. Complete Interface Definitions (HIGH) - -**Estimated Time**: 2-3 hours -**Missing Properties**: - -- `retryCount`, `retryInterval`, `cacheDuration` -- `headers`, `offlineFallback`, `contentHandler` -- `checkPermissions()`, `requestPermissions()` - -## Technical Debt Assessment - -### Code Quality: 6/10 - -- ✅ TypeScript compilation working -- ✅ Interface definitions complete -- ❌ Missing error handling patterns -- ❌ No structured logging -- ❌ Limited validation utilities - -### Platform Support: 4/10 - -- ✅ iOS implementation exists -- ✅ Web implementation (basic) -- ❌ Android implementation missing -- ❌ No platform-specific optimizations - -### Testing: 3/10 - -- ✅ Test structure exists -- ✅ Basic test framework working -- ❌ Most tests failing -- ❌ No integration tests -- ❌ No performance tests - -### Documentation: 7/10 - -- ✅ README and changelog -- ✅ API documentation structure -- ❌ Missing detailed API docs -- ❌ No troubleshooting guides -- ❌ Examples need updating - -### Security: 2/10 - -- ❌ No input validation -- ❌ No secure storage -- ❌ Limited permission handling -- ❌ No audit logging - -## Success Metrics Progress - -### Code Quality - -- [x] Zero TypeScript errors -- [x] Build system working -- [ ] 100% test coverage -- [ ] All linting rules passing - -### Functionality - -- [x] Web platform working -- [x] iOS platform working -- [ ] Android platform working -- [ ] Feature parity across platforms - -### User Experience - -- [ ] Reliable notification delivery -- [ ] Fast response times -- [ ] Intuitive API design -- [ ] Good documentation - -## Recommended Timeline - -### Week 1: Foundation - -- **Days 1-2**: Restore Android implementation -- **Days 3-4**: Fix all test files -- **Days 5-7**: Complete interface definitions - -### Week 2: Core Features - -- **Days 1-3**: Implement error handling and logging -- **Days 4-5**: Add validation utilities -- **Days 6-7**: Implement retry mechanisms - -### Week 3: Advanced Features - -- **Days 1-3**: Add performance monitoring -- **Days 4-5**: Implement security features -- **Days 6-7**: Add analytics and A/B testing - -### Week 4: Production Readiness - -- **Days 1-3**: Comprehensive testing -- **Days 4-5**: Documentation completion -- **Days 6-7**: Performance optimization - -## Risk Assessment - -### High Risk - -- **Android Implementation**: Critical for production use -- **Test Coverage**: Without proper tests, reliability is compromised -- **Error Handling**: Missing error handling could cause crashes - -### Medium Risk - -- **Performance**: No performance monitoring could lead to issues at scale -- **Security**: Missing security features could expose vulnerabilities -- **Documentation**: Poor documentation could hinder adoption - -### Low Risk - -- **Advanced Features**: Nice-to-have but not critical for basic functionality -- **Analytics**: Useful but not essential for core functionality - -## Conclusion - -The Daily Notification Plugin has a solid foundation with modern TypeScript architecture and good build tooling. The critical build issues have been resolved, and the project is now in a state where development can proceed efficiently. - -**Key Achievements**: - -- Fixed all TypeScript compilation errors -- Updated interface definitions to be complete and consistent -- Resolved build system issues -- Created comprehensive improvement roadmap - -**Critical Next Steps**: - -1. Restore the missing Android implementation -2. Fix the failing test suite -3. Implement proper error handling and logging -4. Add security features and input validation - -With these improvements, the project will be ready for production use across all supported platforms. diff --git a/PROJECT_ASSESSMENT.md b/PROJECT_ASSESSMENT.md deleted file mode 100644 index bbf80bb..0000000 --- a/PROJECT_ASSESSMENT.md +++ /dev/null @@ -1,232 +0,0 @@ -# Daily Notification Plugin - Project Assessment - -## Executive Summary - -The Daily Notification Plugin project shows good foundational structure but requires significant improvements to achieve production readiness. The project has been modernized with TypeScript and proper build tooling, but critical gaps exist in native implementations, testing, and documentation. - -## Current State Analysis - -### Strengths ✅ - -1. **Modern Architecture**: Well-structured TypeScript implementation with proper type definitions -2. **Build System**: Modern build pipeline with Rollup and TypeScript compilation -3. **Platform Support**: iOS implementation exists with Swift-based code -4. **Testing Framework**: Comprehensive test structure with Jest and multiple test scenarios -5. **Documentation**: Good README and changelog documentation -6. **Code Quality**: ESLint and Prettier configuration for code quality - -### Critical Issues ❌ - -1. **Build Failures**: Fixed TypeScript compilation errors -2. **Missing Android Implementation**: Native Android code was deleted but not replaced -3. **Interface Mismatches**: Type definitions didn't match implementation expectations -4. **Test Failures**: Tests reference non-existent methods and properties -5. **Incomplete Platform Support**: Web implementation is basic placeholder - -## Detailed Assessment - -### 1. Code Quality & Architecture - -**Current State**: Good TypeScript structure with proper interfaces -**Issues**: - -- Interface definitions were incomplete -- Missing proper error handling patterns -- No structured logging system - -**Recommendations**: - -- Implement comprehensive error handling with custom error types -- Add structured logging with different log levels -- Create proper validation utilities -- Implement retry mechanisms with exponential backoff - -### 2. Native Platform Implementations - -**iOS**: ✅ Good implementation with Swift - -- Proper notification handling -- Battery optimization support -- Background task management - -**Android**: ❌ Missing implementation - -- All native Java files were deleted -- No Android-specific functionality -- Missing permission handling - -**Web**: ⚠️ Basic placeholder implementation - -- Limited to browser notifications -- No advanced features -- Missing offline support - -### 3. Testing Infrastructure - -**Current State**: Comprehensive test structure but failing -**Issues**: - -- Tests reference non-existent methods -- Mock implementations are incomplete -- No integration tests for native platforms - -**Recommendations**: - -- Fix all test files to match current interfaces -- Add proper mock implementations -- Implement platform-specific test suites -- Add performance and stress tests - -### 4. Documentation & Examples - -**Current State**: Good basic documentation -**Issues**: - -- Missing API documentation -- Examples don't match current implementation -- No troubleshooting guides - -**Recommendations**: - -- Generate comprehensive API documentation -- Update examples to match current interfaces -- Add troubleshooting and debugging guides -- Create migration guides for version updates - -## Priority Improvement Recommendations - -### High Priority (Immediate) - -1. **Restore Android Implementation** - - Recreate native Android plugin code - - Implement notification scheduling - - Add battery optimization support - - Handle Android-specific permissions - -2. **Fix Test Suite** - - Update all test files to match current interfaces - - Implement proper mock objects - - Add integration tests - - Ensure 100% test coverage - -3. **Complete Interface Definitions** - - Add missing properties to interfaces - - Implement proper validation - - Add comprehensive error types - - Create utility functions - -### Medium Priority (Next Sprint) - -1. **Enhanced Web Implementation** - - Implement service worker support - - Add offline notification caching - - Improve browser compatibility - - Add progressive web app features - -2. **Advanced Features** - - Implement notification queuing - - Add A/B testing support - - Create analytics tracking - - Add user preference management - -3. **Performance Optimization** - - Implement lazy loading - - Add memory management - - Optimize notification delivery - - Add performance monitoring - -### Low Priority (Future Releases) - -1. **Enterprise Features** - - Multi-tenant support - - Advanced analytics - - Custom notification templates - - Integration with external services - -2. **Platform Extensions** - - Desktop support (Electron) - - Wearable device support - - IoT device integration - - Cross-platform synchronization - -## Technical Debt - -### Code Quality Issues - -- Missing error boundaries -- Incomplete type safety -- No performance monitoring -- Limited logging capabilities - -### Architecture Issues - -- Tight coupling between layers -- Missing abstraction layers -- No plugin system for extensions -- Limited configuration options - -### Security Issues - -- Missing input validation -- No secure storage implementation -- Limited permission handling -- No audit logging - -## Recommended Action Plan - -### Phase 1: Foundation (Week 1-2) - -1. Restore Android implementation -2. Fix all test failures -3. Complete interface definitions -4. Implement basic error handling - -### Phase 2: Enhancement (Week 3-4) - -1. Improve web implementation -2. Add comprehensive logging -3. Implement retry mechanisms -4. Add performance monitoring - -### Phase 3: Advanced Features (Week 5-6) - -1. Add notification queuing -2. Implement analytics -3. Create user preference system -4. Add A/B testing support - -### Phase 4: Production Readiness (Week 7-8) - -1. Security audit and fixes -2. Performance optimization -3. Comprehensive testing -4. Documentation completion - -## Success Metrics - -### Code Quality - -- 100% test coverage -- Zero TypeScript errors -- All linting rules passing -- Performance benchmarks met - -### Functionality - -- All platforms working -- Feature parity across platforms -- Proper error handling -- Comprehensive logging - -### User Experience - -- Reliable notification delivery -- Fast response times -- Intuitive API design -- Good documentation - -## Conclusion - -The Daily Notification Plugin has a solid foundation but requires significant work to achieve production readiness. The immediate focus should be on restoring the Android implementation and fixing the test suite. Once these critical issues are resolved, the project can move forward with advanced features and optimizations. - -The project shows good architectural decisions and modern development practices, but the missing native implementations and test failures prevent it from being usable in production environments. diff --git a/README.md b/README.md index 9b47b3f..6140899 100644 --- a/README.md +++ b/README.md @@ -627,11 +627,10 @@ MIT License - see [LICENSE](LICENSE) file for details. - **API Reference**: Complete TypeScript definitions - **Migration Guide**: [doc/migration-guide.md](doc/migration-guide.md) -- **Enterprise Examples**: [doc/enterprise-callback-examples.md](doc/enterprise-callback-examples.md) -- **Verification Report**: [doc/VERIFICATION_REPORT.md](doc/VERIFICATION_REPORT.md) - Closed-app functionality verification -- **Verification Checklist**: [doc/VERIFICATION_CHECKLIST.md](doc/VERIFICATION_CHECKLIST.md) - Regular verification process +- **Integration Guide**: [INTEGRATION_GUIDE.md](INTEGRATION_GUIDE.md) - Complete integration instructions +- **Implementation Guide**: [doc/STARRED_PROJECTS_POLLING_IMPLEMENTATION.md](doc/STARRED_PROJECTS_POLLING_IMPLEMENTATION.md) - Generic polling interface - **UI Requirements**: [doc/UI_REQUIREMENTS.md](doc/UI_REQUIREMENTS.md) - Complete UI component requirements -- **UI Integration Examples**: [examples/ui-integration-examples.ts](examples/ui-integration-examples.ts) - Ready-to-use UI components +- **Host App Examples**: [examples/hello-poll.ts](examples/hello-poll.ts) - Generic polling integration - **Background Data Fetching Plan**: [doc/BACKGROUND_DATA_FETCHING_PLAN.md](doc/BACKGROUND_DATA_FETCHING_PLAN.md) - Complete Option A implementation guide ### Community diff --git a/USAGE.md b/USAGE.md index 2c33623..3cd61e8 100644 --- a/USAGE.md +++ b/USAGE.md @@ -248,7 +248,6 @@ See `src/definitions.ts` for complete TypeScript interface definitions. ## Examples -- **Basic Usage**: `examples/usage.ts` -- **Phase-by-Phase**: `examples/phase1-*.ts`, `examples/phase2-*.ts`, `examples/phase3-*.ts` -- **Advanced Scenarios**: `examples/advanced-usage.ts` -- **Enterprise Features**: `examples/enterprise-usage.ts` +- **Basic Usage**: `examples/hello-poll.ts` +- **Stale Data UX**: `examples/stale-data-ux.ts` +- **Enterprise Features**: See `INTEGRATION_GUIDE.md` for enterprise integration patterns diff --git a/doc/GLOSSARY.md b/doc/GLOSSARY.md deleted file mode 100644 index 703c566..0000000 --- a/doc/GLOSSARY.md +++ /dev/null @@ -1,31 +0,0 @@ -# Glossary - -**📝 SANITY CHECK IMPROVEMENTS APPLIED:** This document has been updated to accurately reflect current implementation status vs. planned features. - -**T (slot time)** — The local wall-clock time a notification should fire (e.g., 08:00). *See Notification System → Scheduling & T–lead.* - -**T–lead** — The moment **`prefetchLeadMinutes`** before **T** when the system *attempts* a **single** background prefetch. T–lead **controls prefetch attempts, not arming**; locals are pre-armed earlier to guarantee closed-app delivery. *See Notification System → Scheduling & T–lead and Roadmap Phase 2.1.* - -**Lead window** — The interval from **T–lead** up to **T** during which we **try once** to refresh content. It does **not** control arming; we pre-arm earlier. *See Notification System → Scheduling & T–lead.* - -**Rolling window** — Always keep **today's remaining** (and tomorrow if iOS pending caps allow) locals **armed** so the OS can deliver while the app is closed. *See Notification System → Scheduling & T–lead and Roadmap Phase 1.3.* - -**TTL (time-to-live)** — Maximum allowed payload age **at fire time**. If `T − fetchedAt > ttlSeconds`, we **skip** arming for that T. *See Notification System → Policies and Roadmap Phase 1.2.* - -**Shared DB (planned)** — The app and plugin will open the **same SQLite file**; the app owns schema/migrations, the plugin performs short writes with WAL. *Currently using SharedPreferences/UserDefaults.* *See Notification System → Storage and Roadmap Phase 1.1.* - -**WAL (Write-Ahead Logging)** — SQLite journaling mode that permits concurrent reads during writes; recommended for foreground-read + background-write. *See Notification System → Storage and Roadmap Phase 1.1.* - -**`PRAGMA user_version`** — An integer the app increments on each migration; the plugin **checks** (does not migrate) to ensure compatibility. *See Notification System → Storage and Roadmap Phase 1.1.* - -**Exact alarm (Android)** — Minute-precise alarm via `AlarmManager.setExactAndAllowWhileIdle`, subject to policy and permission. *See Notification System → Policies and Roadmap Phase 2.2.* - -**Windowed alarm (Android)** — Batched/inexact alarm via `setWindow(start,len)`; we target **±10 minutes** when exact alarms are unavailable. *See Notification System → Policies and Roadmap Phase 2.2.* - -**Delivery-time mutation (iOS)** — Not available for **local** notifications. Notification Service Extensions mutate **remote** pushes only; locals must be rendered before scheduling. *See Notification System → Policies.* - -**Start-on-Login** — Electron feature that automatically launches the application when the user logs into their system, enabling background notification scheduling and delivery after system reboot. *See Roadmap Phase 2.3.* - -**Tiered Storage (current)** — Current implementation uses SharedPreferences (Android) / UserDefaults (iOS) for quick access, in-memory cache for structured data, and file system for large assets. *See Notification System → Storage and Roadmap Phase 1.1.* - -**No delivery-time network:** Local notifications display **pre-rendered content only**; never fetch at delivery. *See Notification System → Policies.* diff --git a/doc/VERIFICATION_CHECKLIST.md b/doc/VERIFICATION_CHECKLIST.md deleted file mode 100644 index 28a0cbc..0000000 --- a/doc/VERIFICATION_CHECKLIST.md +++ /dev/null @@ -1,354 +0,0 @@ -# Daily Notification Plugin - Verification Checklist - -**Author**: Matthew Raymer -**Version**: 1.0.0 -**Last Updated**: 2025-01-27 -**Purpose**: Regular verification of closed-app notification functionality - ---- - -## Pre-Verification Setup - -### Environment Preparation -- [ ] Clean test environment (no existing notifications) -- [ ] Network connectivity verified -- [ ] Device permissions granted (exact alarms, background refresh) -- [ ] Test API server running (if applicable) -- [ ] Logging enabled at debug level - -### Test Data Preparation -- [ ] Valid JWT token for API authentication -- [ ] Test notification content prepared -- [ ] TTL values configured (1 hour for testing) -- [ ] Background fetch lead time set (10 minutes) - ---- - -## Core Functionality Tests - -### 1. Background Fetch While App Closed - -**Test Steps**: -1. [ ] Schedule notification for T+30 minutes -2. [ ] Close app completely (not just minimize) -3. [ ] Wait for T-lead prefetch (T-10 minutes) -4. [ ] Verify background fetch occurred -5. [ ] Check content stored in database -6. [ ] Verify TTL validation - -**Expected Results**: -- [ ] Log shows `DNP-FETCH-SUCCESS` -- [ ] Content stored in local database -- [ ] TTL timestamp recorded -- [ ] No network errors - -**Platform-Specific Checks**: -- **Android**: [ ] WorkManager task executed -- **iOS**: [ ] BGTaskScheduler task executed -- **Web**: [ ] Service Worker background sync - -### 2. Local Notification Delivery from Cached Data - -**Test Steps**: -1. [ ] Pre-populate database with valid content -2. [ ] Disable network connectivity -3. [ ] Schedule notification for immediate delivery -4. [ ] Close app completely -5. [ ] Wait for notification time -6. [ ] Verify notification delivered - -**Expected Results**: -- [ ] Notification appears on device -- [ ] Content matches cached data -- [ ] No network requests during delivery -- [ ] TTL validation passed - -**Platform-Specific Checks**: -- **Android**: [ ] `NotifyReceiver` triggered -- **iOS**: [ ] Background task handler executed -- **Web**: [ ] Service Worker delivered notification - -### 3. TTL Enforcement at Delivery Time - -**Test Steps**: -1. [ ] Store expired content (TTL < current time) -2. [ ] Schedule notification for immediate delivery -3. [ ] Close app completely -4. [ ] Wait for notification time -5. [ ] Verify notification NOT delivered - -**Expected Results**: -- [ ] No notification appears -- [ ] Log shows `DNP-NOTIFY-SKIP-TTL` -- [ ] TTL validation failed as expected -- [ ] No errors in logs - -### 4. Reboot Recovery and Rescheduling - -**Test Steps**: -1. [ ] Schedule notification for future time (24 hours) -2. [ ] Simulate device reboot -3. [ ] Wait for app to restart -4. [ ] Verify notification re-scheduled -5. [ ] Check background fetch re-scheduled - -**Expected Results**: -- [ ] Notification re-scheduled after reboot -- [ ] Background fetch task re-registered -- [ ] Rolling window maintained -- [ ] No data loss - -**Platform-Specific Checks**: -- **Android**: [ ] `BootReceiver` executed -- **iOS**: [ ] App restart re-registered tasks -- **Web**: [ ] Service Worker re-registered - -### 5. Network Failure Handling - -**Test Steps**: -1. [ ] Store valid cached content -2. [ ] Simulate network failure -3. [ ] Schedule notification with T-lead prefetch -4. [ ] Close app and wait for T-lead -5. [ ] Wait for notification time -6. [ ] Verify notification delivered from cache - -**Expected Results**: -- [ ] Background fetch failed gracefully -- [ ] Log shows `DNP-FETCH-FAILURE` -- [ ] Notification delivered from cached content -- [ ] No infinite retry loops - -### 6. Timezone/DST Changes - -**Test Steps**: -1. [ ] Schedule daily notification for 9:00 AM -2. [ ] Change device timezone -3. [ ] Verify schedule recalculated -4. [ ] Check background fetch re-scheduled - -**Expected Results**: -- [ ] Next run time updated -- [ ] Background fetch task re-scheduled -- [ ] Wall-clock alignment maintained -- [ ] No schedule conflicts - ---- - -## Platform-Specific Tests - -### Android Specific - -#### Battery Optimization -- [ ] Test with exact alarm permission granted -- [ ] Test without exact alarm permission -- [ ] Verify notification timing accuracy -- [ ] Check battery optimization settings - -#### WorkManager Constraints -- [ ] Test with network constraint -- [ ] Test with battery constraint -- [ ] Verify task execution under constraints -- [ ] Check retry logic - -#### Room Database -- [ ] Verify database operations -- [ ] Check migration handling -- [ ] Test concurrent access -- [ ] Verify data persistence - -### iOS Specific - -#### Background App Refresh -- [ ] Test with background refresh enabled -- [ ] Test with background refresh disabled -- [ ] Verify fallback to cached content -- [ ] Check BGTaskScheduler budget - -#### Force Quit Behavior -- [ ] Test notification delivery after force quit -- [ ] Verify pre-armed notifications work -- [ ] Check background task registration -- [ ] Test app restart behavior - -#### Core Data -- [ ] Verify database operations -- [ ] Check migration handling -- [ ] Test concurrent access -- [ ] Verify data persistence - -### Web Specific - -#### Service Worker -- [ ] Test background sync registration -- [ ] Verify offline functionality -- [ ] Check push notification delivery -- [ ] Test browser restart behavior - -#### IndexedDB -- [ ] Verify database operations -- [ ] Check storage quota handling -- [ ] Test concurrent access -- [ ] Verify data persistence - -#### Browser Limitations -- [ ] Test with browser closed -- [ ] Verify fallback mechanisms -- [ ] Check permission handling -- [ ] Test cross-origin restrictions - ---- - -## Performance Tests - -### Background Fetch Performance -- [ ] Measure fetch success rate (target: 95%+) -- [ ] Measure average fetch time (target: <5 seconds) -- [ ] Test timeout handling (12 seconds) -- [ ] Verify retry logic efficiency - -### Notification Delivery Performance -- [ ] Measure delivery rate (target: 99%+) -- [ ] Measure average delivery time (target: <1 second) -- [ ] Test TTL compliance (target: 100%) -- [ ] Measure error rate (target: <1%) - -### Storage Performance -- [ ] Measure database operation times (target: <100ms) -- [ ] Test cache hit rate (target: 90%+) -- [ ] Verify storage efficiency -- [ ] Test concurrent access performance - ---- - -## Security Tests - -### Data Protection -- [ ] Verify encrypted storage (if enabled) -- [ ] Test HTTPS-only API calls -- [ ] Verify JWT token validation -- [ ] Check privacy settings compliance - -### Access Control -- [ ] Verify app-scoped database access -- [ ] Test system-level security -- [ ] Verify certificate pinning (if enabled) -- [ ] Check error handling for sensitive data - ---- - -## Monitoring and Observability Tests - -### Logging -- [ ] Verify structured logging format -- [ ] Check log level configuration -- [ ] Test log rotation and cleanup -- [ ] Verify consistent tagging - -### Metrics -- [ ] Test background fetch metrics -- [ ] Verify notification delivery metrics -- [ ] Check storage performance metrics -- [ ] Test error tracking - -### Health Checks -- [ ] Test database health checks -- [ ] Verify background task health -- [ ] Check network connectivity status -- [ ] Test platform-specific health indicators - ---- - -## Test Results Documentation - -### Test Execution Log -- [ ] Record test start time -- [ ] Document test environment details -- [ ] Record each test step execution -- [ ] Note any deviations or issues - -### Results Summary -- [ ] Count of tests passed/failed -- [ ] Performance metrics recorded -- [ ] Platform-specific results -- [ ] Overall verification status - -### Issues and Recommendations -- [ ] Document any failures or issues -- [ ] Note performance concerns -- [ ] Record platform-specific limitations -- [ ] Provide improvement recommendations - ---- - -## Post-Verification Actions - -### Cleanup -- [ ] Clear test notifications -- [ ] Reset test data -- [ ] Clean up log files -- [ ] Restore original settings - -### Documentation Updates -- [ ] Update verification report if needed -- [ ] Record any new issues discovered -- [ ] Update performance baselines -- [ ] Note any configuration changes - -### Team Communication -- [ ] Share results with development team -- [ ] Update project status -- [ ] Schedule next verification cycle -- [ ] Address any critical issues - ---- - -## Verification Schedule - -### Quarterly Verification (Recommended) -- **Q1**: January 27, 2025 -- **Q2**: April 27, 2025 -- **Q3**: July 27, 2025 -- **Q4**: October 27, 2025 - -### Trigger Events for Additional Verification -- [ ] Major platform updates (Android/iOS/Web) -- [ ] Significant code changes to core functionality -- [ ] New platform support added -- [ ] Performance issues reported -- [ ] Security vulnerabilities discovered - -### Verification Team -- **Primary**: Development Team Lead -- **Secondary**: QA Engineer -- **Reviewer**: Technical Architect -- **Approver**: Product Manager - ---- - -## Success Criteria - -### Minimum Acceptable Performance -- **Background Fetch Success Rate**: ≥90% -- **Notification Delivery Rate**: ≥95% -- **TTL Compliance**: 100% -- **Average Response Time**: <5 seconds - -### Critical Requirements -- [ ] All core functionality tests pass -- [ ] No security vulnerabilities -- [ ] Performance within acceptable limits -- [ ] Platform-specific requirements met - -### Verification Approval -- [ ] All tests completed successfully -- [ ] Performance criteria met -- [ ] Security requirements satisfied -- [ ] Documentation updated -- [ ] Team approval obtained - ---- - -**Next Verification Date**: April 27, 2025 -**Verification Lead**: Development Team -**Approval Required**: Technical Architect diff --git a/doc/VERIFICATION_REPORT.md b/doc/VERIFICATION_REPORT.md deleted file mode 100644 index b9ac9ed..0000000 --- a/doc/VERIFICATION_REPORT.md +++ /dev/null @@ -1,473 +0,0 @@ -# Daily Notification Plugin - Closed-App Verification Report - -**Author**: Matthew Raymer -**Version**: 1.0.0 -**Last Updated**: 2025-01-27 -**Status**: ✅ **VERIFIED** - All requirements met - ---- - -## Executive Summary - -This document provides comprehensive verification that the Daily Notification Plugin meets the core requirement: **"Local notifications read from device database with data populated by scheduled network fetches, all working when the app is closed."** - -### Verification Status -- ✅ **Android**: Fully implemented and verified -- ✅ **iOS**: Fully implemented and verified -- ⚠️ **Web**: Partially implemented (browser limitations) - ---- - -## Requirements Verification - -### 1. Local Notifications from Device Database - -**Requirement**: Notifications must be delivered from locally stored data, not requiring network at delivery time. - -**Implementation Status**: ✅ **VERIFIED** - -#### Android -- **Storage**: Room/SQLite with `ContentCache` table -- **Delivery**: `NotifyReceiver` reads from local database -- **Code Location**: `android/src/main/java/com/timesafari/dailynotification/NotifyReceiver.kt:98-121` - -```kotlin -val db = DailyNotificationDatabase.getDatabase(context) -val latestCache = db.contentCacheDao().getLatest() -// TTL-at-fire check -val now = System.currentTimeMillis() -val ttlExpiry = latestCache.fetchedAt + (latestCache.ttlSeconds * 1000L) -if (now > ttlExpiry) { - Log.i(TAG, "Content TTL expired, skipping notification") - return@launch -} -``` - -#### iOS -- **Storage**: Core Data/SQLite with `notif_contents` table -- **Delivery**: Background task handlers read from local database -- **Code Location**: `ios/Plugin/DailyNotificationBackgroundTasks.swift:67-80` - -```swift -// Get latest cached content -guard let latestContent = try await getLatestContent() else { - print("DNP-NOTIFY-SKIP: No cached content available") - return -} - -// Check TTL -if isContentExpired(content: latestContent) { - print("DNP-NOTIFY-SKIP-TTL: Content TTL expired, skipping notification") - return -} -``` - -#### Web -- **Storage**: IndexedDB with structured notification data -- **Delivery**: Service Worker reads from local storage -- **Code Location**: `src/web/sw.ts:220-489` - ---- - -### 2. Data Populated by Scheduled Network Fetches - -**Requirement**: Local database must be populated by background network requests when app is closed. - -**Implementation Status**: ✅ **VERIFIED** - -#### Android -- **Background Fetch**: WorkManager with `FetchWorker` -- **Scheduling**: T-lead prefetch (configurable minutes before delivery) -- **Code Location**: `src/android/DailyNotificationFetchWorker.java:67-104` - -```java -@Override -public Result doWork() { - try { - Log.d(TAG, "Starting background content fetch"); - - // Attempt to fetch content with timeout - NotificationContent content = fetchContentWithTimeout(); - - if (content != null) { - // Success - save content and schedule notification - handleSuccessfulFetch(content); - return Result.success(); - } else { - // Fetch failed - handle retry logic - return handleFailedFetch(retryCount, scheduledTime); - } - } catch (Exception e) { - Log.e(TAG, "Unexpected error during background fetch", e); - return handleFailedFetch(0, 0); - } -} -``` - -#### iOS -- **Background Fetch**: BGTaskScheduler with `DailyNotificationBackgroundTaskManager` -- **Scheduling**: T-lead prefetch with 12s timeout -- **Code Location**: `ios/Plugin/DailyNotificationBackgroundTaskManager.swift:94-150` - -```swift -func scheduleBackgroundTask(scheduledTime: Date, prefetchLeadMinutes: Int) { - let request = BGAppRefreshTaskRequest(identifier: Self.BACKGROUND_TASK_IDENTIFIER) - let prefetchTime = scheduledTime.addingTimeInterval(-TimeInterval(prefetchLeadMinutes * 60)) - request.earliestBeginDate = prefetchTime - - do { - try BGTaskScheduler.shared.submit(request) - print("\(Self.TAG): Background task scheduled for \(prefetchTime)") - } catch { - print("\(Self.TAG): Failed to schedule background task: \(error)") - } -} -``` - -#### Web -- **Background Fetch**: Service Worker with background sync -- **Scheduling**: Periodic sync with fallback mechanisms -- **Code Location**: `src/web/sw.ts:233-253` - ---- - -### 3. Works When App is Closed - -**Requirement**: All functionality must work when the application is completely closed. - -**Implementation Status**: ✅ **VERIFIED** - -#### Android -- **Delivery**: `NotifyReceiver` with AlarmManager -- **Background Fetch**: WorkManager with system-level scheduling -- **Reboot Recovery**: `BootReceiver` re-arms notifications after device restart -- **Code Location**: `android/src/main/java/com/timesafari/dailynotification/NotifyReceiver.kt:92-121` - -```kotlin -override fun onReceive(context: Context, intent: Intent?) { - Log.i(TAG, "Notification receiver triggered") - - CoroutineScope(Dispatchers.IO).launch { - try { - val db = DailyNotificationDatabase.getDatabase(context) - val latestCache = db.contentCacheDao().getLatest() - - if (latestCache == null) { - Log.w(TAG, "No cached content available for notification") - return@launch - } - - // TTL-at-fire check and notification delivery - // ... (continues with local delivery logic) - } catch (e: Exception) { - Log.e(TAG, "Error in notification receiver", e) - } - } -} -``` - -#### iOS -- **Delivery**: UNUserNotificationCenter with background task handlers -- **Background Fetch**: BGTaskScheduler with system-level scheduling -- **Force-quit Handling**: Pre-armed notifications still deliver -- **Code Location**: `ios/Plugin/DailyNotificationBackgroundTasks.swift:55-98` - -```swift -private func handleBackgroundNotify(task: BGProcessingTask) { - task.expirationHandler = { - print("DNP-NOTIFY-TIMEOUT: Background notify task expired") - task.setTaskCompleted(success: false) - } - - Task { - do { - // Get latest cached content - guard let latestContent = try await getLatestContent() else { - print("DNP-NOTIFY-SKIP: No cached content available") - task.setTaskCompleted(success: true) - return - } - - // Check TTL and show notification - if isContentExpired(content: latestContent) { - print("DNP-NOTIFY-SKIP-TTL: Content TTL expired, skipping notification") - task.setTaskCompleted(success: true) - return - } - - // Show notification - try await showNotification(content: latestContent) - task.setTaskCompleted(success: true) - - } catch { - print("DNP-NOTIFY-FAILURE: Notification failed: \(error)") - task.setTaskCompleted(success: false) - } - } -} -``` - -#### Web -- **Delivery**: Service Worker with Push API (limited by browser) -- **Background Fetch**: Service Worker with background sync -- **Limitations**: Browser-dependent, not fully reliable when closed -- **Code Location**: `src/web/sw.ts:255-268` - ---- - -## Test Scenarios Verification - -### 1. Background Fetch While App Closed - -**Test Case**: T-lead prefetch with app completely closed - -**Status**: ✅ **VERIFIED** -- Android: WorkManager executes background fetch -- iOS: BGTaskScheduler executes background fetch -- Web: Service Worker executes background fetch - -**Evidence**: -- Logs show `DNP-FETCH-SUCCESS` when app is closed -- Content stored in local database -- TTL validation at delivery time - -### 2. Local Notification Delivery from Cached Data - -**Test Case**: Notification delivery with no network connectivity - -**Status**: ✅ **VERIFIED** -- Android: `NotifyReceiver` delivers from cached content -- iOS: Background task delivers from cached content -- Web: Service Worker delivers from IndexedDB - -**Evidence**: -- Notifications delivered without network -- Content matches cached data -- TTL enforcement prevents expired content - -### 3. TTL Enforcement at Delivery Time - -**Test Case**: Expired content should not be delivered - -**Status**: ✅ **VERIFIED** -- All platforms check TTL at delivery time -- Expired content is skipped with proper logging -- No network required for TTL validation - -**Evidence**: -- Logs show `DNP-NOTIFY-SKIP-TTL` for expired content -- Notifications not delivered when TTL expired -- Fresh content delivered when TTL valid - -### 4. Reboot Recovery and Rescheduling - -**Test Case**: Plugin recovers after device reboot - -**Status**: ✅ **VERIFIED** -- Android: `BootReceiver` re-arms notifications -- iOS: App restart re-registers background tasks -- Web: Service Worker re-registers on browser restart - -**Evidence**: -- Notifications re-scheduled after reboot -- Background fetch tasks re-registered -- Rolling window maintained - -### 5. Network Failure Handling - -**Test Case**: Network failure with cached content fallback - -**Status**: ✅ **VERIFIED** -- Background fetch fails gracefully -- Cached content used for delivery -- Circuit breaker prevents excessive retries - -**Evidence**: -- Logs show `DNP-FETCH-FAILURE` on network issues -- Notifications still delivered from cache -- No infinite retry loops - -### 6. Timezone/DST Changes - -**Test Case**: Schedule recalculation on timezone change - -**Status**: ✅ **VERIFIED** -- Schedules recalculated on timezone change -- Background tasks re-scheduled -- Wall-clock alignment maintained - -**Evidence**: -- Next run times updated after timezone change -- Background fetch tasks re-scheduled -- Notification delivery times adjusted - -### 7. Battery Optimization (Android) - -**Test Case**: Exact alarm permissions and battery optimization - -**Status**: ✅ **VERIFIED** -- Exact alarm permission handling -- Fallback to approximate timing -- Battery optimization compliance - -**Evidence**: -- Notifications delivered within ±1m with exact permission -- Notifications delivered within ±10m without exact permission -- Battery optimization settings respected - -### 8. Background App Refresh (iOS) - -**Test Case**: iOS background app refresh behavior - -**Status**: ✅ **VERIFIED** -- Background app refresh setting respected -- Fallback to cached content when disabled -- BGTaskScheduler budget management - -**Evidence**: -- Background fetch occurs when enabled -- Cached content used when disabled -- Task budget properly managed - ---- - -## Performance Metrics - -### Background Fetch Performance -- **Success Rate**: 95%+ (network dependent) -- **Average Fetch Time**: 2-5 seconds -- **Timeout Handling**: 12 seconds with graceful failure -- **Retry Logic**: Exponential backoff with circuit breaker - -### Notification Delivery Performance -- **Delivery Rate**: 99%+ (platform dependent) -- **Average Delivery Time**: <1 second -- **TTL Compliance**: 100% (no expired content delivered) -- **Error Rate**: <1% (mostly platform-specific issues) - -### Storage Performance -- **Database Operations**: <100ms for read/write -- **Cache Hit Rate**: 90%+ for recent content -- **Storage Efficiency**: Minimal disk usage with cleanup -- **Concurrency**: WAL mode for safe concurrent access - ---- - -## Platform-Specific Considerations - -### Android -- **Exact Alarms**: Requires `SCHEDULE_EXACT_ALARM` permission -- **Battery Optimization**: May affect background execution -- **WorkManager**: Reliable background task execution -- **Room Database**: Efficient local storage with type safety - -### iOS -- **Background App Refresh**: User-controlled setting -- **BGTaskScheduler**: System-managed background execution -- **Force Quit**: No background execution after user termination -- **Core Data**: Efficient local storage with migration support - -### Web -- **Service Worker**: Browser-dependent background execution -- **Push API**: Limited reliability when browser closed -- **IndexedDB**: Persistent local storage -- **Background Sync**: Fallback mechanism for offline scenarios - ---- - -## Security Considerations - -### Data Protection -- **Local Storage**: Encrypted database support (SQLCipher) -- **Network Security**: HTTPS-only API calls -- **Authentication**: JWT token validation -- **Privacy**: User-controlled visibility settings - -### Access Control -- **Database Access**: App-scoped permissions -- **Background Tasks**: System-level security -- **Network Requests**: Certificate pinning support -- **Error Handling**: No sensitive data in logs - ---- - -## Monitoring and Observability - -### Logging -- **Structured Logging**: JSON format with timestamps -- **Log Levels**: Debug, Info, Warn, Error -- **Tagging**: Consistent tag format (`DNP-*`) -- **Rotation**: Automatic log cleanup - -### Metrics -- **Background Fetch**: Success rate, duration, error count -- **Notification Delivery**: Delivery rate, TTL compliance -- **Storage**: Database size, cache hit rate -- **Performance**: Response times, memory usage - -### Health Checks -- **Database Health**: Connection status, migration status -- **Background Tasks**: Registration status, execution status -- **Network**: Connectivity status, API health -- **Platform**: Permission status, system health - ---- - -## Known Limitations - -### Web Platform -- **Browser Dependencies**: Service Worker support varies -- **Background Execution**: Limited when browser closed -- **Push Notifications**: Requires user permission -- **Storage Limits**: IndexedDB quota restrictions - -### Platform Constraints -- **Android**: Battery optimization may affect execution -- **iOS**: Background app refresh user-controlled -- **Web**: Browser security model limitations - -### Network Dependencies -- **API Availability**: External service dependencies -- **Network Quality**: Poor connectivity affects fetch success -- **Rate Limiting**: API rate limits may affect frequency -- **Authentication**: Token expiration handling - ---- - -## Recommendations - -### Immediate Actions -1. **Web Platform**: Implement fallback mechanisms for browser limitations -2. **Monitoring**: Add comprehensive health check endpoints -3. **Documentation**: Update integration guide with verification results -4. **Testing**: Add automated verification tests to CI/CD pipeline - -### Future Enhancements -1. **Analytics**: Add detailed performance analytics -2. **Optimization**: Implement adaptive scheduling based on usage patterns -3. **Security**: Add certificate pinning for API calls -4. **Reliability**: Implement redundant storage mechanisms - ---- - -## Conclusion - -The Daily Notification Plugin **successfully meets all core requirements** for closed-app notification functionality: - -✅ **Local notifications from device database** - Implemented across all platforms -✅ **Data populated by scheduled network fetches** - Background tasks working reliably -✅ **Works when app is closed** - Platform-specific mechanisms in place - -The implementation follows best practices for: -- **Reliability**: TTL enforcement, error handling, fallback mechanisms -- **Performance**: Efficient storage, optimized background tasks -- **Security**: Encrypted storage, secure network communication -- **Observability**: Comprehensive logging and monitoring - -**Verification Status**: ✅ **COMPLETE** - Ready for production use - ---- - -**Next Review Date**: 2025-04-27 (Quarterly) -**Reviewer**: Development Team -**Approval**: Pending team review diff --git a/doc/enterprise-callback-examples.md b/doc/enterprise-callback-examples.md deleted file mode 100644 index a2a6e3f..0000000 --- a/doc/enterprise-callback-examples.md +++ /dev/null @@ -1,1083 +0,0 @@ -# Enterprise Callback Examples - -**Author**: Matthew Raymer -**Version**: 2.0.0 -**Created**: 2025-09-22 09:22:32 UTC -**Last Updated**: 2025-09-22 09:22:32 UTC - -## Overview - -This document provides comprehensive examples of enterprise-grade callback implementations for the Daily Notification Plugin, covering analytics, CRM integration, database operations, and monitoring systems. - -## Table of Contents - -1. [Analytics Integration](#analytics-integration) -2. [CRM Integration](#crm-integration) -3. [Database Operations](#database-operations) -4. [Monitoring & Alerting](#monitoring--alerting) -5. [Multi-Service Orchestration](#multi-service-orchestration) -6. [Error Handling Patterns](#error-handling-patterns) -7. [Performance Optimization](#performance-optimization) -8. [Security Best Practices](#security-best-practices) - -## Analytics Integration - -### Google Analytics 4 - -```typescript -import { DailyNotification, CallbackEvent } from '@timesafari/daily-notification-plugin'; - -class GoogleAnalyticsCallback { - private measurementId: string; - private apiSecret: string; - - constructor(measurementId: string, apiSecret: string) { - this.measurementId = measurementId; - this.apiSecret = apiSecret; - } - - async register(): Promise { - await DailyNotification.registerCallback('ga4-analytics', { - kind: 'http', - target: `https://www.google-analytics.com/mp/collect?measurement_id=${this.measurementId}&api_secret=${this.apiSecret}`, - headers: { - 'Content-Type': 'application/json' - } - }); - } - - async handleCallback(event: CallbackEvent): Promise { - const payload = { - client_id: this.generateClientId(), - events: [{ - name: this.mapEventName(event.type), - params: { - event_category: 'daily_notification', - event_label: event.payload?.source || 'unknown', - value: this.calculateEventValue(event), - custom_parameter_1: event.id, - custom_parameter_2: event.at - } - }] - }; - - await this.sendToGA4(payload); - } - - private mapEventName(eventType: string): string { - const eventMap: Record = { - 'onFetchSuccess': 'content_fetch_success', - 'onFetchFailure': 'content_fetch_failure', - 'onNotifyDelivered': 'notification_delivered', - 'onNotifyClicked': 'notification_clicked', - 'onNotifyDismissed': 'notification_dismissed' - }; - return eventMap[eventType] || 'unknown_event'; - } - - private calculateEventValue(event: CallbackEvent): number { - // Calculate engagement value based on event type - const valueMap: Record = { - 'onFetchSuccess': 1, - 'onNotifyDelivered': 2, - 'onNotifyClicked': 5, - 'onNotifyDismissed': 0 - }; - return valueMap[event.type] || 0; - } - - private generateClientId(): string { - // Generate or retrieve client ID for GA4 - return `client_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - } - - private async sendToGA4(payload: any): Promise { - // Implementation would send to GA4 Measurement Protocol - console.log('Sending to GA4:', payload); - } -} - -// Usage -const ga4Callback = new GoogleAnalyticsCallback('G-XXXXXXXXXX', 'your-api-secret'); -await ga4Callback.register(); -``` - -### Mixpanel Integration - -```typescript -class MixpanelCallback { - private projectToken: string; - private baseUrl: string; - - constructor(projectToken: string) { - this.projectToken = projectToken; - this.baseUrl = 'https://api.mixpanel.com'; - } - - async register(): Promise { - await DailyNotification.registerCallback('mixpanel-analytics', { - kind: 'http', - target: `${this.baseUrl}/track`, - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${this.projectToken}` - } - }); - } - - async handleCallback(event: CallbackEvent): Promise { - const eventData = { - event: this.mapEventName(event.type), - properties: { - distinct_id: this.getDistinctId(), - time: Math.floor(event.at / 1000), // Unix timestamp - $app_version: '2.0.0', - $os: this.getPlatform(), - notification_id: event.id, - content_source: event.payload?.source, - ttl_seconds: event.payload?.ttlSeconds, - fetch_duration: event.payload?.duration, - success: event.type.includes('Success') - } - }; - - await this.sendToMixpanel(eventData); - } - - private mapEventName(eventType: string): string { - return eventType.replace('on', '').toLowerCase(); - } - - private getDistinctId(): string { - // Generate or retrieve user ID - return `user_${Date.now()}`; - } - - private getPlatform(): string { - // Detect platform (Android, iOS, Web) - return 'web'; // Simplified for example - } - - private async sendToMixpanel(data: any): Promise { - // Implementation would send to Mixpanel API - console.log('Sending to Mixpanel:', data); - } -} -``` - -## CRM Integration - -### Salesforce Integration - -```typescript -class SalesforceCallback { - private accessToken: string; - private instanceUrl: string; - - constructor(accessToken: string, instanceUrl: string) { - this.accessToken = accessToken; - this.instanceUrl = instanceUrl; - } - - async register(): Promise { - await DailyNotification.registerCallback('salesforce-crm', { - kind: 'http', - target: `${this.instanceUrl}/services/data/v58.0/sobjects/Notification_Event__c/`, - headers: { - 'Authorization': `Bearer ${this.accessToken}`, - 'Content-Type': 'application/json' - } - }); - } - - async handleCallback(event: CallbackEvent): Promise { - const salesforceRecord = { - Name: `Notification_${event.id}`, - Event_Type__c: event.type, - Event_Timestamp__c: new Date(event.at).toISOString(), - Notification_ID__c: event.id, - Content_Source__c: event.payload?.source, - Success__c: event.type.includes('Success'), - Error_Message__c: event.payload?.error || null, - User_Agent__c: this.getUserAgent(), - Platform__c: this.getPlatform() - }; - - await this.createSalesforceRecord(salesforceRecord); - } - - private getUserAgent(): string { - return navigator.userAgent || 'Unknown'; - } - - private getPlatform(): string { - // Detect platform - return 'Web'; // Simplified for example - } - - private async createSalesforceRecord(record: any): Promise { - // Implementation would create Salesforce record - console.log('Creating Salesforce record:', record); - } -} -``` - -### HubSpot Integration - -```typescript -class HubSpotCallback { - private apiKey: string; - private baseUrl: string; - - constructor(apiKey: string) { - this.apiKey = apiKey; - this.baseUrl = 'https://api.hubapi.com'; - } - - async register(): Promise { - await DailyNotification.registerCallback('hubspot-crm', { - kind: 'http', - target: `${this.baseUrl}/crm/v3/objects/notifications`, - headers: { - 'Authorization': `Bearer ${this.apiKey}`, - 'Content-Type': 'application/json' - } - }); - } - - async handleCallback(event: CallbackEvent): Promise { - const hubspotRecord = { - properties: { - notification_id: event.id, - event_type: event.type, - event_timestamp: event.at, - content_source: event.payload?.source, - success: event.type.includes('Success'), - error_message: event.payload?.error || null, - platform: this.getPlatform(), - user_agent: this.getUserAgent() - } - }; - - await this.createHubSpotRecord(hubspotRecord); - } - - private getPlatform(): string { - return 'Web'; - } - - private getUserAgent(): string { - return navigator.userAgent || 'Unknown'; - } - - private async createHubSpotRecord(record: any): Promise { - // Implementation would create HubSpot record - console.log('Creating HubSpot record:', record); - } -} -``` - -## Database Operations - -### PostgreSQL Integration - -```typescript -class PostgreSQLCallback { - private connectionString: string; - - constructor(connectionString: string) { - this.connectionString = connectionString; - } - - async register(): Promise { - await DailyNotification.registerCallback('postgres-db', { - kind: 'http', - target: 'https://your-api.example.com/notifications', - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer your-api-token' - } - }); - } - - async handleCallback(event: CallbackEvent): Promise { - const dbRecord = { - notification_id: event.id, - event_type: event.type, - event_timestamp: new Date(event.at), - content_source: event.payload?.source, - success: event.type.includes('Success'), - error_message: event.payload?.error || null, - platform: this.getPlatform(), - user_agent: this.getUserAgent(), - ttl_seconds: event.payload?.ttlSeconds, - fetch_duration: event.payload?.duration - }; - - await this.insertRecord(dbRecord); - } - - private async insertRecord(record: any): Promise { - // Implementation would insert into PostgreSQL - console.log('Inserting PostgreSQL record:', record); - } - - private getPlatform(): string { - return 'Web'; - } - - private getUserAgent(): string { - return navigator.userAgent || 'Unknown'; - } -} -``` - -### MongoDB Integration - -```typescript -class MongoDBCallback { - private connectionString: string; - - constructor(connectionString: string) { - this.connectionString = connectionString; - } - - async register(): Promise { - await DailyNotification.registerCallback('mongodb-analytics', { - kind: 'http', - target: 'https://your-api.example.com/mongodb/notifications', - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer your-api-token' - } - }); - } - - async handleCallback(event: CallbackEvent): Promise { - const mongoDocument = { - _id: event.id, - eventType: event.type, - timestamp: new Date(event.at), - payload: { - source: event.payload?.source, - success: event.type.includes('Success'), - error: event.payload?.error || null, - platform: this.getPlatform(), - userAgent: this.getUserAgent(), - ttlSeconds: event.payload?.ttlSeconds, - duration: event.payload?.duration - }, - metadata: { - createdAt: new Date(), - version: '2.0.0' - } - }; - - await this.insertDocument(mongoDocument); - } - - private async insertDocument(doc: any): Promise { - // Implementation would insert into MongoDB - console.log('Inserting MongoDB document:', doc); - } - - private getPlatform(): string { - return 'Web'; - } - - private getUserAgent(): string { - return navigator.userAgent || 'Unknown'; - } -} -``` - -## Monitoring & Alerting - -### Datadog Integration - -```typescript -class DatadogCallback { - private apiKey: string; - private appKey: string; - - constructor(apiKey: string, appKey: string) { - this.apiKey = apiKey; - this.appKey = appKey; - } - - async register(): Promise { - await DailyNotification.registerCallback('datadog-monitoring', { - kind: 'http', - target: 'https://api.datadoghq.com/api/v1/events', - headers: { - 'Content-Type': 'application/json', - 'DD-API-KEY': this.apiKey, - 'DD-APPLICATION-KEY': this.appKey - } - }); - } - - async handleCallback(event: CallbackEvent): Promise { - const datadogEvent = { - title: `Daily Notification ${event.type}`, - text: this.formatEventText(event), - priority: this.getPriority(event.type), - alert_type: this.getAlertType(event.type), - tags: [ - `platform:${this.getPlatform()}`, - `event_type:${event.type}`, - `source:${event.payload?.source || 'unknown'}`, - `success:${event.type.includes('Success')}` - ], - source_type_name: 'daily_notification_plugin' - }; - - await this.sendToDatadog(datadogEvent); - } - - private formatEventText(event: CallbackEvent): string { - return `Notification ${event.id} - ${event.type} at ${new Date(event.at).toISOString()}`; - } - - private getPriority(eventType: string): string { - if (eventType.includes('Failure')) return 'high'; - if (eventType.includes('Success')) return 'normal'; - return 'low'; - } - - private getAlertType(eventType: string): string { - if (eventType.includes('Failure')) return 'error'; - if (eventType.includes('Success')) return 'success'; - return 'info'; - } - - private getPlatform(): string { - return 'Web'; - } - - private async sendToDatadog(event: any): Promise { - // Implementation would send to Datadog - console.log('Sending to Datadog:', event); - } -} -``` - -### New Relic Integration - -```typescript -class NewRelicCallback { - private licenseKey: string; - - constructor(licenseKey: string) { - this.licenseKey = licenseKey; - } - - async register(): Promise { - await DailyNotification.registerCallback('newrelic-monitoring', { - kind: 'http', - target: 'https://metric-api.newrelic.com/metric/v1', - headers: { - 'Content-Type': 'application/json', - 'Api-Key': this.licenseKey - } - }); - } - - async handleCallback(event: CallbackEvent): Promise { - const newRelicMetric = { - metrics: [{ - name: `daily_notification.${event.type}`, - type: 'count', - value: 1, - timestamp: Math.floor(event.at / 1000), - attributes: { - notification_id: event.id, - platform: this.getPlatform(), - source: event.payload?.source || 'unknown', - success: event.type.includes('Success'), - error: event.payload?.error || null - } - }] - }; - - await this.sendToNewRelic(newRelicMetric); - } - - private getPlatform(): string { - return 'Web'; - } - - private async sendToNewRelic(metric: any): Promise { - // Implementation would send to New Relic - console.log('Sending to New Relic:', metric); - } -} -``` - -## Multi-Service Orchestration - -### Event Bus Integration - -```typescript -class EventBusCallback { - private eventBusUrl: string; - private apiKey: string; - - constructor(eventBusUrl: string, apiKey: string) { - this.eventBusUrl = eventBusUrl; - this.apiKey = apiKey; - } - - async register(): Promise { - await DailyNotification.registerCallback('event-bus', { - kind: 'http', - target: `${this.eventBusUrl}/events`, - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${this.apiKey}` - } - }); - } - - async handleCallback(event: CallbackEvent): Promise { - const eventBusMessage = { - id: event.id, - type: `daily_notification.${event.type}`, - timestamp: event.at, - source: 'daily_notification_plugin', - version: '2.0.0', - data: { - notification_id: event.id, - event_type: event.type, - payload: event.payload, - platform: this.getPlatform(), - user_agent: this.getUserAgent() - }, - metadata: { - correlation_id: this.generateCorrelationId(), - trace_id: this.generateTraceId() - } - }; - - await this.publishToEventBus(eventBusMessage); - } - - private generateCorrelationId(): string { - return `corr_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - } - - private generateTraceId(): string { - return `trace_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - } - - private getPlatform(): string { - return 'Web'; - } - - private getUserAgent(): string { - return navigator.userAgent || 'Unknown'; - } - - private async publishToEventBus(message: any): Promise { - // Implementation would publish to event bus - console.log('Publishing to event bus:', message); - } -} -``` - -### Apache Kafka Integration - -```typescript -class KafkaCallback { - private kafkaUrl: string; - private topic: string; - - constructor(kafkaUrl: string, topic: string) { - this.kafkaUrl = kafkaUrl; - this.topic = topic; - } - - async register(): Promise { - await DailyNotification.registerCallback('kafka-streams', { - kind: 'http', - target: `${this.kafkaUrl}/topics/${this.topic}`, - headers: { - 'Content-Type': 'application/vnd.kafka.json.v2+json' - } - }); - } - - async handleCallback(event: CallbackEvent): Promise { - const kafkaMessage = { - records: [{ - key: event.id, - value: { - notification_id: event.id, - event_type: event.type, - timestamp: event.at, - payload: event.payload, - platform: this.getPlatform(), - user_agent: this.getUserAgent(), - metadata: { - version: '2.0.0', - source: 'daily_notification_plugin' - } - } - }] - }; - - await this.sendToKafka(kafkaMessage); - } - - private getPlatform(): string { - return 'Web'; - } - - private getUserAgent(): string { - return navigator.userAgent || 'Unknown'; - } - - private async sendToKafka(message: any): Promise { - // Implementation would send to Kafka - console.log('Sending to Kafka:', message); - } -} -``` - -## Error Handling Patterns - -### Circuit Breaker Implementation - -```typescript -class CircuitBreakerCallback { - private failureThreshold: number = 5; - private timeout: number = 60000; // 1 minute - private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED'; - private failureCount: number = 0; - private lastFailureTime: number = 0; - - async register(): Promise { - await DailyNotification.registerCallback('circuit-breaker', { - kind: 'local', - target: 'circuitBreakerHandler' - }); - } - - async handleCallback(event: CallbackEvent): Promise { - if (this.state === 'OPEN') { - if (Date.now() - this.lastFailureTime > this.timeout) { - this.state = 'HALF_OPEN'; - console.log('Circuit breaker transitioning to HALF_OPEN'); - } else { - console.log('Circuit breaker is OPEN, skipping callback'); - return; - } - } - - try { - await this.executeCallback(event); - this.onSuccess(); - } catch (error) { - this.onFailure(); - throw error; - } - } - - private async executeCallback(event: CallbackEvent): Promise { - // Your callback logic here - console.log('Executing callback:', event); - } - - private onSuccess(): void { - this.failureCount = 0; - this.state = 'CLOSED'; - } - - private onFailure(): void { - this.failureCount++; - this.lastFailureTime = Date.now(); - - if (this.failureCount >= this.failureThreshold) { - this.state = 'OPEN'; - console.log(`Circuit breaker opened after ${this.failureCount} failures`); - } - } -} -``` - -### Retry with Exponential Backoff - -```typescript -class RetryCallback { - private maxRetries: number = 5; - private baseDelay: number = 1000; // 1 second - private maxDelay: number = 60000; // 1 minute - - async register(): Promise { - await DailyNotification.registerCallback('retry-handler', { - kind: 'local', - target: 'retryHandler' - }); - } - - async handleCallback(event: CallbackEvent): Promise { - let attempt = 0; - let delay = this.baseDelay; - - while (attempt < this.maxRetries) { - try { - await this.executeCallback(event); - console.log(`Callback succeeded on attempt ${attempt + 1}`); - return; - } catch (error) { - attempt++; - console.log(`Callback failed on attempt ${attempt}:`, error); - - if (attempt >= this.maxRetries) { - console.log('Max retries exceeded, giving up'); - throw error; - } - - // Wait before retry - await this.sleep(delay); - delay = Math.min(delay * 2, this.maxDelay); - } - } - } - - private async executeCallback(event: CallbackEvent): Promise { - // Your callback logic here - console.log('Executing callback:', event); - } - - private sleep(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); - } -} -``` - -## Performance Optimization - -### Batch Processing - -```typescript -class BatchCallback { - private batchSize: number = 10; - private batchTimeout: number = 5000; // 5 seconds - private batch: CallbackEvent[] = []; - private batchTimer: NodeJS.Timeout | null = null; - - async register(): Promise { - await DailyNotification.registerCallback('batch-processor', { - kind: 'local', - target: 'batchHandler' - }); - } - - async handleCallback(event: CallbackEvent): Promise { - this.batch.push(event); - - if (this.batch.length >= this.batchSize) { - await this.processBatch(); - } else if (!this.batchTimer) { - this.batchTimer = setTimeout(() => { - this.processBatch(); - }, this.batchTimeout); - } - } - - private async processBatch(): Promise { - if (this.batch.length === 0) return; - - const currentBatch = [...this.batch]; - this.batch = []; - - if (this.batchTimer) { - clearTimeout(this.batchTimer); - this.batchTimer = null; - } - - try { - await this.sendBatch(currentBatch); - console.log(`Processed batch of ${currentBatch.length} events`); - } catch (error) { - console.error('Batch processing failed:', error); - // Re-queue failed events - this.batch.unshift(...currentBatch); - } - } - - private async sendBatch(events: CallbackEvent[]): Promise { - // Implementation would send batch to external service - console.log('Sending batch:', events); - } -} -``` - -### Rate Limiting - -```typescript -class RateLimitedCallback { - private requestsPerMinute: number = 60; - private requests: number[] = []; - - async register(): Promise { - await DailyNotification.registerCallback('rate-limited', { - kind: 'local', - target: 'rateLimitedHandler' - }); - } - - async handleCallback(event: CallbackEvent): Promise { - if (!this.isRateLimited()) { - await this.executeCallback(event); - this.recordRequest(); - } else { - console.log('Rate limit exceeded, skipping callback'); - } - } - - private isRateLimited(): boolean { - const now = Date.now(); - const oneMinuteAgo = now - 60000; - - // Remove old requests - this.requests = this.requests.filter(time => time > oneMinuteAgo); - - return this.requests.length >= this.requestsPerMinute; - } - - private recordRequest(): void { - this.requests.push(Date.now()); - } - - private async executeCallback(event: CallbackEvent): Promise { - // Your callback logic here - console.log('Executing rate-limited callback:', event); - } -} -``` - -## Security Best Practices - -### Authentication & Authorization - -```typescript -class SecureCallback { - private apiKey: string; - private secretKey: string; - - constructor(apiKey: string, secretKey: string) { - this.apiKey = apiKey; - this.secretKey = secretKey; - } - - async register(): Promise { - const signature = this.generateSignature(); - - await DailyNotification.registerCallback('secure-callback', { - kind: 'http', - target: 'https://your-api.example.com/secure-endpoint', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${this.apiKey}`, - 'X-Signature': signature, - 'X-Timestamp': Date.now().toString() - } - }); - } - - private generateSignature(): string { - const timestamp = Date.now().toString(); - const message = `${this.apiKey}:${timestamp}`; - - // In a real implementation, you'd use HMAC-SHA256 - return btoa(message + this.secretKey); - } - - async handleCallback(event: CallbackEvent): Promise { - const securePayload = { - notification_id: event.id, - event_type: event.type, - timestamp: event.at, - payload: this.sanitizePayload(event.payload), - platform: this.getPlatform(), - user_agent: this.getUserAgent() - }; - - await this.sendSecureRequest(securePayload); - } - - private sanitizePayload(payload: any): any { - // Remove sensitive data - const sanitized = { ...payload }; - delete sanitized.password; - delete sanitized.token; - delete sanitized.secret; - return sanitized; - } - - private getPlatform(): string { - return 'Web'; - } - - private getUserAgent(): string { - return navigator.userAgent || 'Unknown'; - } - - private async sendSecureRequest(payload: any): Promise { - // Implementation would send secure request - console.log('Sending secure request:', payload); - } -} -``` - -### Data Encryption - -```typescript -class EncryptedCallback { - private encryptionKey: string; - - constructor(encryptionKey: string) { - this.encryptionKey = encryptionKey; - } - - async register(): Promise { - await DailyNotification.registerCallback('encrypted-callback', { - kind: 'local', - target: 'encryptedHandler' - }); - } - - async handleCallback(event: CallbackEvent): Promise { - const encryptedPayload = this.encryptPayload(event.payload); - - const secureEvent = { - ...event, - payload: encryptedPayload - }; - - await this.sendEncryptedEvent(secureEvent); - } - - private encryptPayload(payload: any): string { - // In a real implementation, you'd use proper encryption - const jsonString = JSON.stringify(payload); - return btoa(jsonString + this.encryptionKey); - } - - private async sendEncryptedEvent(event: any): Promise { - // Implementation would send encrypted event - console.log('Sending encrypted event:', event); - } -} -``` - -## Usage Examples - -### Complete Enterprise Setup - -```typescript -import { DailyNotification } from '@timesafari/daily-notification-plugin'; - -class EnterpriseNotificationManager { - private callbacks: any[] = []; - - async initialize(): Promise { - // Register all enterprise callbacks - await this.registerAnalyticsCallbacks(); - await this.registerCRMCallbacks(); - await this.registerDatabaseCallbacks(); - await this.registerMonitoringCallbacks(); - - // Configure dual scheduling - await this.configureDualScheduling(); - } - - private async registerAnalyticsCallbacks(): Promise { - const ga4Callback = new GoogleAnalyticsCallback('G-XXXXXXXXXX', 'your-api-secret'); - await ga4Callback.register(); - this.callbacks.push(ga4Callback); - - const mixpanelCallback = new MixpanelCallback('your-project-token'); - await mixpanelCallback.register(); - this.callbacks.push(mixpanelCallback); - } - - private async registerCRMCallbacks(): Promise { - const salesforceCallback = new SalesforceCallback('your-access-token', 'your-instance-url'); - await salesforceCallback.register(); - this.callbacks.push(salesforceCallback); - - const hubspotCallback = new HubSpotCallback('your-api-key'); - await hubspotCallback.register(); - this.callbacks.push(hubspotCallback); - } - - private async registerDatabaseCallbacks(): Promise { - const postgresCallback = new PostgreSQLCallback('your-connection-string'); - await postgresCallback.register(); - this.callbacks.push(postgresCallback); - - const mongoCallback = new MongoDBCallback('your-connection-string'); - await mongoCallback.register(); - this.callbacks.push(mongoCallback); - } - - private async registerMonitoringCallbacks(): Promise { - const datadogCallback = new DatadogCallback('your-api-key', 'your-app-key'); - await datadogCallback.register(); - this.callbacks.push(datadogCallback); - - const newrelicCallback = new NewRelicCallback('your-license-key'); - await newrelicCallback.register(); - this.callbacks.push(newrelicCallback); - } - - private async configureDualScheduling(): Promise { - const config = { - contentFetch: { - schedule: '0 8 * * *', // 8 AM - ttlSeconds: 3600, // 1 hour TTL - source: 'api', - url: 'https://api.example.com/daily-content' - }, - userNotification: { - schedule: '0 9 * * *', // 9 AM - title: 'Daily Update', - body: 'Your daily content is ready', - actions: [ - { id: 'view', title: 'View' }, - { id: 'dismiss', title: 'Dismiss' } - ] - } - }; - - await DailyNotification.scheduleDualNotification(config); - } - - async getStatus(): Promise { - return await DailyNotification.getDualScheduleStatus(); - } - - async getCallbacks(): Promise { - return await DailyNotification.getRegisteredCallbacks(); - } -} - -// Usage -const enterpriseManager = new EnterpriseNotificationManager(); -await enterpriseManager.initialize(); - -// Monitor status -const status = await enterpriseManager.getStatus(); -console.log('Enterprise status:', status); - -// List callbacks -const callbacks = await enterpriseManager.getCallbacks(); -console.log('Registered callbacks:', callbacks); -``` - ---- - -**Next Steps**: After implementing enterprise callbacks, review the [Migration Guide](./migration-guide.md) for platform-specific setup instructions. diff --git a/examples/advanced-usage.ts b/examples/advanced-usage.ts deleted file mode 100644 index 76e8dcc..0000000 --- a/examples/advanced-usage.ts +++ /dev/null @@ -1,259 +0,0 @@ -/** - * Advanced usage examples for the Daily Notification plugin - * Demonstrates complex scenarios and best practices - */ - -import { registerPlugin } from '@capacitor/core'; -import { DailyNotificationPlugin, NotificationOptions, NotificationSettings } from '../src/definitions'; - -const DailyNotification = registerPlugin('DailyNotification'); - -/** - * Example of handling multiple notification schedules - */ -async function handleMultipleSchedules() { - try { - // Morning notification - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/morning-content', - time: '08:00', - title: 'Morning Briefing', - body: 'Your morning briefing is ready', - priority: 'high' - }); - - // Evening notification - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/evening-content', - time: '20:00', - title: 'Evening Summary', - body: 'Your daily summary is ready', - priority: 'normal' - }); - - console.log('Multiple schedules set up successfully'); - } catch (error) { - console.error('Failed to setup multiple schedules:', error); - } -} - -/** - * Example of handling timezone changes - */ -async function handleTimezoneChanges() { - try { - // Get user's timezone - const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; - - // Schedule notification with timezone consideration - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/timezone-aware-content', - time: '09:00', - title: 'Timezone-Aware Update', - body: 'Your timezone-aware content is ready', - timezone: userTimezone - }); - - // Listen for timezone changes - window.addEventListener('timezonechange', async () => { - const newTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; - await DailyNotification.updateSettings({ - timezone: newTimezone - }); - }); - } catch (error) { - console.error('Failed to handle timezone changes:', error); - } -} - -/** - * Example of implementing retry logic with exponential backoff - */ -async function implementRetryLogic() { - const maxRetries = 3; - const baseDelay = 1000; // 1 second - - async function fetchWithRetry(url: string, retryCount = 0): Promise { - try { - const response = await fetch(url); - if (!response.ok) throw new Error('Network response was not ok'); - return response; - } catch (error) { - if (retryCount >= maxRetries) throw error; - - const delay = baseDelay * Math.pow(2, retryCount); - await new Promise(resolve => setTimeout(resolve, delay)); - return fetchWithRetry(url, retryCount + 1); - } - } - - try { - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/unstable-content', - time: '10:00', - title: 'Retry-Enabled Update', - body: 'Your content with retry logic is ready', - retryCount: maxRetries, - retryInterval: baseDelay / 1000, // Convert to seconds - contentHandler: async (response) => { - const data = await response.json(); - return { - title: data.title, - body: data.content, - data: data.metadata - }; - } - }); - } catch (error) { - console.error('Failed to implement retry logic:', error); - } -} - -/** - * Example of implementing offline support - */ -async function implementOfflineSupport() { - try { - // Check if we're online - const isOnline = navigator.onLine; - - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/offline-aware-content', - time: '11:00', - title: 'Offline-Aware Update', - body: 'Your offline-aware content is ready', - offlineFallback: true, - cacheDuration: 24, // Cache for 24 hours - contentHandler: async (response) => { - const data = await response.json(); - - // Store in IndexedDB for offline access - if ('indexedDB' in window) { - const db = await openDB('notificationCache', 1, { - upgrade(db) { - db.createObjectStore('content'); - } - }); - await db.put('content', data, 'latest'); - } - - return { - title: data.title, - body: data.content, - data: data.metadata - }; - } - }); - - // Listen for online/offline events - window.addEventListener('online', () => { - console.log('Back online, syncing content...'); - // Trigger content sync - }); - - window.addEventListener('offline', () => { - console.log('Offline, using cached content...'); - // Use cached content - }); - } catch (error) { - console.error('Failed to implement offline support:', error); - } -} - -/** - * Example of implementing user preferences - */ -async function implementUserPreferences() { - try { - // Get user preferences from storage - const preferences = { - notificationTime: '12:00', - soundEnabled: true, - priority: 'high' as const, - categories: ['news', 'weather', 'tasks'] - }; - - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/personalized-content', - time: preferences.notificationTime, - title: 'Personalized Update', - body: 'Your personalized content is ready', - sound: preferences.soundEnabled, - priority: preferences.priority, - headers: { - 'X-User-Categories': preferences.categories.join(',') - } - }); - - // Listen for preference changes - window.addEventListener('storage', (event) => { - if (event.key === 'notificationPreferences') { - const newPreferences = JSON.parse(event.newValue || '{}'); - DailyNotification.updateSettings({ - time: newPreferences.notificationTime, - sound: newPreferences.soundEnabled, - priority: newPreferences.priority - }); - } - }); - } catch (error) { - console.error('Failed to implement user preferences:', error); - } -} - -/** - * Example of implementing analytics and monitoring - */ -async function implementAnalytics() { - try { - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/analytics-enabled-content', - time: '13:00', - title: 'Analytics-Enabled Update', - body: 'Your analytics-enabled content is ready', - contentHandler: async (response) => { - const data = await response.json(); - - // Track notification delivery - await trackEvent('notification_delivered', { - timestamp: new Date().toISOString(), - contentId: data.id, - contentType: data.type - }); - - return { - title: data.title, - body: data.content, - data: data.metadata - }; - } - }); - - // Track notification interactions - document.addEventListener('notification_clicked', async (event) => { - await trackEvent('notification_clicked', { - timestamp: new Date().toISOString(), - notificationId: event.detail.id, - action: event.detail.action - }); - }); - } catch (error) { - console.error('Failed to implement analytics:', error); - } -} - -// Helper function for analytics -async function trackEvent(eventName: string, properties: Record) { - // Implement your analytics tracking logic here - console.log(`Tracking event: ${eventName}`, properties); -} - -// Export all advanced example functions -export { - handleMultipleSchedules, - handleTimezoneChanges, - implementRetryLogic, - implementOfflineSupport, - implementUserPreferences, - implementAnalytics -}; \ No newline at end of file diff --git a/examples/enterprise-usage.ts b/examples/enterprise-usage.ts deleted file mode 100644 index a8f0a0c..0000000 --- a/examples/enterprise-usage.ts +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Enterprise-level usage examples for the Daily Notification plugin - * Demonstrates advanced features and best practices for large-scale applications - */ - -import { DailyNotification } from '@timesafari/daily-notification-plugin'; -import { NotificationOptions, NotificationSettings } from '../src/definitions'; - -/** - * Example of implementing a notification queue system - */ -async function implementNotificationQueue() { - const plugin = new DailyNotification(); - const queue: NotificationOptions[] = []; - let isProcessing = false; - - async function processQueue() { - if (isProcessing || queue.length === 0) return; - - isProcessing = true; - try { - const notification = queue.shift(); - if (notification) { - await plugin.scheduleDailyNotification(notification); - } - } catch (error) { - console.error('Failed to process notification:', error); - // Retry logic here - } finally { - isProcessing = false; - } - } - - // Add to queue - queue.push({ - url: 'https://api.example.com/enterprise/updates', - time: '09:00', - title: 'Enterprise Update', - priority: 'high', - retryCount: 3, - retryInterval: 5000, - }); - - // Process queue - await processQueue(); -} - -/** - * Example of implementing A/B testing for notifications - */ -async function implementABTesting() { - const plugin = new DailyNotification(); - - async function scheduleABTest() { - const variants = [ - { - title: 'Version A: Direct Call-to-Action', - body: 'Click here to view your daily report', - priority: 'high', - }, - { - title: 'Version B: Value Proposition', - body: 'Your daily insights are ready to help you succeed', - priority: 'normal', - }, - ]; - - // Randomly select a variant - const variant = variants[Math.floor(Math.random() * variants.length)]; - - await plugin.scheduleDailyNotification({ - url: 'https://api.example.com/ab-test-content', - time: '10:00', - ...variant, - data: { - variant: variant === variants[0] ? 'A' : 'B', - timestamp: new Date().toISOString(), - }, - }); - } - - await scheduleABTest(); -} - -/** - * Example of implementing notification analytics and tracking - */ -async function implementAnalyticsTracking() { - const plugin = new DailyNotification(); - - // Track notification delivery - plugin.on('notification', async (event) => { - await trackAnalytics({ - event: 'notification_delivered', - notificationId: event.detail.id, - timestamp: new Date().toISOString(), - platform: navigator.platform, - userAgent: navigator.userAgent, - }); - }); - - // Track notification interactions - document.addEventListener('notification_clicked', async (event) => { - await trackAnalytics({ - event: 'notification_clicked', - notificationId: event.detail.id, - timestamp: new Date().toISOString(), - action: event.detail.action, - data: event.detail.data, - }); - }); - - async function trackAnalytics(data: Record) { - // Implement your analytics tracking logic here - console.log('Analytics Event:', data); - } -} - -/** - * Example of implementing notification preferences management - */ -async function implementPreferencesManagement() { - const plugin = new DailyNotification(); - - interface UserPreferences { - notificationTimes: string[]; - categories: string[]; - priority: 'low' | 'normal' | 'high'; - sound: boolean; - timezone: string; - } - - async function updateUserPreferences(preferences: UserPreferences) { - // Update all scheduled notifications - for (const time of preferences.notificationTimes) { - await plugin.updateSettings({ - time, - priority: preferences.priority, - sound: preferences.sound, - timezone: preferences.timezone, - }); - } - - // Schedule new notifications for each category - for (const category of preferences.categories) { - await plugin.scheduleDailyNotification({ - url: `https://api.example.com/categories/${category}`, - time: preferences.notificationTimes[0], - title: `${category} Update`, - priority: preferences.priority, - sound: preferences.sound, - timezone: preferences.timezone, - }); - } - } - - // Example usage - await updateUserPreferences({ - notificationTimes: ['09:00', '12:00', '18:00'], - categories: ['news', 'tasks', 'updates'], - priority: 'high', - sound: true, - timezone: 'America/New_York', - }); -} - -/** - * Example of implementing notification content personalization - */ -async function implementContentPersonalization() { - const plugin = new DailyNotification(); - - interface UserProfile { - id: string; - name: string; - preferences: { - language: string; - timezone: string; - categories: string[]; - }; - } - - async function schedulePersonalizedNotification(profile: UserProfile) { - await plugin.scheduleDailyNotification({ - url: 'https://api.example.com/personalized-content', - time: '09:00', - title: `Good morning, ${profile.name}!`, - priority: 'high', - timezone: profile.preferences.timezone, - headers: { - 'X-User-ID': profile.id, - 'X-Language': profile.preferences.language, - 'X-Categories': profile.preferences.categories.join(','), - }, - contentHandler: async (response) => { - const data = await response.json(); - return { - title: data.title, - body: data.content, - data: { - ...data.metadata, - userId: profile.id, - timestamp: new Date().toISOString(), - }, - }; - }, - }); - } - - // Example usage - await schedulePersonalizedNotification({ - id: 'user123', - name: 'John Doe', - preferences: { - language: 'en-US', - timezone: 'America/New_York', - categories: ['news', 'weather', 'tasks'], - }, - }); -} - -/** - * Example of implementing notification rate limiting - */ -async function implementRateLimiting() { - const plugin = new DailyNotification(); - - class RateLimiter { - private lastNotificationTime = 0; - private minInterval = 60000; // 1 minute - - async scheduleWithRateLimit(options: NotificationOptions): Promise { - const now = Date.now(); - if (now - this.lastNotificationTime < this.minInterval) { - throw new Error('Rate limit exceeded. Please wait before scheduling another notification.'); - } - - await plugin.scheduleDailyNotification(options); - this.lastNotificationTime = now; - } - } - - const rateLimiter = new RateLimiter(); - - // Example usage - await rateLimiter.scheduleWithRateLimit({ - url: 'https://api.example.com/rate-limited-content', - time: '11:00', - title: 'Rate Limited Update', - priority: 'normal', - }); -} - -// Export all enterprise example functions -export { - implementNotificationQueue, - implementABTesting, - implementAnalyticsTracking, - implementPreferencesManagement, - implementContentPersonalization, - implementRateLimiting, -}; \ No newline at end of file diff --git a/examples/phase1-2-ttl-enforcement.ts b/examples/phase1-2-ttl-enforcement.ts deleted file mode 100644 index cd575e5..0000000 --- a/examples/phase1-2-ttl-enforcement.ts +++ /dev/null @@ -1,173 +0,0 @@ -/** - * Phase 1.2 TTL-at-Fire Enforcement Usage Example - * - * Demonstrates TTL enforcement functionality - * Shows how stale notifications are automatically skipped - * - * @author Matthew Raymer - * @version 1.0.0 - */ - -import { DailyNotification } from '@timesafari/daily-notification-plugin'; - -/** - * Example: Configure TTL enforcement - */ -async function configureTTLEnforcement() { - try { - console.log('Configuring TTL enforcement...'); - - // Configure with TTL settings - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 1800, // 30 minutes TTL - prefetchLeadMinutes: 15 - }); - - console.log('✅ TTL enforcement configured (30 minutes)'); - - // Now the plugin will automatically skip notifications with stale content - // Content older than 30 minutes at fire time will not be armed - - } catch (error) { - console.error('❌ TTL configuration failed:', error); - } -} - -/** - * Example: Schedule notification with TTL enforcement - */ -async function scheduleWithTTLEnforcement() { - try { - // Configure TTL enforcement first - await configureTTLEnforcement(); - - // Schedule a notification - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready', - sound: true - }); - - console.log('✅ Notification scheduled with TTL enforcement'); - - // The plugin will now: - // 1. Check content freshness before arming - // 2. Skip notifications with stale content - // 3. Log TTL violations for debugging - // 4. Store violation statistics - - } catch (error) { - console.error('❌ Scheduling with TTL enforcement failed:', error); - } -} - -/** - * Example: Demonstrate TTL violation scenario - */ -async function demonstrateTTLViolation() { - try { - console.log('Demonstrating TTL violation scenario...'); - - // Configure with short TTL for demonstration - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 300, // 5 minutes TTL (very short for demo) - prefetchLeadMinutes: 2 - }); - - // Schedule notification - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready' - }); - - console.log('✅ Notification scheduled with 5-minute TTL'); - - // If content is fetched more than 5 minutes before 09:00, - // the notification will be skipped due to TTL violation - - console.log('ℹ️ If content is older than 5 minutes at 09:00, notification will be skipped'); - - } catch (error) { - console.error('❌ TTL violation demonstration failed:', error); - } -} - -/** - * Example: Check TTL violation statistics - */ -async function checkTTLStats() { - try { - console.log('Checking TTL violation statistics...'); - - // Configure TTL enforcement - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 1800, // 30 minutes - prefetchLeadMinutes: 15 - }); - - // The plugin automatically tracks TTL violations - // You can check the logs for TTL_VIOLATION entries - // or implement a method to retrieve violation statistics - - console.log('✅ TTL enforcement active - violations will be logged'); - console.log('ℹ️ Check logs for "TTL_VIOLATION" entries'); - - } catch (error) { - console.error('❌ TTL stats check failed:', error); - } -} - -/** - * Example: Different TTL configurations for different use cases - */ -async function configureDifferentTTLScenarios() { - try { - console.log('Configuring different TTL scenarios...'); - - // Scenario 1: Real-time notifications (short TTL) - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 300, // 5 minutes - prefetchLeadMinutes: 2 - }); - - console.log('✅ Real-time notifications: 5-minute TTL'); - - // Scenario 2: Daily digest (longer TTL) - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 7200, // 2 hours - prefetchLeadMinutes: 30 - }); - - console.log('✅ Daily digest: 2-hour TTL'); - - // Scenario 3: Weekly summary (very long TTL) - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 86400, // 24 hours - prefetchLeadMinutes: 60 - }); - - console.log('✅ Weekly summary: 24-hour TTL'); - - } catch (error) { - console.error('❌ TTL scenario configuration failed:', error); - } -} - -// Export examples for use -export { - configureTTLEnforcement, - scheduleWithTTLEnforcement, - demonstrateTTLViolation, - checkTTLStats, - configureDifferentTTLScenarios -}; diff --git a/examples/phase1-3-rolling-window.ts b/examples/phase1-3-rolling-window.ts deleted file mode 100644 index dbc4165..0000000 --- a/examples/phase1-3-rolling-window.ts +++ /dev/null @@ -1,224 +0,0 @@ -/** - * Phase 1.3 Rolling Window Safety Usage Example - * - * Demonstrates rolling window safety functionality - * Shows how notifications are maintained for today and tomorrow - * - * @author Matthew Raymer - * @version 1.0.0 - */ - -import { DailyNotification } from '@timesafari/daily-notification-plugin'; - -/** - * Example: Configure rolling window safety - */ -async function configureRollingWindowSafety() { - try { - console.log('Configuring rolling window safety...'); - - // Configure with rolling window settings - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 1800, // 30 minutes TTL - prefetchLeadMinutes: 15, - maxNotificationsPerDay: 20 // iOS limit - }); - - console.log('✅ Rolling window safety configured'); - - // The plugin will now automatically: - // - Keep today's remaining notifications armed - // - Arm tomorrow's notifications if within iOS caps - // - Maintain window state every 15 minutes - - } catch (error) { - console.error('❌ Rolling window configuration failed:', error); - } -} - -/** - * Example: Manual rolling window maintenance - */ -async function manualRollingWindowMaintenance() { - try { - console.log('Triggering manual rolling window maintenance...'); - - // Force window maintenance (useful for testing) - await DailyNotification.maintainRollingWindow(); - - console.log('✅ Rolling window maintenance completed'); - - // This will: - // - Arm today's remaining notifications - // - Arm tomorrow's notifications if within capacity - // - Update window state and statistics - - } catch (error) { - console.error('❌ Manual maintenance failed:', error); - } -} - -/** - * Example: Check rolling window statistics - */ -async function checkRollingWindowStats() { - try { - console.log('Checking rolling window statistics...'); - - // Get rolling window statistics - const stats = await DailyNotification.getRollingWindowStats(); - - console.log('📊 Rolling Window Statistics:'); - console.log(` Stats: ${stats.stats}`); - console.log(` Maintenance Needed: ${stats.maintenanceNeeded}`); - console.log(` Time Until Next Maintenance: ${stats.timeUntilNextMaintenance}ms`); - - // Example output: - // Stats: Rolling window stats: pending=5/100, daily=3/50, platform=Android - // Maintenance Needed: false - // Time Until Next Maintenance: 450000ms (7.5 minutes) - - } catch (error) { - console.error('❌ Rolling window stats check failed:', error); - } -} - -/** - * Example: Schedule multiple notifications with rolling window - */ -async function scheduleMultipleNotifications() { - try { - console.log('Scheduling multiple notifications with rolling window...'); - - // Configure rolling window safety - await configureRollingWindowSafety(); - - // Schedule notifications for different times - const notifications = [ - { time: '08:00', title: 'Morning Update', body: 'Good morning!' }, - { time: '12:00', title: 'Lunch Reminder', body: 'Time for lunch!' }, - { time: '18:00', title: 'Evening Summary', body: 'End of day summary' }, - { time: '22:00', title: 'Good Night', body: 'Time to rest' } - ]; - - for (const notification of notifications) { - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: notification.time, - title: notification.title, - body: notification.body - }); - } - - console.log('✅ Multiple notifications scheduled'); - - // The rolling window will ensure: - // - All future notifications today are armed - // - Tomorrow's notifications are armed if within iOS caps - // - Window state is maintained automatically - - } catch (error) { - console.error('❌ Multiple notification scheduling failed:', error); - } -} - -/** - * Example: Demonstrate iOS capacity limits - */ -async function demonstrateIOSCapacityLimits() { - try { - console.log('Demonstrating iOS capacity limits...'); - - // Configure with iOS-like limits - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 3600, // 1 hour TTL - prefetchLeadMinutes: 30, - maxNotificationsPerDay: 20 // iOS limit - }); - - // Schedule many notifications to test capacity - const notifications = []; - for (let i = 0; i < 25; i++) { - notifications.push({ - time: `${8 + i}:00`, - title: `Notification ${i + 1}`, - body: `This is notification number ${i + 1}` - }); - } - - for (const notification of notifications) { - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: notification.time, - title: notification.title, - body: notification.body - }); - } - - console.log('✅ Many notifications scheduled'); - - // Check statistics to see capacity management - const stats = await DailyNotification.getRollingWindowStats(); - console.log('📊 Capacity Management:', stats.stats); - - // The rolling window will: - // - Arm notifications up to the daily limit - // - Skip additional notifications if at capacity - // - Log capacity violations for debugging - - } catch (error) { - console.error('❌ iOS capacity demonstration failed:', error); - } -} - -/** - * Example: Monitor rolling window over time - */ -async function monitorRollingWindowOverTime() { - try { - console.log('Monitoring rolling window over time...'); - - // Configure rolling window - await configureRollingWindowSafety(); - - // Schedule some notifications - await scheduleMultipleNotifications(); - - // Monitor window state over time - const monitorInterval = setInterval(async () => { - try { - const stats = await DailyNotification.getRollingWindowStats(); - console.log('📊 Window State:', stats.stats); - - if (stats.maintenanceNeeded) { - console.log('⚠️ Maintenance needed, triggering...'); - await DailyNotification.maintainRollingWindow(); - } - - } catch (error) { - console.error('❌ Monitoring error:', error); - } - }, 60000); // Check every minute - - // Stop monitoring after 5 minutes - setTimeout(() => { - clearInterval(monitorInterval); - console.log('✅ Monitoring completed'); - }, 300000); - - } catch (error) { - console.error('❌ Rolling window monitoring failed:', error); - } -} - -// Export examples for use -export { - configureRollingWindowSafety, - manualRollingWindowMaintenance, - checkRollingWindowStats, - scheduleMultipleNotifications, - demonstrateIOSCapacityLimits, - monitorRollingWindowOverTime -}; diff --git a/examples/phase1-sqlite-usage.ts b/examples/phase1-sqlite-usage.ts deleted file mode 100644 index d570d75..0000000 --- a/examples/phase1-sqlite-usage.ts +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Phase 1.1 Usage Example - * - * Demonstrates SQLite database sharing configuration and usage - * Shows how to configure the plugin for shared storage mode - * - * @author Matthew Raymer - * @version 1.0.0 - */ - -import { DailyNotification } from '@timesafari/daily-notification-plugin'; - -/** - * Example: Configure plugin for shared SQLite storage - */ -async function configureSharedStorage() { - try { - console.log('Configuring plugin for shared SQLite storage...'); - - // Configure the plugin with shared storage mode - await DailyNotification.configure({ - dbPath: '/data/data/com.yourapp/databases/daily_notifications.db', - storage: 'shared', - ttlSeconds: 3600, // 1 hour TTL - prefetchLeadMinutes: 15, // 15 minutes before notification - maxNotificationsPerDay: 5, - retentionDays: 7 - }); - - console.log('✅ Plugin configured successfully for shared storage'); - - // Now the plugin will use SQLite database instead of SharedPreferences - // The database will be shared between app and plugin - // WAL mode enables concurrent reads during writes - - } catch (error) { - console.error('❌ Configuration failed:', error); - } -} - -/** - * Example: Configure plugin for tiered storage (current implementation) - */ -async function configureTieredStorage() { - try { - console.log('Configuring plugin for tiered storage...'); - - // Configure the plugin with tiered storage mode (default) - await DailyNotification.configure({ - storage: 'tiered', - ttlSeconds: 1800, // 30 minutes TTL - prefetchLeadMinutes: 10, // 10 minutes before notification - maxNotificationsPerDay: 3, - retentionDays: 5 - }); - - console.log('✅ Plugin configured successfully for tiered storage'); - - // Plugin will continue using SharedPreferences + in-memory cache - - } catch (error) { - console.error('❌ Configuration failed:', error); - } -} - -/** - * Example: Schedule notification with new configuration - */ -async function scheduleWithNewConfig() { - try { - // First configure for shared storage - await configureSharedStorage(); - - // Then schedule a notification - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready', - sound: true, - priority: 'high' - }); - - console.log('✅ Notification scheduled with shared storage configuration'); - - } catch (error) { - console.error('❌ Scheduling failed:', error); - } -} - -/** - * Example: Check migration status - */ -async function checkMigrationStatus() { - try { - // Configure for shared storage to trigger migration - await DailyNotification.configure({ - storage: 'shared', - dbPath: '/data/data/com.yourapp/databases/daily_notifications.db' - }); - - // The plugin will automatically: - // 1. Create SQLite database with WAL mode - // 2. Migrate existing SharedPreferences data - // 3. Validate migration success - // 4. Log migration statistics - - console.log('✅ Migration completed automatically during configuration'); - - } catch (error) { - console.error('❌ Migration failed:', error); - } -} - -// Export examples for use -export { - configureSharedStorage, - configureTieredStorage, - scheduleWithNewConfig, - checkMigrationStatus -}; diff --git a/examples/phase2-1-ios-background-tasks.ts b/examples/phase2-1-ios-background-tasks.ts deleted file mode 100644 index 41be197..0000000 --- a/examples/phase2-1-ios-background-tasks.ts +++ /dev/null @@ -1,285 +0,0 @@ -/** - * Phase 2.1 iOS Background Tasks Usage Example - * - * Demonstrates iOS background task functionality - * Shows T–lead prefetch with BGTaskScheduler integration - * - * @author Matthew Raymer - * @version 1.0.0 - */ - -import { DailyNotification } from '@timesafari/daily-notification-plugin'; - -/** - * Example: Configure iOS background tasks - */ -async function configureIOSBackgroundTasks() { - try { - console.log('Configuring iOS background tasks...'); - - // Configure with background task settings - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 1800, // 30 minutes TTL - prefetchLeadMinutes: 15, // T–lead prefetch 15 minutes before - maxNotificationsPerDay: 20 // iOS limit - }); - - console.log('✅ iOS background tasks configured'); - - // The plugin will now: - // - Register BGTaskScheduler tasks - // - Schedule T–lead prefetch automatically - // - Handle background execution constraints - // - Respect ETag/304 caching - - } catch (error) { - console.error('❌ iOS background task configuration failed:', error); - } -} - -/** - * Example: Schedule notification with background prefetch - */ -async function scheduleWithBackgroundPrefetch() { - try { - // Configure background tasks first - await configureIOSBackgroundTasks(); - - // Schedule a notification - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready', - sound: true - }); - - console.log('✅ Notification scheduled with background prefetch'); - - // The plugin will now: - // - Schedule notification for 09:00 - // - Schedule background task for 08:45 (T–lead) - // - Perform single-attempt prefetch with 12s timeout - // - Re-arm notification if content is fresh - - } catch (error) { - console.error('❌ Scheduling with background prefetch failed:', error); - } -} - -/** - * Example: Check background task status - */ -async function checkBackgroundTaskStatus() { - try { - console.log('Checking background task status...'); - - // Get background task status - const status = await DailyNotification.getBackgroundTaskStatus(); - - console.log('📱 Background Task Status:'); - console.log(` Available: ${status.available}`); - console.log(` Identifier: ${status.identifier}`); - console.log(` Timeout: ${status.timeout}s`); - console.log(` Expiration: ${status.expiration}s`); - - // Example output: - // Available: true - // Identifier: com.timesafari.dailynotification.prefetch - // Timeout: 12s - // Expiration: 30s - - } catch (error) { - console.error('❌ Background task status check failed:', error); - } -} - -/** - * Example: Manual background task scheduling - */ -async function manualBackgroundTaskScheduling() { - try { - console.log('Manually scheduling background task...'); - - // Configure background tasks - await configureIOSBackgroundTasks(); - - // Manually schedule background task for specific time - await DailyNotification.scheduleBackgroundTask({ - scheduledTime: '10:30' // Schedule for 10:30 - }); - - console.log('✅ Background task manually scheduled for 10:30'); - - // This will: - // - Schedule background task for 10:15 (T–lead) - // - Perform prefetch when iOS allows background execution - // - Handle ETag/304 responses appropriately - // - Update notification content if fresh - - } catch (error) { - console.error('❌ Manual background task scheduling failed:', error); - } -} - -/** - * Example: Demonstrate ETag caching - */ -async function demonstrateETagCaching() { - try { - console.log('Demonstrating ETag caching...'); - - // Configure with short TTL for demonstration - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 300, // 5 minutes TTL - prefetchLeadMinutes: 2 // Very short lead time - }); - - // Schedule notification - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready' - }); - - console.log('✅ Notification scheduled with ETag caching'); - - // The background task will: - // - Send If-None-Match header with stored ETag - // - Receive 304 if content unchanged - // - Receive 200 with new ETag if content updated - // - Update stored content and ETag accordingly - - } catch (error) { - console.error('❌ ETag caching demonstration failed:', error); - } -} - -/** - * Example: Handle background task limitations - */ -async function handleBackgroundTaskLimitations() { - try { - console.log('Handling background task limitations...'); - - // Configure background tasks - await configureIOSBackgroundTasks(); - - // Schedule multiple notifications to test limits - const notifications = [ - { time: '08:00', title: 'Morning Update' }, - { time: '12:00', title: 'Lunch Reminder' }, - { time: '18:00', title: 'Evening Summary' }, - { time: '22:00', title: 'Good Night' } - ]; - - for (const notification of notifications) { - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: notification.time, - title: notification.title, - body: 'Your daily notification is ready' - }); - } - - console.log('✅ Multiple notifications scheduled'); - - // iOS will: - // - Limit background task execution time - // - Provide 30-second expiration window - // - Cancel tasks if they exceed limits - // - Handle task failures gracefully - - // Check status to see current state - const status = await DailyNotification.getBackgroundTaskStatus(); - console.log('📱 Current Status:', status); - - } catch (error) { - console.error('❌ Background task limitations handling failed:', error); - } -} - -/** - * Example: Monitor background task execution - */ -async function monitorBackgroundTaskExecution() { - try { - console.log('Monitoring background task execution...'); - - // Configure background tasks - await configureIOSBackgroundTasks(); - - // Schedule notification - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready' - }); - - // Monitor execution over time - const monitorInterval = setInterval(async () => { - try { - const status = await DailyNotification.getBackgroundTaskStatus(); - console.log('📱 Background Task Status:', status); - - // Check if background task is available and active - if (status.available) { - console.log('✅ Background tasks are available'); - } else { - console.log('⚠️ Background tasks not available:', status.reason); - } - - } catch (error) { - console.error('❌ Monitoring error:', error); - } - }, 60000); // Check every minute - - // Stop monitoring after 5 minutes - setTimeout(() => { - clearInterval(monitorInterval); - console.log('✅ Background task monitoring completed'); - }, 300000); - - } catch (error) { - console.error('❌ Background task monitoring failed:', error); - } -} - -/** - * Example: Cancel background tasks - */ -async function cancelBackgroundTasks() { - try { - console.log('Cancelling background tasks...'); - - // Cancel all background tasks - await DailyNotification.cancelAllBackgroundTasks(); - - console.log('✅ All background tasks cancelled'); - - // This will: - // - Cancel all pending BGTaskScheduler tasks - // - Stop T–lead prefetch scheduling - // - Clear background task queue - // - Maintain existing notifications - - } catch (error) { - console.error('❌ Background task cancellation failed:', error); - } -} - -// Export examples for use -export { - configureIOSBackgroundTasks, - scheduleWithBackgroundPrefetch, - checkBackgroundTaskStatus, - manualBackgroundTaskScheduling, - demonstrateETagCaching, - handleBackgroundTaskLimitations, - monitorBackgroundTaskExecution, - cancelBackgroundTasks -}; diff --git a/examples/phase2-2-android-fallback.ts b/examples/phase2-2-android-fallback.ts deleted file mode 100644 index 9a80c74..0000000 --- a/examples/phase2-2-android-fallback.ts +++ /dev/null @@ -1,321 +0,0 @@ -/** - * Phase 2.2 Android Fallback Completion Usage Example - * - * Demonstrates Android exact alarm fallback functionality - * Shows permission handling, windowed alarms, and reboot recovery - * - * @author Matthew Raymer - * @version 1.0.0 - */ - -import { DailyNotification } from '@timesafari/daily-notification-plugin'; - -/** - * Example: Configure Android exact alarm fallback - */ -async function configureAndroidExactAlarmFallback() { - try { - console.log('Configuring Android exact alarm fallback...'); - - // Configure with fallback settings - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 1800, // 30 minutes TTL - prefetchLeadMinutes: 15, - maxNotificationsPerDay: 50 // Android limit - }); - - console.log('✅ Android exact alarm fallback configured'); - - // The plugin will now: - // - Request SCHEDULE_EXACT_ALARM permission - // - Fall back to windowed alarms (±10m) if denied - // - Handle reboot and time-change recovery - // - Provide deep-link to enable exact alarms - - } catch (error) { - console.error('❌ Android exact alarm fallback configuration failed:', error); - } -} - -/** - * Example: Check exact alarm status - */ -async function checkExactAlarmStatus() { - try { - console.log('Checking exact alarm status...'); - - // Get exact alarm status - const status = await DailyNotification.getExactAlarmStatus(); - - console.log('📱 Exact Alarm Status:'); - console.log(` Supported: ${status.supported}`); - console.log(` Enabled: ${status.enabled}`); - console.log(` Can Schedule: ${status.canSchedule}`); - console.log(` Fallback Window: ${status.fallbackWindow}`); - - // Example output: - // Supported: true - // Enabled: false - // Can Schedule: false - // Fallback Window: ±10 minutes - - if (!status.enabled && status.supported) { - console.log('⚠️ Exact alarms are supported but not enabled'); - console.log('💡 Consider requesting permission or opening settings'); - } - - } catch (error) { - console.error('❌ Exact alarm status check failed:', error); - } -} - -/** - * Example: Request exact alarm permission - */ -async function requestExactAlarmPermission() { - try { - console.log('Requesting exact alarm permission...'); - - // Request exact alarm permission - await DailyNotification.requestExactAlarmPermission(); - - console.log('✅ Exact alarm permission request initiated'); - - // This will: - // - Open the exact alarm settings screen - // - Allow user to enable exact alarms - // - Fall back to windowed alarms if denied - - } catch (error) { - console.error('❌ Exact alarm permission request failed:', error); - } -} - -/** - * Example: Open exact alarm settings - */ -async function openExactAlarmSettings() { - try { - console.log('Opening exact alarm settings...'); - - // Open exact alarm settings - await DailyNotification.openExactAlarmSettings(); - - console.log('✅ Exact alarm settings opened'); - - // This will: - // - Navigate to exact alarm settings - // - Allow user to enable exact alarms - // - Provide fallback information if needed - - } catch (error) { - console.error('❌ Opening exact alarm settings failed:', error); - } -} - -/** - * Example: Schedule notification with fallback handling - */ -async function scheduleWithFallbackHandling() { - try { - console.log('Scheduling notification with fallback handling...'); - - // Configure fallback - await configureAndroidExactAlarmFallback(); - - // Check status first - const status = await DailyNotification.getExactAlarmStatus(); - - if (!status.canSchedule) { - console.log('⚠️ Exact alarms not available, will use windowed fallback'); - } - - // Schedule notification - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready', - sound: true - }); - - console.log('✅ Notification scheduled with fallback handling'); - - // The plugin will: - // - Use exact alarms if available - // - Fall back to windowed alarms (±10m) if not - // - Handle permission changes gracefully - // - Provide appropriate user feedback - - } catch (error) { - console.error('❌ Scheduling with fallback handling failed:', error); - } -} - -/** - * Example: Check reboot recovery status - */ -async function checkRebootRecoveryStatus() { - try { - console.log('Checking reboot recovery status...'); - - // Get reboot recovery status - const status = await DailyNotification.getRebootRecoveryStatus(); - - console.log('🔄 Reboot Recovery Status:'); - console.log(` In Progress: ${status.inProgress}`); - console.log(` Last Recovery Time: ${new Date(status.lastRecoveryTime)}`); - console.log(` Time Since Last Recovery: ${status.timeSinceLastRecovery}ms`); - console.log(` Recovery Needed: ${status.recoveryNeeded}`); - - // Example output: - // In Progress: false - // Last Recovery Time: Mon Sep 08 2025 10:30:00 GMT+0000 - // Time Since Last Recovery: 120000ms - // Recovery Needed: false - - if (status.recoveryNeeded) { - console.log('⚠️ Recovery is needed - system may have rebooted'); - } - - } catch (error) { - console.error('❌ Reboot recovery status check failed:', error); - } -} - -/** - * Example: Demonstrate fallback scenarios - */ -async function demonstrateFallbackScenarios() { - try { - console.log('Demonstrating fallback scenarios...'); - - // Configure fallback - await configureAndroidExactAlarmFallback(); - - // Check initial status - const initialStatus = await DailyNotification.getExactAlarmStatus(); - console.log('📱 Initial Status:', initialStatus); - - // Schedule notification - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready' - }); - - console.log('✅ Notification scheduled'); - - // Check status after scheduling - const afterStatus = await DailyNotification.getExactAlarmStatus(); - console.log('📱 Status After Scheduling:', afterStatus); - - // The plugin will handle: - // - Exact alarms if permission granted - // - Windowed alarms (±10m) if permission denied - // - Graceful degradation based on Android version - // - Appropriate user feedback and guidance - - } catch (error) { - console.error('❌ Fallback scenarios demonstration failed:', error); - } -} - -/** - * Example: Monitor exact alarm changes - */ -async function monitorExactAlarmChanges() { - try { - console.log('Monitoring exact alarm changes...'); - - // Configure fallback - await configureAndroidExactAlarmFallback(); - - // Monitor changes over time - const monitorInterval = setInterval(async () => { - try { - const status = await DailyNotification.getExactAlarmStatus(); - console.log('📱 Exact Alarm Status:', status); - - if (status.enabled && !status.canSchedule) { - console.log('⚠️ Exact alarms enabled but cannot schedule - may need app restart'); - } - - } catch (error) { - console.error('❌ Monitoring error:', error); - } - }, 30000); // Check every 30 seconds - - // Stop monitoring after 5 minutes - setTimeout(() => { - clearInterval(monitorInterval); - console.log('✅ Exact alarm monitoring completed'); - }, 300000); - - } catch (error) { - console.error('❌ Exact alarm monitoring failed:', error); - } -} - -/** - * Example: Handle permission denial gracefully - */ -async function handlePermissionDenialGracefully() { - try { - console.log('Handling permission denial gracefully...'); - - // Configure fallback - await configureAndroidExactAlarmFallback(); - - // Check status - const status = await DailyNotification.getExactAlarmStatus(); - - if (!status.enabled) { - console.log('⚠️ Exact alarms not enabled, using windowed fallback'); - - // Schedule with fallback - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready (windowed fallback)' - }); - - console.log('✅ Notification scheduled with windowed fallback'); - - // Provide user guidance - console.log('💡 To enable exact alarms:'); - console.log(' 1. Call requestExactAlarmPermission()'); - console.log(' 2. Or call openExactAlarmSettings()'); - console.log(' 3. Enable exact alarms in settings'); - - } else { - console.log('✅ Exact alarms enabled, scheduling normally'); - - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready (exact timing)' - }); - } - - } catch (error) { - console.error('❌ Permission denial handling failed:', error); - } -} - -// Export examples for use -export { - configureAndroidExactAlarmFallback, - checkExactAlarmStatus, - requestExactAlarmPermission, - openExactAlarmSettings, - scheduleWithFallbackHandling, - checkRebootRecoveryStatus, - demonstrateFallbackScenarios, - monitorExactAlarmChanges, - handlePermissionDenialGracefully -}; diff --git a/examples/phase3-1-etag-support.ts b/examples/phase3-1-etag-support.ts deleted file mode 100644 index f253e08..0000000 --- a/examples/phase3-1-etag-support.ts +++ /dev/null @@ -1,317 +0,0 @@ -/** - * Phase 3.1 ETag Support Implementation Usage Example - * - * Demonstrates ETag-based conditional requests for efficient content fetching - * Shows 304 Not Modified handling, cache management, and network metrics - * - * @author Matthew Raymer - * @version 1.0.0 - */ - -import { DailyNotification } from '@timesafari/daily-notification-plugin'; - -/** - * Example: Configure ETag support for efficient fetching - */ -async function configureETagSupport() { - try { - console.log('Configuring ETag support for efficient fetching...'); - - // Configure with ETag support - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 1800, // 30 minutes TTL - prefetchLeadMinutes: 15, - enableETagSupport: true // Enable ETag conditional requests - }); - - console.log('✅ ETag support configured'); - - // The plugin will now: - // - Send If-None-Match headers with cached ETags - // - Handle 304 Not Modified responses efficiently - // - Cache ETag values for future requests - // - Track network efficiency metrics - - } catch (error) { - console.error('❌ ETag support configuration failed:', error); - } -} - -/** - * Example: Demonstrate ETag conditional requests - */ -async function demonstrateETagConditionalRequests() { - try { - console.log('Demonstrating ETag conditional requests...'); - - // Configure ETag support - await configureETagSupport(); - - // First request - will fetch content and cache ETag - console.log('📡 First request (no ETag cached)...'); - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready' - }); - - console.log('✅ First request completed - ETag cached'); - - // Second request - will use conditional request - console.log('📡 Second request (ETag cached)...'); - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:15', - title: 'Daily Update', - body: 'Your daily notification is ready' - }); - - console.log('✅ Second request completed - conditional request used'); - - // The plugin will: - // - Send If-None-Match header with cached ETag - // - Receive 304 Not Modified if content unchanged - // - Use cached content instead of downloading - // - Update metrics to track efficiency - - } catch (error) { - console.error('❌ ETag conditional requests demonstration failed:', error); - } -} - -/** - * Example: Check network efficiency metrics - */ -async function checkNetworkEfficiencyMetrics() { - try { - console.log('Checking network efficiency metrics...'); - - // Configure ETag support - await configureETagSupport(); - - // Make some requests to generate metrics - await demonstrateETagConditionalRequests(); - - // Get network metrics - const metrics = await DailyNotification.getNetworkMetrics(); - - console.log('📊 Network Efficiency Metrics:'); - console.log(` Total Requests: ${metrics.totalRequests}`); - console.log(` Cached Responses: ${metrics.cachedResponses}`); - console.log(` Network Responses: ${metrics.networkResponses}`); - console.log(` Errors: ${metrics.errors}`); - console.log(` Cache Hit Ratio: ${(metrics.cacheHitRatio * 100).toFixed(1)}%`); - - // Example output: - // Total Requests: 4 - // Cached Responses: 2 - // Network Responses: 2 - // Errors: 0 - // Cache Hit Ratio: 50.0% - - if (metrics.cacheHitRatio > 0.5) { - console.log('✅ Good cache efficiency - ETag support is working well'); - } else { - console.log('⚠️ Low cache efficiency - content may be changing frequently'); - } - - } catch (error) { - console.error('❌ Network efficiency metrics check failed:', error); - } -} - -/** - * Example: Manage ETag cache - */ -async function manageETagCache() { - try { - console.log('Managing ETag cache...'); - - // Configure ETag support - await configureETagSupport(); - - // Get cache statistics - const cacheStats = await DailyNotification.getCacheStatistics(); - - console.log('🗄️ ETag Cache Statistics:'); - console.log(` Total ETags: ${cacheStats.totalETags}`); - console.log(` Valid ETags: ${cacheStats.validETags}`); - console.log(` Expired ETags: ${cacheStats.expiredETags}`); - - // Clean expired ETags - if (cacheStats.expiredETags > 0) { - console.log('🧹 Cleaning expired ETags...'); - await DailyNotification.cleanExpiredETags(); - console.log('✅ Expired ETags cleaned'); - } - - // Reset metrics - console.log('🔄 Resetting network metrics...'); - await DailyNotification.resetNetworkMetrics(); - console.log('✅ Network metrics reset'); - - } catch (error) { - console.error('❌ ETag cache management failed:', error); - } -} - -/** - * Example: Handle ETag failures gracefully - */ -async function handleETagFailuresGracefully() { - try { - console.log('Handling ETag failures gracefully...'); - - // Configure ETag support - await configureETagSupport(); - - // Schedule notification with potential ETag issues - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/unreliable-content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready' - }); - - console.log('✅ Notification scheduled with ETag fallback'); - - // The plugin will handle ETag failures by: - // - Falling back to full content fetch if ETag fails - // - Logging ETag errors for debugging - // - Continuing with notification scheduling - // - Updating error metrics - - // Check metrics after potential failures - const metrics = await DailyNotification.getNetworkMetrics(); - - if (metrics.errors > 0) { - console.log(`⚠️ ${metrics.errors} ETag errors occurred - fallback used`); - } else { - console.log('✅ No ETag errors - all requests successful'); - } - - } catch (error) { - console.error('❌ ETag failure handling failed:', error); - } -} - -/** - * Example: Monitor ETag performance over time - */ -async function monitorETagPerformance() { - try { - console.log('Monitoring ETag performance over time...'); - - // Configure ETag support - await configureETagSupport(); - - // Monitor performance over multiple requests - const monitoringInterval = setInterval(async () => { - try { - const metrics = await DailyNotification.getNetworkMetrics(); - const cacheStats = await DailyNotification.getCacheStatistics(); - - console.log('📊 Performance Snapshot:'); - console.log(` Cache Hit Ratio: ${(metrics.cacheHitRatio * 100).toFixed(1)}%`); - console.log(` Total Requests: ${metrics.totalRequests}`); - console.log(` Errors: ${metrics.errors}`); - console.log(` Valid ETags: ${cacheStats.validETags}`); - - // Stop monitoring if we have enough data - if (metrics.totalRequests >= 10) { - clearInterval(monitoringInterval); - console.log('✅ Performance monitoring completed'); - } - - } catch (error) { - console.error('❌ Performance monitoring error:', error); - } - }, 5000); // Check every 5 seconds - - // Make some requests to generate data - for (let i = 0; i < 5; i++) { - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: `09:${i.toString().padStart(2, '0')}`, - title: 'Daily Update', - body: 'Your daily notification is ready' - }); - - // Wait between requests - await new Promise(resolve => setTimeout(resolve, 2000)); - } - - } catch (error) { - console.error('❌ ETag performance monitoring failed:', error); - } -} - -/** - * Example: Optimize content fetching with ETags - */ -async function optimizeContentFetchingWithETags() { - try { - console.log('Optimizing content fetching with ETags...'); - - // Configure ETag support - await configureETagSupport(); - - // Schedule multiple notifications for the same content - const notifications = [ - { time: '09:00', title: 'Morning Update' }, - { time: '12:00', title: 'Midday Update' }, - { time: '15:00', title: 'Afternoon Update' }, - { time: '18:00', title: 'Evening Update' } - ]; - - for (const notification of notifications) { - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', // Same URL - time: notification.time, - title: notification.title, - body: 'Your daily notification is ready' - }); - - console.log(`✅ Scheduled ${notification.title} at ${notification.time}`); - } - - // Check final metrics - const metrics = await DailyNotification.getNetworkMetrics(); - - console.log('📊 Optimization Results:'); - console.log(` Total Requests: ${metrics.totalRequests}`); - console.log(` Cached Responses: ${metrics.cachedResponses}`); - console.log(` Cache Hit Ratio: ${(metrics.cacheHitRatio * 100).toFixed(1)}%`); - - // With ETag support, we should see: - // - First request: Network response (200 OK) - // - Subsequent requests: Cached responses (304 Not Modified) - // - High cache hit ratio (75%+) - // - Reduced bandwidth usage - // - Faster response times - - if (metrics.cacheHitRatio >= 0.75) { - console.log('✅ Excellent optimization - ETag support is highly effective'); - } else if (metrics.cacheHitRatio >= 0.5) { - console.log('✅ Good optimization - ETag support is working well'); - } else { - console.log('⚠️ Limited optimization - content may be changing frequently'); - } - - } catch (error) { - console.error('❌ Content fetching optimization failed:', error); - } -} - -// Export examples for use -export { - configureETagSupport, - demonstrateETagConditionalRequests, - checkNetworkEfficiencyMetrics, - manageETagCache, - handleETagFailuresGracefully, - monitorETagPerformance, - optimizeContentFetchingWithETags -}; diff --git a/examples/phase3-2-advanced-error-handling.ts b/examples/phase3-2-advanced-error-handling.ts deleted file mode 100644 index e723ed3..0000000 --- a/examples/phase3-2-advanced-error-handling.ts +++ /dev/null @@ -1,423 +0,0 @@ -/** - * Phase 3.2 Advanced Error Handling Usage Example - * - * Demonstrates comprehensive error handling with categorization, retry logic, and telemetry - * Shows error classification, exponential backoff, and debugging information - * - * @author Matthew Raymer - * @version 1.0.0 - */ - -import { DailyNotification } from '@timesafari/daily-notification-plugin'; - -/** - * Example: Configure advanced error handling - */ -async function configureAdvancedErrorHandling() { - try { - console.log('Configuring advanced error handling...'); - - // Configure with error handling - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 1800, // 30 minutes TTL - prefetchLeadMinutes: 15, - enableErrorHandling: true, - maxRetries: 3, - baseRetryDelay: 1000, // 1 second - maxRetryDelay: 30000, // 30 seconds - backoffMultiplier: 2.0 - }); - - console.log('✅ Advanced error handling configured'); - - // The plugin will now: - // - Categorize errors by type, code, and severity - // - Implement exponential backoff retry logic - // - Track error metrics and telemetry - // - Provide comprehensive debugging information - // - Manage retry state and limits - - } catch (error) { - console.error('❌ Advanced error handling configuration failed:', error); - } -} - -/** - * Example: Demonstrate error categorization - */ -async function demonstrateErrorCategorization() { - try { - console.log('Demonstrating error categorization...'); - - // Configure error handling - await configureAdvancedErrorHandling(); - - // Simulate different types of errors - const errorScenarios = [ - { - name: 'Network Error', - url: 'https://unreachable-api.example.com/content', - expectedCategory: 'NETWORK', - expectedSeverity: 'MEDIUM' - }, - { - name: 'Permission Error', - url: 'https://api.example.com/content', - expectedCategory: 'PERMISSION', - expectedSeverity: 'MEDIUM' - }, - { - name: 'Configuration Error', - url: 'invalid-url', - expectedCategory: 'CONFIGURATION', - expectedSeverity: 'LOW' - } - ]; - - for (const scenario of errorScenarios) { - try { - console.log(`📡 Testing ${scenario.name}...`); - - await DailyNotification.scheduleDailyNotification({ - url: scenario.url, - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready' - }); - - } catch (error) { - console.log(`✅ ${scenario.name} handled:`, error.message); - // The error handler will: - // - Categorize the error by type - // - Assign appropriate severity level - // - Generate unique error codes - // - Track metrics for analysis - } - } - - } catch (error) { - console.error('❌ Error categorization demonstration failed:', error); - } -} - -/** - * Example: Demonstrate retry logic with exponential backoff - */ -async function demonstrateRetryLogic() { - try { - console.log('Demonstrating retry logic with exponential backoff...'); - - // Configure error handling - await configureAdvancedErrorHandling(); - - // Schedule notification with unreliable endpoint - console.log('📡 Scheduling notification with unreliable endpoint...'); - - await DailyNotification.scheduleDailyNotification({ - url: 'https://unreliable-api.example.com/content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready' - }); - - console.log('✅ Notification scheduled with retry logic'); - - // The plugin will: - // - Attempt the request - // - If it fails, categorize the error - // - If retryable, wait with exponential backoff - // - Retry up to maxRetries times - // - Track retry attempts and delays - - // Check retry statistics - const retryStats = await DailyNotification.getRetryStatistics(); - console.log('📊 Retry Statistics:', retryStats); - - } catch (error) { - console.error('❌ Retry logic demonstration failed:', error); - } -} - -/** - * Example: Check error metrics and telemetry - */ -async function checkErrorMetricsAndTelemetry() { - try { - console.log('Checking error metrics and telemetry...'); - - // Configure error handling - await configureAdvancedErrorHandling(); - - // Generate some errors to create metrics - await demonstrateErrorCategorization(); - - // Get error metrics - const errorMetrics = await DailyNotification.getErrorMetrics(); - - console.log('📊 Error Metrics:'); - console.log(` Total Errors: ${errorMetrics.totalErrors}`); - console.log(` Network Errors: ${errorMetrics.networkErrors}`); - console.log(` Storage Errors: ${errorMetrics.storageErrors}`); - console.log(` Scheduling Errors: ${errorMetrics.schedulingErrors}`); - console.log(` Permission Errors: ${errorMetrics.permissionErrors}`); - console.log(` Configuration Errors: ${errorMetrics.configurationErrors}`); - console.log(` System Errors: ${errorMetrics.systemErrors}`); - console.log(` Unknown Errors: ${errorMetrics.unknownErrors}`); - - // Get retry statistics - const retryStats = await DailyNotification.getRetryStatistics(); - console.log('🔄 Retry Statistics:'); - console.log(` Total Operations: ${retryStats.totalOperations}`); - console.log(` Active Retries: ${retryStats.activeRetries}`); - console.log(` Total Retries: ${retryStats.totalRetries}`); - - // Analyze error patterns - if (errorMetrics.networkErrors > 0) { - console.log('⚠️ Network errors detected - check connectivity'); - } - - if (errorMetrics.permissionErrors > 0) { - console.log('⚠️ Permission errors detected - check app permissions'); - } - - if (retryStats.totalRetries > retryStats.totalOperations * 2) { - console.log('⚠️ High retry rate - system may be unstable'); - } - - } catch (error) { - console.error('❌ Error metrics check failed:', error); - } -} - -/** - * Example: Handle custom retry configurations - */ -async function handleCustomRetryConfigurations() { - try { - console.log('Handling custom retry configurations...'); - - // Configure error handling - await configureAdvancedErrorHandling(); - - // Schedule notification with custom retry config - console.log('📡 Scheduling with custom retry configuration...'); - - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready', - retryConfig: { - maxRetries: 5, - baseRetryDelay: 2000, // 2 seconds - maxRetryDelay: 60000, // 60 seconds - backoffMultiplier: 1.5 - } - }); - - console.log('✅ Notification scheduled with custom retry config'); - - // The plugin will: - // - Use custom retry limits (5 instead of 3) - // - Use custom base delay (2s instead of 1s) - // - Use custom max delay (60s instead of 30s) - // - Use custom backoff multiplier (1.5 instead of 2.0) - - } catch (error) { - console.error('❌ Custom retry configuration failed:', error); - } -} - -/** - * Example: Monitor error patterns over time - */ -async function monitorErrorPatternsOverTime() { - try { - console.log('Monitoring error patterns over time...'); - - // Configure error handling - await configureAdvancedErrorHandling(); - - // Monitor errors over multiple operations - const monitoringInterval = setInterval(async () => { - try { - const errorMetrics = await DailyNotification.getErrorMetrics(); - const retryStats = await DailyNotification.getRetryStatistics(); - - console.log('📊 Error Pattern Snapshot:'); - console.log(` Total Errors: ${errorMetrics.totalErrors}`); - console.log(` Network Errors: ${errorMetrics.networkErrors}`); - console.log(` Active Retries: ${retryStats.activeRetries}`); - console.log(` Total Retries: ${retryStats.totalRetries}`); - - // Stop monitoring if we have enough data - if (errorMetrics.totalErrors >= 10) { - clearInterval(monitoringInterval); - console.log('✅ Error pattern monitoring completed'); - } - - } catch (error) { - console.error('❌ Error pattern monitoring error:', error); - } - }, 5000); // Check every 5 seconds - - // Make some requests to generate data - for (let i = 0; i < 5; i++) { - try { - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/content', - time: `09:${i.toString().padStart(2, '0')}`, - title: 'Daily Update', - body: 'Your daily notification is ready' - }); - } catch (error) { - // Errors will be handled by the error handler - console.log(`Request ${i + 1} failed:`, error.message); - } - - // Wait between requests - await new Promise(resolve => setTimeout(resolve, 2000)); - } - - } catch (error) { - console.error('❌ Error pattern monitoring failed:', error); - } -} - -/** - * Example: Reset error metrics and retry states - */ -async function resetErrorMetricsAndRetryStates() { - try { - console.log('Resetting error metrics and retry states...'); - - // Configure error handling - await configureAdvancedErrorHandling(); - - // Get current metrics - const beforeMetrics = await DailyNotification.getErrorMetrics(); - const beforeRetryStats = await DailyNotification.getRetryStatistics(); - - console.log('📊 Before Reset:'); - console.log(` Total Errors: ${beforeMetrics.totalErrors}`); - console.log(` Active Retries: ${beforeRetryStats.activeRetries}`); - - // Reset metrics - await DailyNotification.resetErrorMetrics(); - console.log('✅ Error metrics reset'); - - // Clear retry states - await DailyNotification.clearRetryStates(); - console.log('✅ Retry states cleared'); - - // Get metrics after reset - const afterMetrics = await DailyNotification.getErrorMetrics(); - const afterRetryStats = await DailyNotification.getRetryStatistics(); - - console.log('📊 After Reset:'); - console.log(` Total Errors: ${afterMetrics.totalErrors}`); - console.log(` Active Retries: ${afterRetryStats.activeRetries}`); - - } catch (error) { - console.error('❌ Error metrics reset failed:', error); - } -} - -/** - * Example: Debug error handling information - */ -async function debugErrorHandlingInformation() { - try { - console.log('Debugging error handling information...'); - - // Configure error handling - await configureAdvancedErrorHandling(); - - // Get debugging information - const debugInfo = await DailyNotification.getErrorDebugInfo(); - - console.log('🐛 Error Debug Information:'); - console.log(` Error Handler Status: ${debugInfo.handlerStatus}`); - console.log(` Configuration: ${JSON.stringify(debugInfo.configuration)}`); - console.log(` Recent Errors: ${debugInfo.recentErrors.length}`); - console.log(` Retry States: ${debugInfo.retryStates.length}`); - - // Display recent errors - if (debugInfo.recentErrors.length > 0) { - console.log('📋 Recent Errors:'); - debugInfo.recentErrors.forEach((error, index) => { - console.log(` ${index + 1}. ${error.category} - ${error.severity} - ${error.errorCode}`); - }); - } - - // Display retry states - if (debugInfo.retryStates.length > 0) { - console.log('🔄 Retry States:'); - debugInfo.retryStates.forEach((state, index) => { - console.log(` ${index + 1}. Operation: ${state.operationId} - Attempts: ${state.attemptCount}`); - }); - } - - } catch (error) { - console.error('❌ Error debugging failed:', error); - } -} - -/** - * Example: Optimize error handling for production - */ -async function optimizeErrorHandlingForProduction() { - try { - console.log('Optimizing error handling for production...'); - - // Configure production-optimized error handling - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 1800, - prefetchLeadMinutes: 15, - enableErrorHandling: true, - maxRetries: 3, - baseRetryDelay: 1000, - maxRetryDelay: 30000, - backoffMultiplier: 2.0, - enableErrorTelemetry: true, - errorReportingEndpoint: 'https://api.example.com/errors' - }); - - console.log('✅ Production error handling configured'); - - // The plugin will now: - // - Use production-optimized retry settings - // - Enable error telemetry and reporting - // - Send error data to monitoring endpoint - // - Provide comprehensive debugging information - // - Handle errors gracefully without user impact - - // Schedule notification with production error handling - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready' - }); - - console.log('✅ Notification scheduled with production error handling'); - - } catch (error) { - console.error('❌ Production error handling optimization failed:', error); - } -} - -// Export examples for use -export { - configureAdvancedErrorHandling, - demonstrateErrorCategorization, - demonstrateRetryLogic, - checkErrorMetricsAndTelemetry, - handleCustomRetryConfigurations, - monitorErrorPatternsOverTime, - resetErrorMetricsAndRetryStates, - debugErrorHandlingInformation, - optimizeErrorHandlingForProduction -}; diff --git a/examples/phase3-3-performance-optimization.ts b/examples/phase3-3-performance-optimization.ts deleted file mode 100644 index 48c9c2c..0000000 --- a/examples/phase3-3-performance-optimization.ts +++ /dev/null @@ -1,413 +0,0 @@ -/** - * Phase 3.3 Performance Optimization Usage Example - * - * Demonstrates comprehensive performance optimization including database, memory, and battery - * Shows query optimization, memory management, object pooling, and performance monitoring - * - * @author Matthew Raymer - * @version 1.0.0 - */ - -import { DailyNotification } from '@timesafari/daily-notification-plugin'; - -/** - * Example: Configure performance optimization - */ -async function configurePerformanceOptimization() { - try { - console.log('Configuring performance optimization...'); - - // Configure with performance optimization - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 1800, // 30 minutes TTL - prefetchLeadMinutes: 15, - enablePerformanceOptimization: true, - enableDatabaseIndexes: true, - enableObjectPooling: true, - enableMemoryMonitoring: true, - enableBatteryOptimization: true, - memoryWarningThreshold: 50, // MB - memoryCriticalThreshold: 100, // MB - objectPoolSize: 10, - maxObjectPoolSize: 50 - }); - - console.log('✅ Performance optimization configured'); - - // The plugin will now: - // - Add database indexes for query optimization - // - Implement object pooling for frequently used objects - // - Monitor memory usage with automatic cleanup - // - Optimize battery usage and background CPU - // - Track performance metrics and provide reports - - } catch (error) { - console.error('❌ Performance optimization configuration failed:', error); - } -} - -/** - * Example: Demonstrate database optimization - */ -async function demonstrateDatabaseOptimization() { - try { - console.log('Demonstrating database optimization...'); - - // Configure performance optimization - await configurePerformanceOptimization(); - - // Optimize database - console.log('🗄️ Optimizing database...'); - await DailyNotification.optimizeDatabase(); - - // The plugin will: - // - Add indexes for common queries (slot_id, fetched_at, status, etc.) - // - Optimize query performance with PRAGMA settings - // - Implement connection pooling with cache optimization - // - Analyze database performance and update metrics - - console.log('✅ Database optimization completed'); - - // Check database performance metrics - const dbMetrics = await DailyNotification.getDatabaseMetrics(); - console.log('📊 Database Metrics:'); - console.log(` Page Count: ${dbMetrics.pageCount}`); - console.log(` Page Size: ${dbMetrics.pageSize}`); - console.log(` Cache Size: ${dbMetrics.cacheSize}`); - console.log(` Query Performance: ${dbMetrics.queryPerformance}`); - - } catch (error) { - console.error('❌ Database optimization demonstration failed:', error); - } -} - -/** - * Example: Demonstrate memory optimization - */ -async function demonstrateMemoryOptimization() { - try { - console.log('Demonstrating memory optimization...'); - - // Configure performance optimization - await configurePerformanceOptimization(); - - // Check initial memory usage - const initialMemory = await DailyNotification.getMemoryUsage(); - console.log(`📊 Initial Memory Usage: ${initialMemory.usage}MB`); - - // Optimize memory - console.log('🧠 Optimizing memory...'); - await DailyNotification.optimizeMemory(); - - // The plugin will: - // - Check current memory usage - // - Perform cleanup if thresholds exceeded - // - Optimize object pools - // - Clear old caches - // - Update memory metrics - - console.log('✅ Memory optimization completed'); - - // Check memory after optimization - const optimizedMemory = await DailyNotification.getMemoryUsage(); - console.log(`📊 Optimized Memory Usage: ${optimizedMemory.usage}MB`); - console.log(`📊 Memory Reduction: ${initialMemory.usage - optimizedMemory.usage}MB`); - - // Check memory metrics - const memoryMetrics = await DailyNotification.getMemoryMetrics(); - console.log('📊 Memory Metrics:'); - console.log(` Average Usage: ${memoryMetrics.averageUsage}MB`); - console.log(` Peak Usage: ${memoryMetrics.peakUsage}MB`); - console.log(` Cleanup Count: ${memoryMetrics.cleanupCount}`); - console.log(` Critical Cleanups: ${memoryMetrics.criticalCleanupCount}`); - - } catch (error) { - console.error('❌ Memory optimization demonstration failed:', error); - } -} - -/** - * Example: Demonstrate object pooling - */ -async function demonstrateObjectPooling() { - try { - console.log('Demonstrating object pooling...'); - - // Configure performance optimization - await configurePerformanceOptimization(); - - // Get objects from pool - console.log('🔄 Using object pooling...'); - - const objects = []; - for (let i = 0; i < 5; i++) { - const obj = await DailyNotification.getPooledObject('String'); - objects.push(obj); - console.log(` Got object ${i + 1} from pool`); - } - - // Return objects to pool - for (let i = 0; i < objects.length; i++) { - await DailyNotification.returnPooledObject('String', objects[i]); - console.log(` Returned object ${i + 1} to pool`); - } - - // The plugin will: - // - Reuse objects from pool instead of creating new ones - // - Reduce memory allocation and garbage collection - // - Track pool hits and misses - // - Optimize pool sizes based on usage patterns - - console.log('✅ Object pooling demonstration completed'); - - // Check object pool metrics - const poolMetrics = await DailyNotification.getObjectPoolMetrics(); - console.log('📊 Object Pool Metrics:'); - console.log(` Pool Hits: ${poolMetrics.poolHits}`); - console.log(` Pool Misses: ${poolMetrics.poolMisses}`); - console.log(` Hit Ratio: ${(poolMetrics.hitRatio * 100).toFixed(1)}%`); - console.log(` Active Pools: ${poolMetrics.activePools}`); - - } catch (error) { - console.error('❌ Object pooling demonstration failed:', error); - } -} - -/** - * Example: Demonstrate battery optimization - */ -async function demonstrateBatteryOptimization() { - try { - console.log('Demonstrating battery optimization...'); - - // Configure performance optimization - await configurePerformanceOptimization(); - - // Optimize battery usage - console.log('🔋 Optimizing battery usage...'); - await DailyNotification.optimizeBattery(); - - // The plugin will: - // - Minimize background CPU usage - // - Optimize network requests for efficiency - // - Track battery usage patterns - // - Adjust behavior based on battery level - // - Reduce task frequency during low battery - - console.log('✅ Battery optimization completed'); - - // Check battery metrics - const batteryMetrics = await DailyNotification.getBatteryMetrics(); - console.log('📊 Battery Metrics:'); - console.log(` Background CPU Usage: ${batteryMetrics.backgroundCpuUsage}%`); - console.log(` Network Efficiency: ${batteryMetrics.networkEfficiency}%`); - console.log(` Battery Level: ${batteryMetrics.batteryLevel}%`); - console.log(` Power Saving Mode: ${batteryMetrics.powerSavingMode ? 'Enabled' : 'Disabled'}`); - - } catch (error) { - console.error('❌ Battery optimization demonstration failed:', error); - } -} - -/** - * Example: Monitor performance metrics - */ -async function monitorPerformanceMetrics() { - try { - console.log('Monitoring performance metrics...'); - - // Configure performance optimization - await configurePerformanceOptimization(); - - // Get comprehensive performance metrics - const performanceMetrics = await DailyNotification.getPerformanceMetrics(); - - console.log('📊 Performance Metrics:'); - console.log(` Overall Score: ${performanceMetrics.overallScore}/100`); - console.log(` Database Performance: ${performanceMetrics.databasePerformance}/100`); - console.log(` Memory Efficiency: ${performanceMetrics.memoryEfficiency}/100`); - console.log(` Battery Efficiency: ${performanceMetrics.batteryEfficiency}/100`); - console.log(` Object Pool Efficiency: ${performanceMetrics.objectPoolEfficiency}/100`); - - // Detailed metrics - console.log('📊 Detailed Metrics:'); - console.log(` Database Queries: ${performanceMetrics.totalDatabaseQueries}`); - console.log(` Memory Usage: ${performanceMetrics.averageMemoryUsage}MB`); - console.log(` Object Pool Hits: ${performanceMetrics.objectPoolHits}`); - console.log(` Background CPU: ${performanceMetrics.backgroundCpuUsage}%`); - console.log(` Network Requests: ${performanceMetrics.totalNetworkRequests}`); - - // Performance recommendations - if (performanceMetrics.recommendations.length > 0) { - console.log('💡 Performance Recommendations:'); - performanceMetrics.recommendations.forEach((rec, index) => { - console.log(` ${index + 1}. ${rec}`); - }); - } - - } catch (error) { - console.error('❌ Performance metrics monitoring failed:', error); - } -} - -/** - * Example: Performance optimization for production - */ -async function optimizeForProduction() { - try { - console.log('Optimizing for production...'); - - // Configure production-optimized settings - await DailyNotification.configure({ - storage: 'shared', - ttlSeconds: 1800, - prefetchLeadMinutes: 15, - enablePerformanceOptimization: true, - enableDatabaseIndexes: true, - enableObjectPooling: true, - enableMemoryMonitoring: true, - enableBatteryOptimization: true, - memoryWarningThreshold: 30, // Lower threshold for production - memoryCriticalThreshold: 60, // Lower threshold for production - objectPoolSize: 20, // Larger pool for production - maxObjectPoolSize: 100, // Larger max pool for production - enablePerformanceReporting: true, - performanceReportInterval: 3600000 // 1 hour - }); - - console.log('✅ Production optimization configured'); - - // Run all optimizations - console.log('🚀 Running production optimizations...'); - - await DailyNotification.optimizeDatabase(); - await DailyNotification.optimizeMemory(); - await DailyNotification.optimizeBattery(); - - console.log('✅ Production optimizations completed'); - - // Schedule notifications with optimized performance - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:00', - title: 'Daily Update', - body: 'Your daily notification is ready' - }); - - console.log('✅ Notification scheduled with production optimization'); - - // The plugin will now: - // - Use optimized database queries with indexes - // - Manage memory efficiently with automatic cleanup - // - Pool objects to reduce allocation overhead - // - Monitor battery usage and adjust behavior - // - Provide comprehensive performance reporting - // - Handle high load scenarios gracefully - - } catch (error) { - console.error('❌ Production optimization failed:', error); - } -} - -/** - * Example: Performance stress testing - */ -async function performanceStressTesting() { - try { - console.log('Running performance stress testing...'); - - // Configure performance optimization - await configurePerformanceOptimization(); - - // Stress test with multiple operations - const operations = []; - for (let i = 0; i < 10; i++) { - operations.push( - DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: `09:${i.toString().padStart(2, '0')}`, - title: `Daily Update ${i + 1}`, - body: 'Your daily notification is ready' - }) - ); - } - - console.log('📡 Executing 10 concurrent operations...'); - const startTime = Date.now(); - - await Promise.all(operations); - - const endTime = Date.now(); - const duration = endTime - startTime; - - console.log(`✅ Stress test completed in ${duration}ms`); - - // Check performance under load - const stressMetrics = await DailyNotification.getPerformanceMetrics(); - console.log('📊 Stress Test Results:'); - console.log(` Operations Completed: 10`); - console.log(` Total Duration: ${duration}ms`); - console.log(` Average per Operation: ${duration / 10}ms`); - console.log(` Performance Score: ${stressMetrics.overallScore}/100`); - console.log(` Memory Usage: ${stressMetrics.averageMemoryUsage}MB`); - - // Performance should remain stable under load - if (stressMetrics.overallScore >= 80) { - console.log('✅ Excellent performance under load'); - } else if (stressMetrics.overallScore >= 60) { - console.log('✅ Good performance under load'); - } else { - console.log('⚠️ Performance degradation under load detected'); - } - - } catch (error) { - console.error('❌ Performance stress testing failed:', error); - } -} - -/** - * Example: Reset performance metrics - */ -async function resetPerformanceMetrics() { - try { - console.log('Resetting performance metrics...'); - - // Configure performance optimization - await configurePerformanceOptimization(); - - // Get metrics before reset - const beforeMetrics = await DailyNotification.getPerformanceMetrics(); - console.log('📊 Before Reset:'); - console.log(` Overall Score: ${beforeMetrics.overallScore}/100`); - console.log(` Database Queries: ${beforeMetrics.totalDatabaseQueries}`); - console.log(` Memory Usage: ${beforeMetrics.averageMemoryUsage}MB`); - - // Reset metrics - await DailyNotification.resetPerformanceMetrics(); - console.log('✅ Performance metrics reset'); - - // Get metrics after reset - const afterMetrics = await DailyNotification.getPerformanceMetrics(); - console.log('📊 After Reset:'); - console.log(` Overall Score: ${afterMetrics.overallScore}/100`); - console.log(` Database Queries: ${afterMetrics.totalDatabaseQueries}`); - console.log(` Memory Usage: ${afterMetrics.averageMemoryUsage}MB`); - - } catch (error) { - console.error('❌ Performance metrics reset failed:', error); - } -} - -// Export examples for use -export { - configurePerformanceOptimization, - demonstrateDatabaseOptimization, - demonstrateMemoryOptimization, - demonstrateObjectPooling, - demonstrateBatteryOptimization, - monitorPerformanceMetrics, - optimizeForProduction, - performanceStressTesting, - resetPerformanceMetrics -}; diff --git a/examples/static-daily-reminders.ts b/examples/static-daily-reminders.ts deleted file mode 100644 index 34c9157..0000000 --- a/examples/static-daily-reminders.ts +++ /dev/null @@ -1,342 +0,0 @@ -/** - * Static Daily Reminders Examples - * - * Examples demonstrating the static daily reminder functionality - * that works without network content or content caching. - * - * @author Matthew Raymer - * @version 1.0.0 - */ - -import { DailyNotification } from '@timesafari/daily-notification-plugin'; - -/** - * Basic Daily Reminder - * - * Schedule a simple daily reminder with default settings - */ -export async function scheduleBasicReminder() { - try { - await DailyNotification.scheduleDailyReminder({ - id: 'morning_checkin', - title: 'Good Morning!', - body: 'Time to check your TimeSafari community updates', - time: '09:00' - }); - - console.log('✅ Basic reminder scheduled for 9:00 AM daily'); - } catch (error) { - console.error('❌ Failed to schedule basic reminder:', error); - } -} - -/** - * Customized Daily Reminder - * - * Schedule a reminder with custom sound, vibration, and priority - */ -export async function scheduleCustomizedReminder() { - try { - await DailyNotification.scheduleDailyReminder({ - id: 'evening_reflection', - title: 'Evening Reflection', - body: 'Take a moment to reflect on your day and plan for tomorrow', - time: '20:00', - sound: true, - vibration: true, - priority: 'high', - repeatDaily: true - }); - - console.log('✅ Customized reminder scheduled for 8:00 PM daily'); - } catch (error) { - console.error('❌ Failed to schedule customized reminder:', error); - } -} - -/** - * Multiple Daily Reminders - * - * Schedule multiple reminders throughout the day - */ -export async function scheduleMultipleReminders() { - const reminders = [ - { - id: 'morning_motivation', - title: 'Morning Motivation', - body: 'Start your day with positive energy!', - time: '07:00', - priority: 'high' as const - }, - { - id: 'lunch_break', - title: 'Lunch Break', - body: 'Time for a well-deserved break', - time: '12:30', - priority: 'normal' as const - }, - { - id: 'evening_gratitude', - title: 'Evening Gratitude', - body: 'What are you grateful for today?', - time: '19:00', - priority: 'normal' as const - } - ]; - - try { - for (const reminder of reminders) { - await DailyNotification.scheduleDailyReminder(reminder); - console.log(`✅ Scheduled reminder: ${reminder.id} at ${reminder.time}`); - } - - console.log('✅ All reminders scheduled successfully'); - } catch (error) { - console.error('❌ Failed to schedule multiple reminders:', error); - } -} - -/** - * Get All Scheduled Reminders - * - * Retrieve and display all currently scheduled reminders - */ -export async function getAllScheduledReminders() { - try { - const result = await DailyNotification.getScheduledReminders(); - - if (result.reminders && result.reminders.length > 0) { - console.log(`📋 Found ${result.reminders.length} scheduled reminders:`); - - result.reminders.forEach((reminder, index) => { - console.log(`${index + 1}. ${reminder.title} (${reminder.id})`); - console.log(` Time: ${reminder.time}`); - console.log(` Priority: ${reminder.priority}`); - console.log(` Repeat Daily: ${reminder.repeatDaily}`); - console.log(` Scheduled: ${reminder.isScheduled ? 'Yes' : 'No'}`); - console.log(''); - }); - } else { - console.log('📋 No scheduled reminders found'); - } - } catch (error) { - console.error('❌ Failed to get scheduled reminders:', error); - } -} - -/** - * Update Existing Reminder - * - * Update an existing reminder with new settings - */ -export async function updateExistingReminder() { - try { - // Update the morning check-in reminder - await DailyNotification.updateDailyReminder('morning_checkin', { - title: 'Updated Morning Check-in', - body: 'Time to check your TimeSafari community updates and plan your day', - time: '08:30', - priority: 'high' - }); - - console.log('✅ Morning check-in reminder updated to 8:30 AM with high priority'); - } catch (error) { - console.error('❌ Failed to update reminder:', error); - } -} - -/** - * Cancel Specific Reminder - * - * Cancel a specific reminder by ID - */ -export async function cancelSpecificReminder() { - try { - await DailyNotification.cancelDailyReminder('evening_reflection'); - console.log('✅ Evening reflection reminder cancelled'); - } catch (error) { - console.error('❌ Failed to cancel reminder:', error); - } -} - -/** - * Cancel All Reminders - * - * Cancel all scheduled reminders - */ -export async function cancelAllReminders() { - try { - const result = await DailyNotification.getScheduledReminders(); - - if (result.reminders && result.reminders.length > 0) { - for (const reminder of result.reminders) { - await DailyNotification.cancelDailyReminder(reminder.id); - console.log(`✅ Cancelled reminder: ${reminder.id}`); - } - - console.log('✅ All reminders cancelled successfully'); - } else { - console.log('📋 No reminders to cancel'); - } - } catch (error) { - console.error('❌ Failed to cancel all reminders:', error); - } -} - -/** - * Reminder Management Workflow - * - * Complete workflow demonstrating reminder management - */ -export async function reminderManagementWorkflow() { - console.log('🚀 Starting reminder management workflow...\n'); - - try { - // 1. Schedule initial reminders - console.log('1. Scheduling initial reminders...'); - await scheduleMultipleReminders(); - console.log(''); - - // 2. Get all reminders - console.log('2. Getting all scheduled reminders...'); - await getAllScheduledReminders(); - console.log(''); - - // 3. Update a reminder - console.log('3. Updating morning check-in reminder...'); - await updateExistingReminder(); - console.log(''); - - // 4. Get updated reminders - console.log('4. Getting updated reminders...'); - await getAllScheduledReminders(); - console.log(''); - - // 5. Cancel a specific reminder - console.log('5. Cancelling evening reflection reminder...'); - await cancelSpecificReminder(); - console.log(''); - - // 6. Final reminder list - console.log('6. Final reminder list...'); - await getAllScheduledReminders(); - - console.log('🎉 Reminder management workflow completed successfully!'); - } catch (error) { - console.error('❌ Reminder management workflow failed:', error); - } -} - -/** - * Time Format Validation Examples - * - * Examples of valid and invalid time formats - */ -export function timeFormatExamples() { - console.log('⏰ Time Format Examples:'); - console.log(''); - - const validTimes = [ - '00:00', // Midnight - '06:30', // 6:30 AM - '12:00', // Noon - '18:45', // 6:45 PM - '23:59' // 11:59 PM - ]; - - const invalidTimes = [ - '24:00', // Invalid hour - '12:60', // Invalid minute - '9:00', // Missing leading zero - '12:0', // Missing trailing zero - '12', // Missing minutes - 'abc' // Non-numeric - ]; - - console.log('✅ Valid time formats:'); - validTimes.forEach(time => { - console.log(` "${time}" - Valid`); - }); - - console.log(''); - console.log('❌ Invalid time formats:'); - invalidTimes.forEach(time => { - console.log(` "${time}" - Invalid`); - }); - - console.log(''); - console.log('📝 Time format rules:'); - console.log(' - Use HH:mm format (24-hour)'); - console.log(' - Hours: 00-23'); - console.log(' - Minutes: 00-59'); - console.log(' - Always use leading zeros'); -} - -/** - * Platform-Specific Considerations - * - * Important notes for different platforms - */ -export function platformConsiderations() { - console.log('📱 Platform-Specific Considerations:'); - console.log(''); - - console.log('🤖 Android:'); - console.log(' - Uses AlarmManager for precise scheduling'); - console.log(' - Requires POST_NOTIFICATIONS permission'); - console.log(' - May be affected by battery optimization'); - console.log(' - Survives device reboots'); - console.log(''); - - console.log('🍎 iOS:'); - console.log(' - Uses UNUserNotificationCenter'); - console.log(' - Requires notification permissions'); - console.log(' - Limited to 64 scheduled notifications'); - console.log(' - May be affected by Background App Refresh'); - console.log(''); - - console.log('🌐 Web:'); - console.log(' - Uses setTimeout for scheduling'); - console.log(' - Requires notification permissions'); - console.log(' - Limited by browser tab lifecycle'); - console.log(' - May not persist across browser restarts'); - console.log(''); - - console.log('💡 Best Practices:'); - console.log(' - Always request notification permissions first'); - console.log(' - Handle permission denials gracefully'); - console.log(' - Test on actual devices, not just simulators'); - console.log(' - Consider platform limitations in your app design'); -} - -// Export all functions for easy importing -export const StaticReminderExamples = { - scheduleBasicReminder, - scheduleCustomizedReminder, - scheduleMultipleReminders, - getAllScheduledReminders, - updateExistingReminder, - cancelSpecificReminder, - cancelAllReminders, - reminderManagementWorkflow, - timeFormatExamples, - platformConsiderations -}; - -// Example usage -if (require.main === module) { - console.log('📚 Static Daily Reminders Examples'); - console.log('=====================================\n'); - - // Show time format examples - timeFormatExamples(); - console.log(''); - - // Show platform considerations - platformConsiderations(); - console.log(''); - - console.log('💡 To run the examples, import and call the functions:'); - console.log(' import { StaticReminderExamples } from "./static-daily-reminders";'); - console.log(' await StaticReminderExamples.reminderManagementWorkflow();'); -} diff --git a/examples/ui-integration-examples.ts b/examples/ui-integration-examples.ts deleted file mode 100644 index ca49021..0000000 --- a/examples/ui-integration-examples.ts +++ /dev/null @@ -1,1304 +0,0 @@ -/** - * UI Integration Examples for Daily Notification Plugin - * - * This file provides comprehensive examples of how to integrate - * the Daily Notification Plugin UI components into your application. - * - * @author Matthew Raymer - * @version 1.0.0 - * @created 2025-01-27 12:00:00 UTC - */ - -import { DailyNotification } from '@timesafari/daily-notification-plugin'; -import type { - NotificationSettings, - ConfigureOptions, - DualScheduleStatus, - PermissionStatus, - BatteryStatus, - ExactAlarmStatus, - RollingWindowStats, - PerformanceMetrics -} from '@timesafari/daily-notification-plugin'; - -// ============================================================================ -// 1. PERMISSION MANAGEMENT UI -// ============================================================================ - -/** - * Permission Request Dialog Component - * Handles initial permission requests and status display - */ -export class PermissionRequestDialog { - private container: HTMLElement; - private onAllow: () => Promise; - private onDeny: () => void; - private onNever: () => void; - - constructor(container: HTMLElement, callbacks: { - onAllow: () => Promise; - onDeny: () => void; - onNever: () => void; - }) { - this.container = container; - this.onAllow = callbacks.onAllow; - this.onDeny = callbacks.onDeny; - this.onNever = callbacks.onNever; - } - - /** - * Show permission request dialog - */ - async show(): Promise { - const dialog = this.createDialog(); - this.container.appendChild(dialog); - - // Add event listeners - dialog.querySelector('#allow-btn')?.addEventListener('click', async () => { - await this.onAllow(); - this.hide(); - }); - - dialog.querySelector('#deny-btn')?.addEventListener('click', () => { - this.onDeny(); - this.hide(); - }); - - dialog.querySelector('#never-btn')?.addEventListener('click', () => { - this.onNever(); - this.hide(); - }); - } - - /** - * Hide permission request dialog - */ - hide(): void { - const dialog = this.container.querySelector('.permission-dialog'); - if (dialog) { - dialog.remove(); - } - } - - /** - * Create permission request dialog HTML - */ - private createDialog(): HTMLElement { - const dialog = document.createElement('div'); - dialog.className = 'permission-dialog'; - dialog.innerHTML = ` -
-
-

Enable Daily Notifications

-

Get notified about new offers, projects, people, and items in your TimeSafari community.

-
    -
  • New offers directed to you
  • -
  • Changes to your projects
  • -
  • Updates from favorited people
  • -
  • New items of interest
  • -
-
- - - -
-
-
- `; - return dialog; - } -} - -/** - * Permission Status Display Component - * Shows current permission status and provides actions - */ -export class PermissionStatusDisplay { - private container: HTMLElement; - - constructor(container: HTMLElement) { - this.container = container; - } - - /** - * Update permission status display - */ - async updateStatus(): Promise { - try { - const status = await DailyNotification.checkPermissions(); - this.renderStatus(status); - } catch (error) { - console.error('Failed to check permissions:', error); - this.renderError('Failed to check permission status'); - } - } - - /** - * Render permission status - */ - private renderStatus(status: PermissionStatus): void { - const statusClass = status.granted ? 'status-granted' : 'status-denied'; - const statusText = status.granted ? 'Granted' : 'Denied'; - - this.container.innerHTML = ` -
-
- ${status.granted ? '✓' : '✗'} - ${statusText} -
-
-
- Notifications: - - ${status.notifications} - -
- ${status.backgroundRefresh ? ` -
- Background Refresh: - - ${status.backgroundRefresh} - -
- ` : ''} -
-
- - -
-
- `; - - // Add event listeners - this.container.querySelector('#request-permissions')?.addEventListener('click', async () => { - try { - await DailyNotification.requestPermissions(); - await this.updateStatus(); - } catch (error) { - console.error('Failed to request permissions:', error); - } - }); - - this.container.querySelector('#open-settings')?.addEventListener('click', () => { - // Platform-specific settings opening - this.openSettings(); - }); - } - - /** - * Render error state - */ - private renderError(message: string): void { - this.container.innerHTML = ` -
-
${message}
- -
- `; - - this.container.querySelector('#retry-permissions')?.addEventListener('click', () => { - this.updateStatus(); - }); - } - - /** - * Open platform-specific settings - */ - private openSettings(): void { - // This would be platform-specific implementation - console.log('Opening settings...'); - } -} - -// ============================================================================ -// 2. CONFIGURATION UI -// ============================================================================ - -/** - * Notification Settings Panel Component - * Handles notification configuration and preferences - */ -export class NotificationSettingsPanel { - private container: HTMLElement; - private settings: NotificationSettings; - - constructor(container: HTMLElement) { - this.container = container; - this.settings = this.getDefaultSettings(); - } - - /** - * Initialize settings panel - */ - async initialize(): Promise { - try { - // Load current settings - const status = await DailyNotification.getNotificationStatus(); - this.settings = { - ...this.settings, - ...status - }; - - this.render(); - this.attachEventListeners(); - } catch (error) { - console.error('Failed to initialize settings:', error); - this.renderError('Failed to load settings'); - } - } - - /** - * Render settings panel - */ - private render(): void { - this.container.innerHTML = ` -
-

Notification Settings

- -
- -
- -
- - -
- -
- -
- - - - -
-
- -
- -
- - - -
-
- -
- - -
-
- `; - } - - /** - * Attach event listeners - */ - private attachEventListeners(): void { - // Save settings - this.container.querySelector('#save-settings')?.addEventListener('click', async () => { - await this.saveSettings(); - }); - - // Test notification - this.container.querySelector('#test-notification')?.addEventListener('click', async () => { - await this.sendTestNotification(); - }); - - // Real-time updates - this.container.querySelectorAll('input, select').forEach(element => { - element.addEventListener('change', () => { - this.updateSettingsFromUI(); - }); - }); - } - - /** - * Update settings from UI - */ - private updateSettingsFromUI(): void { - const enableNotifications = (this.container.querySelector('#enable-notifications') as HTMLInputElement)?.checked; - const notificationTime = (this.container.querySelector('#notification-time') as HTMLInputElement)?.value; - const soundEnabled = (this.container.querySelector('#sound-enabled') as HTMLInputElement)?.checked; - const priorityLevel = (this.container.querySelector('#priority-level') as HTMLSelectElement)?.value; - - this.settings = { - ...this.settings, - isEnabled: enableNotifications, - time: notificationTime, - sound: soundEnabled, - priority: priorityLevel - }; - } - - /** - * Save settings - */ - private async saveSettings(): Promise { - try { - await DailyNotification.updateSettings(this.settings); - this.showSuccess('Settings saved successfully'); - } catch (error) { - console.error('Failed to save settings:', error); - this.showError('Failed to save settings'); - } - } - - /** - * Send test notification - */ - private async sendTestNotification(): Promise { - try { - // This would trigger a test notification - this.showSuccess('Test notification sent'); - } catch (error) { - console.error('Failed to send test notification:', error); - this.showError('Failed to send test notification'); - } - } - - /** - * Get default settings - */ - private getDefaultSettings(): NotificationSettings { - return { - isEnabled: true, - time: '09:00', - sound: true, - priority: 'normal', - timezone: Intl.DateTimeFormat().resolvedOptions().timeZone - }; - } - - /** - * Show success message - */ - private showSuccess(message: string): void { - // Implementation for success message - console.log('Success:', message); - } - - /** - * Show error message - */ - private showError(message: string): void { - // Implementation for error message - console.error('Error:', message); - } - - /** - * Render error state - */ - private renderError(message: string): void { - this.container.innerHTML = ` -
-

${message}

- -
- `; - - this.container.querySelector('#retry-settings')?.addEventListener('click', () => { - this.initialize(); - }); - } -} - -// ============================================================================ -// 3. STATUS MONITORING UI -// ============================================================================ - -/** - * Status Dashboard Component - * Displays real-time notification system status - */ -export class StatusDashboard { - private container: HTMLElement; - private refreshInterval: number | null = null; - - constructor(container: HTMLElement) { - this.container = container; - } - - /** - * Initialize status dashboard - */ - async initialize(): Promise { - await this.updateStatus(); - this.startAutoRefresh(); - } - - /** - * Update status display - */ - async updateStatus(): Promise { - try { - const status = await DailyNotification.getDualScheduleStatus(); - this.renderStatus(status); - } catch (error) { - console.error('Failed to get status:', error); - this.renderError('Failed to load status'); - } - } - - /** - * Render status display - */ - private renderStatus(status: DualScheduleStatus): void { - const nextRun = status.nextRuns[0] ? new Date(status.nextRuns[0]) : null; - const nextRunText = nextRun ? this.formatTimeUntil(nextRun) : 'Not scheduled'; - - const lastOutcome = status.lastOutcomes[0] || 'Unknown'; - const lastOutcomeClass = lastOutcome === 'success' ? 'success' : 'error'; - - this.container.innerHTML = ` -
-

Notification Status

- -
-
-
Overall Status
-
- ${status.staleArmed ? 'Stale' : 'Active'} -
-
- -
-
Next Notification
-
${nextRunText}
-
- -
-
Last Outcome
-
${lastOutcome}
-
- -
-
Cache Age
-
${status.cacheAgeMs ? this.formatDuration(status.cacheAgeMs) : 'No cache'}
-
-
- -
-

Performance

-
-
- Success Rate: - ${status.performance.successRate}% -
-
- Error Count: - ${status.performance.errorCount} -
-
-
- -
- - -
-
- `; - - // Add event listeners - this.container.querySelector('#refresh-status')?.addEventListener('click', () => { - this.updateStatus(); - }); - - this.container.querySelector('#view-details')?.addEventListener('click', () => { - this.showDetailedStatus(); - }); - } - - /** - * Start auto-refresh - */ - private startAutoRefresh(): void { - this.refreshInterval = window.setInterval(() => { - this.updateStatus(); - }, 30000); // Refresh every 30 seconds - } - - /** - * Stop auto-refresh - */ - stopAutoRefresh(): void { - if (this.refreshInterval) { - clearInterval(this.refreshInterval); - this.refreshInterval = null; - } - } - - /** - * Format time until date - */ - private formatTimeUntil(date: Date): string { - const now = new Date(); - const diff = date.getTime() - now.getTime(); - - if (diff <= 0) return 'Now'; - - const hours = Math.floor(diff / (1000 * 60 * 60)); - const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); - - if (hours > 0) { - return `${hours}h ${minutes}m`; - } else { - return `${minutes}m`; - } - } - - /** - * Format duration in milliseconds - */ - private formatDuration(ms: number): string { - const seconds = Math.floor(ms / 1000); - const minutes = Math.floor(seconds / 60); - const hours = Math.floor(minutes / 60); - - if (hours > 0) { - return `${hours}h ${minutes % 60}m`; - } else if (minutes > 0) { - return `${minutes}m ${seconds % 60}s`; - } else { - return `${seconds}s`; - } - } - - /** - * Show detailed status - */ - private showDetailedStatus(): void { - // Implementation for detailed status view - console.log('Showing detailed status...'); - } - - /** - * Render error state - */ - private renderError(message: string): void { - this.container.innerHTML = ` -
-

${message}

- -
- `; - - this.container.querySelector('#retry-status')?.addEventListener('click', () => { - this.updateStatus(); - }); - } -} - -// ============================================================================ -// 4. PLATFORM-SPECIFIC UI -// ============================================================================ - -/** - * Android Battery Optimization Dialog - * Handles Android-specific battery optimization prompts - */ -export class AndroidBatteryOptimizationDialog { - private container: HTMLElement; - - constructor(container: HTMLElement) { - this.container = container; - } - - /** - * Check and show battery optimization dialog if needed - */ - async checkAndShow(): Promise { - try { - const batteryStatus = await DailyNotification.getBatteryStatus(); - - if (batteryStatus.optimizationEnabled) { - this.showDialog(); - } - } catch (error) { - console.error('Failed to check battery status:', error); - } - } - - /** - * Show battery optimization dialog - */ - private showDialog(): void { - const dialog = document.createElement('div'); - dialog.className = 'battery-optimization-dialog'; - dialog.innerHTML = ` -
-
-

Battery Optimization

-

Battery optimization may prevent notifications from being delivered reliably.

-

For the best experience, please disable battery optimization for this app.

-
- - -
-
-
- `; - - this.container.appendChild(dialog); - - // Add event listeners - dialog.querySelector('#open-battery-settings')?.addEventListener('click', async () => { - try { - await DailyNotification.requestBatteryOptimizationExemption(); - this.hideDialog(); - } catch (error) { - console.error('Failed to open battery settings:', error); - } - }); - - dialog.querySelector('#skip-battery-optimization')?.addEventListener('click', () => { - this.hideDialog(); - }); - } - - /** - * Hide battery optimization dialog - */ - private hideDialog(): void { - const dialog = this.container.querySelector('.battery-optimization-dialog'); - if (dialog) { - dialog.remove(); - } - } -} - -/** - * iOS Background App Refresh Dialog - * Handles iOS-specific background app refresh prompts - */ -export class iOSBackgroundRefreshDialog { - private container: HTMLElement; - - constructor(container: HTMLElement) { - this.container = container; - } - - /** - * Check and show background refresh dialog if needed - */ - async checkAndShow(): Promise { - try { - const status = await DailyNotification.checkPermissions(); - - if (status.backgroundRefresh === 'denied') { - this.showDialog(); - } - } catch (error) { - console.error('Failed to check background refresh status:', error); - } - } - - /** - * Show background refresh dialog - */ - private showDialog(): void { - const dialog = document.createElement('div'); - dialog.className = 'background-refresh-dialog'; - dialog.innerHTML = ` -
-
-

Background App Refresh

-

Background App Refresh enables the app to fetch new content even when it's not actively being used.

-

Without this, notifications will only show cached content.

-
- - -
-
-
- `; - - this.container.appendChild(dialog); - - // Add event listeners - dialog.querySelector('#open-settings')?.addEventListener('click', () => { - // Open iOS settings - window.open('app-settings:', '_blank'); - this.hideDialog(); - }); - - dialog.querySelector('#continue-without')?.addEventListener('click', () => { - this.hideDialog(); - }); - } - - /** - * Hide background refresh dialog - */ - private hideDialog(): void { - const dialog = this.container.querySelector('.background-refresh-dialog'); - if (dialog) { - dialog.remove(); - } - } -} - -// ============================================================================ -// 5. ERROR HANDLING UI -// ============================================================================ - -/** - * Error Display Component - * Shows errors and provides recovery options - */ -export class ErrorDisplay { - private container: HTMLElement; - - constructor(container: HTMLElement) { - this.container = container; - } - - /** - * Show error - */ - showError(error: Error, recoveryActions?: { - onRetry?: () => Promise; - onReset?: () => Promise; - onContactSupport?: () => void; - }): void { - this.container.innerHTML = ` -
-
⚠️
-
-

Something went wrong

-

${error.message}

-

Error Code: ${error.name}

-
-
- ${recoveryActions?.onRetry ? '' : ''} - ${recoveryActions?.onReset ? '' : ''} - ${recoveryActions?.onContactSupport ? '' : ''} -
-
- `; - - // Add event listeners - if (recoveryActions?.onRetry) { - this.container.querySelector('#retry-action')?.addEventListener('click', async () => { - try { - await recoveryActions.onRetry!(); - this.hide(); - } catch (retryError) { - console.error('Retry failed:', retryError); - } - }); - } - - if (recoveryActions?.onReset) { - this.container.querySelector('#reset-action')?.addEventListener('click', async () => { - try { - await recoveryActions.onReset!(); - this.hide(); - } catch (resetError) { - console.error('Reset failed:', resetError); - } - }); - } - - if (recoveryActions?.onContactSupport) { - this.container.querySelector('#support-action')?.addEventListener('click', () => { - recoveryActions.onContactSupport!(); - }); - } - } - - /** - * Hide error display - */ - hide(): void { - this.container.innerHTML = ''; - } -} - -// ============================================================================ -// 6. USAGE EXAMPLES -// ============================================================================ - -/** - * Example: Complete UI Integration - * Shows how to integrate all UI components - */ -export class NotificationUIManager { - private permissionDialog: PermissionRequestDialog; - private permissionStatus: PermissionStatusDisplay; - private settingsPanel: NotificationSettingsPanel; - private statusDashboard: StatusDashboard; - private errorDisplay: ErrorDisplay; - private batteryDialog: AndroidBatteryOptimizationDialog; - private backgroundRefreshDialog: iOSBackgroundRefreshDialog; - - constructor(container: HTMLElement) { - // Initialize all UI components - this.permissionDialog = new PermissionRequestDialog( - container.querySelector('#permission-dialog-container')!, - { - onAllow: this.handlePermissionAllow.bind(this), - onDeny: this.handlePermissionDeny.bind(this), - onNever: this.handlePermissionNever.bind(this) - } - ); - - this.permissionStatus = new PermissionStatusDisplay( - container.querySelector('#permission-status-container')! - ); - - this.settingsPanel = new NotificationSettingsPanel( - container.querySelector('#settings-container')! - ); - - this.statusDashboard = new StatusDashboard( - container.querySelector('#status-container')! - ); - - this.errorDisplay = new ErrorDisplay( - container.querySelector('#error-container')! - ); - - this.batteryDialog = new AndroidBatteryOptimizationDialog( - container.querySelector('#battery-dialog-container')! - ); - - this.backgroundRefreshDialog = new iOSBackgroundRefreshDialog( - container.querySelector('#background-refresh-dialog-container')! - ); - } - - /** - * Initialize the complete UI - */ - async initialize(): Promise { - try { - // Check permissions first - const permissionStatus = await DailyNotification.checkPermissions(); - - if (!permissionStatus.granted) { - await this.permissionDialog.show(); - } else { - await this.permissionStatus.updateStatus(); - } - - // Initialize other components - await this.settingsPanel.initialize(); - await this.statusDashboard.initialize(); - - // Check platform-specific requirements - await this.batteryDialog.checkAndShow(); - await this.backgroundRefreshDialog.checkAndShow(); - - } catch (error) { - console.error('Failed to initialize UI:', error); - this.errorDisplay.showError(error as Error, { - onRetry: () => this.initialize(), - onReset: () => this.resetConfiguration(), - onContactSupport: () => this.contactSupport() - }); - } - } - - /** - * Handle permission allow - */ - private async handlePermissionAllow(): Promise { - try { - await DailyNotification.requestPermissions(); - await this.permissionStatus.updateStatus(); - } catch (error) { - console.error('Failed to request permissions:', error); - this.errorDisplay.showError(error as Error); - } - } - - /** - * Handle permission deny - */ - private handlePermissionDeny(): void { - console.log('User denied permissions'); - // Show limited functionality message - } - - /** - * Handle permission never - */ - private handlePermissionNever(): void { - console.log('User chose never ask again'); - // Store preference and show limited functionality message - } - - /** - * Reset configuration - */ - private async resetConfiguration(): Promise { - try { - await DailyNotification.cancelAllNotifications(); - // Reset to default settings - await this.settingsPanel.initialize(); - } catch (error) { - console.error('Failed to reset configuration:', error); - } - } - - /** - * Contact support - */ - private contactSupport(): void { - // Implementation for contacting support - console.log('Contacting support...'); - } - - /** - * Cleanup - */ - destroy(): void { - this.statusDashboard.stopAutoRefresh(); - } -} - -// ============================================================================ -// 7. CSS STYLES (for reference) -// ============================================================================ - -export const notificationUIStyles = ` -/* Permission Dialog Styles */ -.permission-dialog .dialog-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.5); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; -} - -.permission-dialog .dialog-content { - background: white; - border-radius: 12px; - padding: 24px; - max-width: 400px; - margin: 20px; -} - -.permission-dialog h2 { - margin: 0 0 16px 0; - color: #333; -} - -.permission-dialog p { - margin: 0 0 16px 0; - color: #666; - line-height: 1.5; -} - -.permission-dialog ul { - margin: 0 0 24px 0; - padding-left: 20px; -} - -.permission-dialog li { - margin: 8px 0; - color: #666; -} - -.dialog-actions { - display: flex; - flex-direction: column; - gap: 12px; -} - -.btn-primary, .btn-secondary, .btn-tertiary { - padding: 12px 24px; - border: none; - border-radius: 8px; - font-size: 16px; - cursor: pointer; - transition: background-color 0.2s; -} - -.btn-primary { - background: #1976d2; - color: white; -} - -.btn-primary:hover { - background: #1565c0; -} - -.btn-secondary { - background: #f5f5f5; - color: #333; - border: 1px solid #ddd; -} - -.btn-secondary:hover { - background: #e0e0e0; -} - -.btn-tertiary { - background: transparent; - color: #666; -} - -.btn-tertiary:hover { - background: #f5f5f5; -} - -/* Status Display Styles */ -.permission-status { - padding: 16px; - border-radius: 8px; - margin: 16px 0; -} - -.status-granted { - background: #e8f5e8; - border: 1px solid #4caf50; -} - -.status-denied { - background: #ffebee; - border: 1px solid #f44336; -} - -.status-indicator { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 12px; -} - -.status-icon { - font-size: 20px; - font-weight: bold; -} - -.permission-details { - margin: 12px 0; -} - -.permission-item { - display: flex; - justify-content: space-between; - margin: 8px 0; -} - -.granted { - color: #4caf50; - font-weight: bold; -} - -.denied { - color: #f44336; - font-weight: bold; -} - -/* Settings Panel Styles */ -.settings-panel { - background: white; - border-radius: 12px; - padding: 24px; - margin: 16px 0; -} - -.settings-panel h3 { - margin: 0 0 24px 0; - color: #333; -} - -.setting-group { - margin: 20px 0; -} - -.setting-label { - display: flex; - align-items: center; - gap: 8px; - font-weight: 500; - color: #333; -} - -.checkbox-group { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 12px; - margin-top: 8px; -} - -.preference-grid { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - gap: 16px; - margin-top: 8px; -} - -.setting-actions { - display: flex; - gap: 12px; - margin-top: 24px; -} - -/* Status Dashboard Styles */ -.status-dashboard { - background: white; - border-radius: 12px; - padding: 24px; - margin: 16px 0; -} - -.status-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 16px; - margin: 20px 0; -} - -.status-item { - padding: 16px; - background: #f8f9fa; - border-radius: 8px; -} - -.status-label { - font-size: 14px; - color: #666; - margin-bottom: 8px; -} - -.status-value { - font-size: 18px; - font-weight: bold; - color: #333; -} - -.status-value.active { - color: #4caf50; -} - -.status-value.warning { - color: #ff9800; -} - -.status-value.error { - color: #f44336; -} - -.performance-metrics { - margin: 24px 0; - padding: 16px; - background: #f8f9fa; - border-radius: 8px; -} - -.metrics-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 16px; - margin-top: 12px; -} - -.metric-item { - display: flex; - justify-content: space-between; -} - -.metric-label { - color: #666; -} - -.metric-value { - font-weight: bold; - color: #333; -} - -/* Error Display Styles */ -.error-display { - background: #ffebee; - border: 1px solid #f44336; - border-radius: 8px; - padding: 16px; - margin: 16px 0; -} - -.error-icon { - font-size: 24px; - margin-bottom: 12px; -} - -.error-content h3 { - margin: 0 0 8px 0; - color: #d32f2f; -} - -.error-message { - color: #666; - margin: 8px 0; -} - -.error-code { - font-size: 12px; - color: #999; - font-family: monospace; -} - -.error-actions { - display: flex; - gap: 12px; - margin-top: 16px; -} - -/* Responsive Design */ -@media (max-width: 768px) { - .status-grid { - grid-template-columns: 1fr; - } - - .preference-grid { - grid-template-columns: 1fr; - } - - .checkbox-group { - grid-template-columns: 1fr; - } - - .metrics-grid { - grid-template-columns: 1fr; - } -} -`; - -// ============================================================================ -// 8. EXPORT ALL COMPONENTS -// ============================================================================ - -export { - PermissionRequestDialog, - PermissionStatusDisplay, - NotificationSettingsPanel, - StatusDashboard, - AndroidBatteryOptimizationDialog, - iOSBackgroundRefreshDialog, - ErrorDisplay, - NotificationUIManager -}; diff --git a/examples/usage.ts b/examples/usage.ts deleted file mode 100644 index 930e9c6..0000000 --- a/examples/usage.ts +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Example usage of the Daily Notification plugin - * Demonstrates various features and best practices - */ - -import { registerPlugin } from '@capacitor/core'; -import { DailyNotificationPlugin } from '../src/definitions'; - -const DailyNotification = registerPlugin('DailyNotification'); - -/** - * Basic setup for daily notifications - */ -async function setupBasicNotifications() { - try { - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '08:00', // 8 AM - title: 'Daily Update', - body: 'Your daily content is ready!' - }); - console.log('Daily notifications scheduled successfully'); - } catch (error) { - console.error('Failed to schedule notifications:', error); - } -} - -/** - * Advanced setup with custom options - */ -async function setupAdvancedNotifications() { - try { - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '09:00', // 9 AM - title: 'Daily Digest', - body: 'Your personalized daily digest is ready', - sound: true, - vibrate: true, - priority: 'high', - retryCount: 3, - retryInterval: 15, // minutes - cacheDuration: 24, // hours - headers: { - 'Authorization': 'Bearer your-token' - } - }); - console.log('Advanced notification setup completed'); - } catch (error) { - console.error('Failed to setup advanced notifications:', error); - } -} - -/** - * Handle notification response - */ -async function handleNotificationResponse() { - try { - const response = await DailyNotification.getLastNotification(); - if (response) { - console.log('Last notification:', response); - // Handle the notification data - } - } catch (error) { - console.error('Failed to get last notification:', error); - } -} - -/** - * Cancel all scheduled notifications - */ -async function cancelNotifications() { - try { - await DailyNotification.cancelAllNotifications(); - console.log('All notifications cancelled'); - } catch (error) { - console.error('Failed to cancel notifications:', error); - } -} - -/** - * Check notification status - */ -async function checkNotificationStatus() { - try { - const status = await DailyNotification.getNotificationStatus(); - console.log('Notification status:', status); - return status; - } catch (error) { - console.error('Failed to get notification status:', error); - return null; - } -} - -/** - * Update notification settings - */ -async function updateNotificationSettings() { - try { - await DailyNotification.updateSettings({ - time: '10:00', // Change to 10 AM - sound: false, // Disable sound - priority: 'normal' - }); - console.log('Notification settings updated'); - } catch (error) { - console.error('Failed to update settings:', error); - } -} - -/** - * Example of handling network errors - */ -async function handleNetworkErrors() { - try { - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '08:00', - retryCount: 3, - retryInterval: 15, - offlineFallback: true - }); - } catch (error) { - if (error.code === 'NETWORK_ERROR') { - console.log('Network error, will retry later'); - } else { - console.error('Unexpected error:', error); - } - } -} - -/** - * Example of using custom content handlers - */ -async function useCustomContentHandler() { - try { - await DailyNotification.scheduleDailyNotification({ - url: 'https://api.example.com/daily-content', - time: '08:00', - contentHandler: async (response) => { - // Custom processing of the API response - const data = await response.json(); - return { - title: data.title, - body: data.summary, - data: data.details - }; - } - }); - } catch (error) { - console.error('Failed to setup custom content handler:', error); - } -} - -// Export all example functions -export { - setupBasicNotifications, - setupAdvancedNotifications, - handleNotificationResponse, - cancelNotifications, - checkNotificationStatus, - updateNotificationSettings, - handleNetworkErrors, - useCustomContentHandler -}; \ No newline at end of file diff --git a/src/callback-registry.ts b/src/callback-registry.ts deleted file mode 100644 index 5cf4230..0000000 --- a/src/callback-registry.ts +++ /dev/null @@ -1,413 +0,0 @@ -/** - * Callback Registry Implementation - * Provides uniform callback lifecycle usable from any platform - * - * @author Matthew Raymer - * @version 1.1.0 - */ - -export type CallbackKind = 'http' | 'local' | 'queue'; - -export interface CallbackEvent { - id: string; - at: number; - type: 'onFetchStart' | 'onFetchSuccess' | 'onFetchFailure' | - 'onNotifyStart' | 'onNotifyDelivered' | 'onNotifySkippedTTL' | 'onNotifyFailure'; - payload?: unknown; -} - -export type CallbackFunction = (e: CallbackEvent) => Promise | void; - -export interface CallbackRecord { - id: string; - kind: CallbackKind; - target: string; - headers?: Record; - enabled: boolean; - createdAt: number; - retryCount?: number; - lastFailure?: number; - circuitOpen?: boolean; -} - -export interface CallbackRegistry { - register(id: string, callback: CallbackRecord): Promise; - unregister(id: string): Promise; - fire(event: CallbackEvent): Promise; - getRegistered(): Promise; - getStatus(): Promise<{ - total: number; - enabled: number; - circuitOpen: number; - lastActivity: number; - }>; -} - -/** - * Callback Registry Implementation - * Handles callback registration, delivery, and circuit breaker logic - */ -export class CallbackRegistryImpl implements CallbackRegistry { - private callbacks = new Map(); - private localCallbacks = new Map(); - private retryQueue = new Map(); - private circuitBreakers = new Map(); - // Phase 2: TimeSafari ActiveDid change tracking - private lastRetryAttempts?: Map; - - constructor() { - this.startRetryProcessor(); - } - - async register(id: string, callback: CallbackRecord): Promise { - this.callbacks.set(id, callback); - - // Initialize circuit breaker - if (!this.circuitBreakers.has(id)) { - this.circuitBreakers.set(id, { - failures: 0, - lastFailure: 0, - open: false - }); - } - - console.log(`DNP-CB-REGISTER: Callback ${id} registered (${callback.kind})`); - } - - async unregister(id: string): Promise { - this.callbacks.delete(id); - this.localCallbacks.delete(id); - this.retryQueue.delete(id); - this.circuitBreakers.delete(id); - - console.log(`DNP-CB-UNREGISTER: Callback ${id} unregistered`); - } - - async fire(event: CallbackEvent): Promise { - const enabledCallbacks = Array.from(this.callbacks.values()) - .filter(cb => cb.enabled); - - console.log(`DNP-CB-FIRE: Firing event ${event.type} to ${enabledCallbacks.length} callbacks`); - - for (const callback of enabledCallbacks) { - try { - await this.deliverCallback(callback, event); - } catch (error) { - console.error(`DNP-CB-FIRE-ERROR: Failed to deliver to ${callback.id}`, error); - await this.handleCallbackFailure(callback, event, error); - } - } - } - - async getRegistered(): Promise { - return Array.from(this.callbacks.values()); - } - - async getStatus(): Promise<{ - total: number; - enabled: number; - circuitOpen: number; - lastActivity: number; - }> { - const callbacks = Array.from(this.callbacks.values()); - const circuitBreakers = Array.from(this.circuitBreakers.values()); - - return { - total: callbacks.length, - enabled: callbacks.filter(cb => cb.enabled).length, - circuitOpen: circuitBreakers.filter(cb => cb.open).length, - lastActivity: Math.max( - ...callbacks.map(cb => cb.createdAt), - ...circuitBreakers.map(cb => cb.lastFailure) - ) - }; - } - - private async deliverCallback(callback: CallbackRecord, event: CallbackEvent): Promise { - const circuitBreaker = this.circuitBreakers.get(callback.id); - - // Check circuit breaker - if (circuitBreaker?.open) { - console.warn(`DNP-CB-CIRCUIT: Circuit open for ${callback.id}, skipping delivery`); - return; - } - - const start = performance.now(); - - try { - switch (callback.kind) { - case 'http': - await this.deliverHttpCallback(callback, event); - break; - case 'local': - await this.deliverLocalCallback(callback, event); - break; - case 'queue': - await this.deliverQueueCallback(callback, event); - break; - default: - throw new Error(`Unknown callback kind: ${callback.kind}`); - } - - // Reset circuit breaker on success - if (circuitBreaker) { - circuitBreaker.failures = 0; - circuitBreaker.open = false; - } - - const duration = performance.now() - start; - console.log(`DNP-CB-SUCCESS: Delivered to ${callback.id} in ${duration.toFixed(2)}ms`); - - } catch (error) { - throw error; - } - } - - private async deliverHttpCallback(callback: CallbackRecord, event: CallbackEvent): Promise { - const response = await fetch(callback.target, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...callback.headers - }, - body: JSON.stringify({ - ...event, - callbackId: callback.id, - timestamp: Date.now() - }) - }); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - } - - private async deliverLocalCallback(callback: CallbackRecord, event: CallbackEvent): Promise { - const localCallback = this.localCallbacks.get(callback.id); - if (!localCallback) { - throw new Error(`Local callback ${callback.id} not found`); - } - - await localCallback(event); - } - - private async deliverQueueCallback(callback: CallbackRecord, event: CallbackEvent): Promise { - // Queue callback implementation would go here - // For now, just log the event - console.log(`DNP-CB-QUEUE: Queued event ${event.type} for ${callback.id}`); - } - - private async handleCallbackFailure( - callback: CallbackRecord, - event: CallbackEvent, - error: unknown - ): Promise { - const circuitBreaker = this.circuitBreakers.get(callback.id); - - if (circuitBreaker) { - circuitBreaker.failures++; - circuitBreaker.lastFailure = Date.now(); - - // Open circuit after 5 consecutive failures - if (circuitBreaker.failures >= 5) { - circuitBreaker.open = true; - console.error(`DNP-CB-CIRCUIT-OPEN: Circuit opened for ${callback.id} after ${circuitBreaker.failures} failures`); - } - } - - // Schedule retry with exponential backoff - await this.scheduleRetry(callback, event); - - console.error(`DNP-CB-FAILURE: Callback ${callback.id} failed`, error); - } - - private async scheduleRetry(callback: CallbackRecord, event: CallbackEvent): Promise { - const retryCount = callback.retryCount || 0; - - // Phase 2: Enhanced retry logic with TimeSafari activeDid support - const maxRetries = Math.max(5, this.getMaxRetriesForTimeSafari()); - - if (retryCount >= maxRetries && !this.shouldRetryForActiveDidChange()) { - console.warn(`DNP-CB-RETRY-LIMIT: Max retries reached for ${callback.id}`); - return; - } - - // Phase 2: Check for activeDid change retry triggers - if (this.shouldRetryForActiveDidChange()) { - console.log(`DNP-CB-PHASE2: ActiveDid change detected - resetting retry count for ${callback.id}`); - callback.retryCount = 0; // Reset retry count for activeDid change - } - - const actualRetryCount = callback.retryCount || 0; - - const backoffMs = Math.min(1000 * Math.pow(2, actualRetryCount), 60000); // Cap at 1 minute - const retryEvent = { ...event, retryCount: actualRetryCount + 1 }; - - if (!this.retryQueue.has(callback.id)) { - this.retryQueue.set(callback.id, []); - } - - this.retryQueue.get(callback.id)!.push(retryEvent); - - // Phase 2: Store last retry attempt for activeDid change detection - this.storeLastRetryAttempt(callback.id); - - console.log(`DNP-CB-PHASE2-RETRY: Scheduled retry ${actualRetryCount + 1} for ${callback.id} in ${backoffMs}ms with TimeSafari support`); - } - - // Phase 2: Enhanced retry methods for TimeSafari integration - - /** - * Phase 2: Get max retries with TimeSafari enhancements - */ - private getMaxRetriesForTimeSafari(): number { - // Base retries + additional for activeDid changes - return 7; // Extra retries for TimeSafari integration - } - - /** - * Phase 2: Check if retry is needed due to activeDid change - */ - private shouldRetryForActiveDidChange(): boolean { - try { - // Check if activeDid has changed since last retry attempt - const lastRetryAttempt = this.getLastRetryAttempt(); - const lastActiveDidChange = this.getLastActiveDidChange(); - - if (lastActiveDidChange > lastRetryAttempt) { - console.log('DNP-CB-PHASE2: ActiveDid change detected in web retry logic'); - return true; - } - - return false; - - } catch (error) { - console.error('DNP-CB-PHASE2: Error checking activeDid change', error); - return false; - } - } - - /** - * Phase 2: Store last retry attempt for activeDid change detection - */ - private storeLastRetryAttempt(callbackId: string): void { - try { - const lastRetryMap = this.getLastRetryMap(); - lastRetryMap.set(callbackId, Date.now()); - - // Store in sessionStorage for web persistence - if (typeof window !== 'undefined' && window.sessionStorage) { - const retryMapKey = 'dailynotification_last_retry_attempts'; - const retryMapObj = Object.fromEntries(lastRetryMap); - window.sessionStorage.setItem(retryMapKey, JSON.stringify(retryMapObj)); - } - - } catch (error) { - console.error('DNP-CB-PHASE2: Error storing last retry attempt', error); - } - } - - /** - * Phase 2: Get last retry attempt timestamp - */ - private getLastRetryAttempt(): number { - try { - const lastRetryMap = this.getLastRetryMap(); - // Get the most recent retry attempt across all callbacks - let maxRetryTime = 0; - for (const retryTime of lastRetryMap.values()) { - maxRetryTime = Math.max(maxRetryTime, retryTime); - } - return maxRetryTime; - } catch (error) { - console.error('DNP-CB-PHASE2: Error getting last retry attempt', error); - return 0; - } - } - - /** - * Phase 2: Get last activeDid change timestamp - */ - private getLastActiveDidChange(): number { - try { - // This would be set when activeDid changes - if (typeof window !== 'undefined' && window.sessionStorage) { - const lastChangeStr = window.sessionStorage.getItem('dailynotification_last_active_did_change'); - return lastChangeStr ? parseInt(lastChangeStr, 10) : 0; - } - return 0; - } catch (error) { - console.error('DNP-CB-PHASE2: Error getting last activeDid change', error); - return 0; - } - } - - /** - * Phase 2: Get last retry map - */ - private getLastRetryMap(): Map { - try { - if (!this.lastRetryAttempts) { - this.lastRetryAttempts = new Map(); - - // Load from sessionStorage if available - if (typeof window !== 'undefined' && window.sessionStorage) { - const retryMapKey = 'dailynotification_last_retry_attempts'; - const retryMapStr = window.sessionStorage.getItem(retryMapKey); - if (retryMapStr) { - const retryMapObj = JSON.parse(retryMapStr); - for (const [key, value] of Object.entries(retryMapObj)) { - this.lastRetryAttempts.set(key, value as number); - } - } - } - } - return this.lastRetryAttempts; - } catch (error) { - console.error('DNP-CB-PHASE2: Error getting last retry map', error); - return new Map(); - } - } - - private startRetryProcessor(): void { - setInterval(async () => { - for (const [callbackId, events] of this.retryQueue.entries()) { - if (events.length === 0) continue; - - const callback = this.callbacks.get(callbackId); - if (!callback) { - this.retryQueue.delete(callbackId); - continue; - } - - const event = events.shift(); - if (!event) continue; - - try { - await this.deliverCallback(callback, event); - } catch (error) { - console.error(`DNP-CB-RETRY-FAILED: Retry failed for ${callbackId}`, error); - } - } - }, 5000); // Process retries every 5 seconds - } - - // Register local callback function - registerLocalCallback(id: string, callback: CallbackFunction): void { - this.localCallbacks.set(id, callback); - console.log(`DNP-CB-LOCAL: Local callback ${id} registered`); - } - - // Unregister local callback function - unregisterLocalCallback(id: string): void { - this.localCallbacks.delete(id); - console.log(`DNP-CB-LOCAL: Local callback ${id} unregistered`); - } -} - -// Singleton instance -export const callbackRegistry = new CallbackRegistryImpl(); diff --git a/src/daily-notification.ts b/src/daily-notification.ts deleted file mode 100644 index 60bbc04..0000000 --- a/src/daily-notification.ts +++ /dev/null @@ -1,288 +0,0 @@ -/** - * DailyNotification class implementation - * Handles scheduling and managing daily notifications - * Aligned with updated interface definitions - * - * @author Matthew Raymer - * @version 2.0.0 - */ - -import { DailyNotificationPlugin, NotificationOptions, NotificationSettings, NotificationResponse, PermissionStatus } from './definitions'; - -export class DailyNotification { - private plugin: DailyNotificationPlugin; - private eventListeners: Map>; - - constructor(plugin: DailyNotificationPlugin) { - this.plugin = plugin; - this.eventListeners = new Map(); - this.setupEventListeners(); - } - - /** - * Schedule a daily notification with the specified options - * @param options Notification options including URL and time - */ - async scheduleDailyNotification(options: NotificationOptions): Promise { - await this.validateOptions(options); - await this.plugin.scheduleDailyNotification(options); - } - - /** - * Get the last notification that was delivered - */ - async getLastNotification(): Promise { - return this.plugin.getLastNotification(); - } - - /** - * Cancel all scheduled notifications - */ - async cancelAllNotifications(): Promise { - await this.plugin.cancelAllNotifications(); - } - - /** - * Get the current status of notifications - */ - async getNotificationStatus() { - return this.plugin.getNotificationStatus(); - } - - /** - * Update notification settings - * @param settings New notification settings - */ - async updateSettings(settings: NotificationSettings): Promise { - this.validateSettings(settings); - await this.plugin.updateSettings(settings); - } - - /** - * Get battery status information - */ - async getBatteryStatus() { - return this.plugin.getBatteryStatus(); - } - - /** - * Request battery optimization exemption - */ - async requestBatteryOptimizationExemption(): Promise { - await this.plugin.requestBatteryOptimizationExemption(); - } - - /** - * Set adaptive scheduling based on device state - * @param options Options containing enabled flag - */ - async setAdaptiveScheduling(options: { enabled: boolean }): Promise { - await this.plugin.setAdaptiveScheduling(options); - } - - /** - * Get current power state information - */ - async getPowerState() { - return this.plugin.getPowerState(); - } - - /** - * Check current permissions - */ - async checkPermissions(): Promise { - return this.plugin.checkPermissions(); - } - - /** - * Request permissions - */ - async requestPermissions(): Promise { - return this.plugin.requestPermissions(); - } - - /** - * Add an event listener for notification events - * @param event Event type to listen for - * @param handler Event handler function - */ - on(event: string, handler: EventListener): void { - if (!this.eventListeners.has(event)) { - this.eventListeners.set(event, new Set()); - } - this.eventListeners.get(event)?.add(handler); - } - - /** - * Remove an event listener - * @param event Event type to remove listener from - * @param handler Event handler function to remove - */ - off(event: string, handler: EventListener): void { - this.eventListeners.get(event)?.delete(handler); - } - - /** - * Remove all event listeners for a specific event - * @param event Event type to clear listeners for - */ - offAll(event: string): void { - this.eventListeners.delete(event); - } - - /** - * Get all event listeners for a specific event - * @param event Event type to get listeners for - */ - getListeners(event: string): EventListener[] { - const listeners = this.eventListeners.get(event); - return listeners ? Array.from(listeners) : []; - } - - private setupEventListeners(): void { - document.addEventListener('notification', (event: Event) => { - this.eventListeners.get('notification')?.forEach(handler => { - try { - handler(event); - } catch (error) { - console.error('Error in event handler:', error); - } - }); - }); - } - - private async validateOptions(options: NotificationOptions): Promise { - if (!options.url) { - throw new Error('URL is required'); - } - if (!this.isValidUrl(options.url)) { - throw new Error('Invalid URL format'); - } - if (options.time && !this.isValidTime(options.time)) { - throw new Error('Invalid time format'); - } - if (options.timezone && !this.isValidTimezone(options.timezone)) { - throw new Error('Invalid timezone'); - } - if (options.retryCount !== undefined && (options.retryCount < 0 || options.retryCount > 10)) { - throw new Error('Retry count must be between 0 and 10'); - } - if (options.retryInterval !== undefined && (options.retryInterval < 100 || options.retryInterval > 60000)) { - throw new Error('Retry interval must be between 100ms and 60s'); - } - - // Check for schedule conflicts (basic implementation) - if (options.time) { - await this.checkScheduleConflict(options); - } - - // Validate content handler if provided - if (options.contentHandler) { - await this.validateContentHandler(options.contentHandler); - } - } - - private validateSettings(settings: NotificationSettings): void { - if (settings.time && !this.isValidTime(settings.time)) { - throw new Error('Invalid time format'); - } - if (settings.timezone && !this.isValidTimezone(settings.timezone)) { - throw new Error('Invalid timezone'); - } - if (settings.retryCount !== undefined && (settings.retryCount < 0 || settings.retryCount > 10)) { - throw new Error('Retry count must be between 0 and 10'); - } - if (settings.retryInterval !== undefined && (settings.retryInterval < 100 || settings.retryInterval > 60000)) { - throw new Error('Retry interval must be between 100ms and 60s'); - } - } - - private isValidUrl(url: string): boolean { - try { - new URL(url); - return true; - } catch { - return false; - } - } - - private isValidTime(time: string): boolean { - const timeRegex = /^([0-1][0-9]|2[0-3]):[0-5][0-9]$/; - return timeRegex.test(time); - } - - private isValidTimezone(timezone: string): boolean { - try { - Intl.DateTimeFormat(undefined, { timeZone: timezone }); - return true; - } catch { - return false; - } - } - - /** - * Validate notification content - * @param content Content to validate - */ - validateContent(content: { title: string; body: string; data?: any }): boolean { - if (!content.title || typeof content.title !== 'string' || content.title.trim().length === 0) { - return false; - } - if (!content.body || typeof content.body !== 'string' || content.body.trim().length === 0) { - return false; - } - return true; - } - - /** - * Check for schedule conflicts - */ - private async checkScheduleConflict(options: NotificationOptions): Promise { - // In a real implementation, this would check against existing schedules - // For now, we'll implement a basic conflict detection that doesn't block tests - if (options.time === '09:00' && options.url?.includes('updates')) { - // Instead of throwing an error, we'll log a warning and allow the schedule - // This prevents test failures while maintaining the conflict detection logic - console.warn('Potential schedule conflict detected for 09:00 with updates URL'); - } - } - - /** - * Validate content handler - */ - private async validateContentHandler(handler: any): Promise { - try { - // Test the handler with a timeout - const result = await Promise.race([ - handler(), - new Promise((_, reject) => - setTimeout(() => reject(new Error('Content handler timeout')), 1000) - ) - ]); - - // Validate the result - if (!this.validateContent(result)) { - throw new Error('Invalid content handler response'); - } - } catch (error) { - if (error instanceof Error) { - throw error; - } - throw new Error('Content handler validation failed'); - } - } - - /** - * Check if the plugin is available - */ - isAvailable(): boolean { - return this.plugin !== null && this.plugin !== undefined; - } - - /** - * Get plugin version information - */ - getVersion(): string { - return '2.0.0'; - } -} \ No newline at end of file diff --git a/src/definitions.ts b/src/definitions.ts index 578a11d..9e276f1 100644 --- a/src/definitions.ts +++ b/src/definitions.ts @@ -27,28 +27,9 @@ export interface NotificationOptions { retryCount?: number; retryInterval?: number; offlineFallback?: boolean; - contentHandler?: ContentHandler; headers?: Record; } -export interface ContentHandler { - (response?: any): Promise<{ title: string; body: string; data?: any }>; -} - - - -export interface ScheduleOptions { - url?: string; - time?: string; - sound?: boolean; - priority?: 'high' | 'default' | 'low' | 'min' | 'max' | 'normal'; - timezone?: string; - retryCount?: number; - retryInterval?: number; - offlineFallback?: boolean; - contentHandler?: ContentHandler; - headers?: Record; -} export interface NotificationSettings { url?: string; @@ -223,7 +204,6 @@ export interface ContentFetchConfig { onError?: (error: Error) => Promise; onComplete?: (result: ContentFetchResult) => Promise; }; - contentHandler?: ContentHandler; cachePolicy?: CachePolicy; networkConfig?: NetworkConfig; // Phase 2: TimeSafari Endorser.ch API configuration @@ -355,7 +335,7 @@ export interface DailyNotificationPlugin { }>; // Existing methods - scheduleDailyNotification(options: NotificationOptions | ScheduleOptions): Promise; + scheduleDailyNotification(options: NotificationOptions): Promise; getLastNotification(): Promise; cancelAllNotifications(): Promise; getNotificationStatus(): Promise; diff --git a/src/index.ts b/src/index.ts index 5792ef3..589b450 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,6 +16,5 @@ const DailyNotification = registerPlugin('DailyNotifica observability.logEvent('INFO', EVENT_CODES.FETCH_START, 'Daily Notification Plugin initialized'); export * from './definitions'; -export * from './callback-registry'; export * from './observability'; export { DailyNotification }; diff --git a/src/web.ts b/src/web.ts deleted file mode 100644 index fbad0ec..0000000 --- a/src/web.ts +++ /dev/null @@ -1,1128 +0,0 @@ -/** - * Web implementation of the Daily Notification plugin - * - * @author Matthew Raymer - * @version 2.0.0 - */ - -import { WebPlugin } from '@capacitor/core'; -import type { DailyNotificationPlugin, NotificationOptions, NotificationSettings, NotificationResponse, NotificationStatus, BatteryStatus, PowerState, PermissionStatus } from './definitions'; -import { callbackRegistry } from './callback-registry'; -import { observability, EVENT_CODES } from './observability'; - -export class DailyNotificationWeb extends WebPlugin implements DailyNotificationPlugin { - private contentCache = new Map(); - private callbacks = new Map(); - private activeDid?: string; - - async configure(_options: any): Promise { - observability.logEvent('INFO', EVENT_CODES.SCHEDULE_UPDATE, 'Plugin configured on web platform'); - console.log('Configure called on web platform'); - } - - async maintainRollingWindow(): Promise { - console.log('Maintain rolling window called on web platform'); - } - - async getRollingWindowStats(): Promise<{ - stats: string; - maintenanceNeeded: boolean; - timeUntilNextMaintenance: number; - }> { - console.log('Get rolling window stats called on web platform'); - return { - stats: 'Web platform - rolling window not applicable', - maintenanceNeeded: false, - timeUntilNextMaintenance: 0 - }; - } - - async getExactAlarmStatus(): Promise<{ - supported: boolean; - enabled: boolean; - canSchedule: boolean; - fallbackWindow: string; - }> { - console.log('Get exact alarm status called on web platform'); - return { - supported: false, - enabled: false, - canSchedule: false, - fallbackWindow: 'Not applicable on web' - }; - } - - async requestExactAlarmPermission(): Promise { - console.log('Request exact alarm permission called on web platform'); - } - - async openExactAlarmSettings(): Promise { - console.log('Open exact alarm settings called on web platform'); - } - - async getRebootRecoveryStatus(): Promise<{ - inProgress: boolean; - lastRecoveryTime: number; - timeSinceLastRecovery: number; - recoveryNeeded: boolean; - }> { - console.log('Get reboot recovery status called on web platform'); - return { - inProgress: false, - lastRecoveryTime: 0, - timeSinceLastRecovery: 0, - recoveryNeeded: false - }; - } - - async scheduleDailyNotification(_options: NotificationOptions | any): Promise { - // Web implementation placeholder - console.log('Schedule daily notification called on web platform'); - } - - async getLastNotification(): Promise { - return { - id: 'web-notification', - title: 'Web Notification', - body: 'This is a web notification', - timestamp: Date.now() - }; - } - - async cancelAllNotifications(): Promise { - console.log('Cancel all notifications called on web platform'); - } - - async getNotificationStatus(): Promise { - return { - lastNotificationTime: Date.now(), - nextNotificationTime: Date.now() + 86400000, // 24 hours - settings: {} - }; - } - - async updateSettings(_settings: NotificationSettings): Promise { - console.log('Update settings called on web platform'); - } - - async getBatteryStatus(): Promise { - return { - level: 100, - isCharging: false, - powerState: 1, - isOptimizationExempt: true - }; - } - - async requestBatteryOptimizationExemption(): Promise { - console.log('Request battery optimization exemption called on web platform'); - } - - async setAdaptiveScheduling(_options: { enabled: boolean }): Promise { - console.log('Set adaptive scheduling called on web platform'); - } - - async getPowerState(): Promise { - return { - powerState: 1, - isOptimizationExempt: true - }; - } - - async checkPermissions(): Promise { - return { - status: 'granted', - granted: true, - notifications: 'granted', - alert: true, - badge: true, - sound: true, - lockScreen: true, - carPlay: false - }; - } - - async requestPermissions(): Promise { - return { - status: 'granted', - granted: true, - notifications: 'granted', - alert: true, - badge: true, - sound: true, - lockScreen: true, - carPlay: false - }; - } - - // Dual Scheduling Methods Implementation - - async scheduleContentFetch(config: any): Promise { - const start = performance.now(); - observability.logEvent('INFO', EVENT_CODES.FETCH_START, 'Content fetch scheduled on web platform'); - - try { - // Simplified web implementation - use immediate fetch - await this.performImmediateContentFetch(config); - observability.logEvent('INFO', EVENT_CODES.FETCH_SUCCESS, 'Content fetch performed (simplified web implementation)'); - - const duration = performance.now() - start; - observability.recordMetric('fetch', duration, true); - - } catch (error) { - const duration = performance.now() - start; - observability.recordMetric('fetch', duration, false); - observability.logEvent('ERROR', EVENT_CODES.FETCH_FAILURE, 'Content fetch failed', { error: String(error) }); - throw error; - } - } - - async scheduleUserNotification(config: any): Promise { - const start = performance.now(); - observability.logEvent('INFO', EVENT_CODES.NOTIFY_START, 'User notification scheduled on web platform'); - - try { - // Simplified web implementation - use immediate notification - await this.showImmediateNotification(config); - observability.logEvent('INFO', EVENT_CODES.NOTIFY_SUCCESS, 'Notification shown (simplified)'); - - const duration = performance.now() - start; - observability.recordMetric('notify', duration, true); - - } catch (error) { - const duration = performance.now() - start; - observability.recordMetric('notify', duration, false); - observability.logEvent('ERROR', EVENT_CODES.NOTIFY_FAILURE, 'User notification failed', { error: String(error) }); - throw error; - } - } - - async scheduleDualNotification(config: any): Promise { - observability.logEvent('INFO', EVENT_CODES.SCHEDULE_UPDATE, 'Dual notification scheduled on web platform'); - - try { - await this.scheduleContentFetch(config.contentFetch); - await this.scheduleUserNotification(config.userNotification); - - observability.logEvent('INFO', EVENT_CODES.SCHEDULE_UPDATE, 'Dual notification completed successfully'); - } catch (error) { - observability.logEvent('ERROR', EVENT_CODES.SCHEDULE_UPDATE, 'Dual notification failed', { error: String(error) }); - throw error; - } - } - - async getDualScheduleStatus(): Promise { - try { - // Simplified web implementation - return local status - const healthStatus = await observability.getHealthStatus(); - return { - platform: 'web', - simplified: true, - nextRuns: healthStatus.nextRuns, - lastOutcomes: healthStatus.lastOutcomes, - cacheAgeMs: healthStatus.cacheAgeMs, - staleArmed: healthStatus.staleArmed, - queueDepth: healthStatus.queueDepth, - circuitBreakers: healthStatus.circuitBreakers, - performance: healthStatus.performance - }; - } catch (error) { - console.error('DNP-WEB-STATUS: Failed to get dual schedule status:', error); - // Return fallback status - return { - nextRuns: [], - lastOutcomes: [], - cacheAgeMs: null, - staleArmed: true, - queueDepth: 0, - circuitBreakers: { total: 0, open: 0, failures: 0 }, - performance: { avgFetchTime: 0, avgNotifyTime: 0, successRate: 0 } - }; - } - } - - async updateDualScheduleConfig(_config: any): Promise { - console.log('Update dual schedule config called on web platform'); - } - - async cancelDualSchedule(): Promise { - console.log('Cancel dual schedule called on web platform'); - } - - async pauseDualSchedule(): Promise { - console.log('Pause dual schedule called on web platform'); - } - - async resumeDualSchedule(): Promise { - console.log('Resume dual schedule called on web platform'); - } - - async getContentCache(): Promise> { - return {}; - } - - async clearContentCache(): Promise { - console.log('Clear content cache called on web platform'); - } - - async getContentHistory(): Promise { - return []; - } - - async registerCallback(name: string, callback: Function): Promise { - observability.logEvent('INFO', EVENT_CODES.CALLBACK_START, `Callback ${name} registered on web platform`); - - // Register with callback registry - await callbackRegistry.register(name, { - id: name, - kind: 'local', - target: '', - enabled: true, - createdAt: Date.now() - }); - - // Register local callback function - callbackRegistry.registerLocalCallback(name, callback as any); - this.callbacks.set(name, callback); - } - - async unregisterCallback(name: string): Promise { - observability.logEvent('INFO', EVENT_CODES.CALLBACK_START, `Callback ${name} unregistered on web platform`); - - await callbackRegistry.unregister(name); - callbackRegistry.unregisterLocalCallback(name); - this.callbacks.delete(name); - } - - async getRegisteredCallbacks(): Promise { - const callbacks = await callbackRegistry.getRegistered(); - return callbacks.map(cb => cb.id); - } - - // Helper methods for fallback functionality - private async performImmediateContentFetch(_config: any): Promise { - // Mock content fetch implementation for browsers without Service Worker support - const mockContent = { - id: `fetch_${Date.now()}`, - timestamp: Date.now(), - content: 'Mock daily content (no Service Worker)', - source: 'web_platform_fallback' - }; - - this.contentCache.set(mockContent.id, mockContent); - - // Fire callbacks - await callbackRegistry.fire({ - id: mockContent.id, - at: Date.now(), - type: 'onFetchSuccess', - payload: mockContent - }); - } - - private async showImmediateNotification(config: any): Promise { - // Immediate notification implementation for browsers without Service Worker support - if ('Notification' in window && Notification.permission === 'granted') { - const notification = new Notification(config.title || 'Daily Notification', { - body: config.body || 'Your daily update is ready', - icon: '/favicon.ico' - }); - - notification.onclick = () => { - observability.logEvent('INFO', EVENT_CODES.NOTIFY_SUCCESS, 'Notification clicked'); - }; - - // Fire callbacks - await callbackRegistry.fire({ - id: `notify_${Date.now()}`, - at: Date.now(), - type: 'onNotifyDelivered', - payload: config - }); - } - } - - // Phase 1: ActiveDid Management Methods Implementation - async setActiveDidFromHost(activeDid: string): Promise { - try { - console.log('DNP-WEB: Setting activeDid from host:', activeDid, 'stored:', this.activeDid); - - // Store activeDid for future use - this.activeDid = activeDid; - - // Log the change - observability.logEvent('INFO', EVENT_CODES.SCHEDULE_UPDATE, 'ActiveDid set from host', { - activeDid: activeDid - }); - - console.log('DNP-WEB: ActiveDid set successfully'); - - } catch (error) { - console.error('DNP-WEB: Error setting activeDid from host:', error); - throw error; - } - } - - onActiveDidChange(callback: (newActiveDid: string) => Promise): void { - try { - console.log('DNP-WEB: Setting up activeDid change listener'); - - // Set up event listener for activeDidChanged events - document.addEventListener('activeDidChanged', async (event: any) => { - try { - const eventDetail = event.detail; - if (eventDetail && eventDetail.activeDid) { - console.log('DNP-WEB: ActiveDid changed to:', eventDetail.activeDid); - - // Clear current cached content - await this.clearCacheForNewIdentity(); - - // Update authentication for new identity - await this.refreshAuthenticationForNewIdentity(eventDetail.activeDid); - - // Call the provided callback - await callback(eventDetail.activeDid); - - observability.logEvent('INFO', EVENT_CODES.SCHEDULE_UPDATE, 'ActiveDid changed processed', { - activeDid: eventDetail.activeDid - }); - } - } catch (error) { - console.error('DNP-WEB: Error processing activeDid change:', error); - observability.logEvent('ERROR', EVENT_CODES.NOTIFY_FAILURE, 'ActiveDid change error', { - error: String(error) - }); - } - }); - - console.log('DNP-WEB: ActiveDid change listener configured'); - - } catch (error) { - console.error('DNP-WEB: Error setting up activeDid change listener:', error); - throw error; - } - } - - async refreshAuthenticationForNewIdentity(activeDid: string): Promise { - try { - console.log('DNP-WEB: Refreshing authentication for activeDid:', activeDid); - - // Update current activeDid - this.activeDid = activeDid; - - // In a real implementation, this would refresh JWT tokens - // For Phase 1, we'll just log the change - observability.logEvent('INFO', EVENT_CODES.SCHEDULE_UPDATE, 'Authentication refreshed', { - activeDid: activeDid, - platform: 'web' - }); - - console.log('DNP-WEB: Authentication refreshed successfully'); - - } catch (error) { - console.error('DNP-WEB: Error refreshing authentication:', error); - throw error; - } - } - - async clearCacheForNewIdentity(): Promise { - try { - console.log('DNP-WEB: Clearing cache for new identity'); - - // Clear content cache - this.contentCache.clear(); - - // Clear callback registrations (optional) - this.callbacks.clear(); - - observability.logEvent('INFO', EVENT_CODES.CACHE_MISS, 'Cache cleared for new identity', { - platform: 'web' - }); - - console.log('DNP-WEB: Cache cleared successfully'); - - } catch (error) { - console.error('DNP-WEB: Error clearing cache for new identity:', error); - throw error; - } - } - - async updateBackgroundTaskIdentity(activeDid: string): Promise { - try { - console.log('DNP-WEB: Updating background task identity:', activeDid); - - // Update current activeDid - this.activeDid = activeDid; - - // In web environment, we don't have background tasks like native apps - // This method is here for interface compliance - observability.logEvent('INFO', EVENT_CODES.SCHEDULE_UPDATE, 'Background task identity updated', { - activeDid: activeDid, - platform: 'web', - note: 'Web platform - no background tasks to update' - }); - - console.log('DNP-WEB: Background task identity updated successfully'); - - } catch (error) { - console.error('DNP-WEB: Error updating background task identity:', error); - throw error; - } - } - - // MARK: - Phase 3: Web TimeSafari Coordination Methods - - /** - * Phase 3: Coordinate background tasks with TimeSafari PlatformServiceMixin - */ - async coordinateBackgroundTasks(): Promise { - try { - console.log('DNP-WEB-PHASE3: Coordinating background tasks with PlatformServiceMixin'); - - // Check visibility state for coordination - const visibilityState = document.visibilityState; - const isBackgrounded = this.isAppBackgrounded(); - - console.log('DNP-WEB-PHASE3: App state - visibility:', visibilityState, 'backgrounded:', isBackgrounded); - - if (isBackgrounded) { - // Activate background coordination - await this.activateBackgroundCoordination(); - } else { - // Pause non-critical coordination for foreground usage - await this.pauseNonCriticalCoordination(); - } - - console.log('DNP-WEB-PHASE3: Background task coordination completed'); - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error coordinating background tasks:', error); - throw error; - } - } - - /** - * Phase 3: Handle app lifecycle events for web platform - */ - async handleAppLifecycleEvent(lifecycleEvent: string): Promise { - try { - console.log('DNP-WEB-PHASE3: Handling app lifecycle event:', lifecycleEvent); - - switch (lifecycleEvent) { - case 'app_visibility_change': - await this.handleVisibilityChange(); - break; - case 'app_hidden': - await this.handleAppHidden(); - break; - case 'app_visible': - await this.handleAppVisible(); - break; - case 'app_blur': - await this.handleWindowBlur(); - break; - case 'app_focus': - await this.handleWindowFocus(); - break; - default: - console.warn('DNP-WEB-PHASE3: Unknown lifecycle event:', lifecycleEvent); - } - - console.log('DNP-WEB-PHASE3: Lifecycle event handled:', lifecycleEvent); - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error handling lifecycle event:', error); - throw error; - } - } - - /** - * Phase 3: Check if app is backgrounded on web - */ - private isAppBackgrounded(): boolean { - try { - // Check multiple indicators of background state - const isHidden = document.visibilityState === 'hidden'; - const isNotFocused = document.hasFocus() === false; - const isBlurred = window.blur !== undefined; - - const backgrounded = isHidden || isNotFocused || isBlurred; - - console.log('DNP-WEB-PHASE3: Background check - hidden:', isHidden, 'focused:', isNotFocused, 'blurred:', isBlurred, 'result:', backgrounded); - - return backgrounded; - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error checking background state:', error); - return false; - } - } - - /** - * Phase 3: Activate background coordination - */ - private async activateBackgroundCoordination(): Promise { - try { - console.log('DNP-WEB-PHASE3: Activating background coordination'); - - // Store coordination state - sessionStorage.setItem('dnp_coordination_active', 'true'); - sessionStorage.setItem('dnp_coordination_timestamp', Date.now().toString()); - - // Set up background coordination listener - this.setupBackgroundCoordinationListener(); - - console.log('DNP-WEB-PHASE3: Background coordination activated'); - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error activating background coordination:', error); - } - } - - /** - * Phase 3: Pause non-critical coordination - */ - private async pauseNonCriticalCoordination(): Promise { - try { - console.log('DNP-WEB-PHASE3: Pausing non-critical coordination'); - - // Store pause state - sessionStorage.setItem('dnp_coordination_paused', 'true'); - sessionStorage.setItem('dnp_coordination_pause_timestamp', Date.now().toString()); - - // Remove background coordination listener - this.removeBackgroundCoordinationListener(); - - console.log('DNP-WEB-PHASE3: Non-critical coordination paused'); - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error pausing coordination:', error); - } - } - - /** - * Phase 3: Handle visibility change - */ - private async handleVisibilityChange(): Promise { - try { - console.log('DNP-WEB-PHASE3: Handling visibility change'); - - const isBackgrounded = this.isAppBackgrounded(); - - if (isBackgrounded) { - await this.activateBackgroundCoordination(); - } else { - await this.pauseNonCriticalCoordination(); - // Sync state when coming to foreground - await this.syncTimeSafariState(); - } - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error handling visibility change:', error); - } - } - - /** - * Phase 3: Handle app hidden - */ - private async handleAppHidden(): Promise { - try { - console.log('DNP-WEB-PHASE3: App hidden - activating background coordination'); - - await this.activateBackgroundCoordination(); - - // Store app state - sessionStorage.setItem('dnp_app_hidden_timestamp', Date.now().toString()); - sessionStorage.setItem('dnp_app_is_hidden', 'true'); - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error handling app hidden:', error); - } - } - - /** - * Phase 3: Handle app visible - */ - private async handleAppVisible(): Promise { - try { - console.log('DNP-WEB-PHASE3: App visible - updating coordination'); - - await this.pauseNonCriticalCoordination(); - - // Store app state - sessionStorage.setItem('dnp_app_visible_timestamp', Date.now().toString()); - sessionStorage.setItem('dnp_app_is_hidden', 'false'); - - // Check activeDid coordination - await this.checkActiveDidCoordinationWeb(); - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error handling app visible:', error); - } - } - - /** - * Phase 3: Handle window blur - */ - private async handleWindowBlur(): Promise { - try { - console.log('DNP-WEB-PHASE3: Window blurred - activating background coordination'); - - await this.activateBackgroundCoordination(); - - sessionStorage.setItem('dnp_window_blur_timestamp', Date.now().toString()); - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error handling window blur:', error); - } - } - - /** - * Phase 3: Handle window focus - */ - private async handleWindowFocus(): Promise { - try { - console.log('DNP-WEB-PHASE3: Window focused - updating coordination'); - - await this.pauseNonCriticalCoordination(); - - sessionStorage.setItem('dnp_window_focus_timestamp', Date.now().toString()); - - // Sync TimeSafari state - await this.syncTimeSafariState(); - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error handling window focus:', error); - } - } - - /** - * Phase 3: Check activeDid coordination for web - */ - private async checkActiveDidCoordinationWeb(): Promise { - try { - console.log('DNP-WEB-PHASE3: Checking activeDid coordination'); - - const lastActiveDidChange = sessionStorage.getItem('dnp_last_active_did_change'); - const lastAppVisible = sessionStorage.getItem('dnp_app_visible_timestamp'); - - if (lastActiveDidChange && lastAppVisible) { - const activeDidChangeTime = parseInt(lastActiveDidChange); - const visibleTime = parseInt(lastAppVisible); - - // If activeDid changed while app was hidden, update coordination - if (activeDidChangeTime > visibleTime) { - console.log('DNP-WEB-PHASE3: ActiveDid changed while hidden - updating coordination'); - - await this.clearCacheForNewIdentity(); - await this.refreshAuthenticationForNewIdentity(''); - } - } - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error checking activeDid coordination:', error); - } - } - - /** - * Phase 3: Sync TimeSafari state for web - */ - private async syncTimeSafariState(): Promise { - try { - console.log('DNP-WEB-PHASE3: Syncing TimeSafari state'); - - // Sync authentication state (placeholder - implement if needed) - console.log('DNP-WEB-PHASE3: Sync authentication state (placeholder)'); - - // Sync notification delivery tracking - const lastBackgroundDelivery = sessionStorage.getItem('dnp_last_background_delivery'); - if (lastBackgroundDelivery) { - const deliveryId = sessionStorage.getItem('dnp_last_background_delivery_id'); - console.log('DNP-WEB-PHASE3: Synced background delivery:', deliveryId); - } - - console.log('DNP-WEB-PHASE3: TimeSafari state sync completed'); - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error syncing TimeSafari state:', error); - } - } - - /** - * Phase 3: Set up background coordination listener - */ - private setupBackgroundCoordinationListener(): void { - try { - console.log('DNP-WEB-PHASE3: Setting up background coordination listener'); - - // Listen for visibility changes - document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this)); - - // Listen for window blur/focus - window.addEventListener('blur', this.handleWindowBlur.bind(this)); - window.addEventListener('focus', this.handleWindowFocus.bind(this)); - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error setting up coordination listener:', error); - } - } - - /** - * Phase 3: Remove background coordination listener - */ - private removeBackgroundCoordinationListener(): void { - try { - console.log('DNP-WEB-PHASE3: Removing background coordination listener'); - - // Remove listeners (using bound functions) - document.removeEventListener('visibilitychange', this.handleVisibilityChange.bind(this)); - window.removeEventListener('blur', this.handleWindowBlur.bind(this)); - window.removeEventListener('focus', this.handleWindowFocus.bind(this)); - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error removing coordination listener:', error); - } - } - - /** - * Phase 3: Get coordination status for web debugging - */ - async getCoordinationStatus(): Promise> { - try { - console.log('DNP-WEB-PHASE3: Getting coordination status'); - - const status = { - platform: 'web', - coordinationActive: sessionStorage.getItem('dnp_coordination_active') === 'true', - coordinationPaused: sessionStorage.getItem('dnp_coordination_paused') === 'true', - appHidden: sessionStorage.getItem('dnp_app_is_hidden') === 'true', - visibilityState: document.visibilityState, - focused: document.hasFocus(), - lastActiveDidChange: sessionStorage.getItem('dnp_last_active_did_change'), - lastCoordinationTimestamp: sessionStorage.getItem('dnp_coordination_timestamp'), - lastVisibilityChange: sessionStorage.getItem('dnp_app_visible_timestamp') - }; - - console.log('DNP-WEB-PHASE3: Coordination status:', status); - return status; - - } catch (error) { - console.error('DNP-WEB-PHASE3: Error getting coordination status:', error); - throw error; - } - } - - // Static Daily Reminder Methods - async scheduleDailyReminder(options: any): Promise { - try { - console.log('DNP-WEB-REMINDER: Scheduling daily reminder:', options.id); - - const { id, title, body, time, sound = true, vibration = true, priority = 'normal', repeatDaily = true, timezone } = options; - - // Validate required parameters - if (!id || !title || !body || !time) { - throw new Error('Missing required parameters: id, title, body, time'); - } - - // Parse time (HH:mm format) - const timeParts = time.split(':'); - if (timeParts.length !== 2) { - throw new Error('Invalid time format. Use HH:mm (e.g., 09:00)'); - } - - const hour = parseInt(timeParts[0]); - const minute = parseInt(timeParts[1]); - - if (hour < 0 || hour > 23 || minute < 0 || minute > 59) { - throw new Error('Invalid time values. Hour must be 0-23, minute must be 0-59'); - } - - // Check if notifications are supported - if (!('Notification' in window)) { - throw new Error('This browser does not support notifications'); - } - - // Request permission if not granted - if (Notification.permission === 'default') { - const permission = await Notification.requestPermission(); - if (permission !== 'granted') { - throw new Error('Notification permission denied'); - } - } - - if (Notification.permission !== 'granted') { - throw new Error('Notification permission not granted'); - } - - // Calculate next trigger time - const now = new Date(); - const triggerTime = new Date(); - triggerTime.setHours(hour, minute, 0, 0); - - // If time has passed today, schedule for tomorrow - if (triggerTime <= now) { - triggerTime.setDate(triggerTime.getDate() + 1); - } - - const timeUntilTrigger = triggerTime.getTime() - now.getTime(); - - // Store reminder in localStorage - this.storeReminderInLocalStorage(id, title, body, time, sound, vibration, priority, repeatDaily, timezone); - - // Schedule the notification using setTimeout (simplified web implementation) - const timeoutId = setTimeout(() => { - this.showReminderNotification(title, body, sound, vibration, priority, id); - - // If repeatDaily is true, schedule the next occurrence - if (repeatDaily) { - this.scheduleDailyReminder(options); - } - }, timeUntilTrigger); - - // Store timeout ID for cancellation - this.storeReminderTimeout(id, timeoutId); - - console.log('DNP-WEB-REMINDER: Daily reminder scheduled successfully:', id); - } catch (error) { - console.error('DNP-WEB-REMINDER: Error scheduling daily reminder:', error); - throw error; - } - } - - async cancelDailyReminder(reminderId: string): Promise { - try { - console.log('DNP-WEB-REMINDER: Cancelling daily reminder:', reminderId); - - // Cancel the timeout - this.cancelReminderTimeout(reminderId); - - // Remove from localStorage - this.removeReminderFromLocalStorage(reminderId); - - console.log('DNP-WEB-REMINDER: Daily reminder cancelled:', reminderId); - } catch (error) { - console.error('DNP-WEB-REMINDER: Error cancelling daily reminder:', error); - throw error; - } - } - - async getScheduledReminders(): Promise { - try { - console.log('DNP-WEB-REMINDER: Getting scheduled reminders'); - - const reminders = this.getRemindersFromLocalStorage(); - - return { reminders }; - } catch (error) { - console.error('DNP-WEB-REMINDER: Error getting scheduled reminders:', error); - throw error; - } - } - - async updateDailyReminder(reminderId: string, options: any): Promise { - try { - console.log('DNP-WEB-REMINDER: Updating daily reminder:', reminderId); - - // Cancel existing reminder - await this.cancelDailyReminder(reminderId); - - // Update in localStorage - this.updateReminderInLocalStorage(reminderId, options); - - // Reschedule with new settings if all required fields are provided - if (options.title && options.body && options.time) { - await this.scheduleDailyReminder({ ...options, id: reminderId }); - } - - console.log('DNP-WEB-REMINDER: Daily reminder updated:', reminderId); - } catch (error) { - console.error('DNP-WEB-REMINDER: Error updating daily reminder:', error); - throw error; - } - } - - // Helper methods for web reminder functionality - private showReminderNotification( - title: string, - body: string, - sound: boolean, - vibration: boolean, - priority: string, - reminderId: string - ): void { - try { - const notification = new Notification(title, { - body: body, - icon: '/favicon.ico', // You can customize this - badge: '/favicon.ico', - tag: `reminder_${reminderId}`, - requireInteraction: priority === 'high', - silent: !sound - }); - - // Handle notification click - notification.onclick = () => { - console.log('DNP-WEB-REMINDER: Reminder notification clicked:', reminderId); - notification.close(); - }; - - // Handle notification close - notification.onclose = () => { - console.log('DNP-WEB-REMINDER: Reminder notification closed:', reminderId); - }; - - // Handle notification error - notification.onerror = (error) => { - console.error('DNP-WEB-REMINDER: Reminder notification error:', error); - }; - - // Auto-close after 10 seconds for non-high priority - if (priority !== 'high') { - setTimeout(() => { - notification.close(); - }, 10000); - } - - // Trigger vibration if supported and enabled - if (vibration && 'vibrate' in navigator) { - navigator.vibrate([200, 100, 200]); - } - - // Record reminder trigger - this.recordReminderTrigger(reminderId); - - console.log('DNP-WEB-REMINDER: Reminder notification displayed:', title); - } catch (error) { - console.error('DNP-WEB-REMINDER: Error showing reminder notification:', error); - } - } - - private storeReminderInLocalStorage( - id: string, - title: string, - body: string, - time: string, - sound: boolean, - vibration: boolean, - priority: string, - repeatDaily: boolean, - timezone?: string - ): void { - try { - const reminderData = { - id, - title, - body, - time, - sound, - vibration, - priority, - repeatDaily, - timezone: timezone || '', - createdAt: Date.now(), - lastTriggered: 0 - }; - - const reminders = this.getRemindersFromLocalStorage(); - reminders.push(reminderData); - localStorage.setItem('daily_reminders', JSON.stringify(reminders)); - - console.log('DNP-WEB-REMINDER: Reminder stored:', id); - } catch (error) { - console.error('DNP-WEB-REMINDER: Error storing reminder:', error); - } - } - - private removeReminderFromLocalStorage(id: string): void { - try { - const reminders = this.getRemindersFromLocalStorage(); - const filteredReminders = reminders.filter((reminder: any) => reminder.id !== id); - localStorage.setItem('daily_reminders', JSON.stringify(filteredReminders)); - - console.log('DNP-WEB-REMINDER: Reminder removed:', id); - } catch (error) { - console.error('DNP-WEB-REMINDER: Error removing reminder:', error); - } - } - - private getRemindersFromLocalStorage(): any[] { - try { - const reminders = localStorage.getItem('daily_reminders'); - return reminders ? JSON.parse(reminders) : []; - } catch (error) { - console.error('DNP-WEB-REMINDER: Error getting reminders from localStorage:', error); - return []; - } - } - - private updateReminderInLocalStorage(id: string, options: any): void { - try { - const reminders = this.getRemindersFromLocalStorage(); - const reminderIndex = reminders.findIndex((reminder: any) => reminder.id === id); - - if (reminderIndex !== -1) { - if (options.title) reminders[reminderIndex].title = options.title; - if (options.body) reminders[reminderIndex].body = options.body; - if (options.time) reminders[reminderIndex].time = options.time; - if (options.sound !== undefined) reminders[reminderIndex].sound = options.sound; - if (options.vibration !== undefined) reminders[reminderIndex].vibration = options.vibration; - if (options.priority) reminders[reminderIndex].priority = options.priority; - if (options.repeatDaily !== undefined) reminders[reminderIndex].repeatDaily = options.repeatDaily; - if (options.timezone) reminders[reminderIndex].timezone = options.timezone; - - localStorage.setItem('daily_reminders', JSON.stringify(reminders)); - console.log('DNP-WEB-REMINDER: Reminder updated:', id); - } - } catch (error) { - console.error('DNP-WEB-REMINDER: Error updating reminder:', error); - } - } - - private storeReminderTimeout(id: string, timeoutId: number): void { - try { - const timeouts = this.getReminderTimeouts(); - timeouts[id] = timeoutId; - localStorage.setItem('daily_reminder_timeouts', JSON.stringify(timeouts)); - } catch (error) { - console.error('DNP-WEB-REMINDER: Error storing reminder timeout:', error); - } - } - - private cancelReminderTimeout(id: string): void { - try { - const timeouts = this.getReminderTimeouts(); - if (timeouts[id]) { - clearTimeout(timeouts[id]); - delete timeouts[id]; - localStorage.setItem('daily_reminder_timeouts', JSON.stringify(timeouts)); - } - } catch (error) { - console.error('DNP-WEB-REMINDER: Error cancelling reminder timeout:', error); - } - } - - private getReminderTimeouts(): Record { - try { - const timeouts = localStorage.getItem('daily_reminder_timeouts'); - return timeouts ? JSON.parse(timeouts) : {}; - } catch (error) { - console.error('DNP-WEB-REMINDER: Error getting reminder timeouts:', error); - return {}; - } - } - - private recordReminderTrigger(id: string): void { - try { - const reminders = this.getRemindersFromLocalStorage(); - const reminderIndex = reminders.findIndex((reminder: any) => reminder.id === id); - - if (reminderIndex !== -1) { - reminders[reminderIndex].lastTriggered = Date.now(); - localStorage.setItem('daily_reminders', JSON.stringify(reminders)); - console.log('DNP-WEB-REMINDER: Reminder trigger recorded:', id); - } - } catch (error) { - console.error('DNP-WEB-REMINDER: Error recording reminder trigger:', error); - } - } -} diff --git a/test-apps/android-test/src/index.ts b/test-apps/android-test/src/index.ts index bc25741..cb8193f 100644 --- a/test-apps/android-test/src/index.ts +++ b/test-apps/android-test/src/index.ts @@ -2,9 +2,9 @@ import { Capacitor } from '@capacitor/core'; import { DailyNotificationPlugin } from '@timesafari/daily-notification-plugin'; // Phase 4: Import TimeSafari components -import { EndorserAPIClient, TIMESAFARI_ENDSORER_CONFIG } from '../../../src/typescript/EndorserAPIClient'; -import { SecurityManager, TIMESAFARI_SECURITY_CONFIG } from '../../../src/typescript/SecurityManager'; -import { TimeSafariNotificationManager, DEFAULT_TIMESAFARI_PREFERENCES } from '../../../src/typescript/TimeSafariNotificationManager'; +import { EndorserAPIClient, TIMESAFARI_ENDSORER_CONFIG } from '../shared/typescript/EndorserAPIClient'; +import { SecurityManager, TIMESAFARI_SECURITY_CONFIG } from '../shared/typescript/SecurityManager'; +import { TimeSafariNotificationManager, DEFAULT_TIMESAFARI_PREFERENCES } from '../shared/typescript/TimeSafariNotificationManager'; import { TimeSafariUser, TimeSafariPreferences, diff --git a/test-apps/electron-test/src/index.ts b/test-apps/electron-test/src/index.ts index 996a466..a65b4fa 100644 --- a/test-apps/electron-test/src/index.ts +++ b/test-apps/electron-test/src/index.ts @@ -1,9 +1,9 @@ import { ConfigLoader, MockDailyNotificationService, TestLogger } from '../shared/config-loader'; // Phase 4: Import TimeSafari components -import { EndorserAPIClient, TIMESAFARI_ENDSORER_CONFIG } from '../../../src/typescript/EndorserAPIClient'; -import { SecurityManager, TIMESAFARI_SECURITY_CONFIG } from '../../../src/typescript/SecurityManager'; -import { TimeSafariNotificationManager, DEFAULT_TIMESAFARI_PREFERENCES } from '../../../src/typescript/TimeSafariNotificationManager'; +import { EndorserAPIClient, TIMESAFARI_ENDSORER_CONFIG } from '../shared/typescript/EndorserAPIClient'; +import { SecurityManager, TIMESAFARI_SECURITY_CONFIG } from '../shared/typescript/SecurityManager'; +import { TimeSafariNotificationManager, DEFAULT_TIMESAFARI_PREFERENCES } from '../shared/typescript/TimeSafariNotificationManager'; import { TimeSafariUser, TimeSafariPreferences, diff --git a/test-apps/ios-test/src/index.ts b/test-apps/ios-test/src/index.ts index f03fd18..f2e5c7b 100644 --- a/test-apps/ios-test/src/index.ts +++ b/test-apps/ios-test/src/index.ts @@ -2,9 +2,9 @@ import { Capacitor } from '@capacitor/core'; import { ConfigLoader, MockDailyNotificationService, TestLogger } from '../shared/config-loader'; // Phase 4: Import TimeSafari components -import { EndorserAPIClient, TIMESAFARI_ENDSORER_CONFIG } from '../../../src/typescript/EndorserAPIClient'; -import { SecurityManager, TIMESAFARI_SECURITY_CONFIG } from '../../../src/typescript/SecurityManager'; -import { TimeSafariNotificationManager, DEFAULT_TIMESAFARI_PREFERENCES } from '../../../src/typescript/TimeSafariNotificationManager'; +import { EndorserAPIClient, TIMESAFARI_ENDSORER_CONFIG } from '../shared/typescript/EndorserAPIClient'; +import { SecurityManager, TIMESAFARI_SECURITY_CONFIG } from '../shared/typescript/SecurityManager'; +import { TimeSafariNotificationManager, DEFAULT_TIMESAFARI_PREFERENCES } from '../shared/typescript/TimeSafariNotificationManager'; import { TimeSafariUser, TimeSafariPreferences, diff --git a/src/typescript/EndorserAPIClient.ts b/test-apps/shared/typescript/EndorserAPIClient.ts similarity index 100% rename from src/typescript/EndorserAPIClient.ts rename to test-apps/shared/typescript/EndorserAPIClient.ts diff --git a/src/typescript/SecurityManager.ts b/test-apps/shared/typescript/SecurityManager.ts similarity index 100% rename from src/typescript/SecurityManager.ts rename to test-apps/shared/typescript/SecurityManager.ts diff --git a/src/typescript/TimeSafariNotificationManager.ts b/test-apps/shared/typescript/TimeSafariNotificationManager.ts similarity index 100% rename from src/typescript/TimeSafariNotificationManager.ts rename to test-apps/shared/typescript/TimeSafariNotificationManager.ts