Browse Source
- Add Android test app with exact alarm permission testing - Add iOS test app with rolling window and BGTaskScheduler testing - Add Electron test app with mock implementations and IPC - Include automated setup scripts for each platform - Provide comprehensive testing checklist and troubleshooting guide - Follow best practices for Capacitor plugin testing Test apps include: - Plugin configuration and scheduling validation - Platform-specific feature testing (Android exact alarms, iOS rolling window) - Performance monitoring and debug information - Error handling and edge case testing - Cross-platform API consistency validation Setup: Run ./setup-*.sh scripts for automated platform setup Testing: Each app provides interactive UI for comprehensive plugin validation Files: 25+ new files across test-apps/ directoryresearch/notification-plugin-enhancement
29 changed files with 2165 additions and 37 deletions
@ -0,0 +1,208 @@ |
|||||
|
# API Reference |
||||
|
|
||||
|
## DailyNotificationPlugin Interface |
||||
|
|
||||
|
### Configuration |
||||
|
|
||||
|
#### `configure(options: ConfigureOptions): Promise<void>` |
||||
|
Configure the plugin with storage, TTL, and optimization settings. |
||||
|
|
||||
|
**Parameters:** |
||||
|
- `options.storage`: `'shared'` | `'tiered'` - Storage mode |
||||
|
- `options.ttlSeconds`: `number` - TTL in seconds (default: 1800) |
||||
|
- `options.prefetchLeadMinutes`: `number` - Prefetch lead time (default: 15) |
||||
|
- `options.enableETagSupport`: `boolean` - Enable ETag conditional requests |
||||
|
- `options.enableErrorHandling`: `boolean` - Enable advanced error handling |
||||
|
- `options.enablePerformanceOptimization`: `boolean` - Enable performance optimization |
||||
|
|
||||
|
### Core Methods |
||||
|
|
||||
|
#### `scheduleDailyNotification(options: NotificationOptions): Promise<void>` |
||||
|
Schedule a daily notification with content fetching. |
||||
|
|
||||
|
**Parameters:** |
||||
|
- `options.url`: `string` - Content endpoint URL |
||||
|
- `options.time`: `string` - Time in HH:MM format |
||||
|
- `options.title`: `string` - Notification title |
||||
|
- `options.body`: `string` - Notification body |
||||
|
- `options.sound`: `boolean` - Enable sound (optional) |
||||
|
- `options.retryConfig`: `RetryConfiguration` - Custom retry settings (optional) |
||||
|
|
||||
|
#### `getLastNotification(): Promise<NotificationResponse | null>` |
||||
|
Get the last scheduled notification. |
||||
|
|
||||
|
#### `cancelAllNotifications(): Promise<void>` |
||||
|
Cancel all scheduled notifications. |
||||
|
|
||||
|
### Platform-Specific Methods |
||||
|
|
||||
|
#### Android Only |
||||
|
|
||||
|
##### `getExactAlarmStatus(): Promise<ExactAlarmStatus>` |
||||
|
Get exact alarm permission and capability status. |
||||
|
|
||||
|
##### `requestExactAlarmPermission(): Promise<void>` |
||||
|
Request exact alarm permission from user. |
||||
|
|
||||
|
##### `openExactAlarmSettings(): Promise<void>` |
||||
|
Open exact alarm settings in system preferences. |
||||
|
|
||||
|
##### `getRebootRecoveryStatus(): Promise<RecoveryStatus>` |
||||
|
Get reboot recovery status and statistics. |
||||
|
|
||||
|
### Management Methods |
||||
|
|
||||
|
#### `maintainRollingWindow(): Promise<void>` |
||||
|
Manually trigger rolling window maintenance. |
||||
|
|
||||
|
#### `getRollingWindowStats(): Promise<RollingWindowStats>` |
||||
|
Get rolling window statistics and status. |
||||
|
|
||||
|
### Optimization Methods |
||||
|
|
||||
|
#### `optimizeDatabase(): Promise<void>` |
||||
|
Optimize database performance with indexes and settings. |
||||
|
|
||||
|
#### `optimizeMemory(): Promise<void>` |
||||
|
Optimize memory usage and perform cleanup. |
||||
|
|
||||
|
#### `optimizeBattery(): Promise<void>` |
||||
|
Optimize battery usage and background CPU. |
||||
|
|
||||
|
### Metrics and Monitoring |
||||
|
|
||||
|
#### `getPerformanceMetrics(): Promise<PerformanceMetrics>` |
||||
|
Get comprehensive performance metrics. |
||||
|
|
||||
|
#### `getErrorMetrics(): Promise<ErrorMetrics>` |
||||
|
Get error handling metrics and statistics. |
||||
|
|
||||
|
#### `getNetworkMetrics(): Promise<NetworkMetrics>` |
||||
|
Get network efficiency metrics (ETag support). |
||||
|
|
||||
|
#### `getMemoryMetrics(): Promise<MemoryMetrics>` |
||||
|
Get memory usage metrics and statistics. |
||||
|
|
||||
|
#### `getObjectPoolMetrics(): Promise<ObjectPoolMetrics>` |
||||
|
Get object pooling efficiency metrics. |
||||
|
|
||||
|
### Utility Methods |
||||
|
|
||||
|
#### `resetPerformanceMetrics(): Promise<void>` |
||||
|
Reset all performance metrics to zero. |
||||
|
|
||||
|
#### `resetErrorMetrics(): Promise<void>` |
||||
|
Reset error handling metrics. |
||||
|
|
||||
|
#### `clearRetryStates(): Promise<void>` |
||||
|
Clear all retry states and operations. |
||||
|
|
||||
|
#### `cleanExpiredETags(): Promise<void>` |
||||
|
Clean expired ETag cache entries. |
||||
|
|
||||
|
## Data Types |
||||
|
|
||||
|
### ConfigureOptions |
||||
|
```typescript |
||||
|
interface ConfigureOptions { |
||||
|
storage?: 'shared' | 'tiered'; |
||||
|
ttlSeconds?: number; |
||||
|
prefetchLeadMinutes?: number; |
||||
|
enableETagSupport?: boolean; |
||||
|
enableErrorHandling?: boolean; |
||||
|
enablePerformanceOptimization?: boolean; |
||||
|
maxRetries?: number; |
||||
|
baseRetryDelay?: number; |
||||
|
maxRetryDelay?: number; |
||||
|
backoffMultiplier?: number; |
||||
|
memoryWarningThreshold?: number; |
||||
|
memoryCriticalThreshold?: number; |
||||
|
objectPoolSize?: number; |
||||
|
maxObjectPoolSize?: number; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### NotificationOptions |
||||
|
```typescript |
||||
|
interface NotificationOptions { |
||||
|
url: string; |
||||
|
time: string; |
||||
|
title: string; |
||||
|
body: string; |
||||
|
sound?: boolean; |
||||
|
retryConfig?: RetryConfiguration; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### ExactAlarmStatus (Android) |
||||
|
```typescript |
||||
|
interface ExactAlarmStatus { |
||||
|
supported: boolean; |
||||
|
enabled: boolean; |
||||
|
canSchedule: boolean; |
||||
|
fallbackWindow: string; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### PerformanceMetrics |
||||
|
```typescript |
||||
|
interface PerformanceMetrics { |
||||
|
overallScore: number; |
||||
|
databasePerformance: number; |
||||
|
memoryEfficiency: number; |
||||
|
batteryEfficiency: number; |
||||
|
objectPoolEfficiency: number; |
||||
|
totalDatabaseQueries: number; |
||||
|
averageMemoryUsage: number; |
||||
|
objectPoolHits: number; |
||||
|
backgroundCpuUsage: number; |
||||
|
totalNetworkRequests: number; |
||||
|
recommendations: string[]; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### ErrorMetrics |
||||
|
```typescript |
||||
|
interface ErrorMetrics { |
||||
|
totalErrors: number; |
||||
|
networkErrors: number; |
||||
|
storageErrors: number; |
||||
|
schedulingErrors: number; |
||||
|
permissionErrors: number; |
||||
|
configurationErrors: number; |
||||
|
systemErrors: number; |
||||
|
unknownErrors: number; |
||||
|
cacheHitRatio: number; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## Error Handling |
||||
|
|
||||
|
All methods return promises that reject with descriptive error messages. The plugin includes comprehensive error categorization and retry logic. |
||||
|
|
||||
|
### Common Error Types |
||||
|
- **Network Errors**: Connection timeouts, DNS failures |
||||
|
- **Storage Errors**: Database corruption, disk full |
||||
|
- **Permission Errors**: Missing exact alarm permission |
||||
|
- **Configuration Errors**: Invalid parameters, unsupported settings |
||||
|
- **System Errors**: Out of memory, platform limitations |
||||
|
|
||||
|
## Platform Differences |
||||
|
|
||||
|
### Android |
||||
|
- Requires `SCHEDULE_EXACT_ALARM` permission for precise timing |
||||
|
- Falls back to windowed alarms (±10m) if exact permission denied |
||||
|
- Supports reboot recovery with broadcast receivers |
||||
|
- Full performance optimization features |
||||
|
|
||||
|
### iOS |
||||
|
- Uses `BGTaskScheduler` for background prefetch |
||||
|
- Limited to 64 pending notifications |
||||
|
- Automatic background task management |
||||
|
- Battery optimization built-in |
||||
|
|
||||
|
### Web |
||||
|
- Placeholder implementations for development |
||||
|
- No actual notification scheduling |
||||
|
- All methods return mock data |
||||
|
- Used for testing and development |
@ -0,0 +1,181 @@ |
|||||
|
# Daily Notification Plugin - Usage Guide |
||||
|
|
||||
|
## Quick Start |
||||
|
|
||||
|
```typescript |
||||
|
import { DailyNotification } from '@timesafari/daily-notification-plugin'; |
||||
|
|
||||
|
// 1. Configure the plugin |
||||
|
await DailyNotification.configure({ |
||||
|
storage: 'shared', // Use shared SQLite database |
||||
|
ttlSeconds: 1800, // 30 minutes TTL |
||||
|
prefetchLeadMinutes: 15 // Prefetch 15 minutes before delivery |
||||
|
}); |
||||
|
|
||||
|
// 2. 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' |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
## Configuration Options |
||||
|
|
||||
|
### Storage Mode |
||||
|
- **`'shared'`** (Recommended): Uses shared SQLite database with WAL mode |
||||
|
- **`'tiered'`** (Legacy): Uses SharedPreferences/UserDefaults + in-memory cache |
||||
|
|
||||
|
### TTL Settings |
||||
|
- **`ttlSeconds`**: Maximum age of content at delivery time (default: 1800 = 30 minutes) |
||||
|
- **`prefetchLeadMinutes`**: How early to prefetch content (default: 15 minutes) |
||||
|
|
||||
|
### Performance Optimization |
||||
|
- **`enableETagSupport`**: Use conditional requests for bandwidth savings |
||||
|
- **`enableErrorHandling`**: Advanced retry logic with exponential backoff |
||||
|
- **`enablePerformanceOptimization`**: Database indexes, memory management, object pooling |
||||
|
|
||||
|
## Platform-Specific Features |
||||
|
|
||||
|
### Android |
||||
|
```typescript |
||||
|
// Check exact alarm status |
||||
|
const alarmStatus = await DailyNotification.getExactAlarmStatus(); |
||||
|
if (!alarmStatus.canSchedule) { |
||||
|
// Request permission or use windowed fallback |
||||
|
await DailyNotification.requestExactAlarmPermission(); |
||||
|
} |
||||
|
|
||||
|
// Check reboot recovery status |
||||
|
const recoveryStatus = await DailyNotification.getRebootRecoveryStatus(); |
||||
|
if (recoveryStatus.recoveryNeeded) { |
||||
|
console.log('System may have rebooted - notifications restored'); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### iOS |
||||
|
```typescript |
||||
|
// Background tasks are automatically handled |
||||
|
// The plugin uses BGTaskScheduler for T–lead prefetch |
||||
|
// No additional configuration needed |
||||
|
``` |
||||
|
|
||||
|
## Advanced Usage |
||||
|
|
||||
|
### Error Handling |
||||
|
```typescript |
||||
|
// Configure retry behavior |
||||
|
await DailyNotification.configure({ |
||||
|
maxRetries: 3, |
||||
|
baseRetryDelay: 1000, // 1 second |
||||
|
maxRetryDelay: 30000, // 30 seconds |
||||
|
backoffMultiplier: 2.0 |
||||
|
}); |
||||
|
|
||||
|
// Monitor error metrics |
||||
|
const errorMetrics = await DailyNotification.getErrorMetrics(); |
||||
|
console.log(`Network errors: ${errorMetrics.networkErrors}`); |
||||
|
console.log(`Cache hit ratio: ${errorMetrics.cacheHitRatio}`); |
||||
|
``` |
||||
|
|
||||
|
### Performance Monitoring |
||||
|
```typescript |
||||
|
// Get performance metrics |
||||
|
const metrics = await DailyNotification.getPerformanceMetrics(); |
||||
|
console.log(`Performance score: ${metrics.overallScore}/100`); |
||||
|
console.log(`Memory usage: ${metrics.averageMemoryUsage}MB`); |
||||
|
|
||||
|
// Optimize if needed |
||||
|
if (metrics.overallScore < 70) { |
||||
|
await DailyNotification.optimizeMemory(); |
||||
|
await DailyNotification.optimizeDatabase(); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### Rolling Window Management |
||||
|
```typescript |
||||
|
// Manual maintenance |
||||
|
await DailyNotification.maintainRollingWindow(); |
||||
|
|
||||
|
// Check status |
||||
|
const windowStats = await DailyNotification.getRollingWindowStats(); |
||||
|
console.log(`Maintenance needed: ${windowStats.maintenanceNeeded}`); |
||||
|
console.log(`Time until next: ${windowStats.timeUntilNextMaintenance}ms`); |
||||
|
``` |
||||
|
|
||||
|
## Production Configuration |
||||
|
|
||||
|
```typescript |
||||
|
// Recommended production settings |
||||
|
await DailyNotification.configure({ |
||||
|
storage: 'shared', |
||||
|
ttlSeconds: 1800, |
||||
|
prefetchLeadMinutes: 15, |
||||
|
enableETagSupport: true, |
||||
|
enableErrorHandling: true, |
||||
|
enablePerformanceOptimization: true, |
||||
|
memoryWarningThreshold: 50, // MB |
||||
|
memoryCriticalThreshold: 100, // MB |
||||
|
objectPoolSize: 20, |
||||
|
maxObjectPoolSize: 100 |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
## Troubleshooting |
||||
|
|
||||
|
### Common Issues |
||||
|
|
||||
|
1. **Notifications not firing** |
||||
|
- Check exact alarm permissions on Android |
||||
|
- Verify TTL settings aren't too restrictive |
||||
|
- Ensure rolling window is maintained |
||||
|
|
||||
|
2. **High memory usage** |
||||
|
- Enable performance optimization |
||||
|
- Check memory thresholds |
||||
|
- Monitor object pool efficiency |
||||
|
|
||||
|
3. **Network efficiency** |
||||
|
- Enable ETag support |
||||
|
- Monitor cache hit ratios |
||||
|
- Check error retry patterns |
||||
|
|
||||
|
### Debug Information |
||||
|
|
||||
|
```typescript |
||||
|
// Get comprehensive debug info |
||||
|
const debugInfo = await DailyNotification.getDebugInfo(); |
||||
|
console.log('Plugin Status:', debugInfo.status); |
||||
|
console.log('Configuration:', debugInfo.configuration); |
||||
|
console.log('Recent Errors:', debugInfo.recentErrors); |
||||
|
``` |
||||
|
|
||||
|
## Migration from Legacy Storage |
||||
|
|
||||
|
```typescript |
||||
|
// The plugin automatically migrates from tiered to shared storage |
||||
|
// No manual migration needed - just configure with storage: 'shared' |
||||
|
await DailyNotification.configure({ |
||||
|
storage: 'shared' // Triggers automatic migration |
||||
|
}); |
||||
|
``` |
||||
|
|
||||
|
## Best Practices |
||||
|
|
||||
|
1. **Always configure before scheduling** - Set up storage, TTL, and optimization features |
||||
|
2. **Monitor performance metrics** - Use built-in monitoring to optimize settings |
||||
|
3. **Handle errors gracefully** - Implement retry logic and fallback mechanisms |
||||
|
4. **Test on both platforms** - Android and iOS have different capabilities and limitations |
||||
|
5. **Use production settings** - Enable all optimization features for production use |
||||
|
|
||||
|
## API Reference |
||||
|
|
||||
|
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` |
@ -0,0 +1,160 @@ |
|||||
|
# Test Apps Setup Guide |
||||
|
|
||||
|
## Overview |
||||
|
|
||||
|
This guide creates minimal Capacitor test apps for validating the Daily Notification Plugin across all target platforms. |
||||
|
|
||||
|
## Directory Structure |
||||
|
|
||||
|
``` |
||||
|
test-apps/ |
||||
|
├── android-test/ # Android test app |
||||
|
├── ios-test/ # iOS test app |
||||
|
├── electron-test/ # Electron test app |
||||
|
├── setup-android.sh # Android setup script |
||||
|
├── setup-ios.sh # iOS setup script |
||||
|
├── setup-electron.sh # Electron setup script |
||||
|
└── README.md # This guide |
||||
|
``` |
||||
|
|
||||
|
## Prerequisites |
||||
|
|
||||
|
- Node.js 18+ |
||||
|
- Capacitor CLI: `npm install -g @capacitor/cli` |
||||
|
- Android Studio (for Android) |
||||
|
- Xcode (for iOS) |
||||
|
- Platform-specific SDKs |
||||
|
|
||||
|
## Quick Start |
||||
|
|
||||
|
### Option 1: Automated Setup (Recommended) |
||||
|
```bash |
||||
|
# Setup all platforms |
||||
|
./setup-android.sh |
||||
|
./setup-ios.sh |
||||
|
./setup-electron.sh |
||||
|
``` |
||||
|
|
||||
|
### Option 2: Manual Setup |
||||
|
```bash |
||||
|
# Android |
||||
|
cd android-test |
||||
|
npm install |
||||
|
npx cap init "Daily Notification Android Test" "com.timesafari.dailynotification.androidtest" |
||||
|
npx cap add android |
||||
|
npm run build |
||||
|
npx cap sync android |
||||
|
|
||||
|
# iOS |
||||
|
cd ios-test |
||||
|
npm install |
||||
|
npx cap init "Daily Notification iOS Test" "com.timesafari.dailynotification.iostest" |
||||
|
npx cap add ios |
||||
|
npm run build |
||||
|
npx cap sync ios |
||||
|
|
||||
|
# Electron |
||||
|
cd electron-test |
||||
|
npm install |
||||
|
npm run build-web |
||||
|
``` |
||||
|
|
||||
|
## Test App Features |
||||
|
|
||||
|
Each test app includes: |
||||
|
- **Plugin Configuration**: Test shared SQLite, TTL, prefetch settings |
||||
|
- **Notification Scheduling**: Basic daily notification setup |
||||
|
- **Platform-Specific Features**: |
||||
|
- Android: Exact alarm permissions, reboot recovery |
||||
|
- iOS: Rolling window management, BGTaskScheduler |
||||
|
- Electron: Mock implementations, IPC communication |
||||
|
- **Performance Monitoring**: Metrics collection and display |
||||
|
- **Error Handling**: Comprehensive error testing |
||||
|
- **Debug Information**: Platform-specific debug data |
||||
|
|
||||
|
## Platform-Specific Testing |
||||
|
|
||||
|
### Android Test App |
||||
|
- **Exact Alarm Status**: Check permission and capability |
||||
|
- **Permission Requests**: Test exact alarm permission flow |
||||
|
- **Performance Metrics**: Monitor Android-specific optimizations |
||||
|
- **Reboot Recovery**: Validate system restart handling |
||||
|
|
||||
|
### iOS Test App |
||||
|
- **Rolling Window**: Test notification limit management |
||||
|
- **Background Tasks**: Validate BGTaskScheduler integration |
||||
|
- **Performance Metrics**: Monitor iOS-specific optimizations |
||||
|
- **Memory Management**: Test object pooling and cleanup |
||||
|
|
||||
|
### Electron Test App |
||||
|
- **Mock Implementations**: Test web platform compatibility |
||||
|
- **IPC Communication**: Validate Electron-specific APIs |
||||
|
- **Development Workflow**: Test plugin integration |
||||
|
- **Debug Information**: Platform-specific status display |
||||
|
|
||||
|
## Running the Test Apps |
||||
|
|
||||
|
### Android |
||||
|
```bash |
||||
|
cd android-test |
||||
|
npm run dev # Web development server |
||||
|
npx cap open android # Open in Android Studio |
||||
|
npx cap run android # Run on device/emulator |
||||
|
``` |
||||
|
|
||||
|
### iOS |
||||
|
```bash |
||||
|
cd ios-test |
||||
|
npm run dev # Web development server |
||||
|
npx cap open ios # Open in Xcode |
||||
|
npx cap run ios # Run on device/simulator |
||||
|
``` |
||||
|
|
||||
|
### Electron |
||||
|
```bash |
||||
|
cd electron-test |
||||
|
npm start # Run Electron app |
||||
|
npm run dev # Run in development mode |
||||
|
``` |
||||
|
|
||||
|
## Testing Checklist |
||||
|
|
||||
|
### Core Functionality |
||||
|
- [ ] Plugin configuration works |
||||
|
- [ ] Notification scheduling succeeds |
||||
|
- [ ] Error handling functions properly |
||||
|
- [ ] Performance metrics are accurate |
||||
|
|
||||
|
### Platform-Specific |
||||
|
- [ ] Android exact alarm permissions |
||||
|
- [ ] iOS rolling window management |
||||
|
- [ ] Electron mock implementations |
||||
|
- [ ] Cross-platform API consistency |
||||
|
|
||||
|
### Integration |
||||
|
- [ ] Plugin loads without errors |
||||
|
- [ ] Configuration persists across sessions |
||||
|
- [ ] Performance optimizations active |
||||
|
- [ ] Debug information accessible |
||||
|
|
||||
|
## Troubleshooting |
||||
|
|
||||
|
### Common Issues |
||||
|
1. **Build Failures**: Ensure all dependencies installed |
||||
|
2. **Platform Errors**: Check platform-specific SDKs installed |
||||
|
3. **Permission Issues**: Verify platform permissions configured |
||||
|
4. **Sync Problems**: Run `npx cap sync` after changes |
||||
|
|
||||
|
### Development Tips |
||||
|
- Use `npm run dev` for web testing |
||||
|
- Use platform-specific tools for native testing |
||||
|
- Check console logs for detailed error information |
||||
|
- Test on both physical devices and simulators |
||||
|
|
||||
|
## Next Steps |
||||
|
|
||||
|
1. **Run Setup Scripts**: Execute platform-specific setup |
||||
|
2. **Test Core Features**: Validate basic functionality |
||||
|
3. **Test Platform Features**: Verify platform-specific capabilities |
||||
|
4. **Integration Testing**: Test with actual plugin implementation |
||||
|
5. **Performance Validation**: Monitor metrics and optimizations |
@ -0,0 +1,105 @@ |
|||||
|
# Dependencies |
||||
|
node_modules/ |
||||
|
npm-debug.log* |
||||
|
yarn-debug.log* |
||||
|
yarn-error.log* |
||||
|
|
||||
|
# Build outputs |
||||
|
dist/ |
||||
|
build/ |
||||
|
*.tsbuildinfo |
||||
|
|
||||
|
# Capacitor |
||||
|
android/ |
||||
|
ios/ |
||||
|
.capacitor/ |
||||
|
|
||||
|
# IDE and editor files |
||||
|
.vscode/ |
||||
|
.idea/ |
||||
|
*.swp |
||||
|
*.swo |
||||
|
*~ |
||||
|
|
||||
|
# OS generated files |
||||
|
.DS_Store |
||||
|
.DS_Store? |
||||
|
._* |
||||
|
.Spotlight-V100 |
||||
|
.Trashes |
||||
|
ehthumbs.db |
||||
|
Thumbs.db |
||||
|
|
||||
|
# Logs |
||||
|
logs |
||||
|
*.log |
||||
|
|
||||
|
# Runtime data |
||||
|
pids |
||||
|
*.pid |
||||
|
*.seed |
||||
|
*.pid.lock |
||||
|
|
||||
|
# Coverage directory used by tools like istanbul |
||||
|
coverage/ |
||||
|
*.lcov |
||||
|
|
||||
|
# nyc test coverage |
||||
|
.nyc_output |
||||
|
|
||||
|
# Dependency directories |
||||
|
jspm_packages/ |
||||
|
|
||||
|
# Optional npm cache directory |
||||
|
.npm |
||||
|
|
||||
|
# Optional eslint cache |
||||
|
.eslintcache |
||||
|
|
||||
|
# Microbundle cache |
||||
|
.rpt2_cache/ |
||||
|
.rts2_cache_cjs/ |
||||
|
.rts2_cache_es/ |
||||
|
.rts2_cache_umd/ |
||||
|
|
||||
|
# Optional REPL history |
||||
|
.node_repl_history |
||||
|
|
||||
|
# Output of 'npm pack' |
||||
|
*.tgz |
||||
|
|
||||
|
# Yarn Integrity file |
||||
|
.yarn-integrity |
||||
|
|
||||
|
# dotenv environment variables file |
||||
|
.env |
||||
|
.env.test |
||||
|
.env.local |
||||
|
.env.development.local |
||||
|
.env.test.local |
||||
|
.env.production.local |
||||
|
|
||||
|
# parcel-bundler cache (https://parceljs.org/) |
||||
|
.cache |
||||
|
.parcel-cache |
||||
|
|
||||
|
# next.js build output |
||||
|
.next |
||||
|
|
||||
|
# nuxt.js build output |
||||
|
.nuxt |
||||
|
|
||||
|
# vuepress build output |
||||
|
.vuepress/dist |
||||
|
|
||||
|
# Serverless directories |
||||
|
.serverless/ |
||||
|
|
||||
|
# FuseBox cache |
||||
|
.fusebox/ |
||||
|
|
||||
|
# DynamoDB Local files |
||||
|
.dynamodb/ |
||||
|
|
||||
|
# TernJS port file |
||||
|
.tern-port |
@ -0,0 +1,25 @@ |
|||||
|
import { CapacitorConfig } from '@capacitor/cli'; |
||||
|
|
||||
|
const config: CapacitorConfig = { |
||||
|
appId: 'com.timesafari.dailynotification.androidtest', |
||||
|
appName: 'Daily Notification Android Test', |
||||
|
webDir: 'dist', |
||||
|
server: { |
||||
|
androidScheme: 'https' |
||||
|
}, |
||||
|
plugins: { |
||||
|
DailyNotification: { |
||||
|
storage: 'shared', |
||||
|
ttlSeconds: 1800, |
||||
|
prefetchLeadMinutes: 15, |
||||
|
enableETagSupport: true, |
||||
|
enableErrorHandling: true, |
||||
|
enablePerformanceOptimization: true |
||||
|
} |
||||
|
}, |
||||
|
android: { |
||||
|
allowMixedContent: true |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export default config; |
@ -0,0 +1,29 @@ |
|||||
|
{ |
||||
|
"name": "daily-notification-android-test", |
||||
|
"version": "1.0.0", |
||||
|
"description": "Minimal Android test app for Daily Notification Plugin", |
||||
|
"main": "index.js", |
||||
|
"scripts": { |
||||
|
"build": "webpack --mode=production", |
||||
|
"dev": "webpack serve --mode=development", |
||||
|
"android": "npx cap run android", |
||||
|
"sync": "npx cap sync android", |
||||
|
"open": "npx cap open android" |
||||
|
}, |
||||
|
"keywords": ["capacitor", "android", "notifications", "test"], |
||||
|
"author": "Matthew Raymer", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"@capacitor/core": "^5.0.0", |
||||
|
"@capacitor/android": "^5.0.0", |
||||
|
"@capacitor/cli": "^5.0.0" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"webpack": "^5.88.0", |
||||
|
"webpack-cli": "^5.1.0", |
||||
|
"webpack-dev-server": "^4.15.0", |
||||
|
"html-webpack-plugin": "^5.5.0", |
||||
|
"typescript": "^5.0.0", |
||||
|
"ts-loader": "^9.4.0" |
||||
|
} |
||||
|
} |
@ -0,0 +1,108 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>Daily Notification - Android Test</title> |
||||
|
<style> |
||||
|
body { |
||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; |
||||
|
margin: 0; |
||||
|
padding: 20px; |
||||
|
background-color: #f5f5f5; |
||||
|
} |
||||
|
.container { |
||||
|
max-width: 600px; |
||||
|
margin: 0 auto; |
||||
|
background: white; |
||||
|
border-radius: 12px; |
||||
|
padding: 20px; |
||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1); |
||||
|
} |
||||
|
h1 { |
||||
|
color: #333; |
||||
|
text-align: center; |
||||
|
margin-bottom: 30px; |
||||
|
} |
||||
|
.status { |
||||
|
background: #e3f2fd; |
||||
|
padding: 15px; |
||||
|
border-radius: 8px; |
||||
|
margin-bottom: 20px; |
||||
|
text-align: center; |
||||
|
font-weight: bold; |
||||
|
color: #1976d2; |
||||
|
} |
||||
|
.button-grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
gap: 10px; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
button { |
||||
|
background: #1976d2; |
||||
|
color: white; |
||||
|
border: none; |
||||
|
padding: 12px 16px; |
||||
|
border-radius: 6px; |
||||
|
cursor: pointer; |
||||
|
font-size: 14px; |
||||
|
transition: background-color 0.2s; |
||||
|
} |
||||
|
button:hover { |
||||
|
background: #1565c0; |
||||
|
} |
||||
|
button:disabled { |
||||
|
background: #ccc; |
||||
|
cursor: not-allowed; |
||||
|
} |
||||
|
.log-container { |
||||
|
background: #f8f9fa; |
||||
|
border: 1px solid #dee2e6; |
||||
|
border-radius: 6px; |
||||
|
padding: 15px; |
||||
|
height: 300px; |
||||
|
overflow-y: auto; |
||||
|
font-family: 'Courier New', monospace; |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
.timestamp { |
||||
|
color: #666; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
pre { |
||||
|
background: #e9ecef; |
||||
|
padding: 8px; |
||||
|
border-radius: 4px; |
||||
|
margin: 5px 0; |
||||
|
overflow-x: auto; |
||||
|
} |
||||
|
.clear-button { |
||||
|
background: #dc3545; |
||||
|
margin-top: 10px; |
||||
|
width: 100%; |
||||
|
} |
||||
|
.clear-button:hover { |
||||
|
background: #c82333; |
||||
|
} |
||||
|
</style> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div class="container"> |
||||
|
<h1>📱 Daily Notification Plugin - Android Test</h1> |
||||
|
|
||||
|
<div class="status" id="status">Ready</div> |
||||
|
|
||||
|
<div class="button-grid"> |
||||
|
<button id="configure">Configure Plugin</button> |
||||
|
<button id="schedule">Schedule Notification</button> |
||||
|
<button id="alarm-status">Check Alarm Status</button> |
||||
|
<button id="request-permission">Request Permission</button> |
||||
|
<button id="performance">Performance Metrics</button> |
||||
|
</div> |
||||
|
|
||||
|
<div class="log-container" id="log"></div> |
||||
|
<button class="clear-button" id="clear-log">Clear Log</button> |
||||
|
</div> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,154 @@ |
|||||
|
import { Capacitor } from '@capacitor/core'; |
||||
|
|
||||
|
// Mock plugin for development
|
||||
|
const DailyNotification = { |
||||
|
async configure(options: any) { |
||||
|
console.log('Configure called:', options); |
||||
|
return Promise.resolve(); |
||||
|
}, |
||||
|
async scheduleDailyNotification(options: any) { |
||||
|
console.log('Schedule called:', options); |
||||
|
return Promise.resolve(); |
||||
|
}, |
||||
|
async getExactAlarmStatus() { |
||||
|
return Promise.resolve({ |
||||
|
supported: true, |
||||
|
enabled: false, |
||||
|
canSchedule: false, |
||||
|
fallbackWindow: '±10 minutes' |
||||
|
}); |
||||
|
}, |
||||
|
async requestExactAlarmPermission() { |
||||
|
console.log('Request exact alarm permission'); |
||||
|
return Promise.resolve(); |
||||
|
}, |
||||
|
async getPerformanceMetrics() { |
||||
|
return Promise.resolve({ |
||||
|
overallScore: 85, |
||||
|
databasePerformance: 90, |
||||
|
memoryEfficiency: 80, |
||||
|
batteryEfficiency: 85, |
||||
|
objectPoolEfficiency: 90, |
||||
|
totalDatabaseQueries: 150, |
||||
|
averageMemoryUsage: 25.5, |
||||
|
objectPoolHits: 45, |
||||
|
backgroundCpuUsage: 2.3, |
||||
|
totalNetworkRequests: 12, |
||||
|
recommendations: ['Enable ETag support', 'Optimize memory usage'] |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// Test interface
|
||||
|
class TestApp { |
||||
|
private statusElement: HTMLElement; |
||||
|
private logElement: HTMLElement; |
||||
|
|
||||
|
constructor() { |
||||
|
this.statusElement = document.getElementById('status')!; |
||||
|
this.logElement = document.getElementById('log')!; |
||||
|
this.setupEventListeners(); |
||||
|
this.log('Test app initialized'); |
||||
|
} |
||||
|
|
||||
|
private setupEventListeners() { |
||||
|
document.getElementById('configure')?.addEventListener('click', () => this.testConfigure()); |
||||
|
document.getElementById('schedule')?.addEventListener('click', () => this.testSchedule()); |
||||
|
document.getElementById('alarm-status')?.addEventListener('click', () => this.testAlarmStatus()); |
||||
|
document.getElementById('request-permission')?.addEventListener('click', () => this.testRequestPermission()); |
||||
|
document.getElementById('performance')?.addEventListener('click', () => this.testPerformance()); |
||||
|
document.getElementById('clear-log')?.addEventListener('click', () => this.clearLog()); |
||||
|
} |
||||
|
|
||||
|
private async testConfigure() { |
||||
|
try { |
||||
|
this.log('Testing configuration...'); |
||||
|
await DailyNotification.configure({ |
||||
|
storage: 'shared', |
||||
|
ttlSeconds: 1800, |
||||
|
prefetchLeadMinutes: 15, |
||||
|
enableETagSupport: true, |
||||
|
enableErrorHandling: true, |
||||
|
enablePerformanceOptimization: true |
||||
|
}); |
||||
|
this.log('✅ Configuration successful'); |
||||
|
this.updateStatus('Configured'); |
||||
|
} catch (error) { |
||||
|
this.log(`❌ Configuration failed: ${error}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async testSchedule() { |
||||
|
try { |
||||
|
this.log('Testing notification scheduling...'); |
||||
|
await DailyNotification.scheduleDailyNotification({ |
||||
|
url: 'https://api.example.com/daily-content', |
||||
|
time: '09:00', |
||||
|
title: 'Daily Test Notification', |
||||
|
body: 'This is a test notification from the Android test app' |
||||
|
}); |
||||
|
this.log('✅ Notification scheduled successfully'); |
||||
|
this.updateStatus('Scheduled'); |
||||
|
} catch (error) { |
||||
|
this.log(`❌ Scheduling failed: ${error}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async testAlarmStatus() { |
||||
|
try { |
||||
|
this.log('Testing exact alarm status...'); |
||||
|
const status = await DailyNotification.getExactAlarmStatus(); |
||||
|
this.log(`📱 Alarm Status:`, status); |
||||
|
this.updateStatus(`Alarm: ${status.canSchedule ? 'Enabled' : 'Disabled'}`); |
||||
|
} catch (error) { |
||||
|
this.log(`❌ Alarm status check failed: ${error}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async testRequestPermission() { |
||||
|
try { |
||||
|
this.log('Testing permission request...'); |
||||
|
await DailyNotification.requestExactAlarmPermission(); |
||||
|
this.log('✅ Permission request sent'); |
||||
|
this.updateStatus('Permission Requested'); |
||||
|
} catch (error) { |
||||
|
this.log(`❌ Permission request failed: ${error}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async testPerformance() { |
||||
|
try { |
||||
|
this.log('Testing performance metrics...'); |
||||
|
const metrics = await DailyNotification.getPerformanceMetrics(); |
||||
|
this.log(`📊 Performance Metrics:`, metrics); |
||||
|
this.updateStatus(`Performance: ${metrics.overallScore}/100`); |
||||
|
} catch (error) { |
||||
|
this.log(`❌ Performance check failed: ${error}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private log(message: string, data?: any) { |
||||
|
const timestamp = new Date().toLocaleTimeString(); |
||||
|
const logEntry = document.createElement('div'); |
||||
|
logEntry.innerHTML = `<span class="timestamp">[${timestamp}]</span> ${message}`; |
||||
|
if (data) { |
||||
|
logEntry.innerHTML += `<pre>${JSON.stringify(data, null, 2)}</pre>`; |
||||
|
} |
||||
|
this.logElement.appendChild(logEntry); |
||||
|
this.logElement.scrollTop = this.logElement.scrollHeight; |
||||
|
} |
||||
|
|
||||
|
private clearLog() { |
||||
|
this.logElement.innerHTML = ''; |
||||
|
this.log('Log cleared'); |
||||
|
} |
||||
|
|
||||
|
private updateStatus(status: string) { |
||||
|
this.statusElement.textContent = status; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Initialize app when DOM is ready
|
||||
|
document.addEventListener('DOMContentLoaded', () => { |
||||
|
new TestApp(); |
||||
|
}); |
@ -0,0 +1,15 @@ |
|||||
|
{ |
||||
|
"compilerOptions": { |
||||
|
"target": "ES2020", |
||||
|
"module": "ES2020", |
||||
|
"moduleResolution": "node", |
||||
|
"strict": true, |
||||
|
"esModuleInterop": true, |
||||
|
"skipLibCheck": true, |
||||
|
"forceConsistentCasingInFileNames": true, |
||||
|
"outDir": "./dist", |
||||
|
"rootDir": "./src" |
||||
|
}, |
||||
|
"include": ["src/**/*"], |
||||
|
"exclude": ["node_modules", "dist"] |
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
const path = require('path'); |
||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin'); |
||||
|
|
||||
|
module.exports = { |
||||
|
entry: './src/index.ts', |
||||
|
module: { |
||||
|
rules: [ |
||||
|
{ |
||||
|
test: /\.ts$/, |
||||
|
use: 'ts-loader', |
||||
|
exclude: /node_modules/, |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
resolve: { |
||||
|
extensions: ['.ts', '.js'], |
||||
|
}, |
||||
|
output: { |
||||
|
filename: 'bundle.js', |
||||
|
path: path.resolve(__dirname, 'dist'), |
||||
|
clean: true, |
||||
|
}, |
||||
|
plugins: [ |
||||
|
new HtmlWebpackPlugin({ |
||||
|
template: './src/index.html', |
||||
|
}), |
||||
|
], |
||||
|
devServer: { |
||||
|
static: './dist', |
||||
|
port: 3000, |
||||
|
hot: true, |
||||
|
}, |
||||
|
}; |
@ -0,0 +1,114 @@ |
|||||
|
# Dependencies |
||||
|
node_modules/ |
||||
|
npm-debug.log* |
||||
|
yarn-debug.log* |
||||
|
yarn-error.log* |
||||
|
|
||||
|
# Build outputs |
||||
|
dist/ |
||||
|
build/ |
||||
|
*.tsbuildinfo |
||||
|
|
||||
|
# Electron |
||||
|
out/ |
||||
|
app/ |
||||
|
packages/ |
||||
|
|
||||
|
# IDE and editor files |
||||
|
.vscode/ |
||||
|
.idea/ |
||||
|
*.swp |
||||
|
*.swo |
||||
|
*~ |
||||
|
|
||||
|
# OS generated files |
||||
|
.DS_Store |
||||
|
.DS_Store? |
||||
|
._* |
||||
|
.Spotlight-V100 |
||||
|
.Trashes |
||||
|
ehthumbs.db |
||||
|
Thumbs.db |
||||
|
|
||||
|
# Logs |
||||
|
logs |
||||
|
*.log |
||||
|
|
||||
|
# Runtime data |
||||
|
pids |
||||
|
*.pid |
||||
|
*.seed |
||||
|
*.pid.lock |
||||
|
|
||||
|
# Coverage directory used by tools like istanbul |
||||
|
coverage/ |
||||
|
*.lcov |
||||
|
|
||||
|
# nyc test coverage |
||||
|
.nyc_output |
||||
|
|
||||
|
# Dependency directories |
||||
|
jspm_packages/ |
||||
|
|
||||
|
# Optional npm cache directory |
||||
|
.npm |
||||
|
|
||||
|
# Optional eslint cache |
||||
|
.eslintcache |
||||
|
|
||||
|
# Microbundle cache |
||||
|
.rpt2_cache/ |
||||
|
.rts2_cache_cjs/ |
||||
|
.rts2_cache_es/ |
||||
|
.rts2_cache_umd/ |
||||
|
|
||||
|
# Optional REPL history |
||||
|
.node_repl_history |
||||
|
|
||||
|
# Output of 'npm pack' |
||||
|
*.tgz |
||||
|
|
||||
|
# Yarn Integrity file |
||||
|
.yarn-integrity |
||||
|
|
||||
|
# dotenv environment variables file |
||||
|
.env |
||||
|
.env.test |
||||
|
.env.local |
||||
|
.env.development.local |
||||
|
.env.test.local |
||||
|
.env.production.local |
||||
|
|
||||
|
# parcel-bundler cache (https://parceljs.org/) |
||||
|
.cache |
||||
|
.parcel-cache |
||||
|
|
||||
|
# next.js build output |
||||
|
.next |
||||
|
|
||||
|
# nuxt.js build output |
||||
|
.nuxt |
||||
|
|
||||
|
# vuepress build output |
||||
|
.vuepress/dist |
||||
|
|
||||
|
# Serverless directories |
||||
|
.serverless/ |
||||
|
|
||||
|
# FuseBox cache |
||||
|
.fusebox/ |
||||
|
|
||||
|
# DynamoDB Local files |
||||
|
.dynamodb/ |
||||
|
|
||||
|
# TernJS port file |
||||
|
.tern-port |
||||
|
|
||||
|
# Electron specific |
||||
|
*.app |
||||
|
*.dmg |
||||
|
*.exe |
||||
|
*.deb |
||||
|
*.rpm |
||||
|
*.AppImage |
||||
|
*.snap |
@ -0,0 +1,117 @@ |
|||||
|
const { app, BrowserWindow, ipcMain } = require('electron'); |
||||
|
const path = require('path'); |
||||
|
|
||||
|
// Mock plugin for Electron development
|
||||
|
const DailyNotification = { |
||||
|
async configure(options) { |
||||
|
console.log('Electron Configure called:', options); |
||||
|
return Promise.resolve(); |
||||
|
}, |
||||
|
async scheduleDailyNotification(options) { |
||||
|
console.log('Electron Schedule called:', options); |
||||
|
return Promise.resolve(); |
||||
|
}, |
||||
|
async getDebugInfo() { |
||||
|
return Promise.resolve({ |
||||
|
status: 'Electron Mock Mode', |
||||
|
configuration: { |
||||
|
storage: 'mock', |
||||
|
platform: 'electron', |
||||
|
version: '1.0.0' |
||||
|
}, |
||||
|
recentErrors: [], |
||||
|
performance: { |
||||
|
overallScore: 95, |
||||
|
memoryUsage: 15.2, |
||||
|
cpuUsage: 1.2 |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
async getPerformanceMetrics() { |
||||
|
return Promise.resolve({ |
||||
|
overallScore: 95, |
||||
|
databasePerformance: 100, |
||||
|
memoryEfficiency: 95, |
||||
|
batteryEfficiency: 100, |
||||
|
objectPoolEfficiency: 100, |
||||
|
totalDatabaseQueries: 0, |
||||
|
averageMemoryUsage: 15.2, |
||||
|
objectPoolHits: 0, |
||||
|
backgroundCpuUsage: 0.5, |
||||
|
totalNetworkRequests: 0, |
||||
|
recommendations: ['Electron mock mode - no optimizations needed'] |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// IPC handlers for Electron
|
||||
|
ipcMain.handle('configure-plugin', async (event, options) => { |
||||
|
try { |
||||
|
await DailyNotification.configure(options); |
||||
|
return { success: true, message: 'Configuration successful' }; |
||||
|
} catch (error) { |
||||
|
return { success: false, error: error.message }; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
ipcMain.handle('schedule-notification', async (event, options) => { |
||||
|
try { |
||||
|
await DailyNotification.scheduleDailyNotification(options); |
||||
|
return { success: true, message: 'Notification scheduled' }; |
||||
|
} catch (error) { |
||||
|
return { success: false, error: error.message }; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
ipcMain.handle('get-debug-info', async () => { |
||||
|
try { |
||||
|
const info = await DailyNotification.getDebugInfo(); |
||||
|
return { success: true, data: info }; |
||||
|
} catch (error) { |
||||
|
return { success: false, error: error.message }; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
ipcMain.handle('get-performance-metrics', async () => { |
||||
|
try { |
||||
|
const metrics = await DailyNotification.getPerformanceMetrics(); |
||||
|
return { success: true, data: metrics }; |
||||
|
} catch (error) { |
||||
|
return { success: false, error: error.message }; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
function createWindow() { |
||||
|
const mainWindow = new BrowserWindow({ |
||||
|
width: 800, |
||||
|
height: 600, |
||||
|
webPreferences: { |
||||
|
nodeIntegration: false, |
||||
|
contextIsolation: true, |
||||
|
preload: path.join(__dirname, 'preload.js') |
||||
|
}, |
||||
|
title: 'Daily Notification - Electron Test' |
||||
|
}); |
||||
|
|
||||
|
// Load the web app
|
||||
|
mainWindow.loadFile('dist/index.html'); |
||||
|
|
||||
|
// Open DevTools in development
|
||||
|
if (process.argv.includes('--dev')) { |
||||
|
mainWindow.webContents.openDevTools(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
app.whenReady().then(createWindow); |
||||
|
|
||||
|
app.on('window-all-closed', () => { |
||||
|
if (process.platform !== 'darwin') { |
||||
|
app.quit(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
app.on('activate', () => { |
||||
|
if (BrowserWindow.getAllWindows().length === 0) { |
||||
|
createWindow(); |
||||
|
} |
||||
|
}); |
@ -0,0 +1,28 @@ |
|||||
|
{ |
||||
|
"name": "daily-notification-electron-test", |
||||
|
"version": "1.0.0", |
||||
|
"description": "Minimal Electron test app for Daily Notification Plugin", |
||||
|
"main": "main.js", |
||||
|
"scripts": { |
||||
|
"start": "electron .", |
||||
|
"dev": "electron . --dev", |
||||
|
"build": "webpack --mode=production", |
||||
|
"build-web": "webpack --mode=production", |
||||
|
"electron": "npm run build-web && electron ." |
||||
|
}, |
||||
|
"keywords": ["capacitor", "electron", "notifications", "test"], |
||||
|
"author": "Matthew Raymer", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"@capacitor/core": "^5.0.0", |
||||
|
"@capacitor/cli": "^5.0.0", |
||||
|
"electron": "^25.0.0" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"webpack": "^5.88.0", |
||||
|
"webpack-cli": "^5.1.0", |
||||
|
"html-webpack-plugin": "^5.5.0", |
||||
|
"typescript": "^5.0.0", |
||||
|
"ts-loader": "^9.4.0" |
||||
|
} |
||||
|
} |
@ -0,0 +1,10 @@ |
|||||
|
const { contextBridge, ipcRenderer } = require('electron'); |
||||
|
|
||||
|
// Expose protected methods that allow the renderer process to use
|
||||
|
// the ipcRenderer without exposing the entire object
|
||||
|
contextBridge.exposeInMainWorld('electronAPI', { |
||||
|
configurePlugin: (options) => ipcRenderer.invoke('configure-plugin', options), |
||||
|
scheduleNotification: (options) => ipcRenderer.invoke('schedule-notification', options), |
||||
|
getDebugInfo: () => ipcRenderer.invoke('get-debug-info'), |
||||
|
getPerformanceMetrics: () => ipcRenderer.invoke('get-performance-metrics') |
||||
|
}); |
@ -0,0 +1,107 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>Daily Notification - Electron Test</title> |
||||
|
<style> |
||||
|
body { |
||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; |
||||
|
margin: 0; |
||||
|
padding: 20px; |
||||
|
background-color: #f5f5f5; |
||||
|
} |
||||
|
.container { |
||||
|
max-width: 600px; |
||||
|
margin: 0 auto; |
||||
|
background: white; |
||||
|
border-radius: 12px; |
||||
|
padding: 20px; |
||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1); |
||||
|
} |
||||
|
h1 { |
||||
|
color: #333; |
||||
|
text-align: center; |
||||
|
margin-bottom: 30px; |
||||
|
} |
||||
|
.status { |
||||
|
background: #fff3e0; |
||||
|
padding: 15px; |
||||
|
border-radius: 8px; |
||||
|
margin-bottom: 20px; |
||||
|
text-align: center; |
||||
|
font-weight: bold; |
||||
|
color: #f57c00; |
||||
|
} |
||||
|
.button-grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
gap: 10px; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
button { |
||||
|
background: #f57c00; |
||||
|
color: white; |
||||
|
border: none; |
||||
|
padding: 12px 16px; |
||||
|
border-radius: 6px; |
||||
|
cursor: pointer; |
||||
|
font-size: 14px; |
||||
|
transition: background-color 0.2s; |
||||
|
} |
||||
|
button:hover { |
||||
|
background: #ef6c00; |
||||
|
} |
||||
|
button:disabled { |
||||
|
background: #ccc; |
||||
|
cursor: not-allowed; |
||||
|
} |
||||
|
.log-container { |
||||
|
background: #f8f9fa; |
||||
|
border: 1px solid #dee2e6; |
||||
|
border-radius: 6px; |
||||
|
padding: 15px; |
||||
|
height: 300px; |
||||
|
overflow-y: auto; |
||||
|
font-family: 'Courier New', monospace; |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
.timestamp { |
||||
|
color: #666; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
pre { |
||||
|
background: #e9ecef; |
||||
|
padding: 8px; |
||||
|
border-radius: 4px; |
||||
|
margin: 5px 0; |
||||
|
overflow-x: auto; |
||||
|
} |
||||
|
.clear-button { |
||||
|
background: #dc3545; |
||||
|
margin-top: 10px; |
||||
|
width: 100%; |
||||
|
} |
||||
|
.clear-button:hover { |
||||
|
background: #c82333; |
||||
|
} |
||||
|
</style> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div class="container"> |
||||
|
<h1>⚡ Daily Notification Plugin - Electron Test</h1> |
||||
|
|
||||
|
<div class="status" id="status">Ready</div> |
||||
|
|
||||
|
<div class="button-grid"> |
||||
|
<button id="configure">Configure Plugin</button> |
||||
|
<button id="schedule">Schedule Notification</button> |
||||
|
<button id="debug-info">Debug Info</button> |
||||
|
<button id="performance">Performance Metrics</button> |
||||
|
</div> |
||||
|
|
||||
|
<div class="log-container" id="log"></div> |
||||
|
<button class="clear-button" id="clear-log">Clear Log</button> |
||||
|
</div> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,121 @@ |
|||||
|
// Electron test interface
|
||||
|
class TestApp { |
||||
|
private statusElement: HTMLElement; |
||||
|
private logElement: HTMLElement; |
||||
|
|
||||
|
constructor() { |
||||
|
this.statusElement = document.getElementById('status')!; |
||||
|
this.logElement = document.getElementById('log')!; |
||||
|
this.setupEventListeners(); |
||||
|
this.log('Electron Test app initialized'); |
||||
|
} |
||||
|
|
||||
|
private setupEventListeners() { |
||||
|
document.getElementById('configure')?.addEventListener('click', () => this.testConfigure()); |
||||
|
document.getElementById('schedule')?.addEventListener('click', () => this.testSchedule()); |
||||
|
document.getElementById('debug-info')?.addEventListener('click', () => this.testDebugInfo()); |
||||
|
document.getElementById('performance')?.addEventListener('click', () => this.testPerformance()); |
||||
|
document.getElementById('clear-log')?.addEventListener('click', () => this.clearLog()); |
||||
|
} |
||||
|
|
||||
|
private async testConfigure() { |
||||
|
try { |
||||
|
this.log('Testing Electron configuration...'); |
||||
|
const result = await (window as any).electronAPI.configurePlugin({ |
||||
|
storage: 'mock', |
||||
|
ttlSeconds: 1800, |
||||
|
prefetchLeadMinutes: 15, |
||||
|
enableETagSupport: true, |
||||
|
enableErrorHandling: true, |
||||
|
enablePerformanceOptimization: true |
||||
|
}); |
||||
|
|
||||
|
if (result.success) { |
||||
|
this.log('✅ Electron Configuration successful'); |
||||
|
this.updateStatus('Configured'); |
||||
|
} else { |
||||
|
this.log(`❌ Configuration failed: ${result.error}`); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
this.log(`❌ Configuration error: ${error}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async testSchedule() { |
||||
|
try { |
||||
|
this.log('Testing Electron notification scheduling...'); |
||||
|
const result = await (window as any).electronAPI.scheduleNotification({ |
||||
|
url: 'https://api.example.com/daily-content', |
||||
|
time: '09:00', |
||||
|
title: 'Daily Electron Test Notification', |
||||
|
body: 'This is a test notification from the Electron test app' |
||||
|
}); |
||||
|
|
||||
|
if (result.success) { |
||||
|
this.log('✅ Electron Notification scheduled successfully'); |
||||
|
this.updateStatus('Scheduled'); |
||||
|
} else { |
||||
|
this.log(`❌ Scheduling failed: ${result.error}`); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
this.log(`❌ Scheduling error: ${error}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async testDebugInfo() { |
||||
|
try { |
||||
|
this.log('Testing Electron debug info...'); |
||||
|
const result = await (window as any).electronAPI.getDebugInfo(); |
||||
|
|
||||
|
if (result.success) { |
||||
|
this.log('🔍 Electron Debug Info:', result.data); |
||||
|
this.updateStatus(`Debug: ${result.data.status}`); |
||||
|
} else { |
||||
|
this.log(`❌ Debug info failed: ${result.error}`); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
this.log(`❌ Debug info error: ${error}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async testPerformance() { |
||||
|
try { |
||||
|
this.log('Testing Electron performance metrics...'); |
||||
|
const result = await (window as any).electronAPI.getPerformanceMetrics(); |
||||
|
|
||||
|
if (result.success) { |
||||
|
this.log('📊 Electron Performance Metrics:', result.data); |
||||
|
this.updateStatus(`Performance: ${result.data.overallScore}/100`); |
||||
|
} else { |
||||
|
this.log(`❌ Performance check failed: ${result.error}`); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
this.log(`❌ Performance error: ${error}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private log(message: string, data?: any) { |
||||
|
const timestamp = new Date().toLocaleTimeString(); |
||||
|
const logEntry = document.createElement('div'); |
||||
|
logEntry.innerHTML = `<span class="timestamp">[${timestamp}]</span> ${message}`; |
||||
|
if (data) { |
||||
|
logEntry.innerHTML += `<pre>${JSON.stringify(data, null, 2)}</pre>`; |
||||
|
} |
||||
|
this.logElement.appendChild(logEntry); |
||||
|
this.logElement.scrollTop = this.logElement.scrollHeight; |
||||
|
} |
||||
|
|
||||
|
private clearLog() { |
||||
|
this.logElement.innerHTML = ''; |
||||
|
this.log('Log cleared'); |
||||
|
} |
||||
|
|
||||
|
private updateStatus(status: string) { |
||||
|
this.statusElement.textContent = status; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Initialize app when DOM is ready
|
||||
|
document.addEventListener('DOMContentLoaded', () => { |
||||
|
new TestApp(); |
||||
|
}); |
@ -0,0 +1,15 @@ |
|||||
|
{ |
||||
|
"compilerOptions": { |
||||
|
"target": "ES2020", |
||||
|
"module": "ES2020", |
||||
|
"moduleResolution": "node", |
||||
|
"strict": true, |
||||
|
"esModuleInterop": true, |
||||
|
"skipLibCheck": true, |
||||
|
"forceConsistentCasingInFileNames": true, |
||||
|
"outDir": "./dist", |
||||
|
"rootDir": "./src" |
||||
|
}, |
||||
|
"include": ["src/**/*"], |
||||
|
"exclude": ["node_modules", "dist"] |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
const path = require('path'); |
||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin'); |
||||
|
|
||||
|
module.exports = { |
||||
|
entry: './src/index.ts', |
||||
|
module: { |
||||
|
rules: [ |
||||
|
{ |
||||
|
test: /\.ts$/, |
||||
|
use: 'ts-loader', |
||||
|
exclude: /node_modules/, |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
resolve: { |
||||
|
extensions: ['.ts', '.js'], |
||||
|
}, |
||||
|
output: { |
||||
|
filename: 'bundle.js', |
||||
|
path: path.resolve(__dirname, 'dist'), |
||||
|
clean: true, |
||||
|
}, |
||||
|
plugins: [ |
||||
|
new HtmlWebpackPlugin({ |
||||
|
template: './src/index.html', |
||||
|
}), |
||||
|
], |
||||
|
}; |
@ -0,0 +1,105 @@ |
|||||
|
# Dependencies |
||||
|
node_modules/ |
||||
|
npm-debug.log* |
||||
|
yarn-debug.log* |
||||
|
yarn-error.log* |
||||
|
|
||||
|
# Build outputs |
||||
|
dist/ |
||||
|
build/ |
||||
|
*.tsbuildinfo |
||||
|
|
||||
|
# Capacitor |
||||
|
android/ |
||||
|
ios/ |
||||
|
.capacitor/ |
||||
|
|
||||
|
# IDE and editor files |
||||
|
.vscode/ |
||||
|
.idea/ |
||||
|
*.swp |
||||
|
*.swo |
||||
|
*~ |
||||
|
|
||||
|
# OS generated files |
||||
|
.DS_Store |
||||
|
.DS_Store? |
||||
|
._* |
||||
|
.Spotlight-V100 |
||||
|
.Trashes |
||||
|
ehthumbs.db |
||||
|
Thumbs.db |
||||
|
|
||||
|
# Logs |
||||
|
logs |
||||
|
*.log |
||||
|
|
||||
|
# Runtime data |
||||
|
pids |
||||
|
*.pid |
||||
|
*.seed |
||||
|
*.pid.lock |
||||
|
|
||||
|
# Coverage directory used by tools like istanbul |
||||
|
coverage/ |
||||
|
*.lcov |
||||
|
|
||||
|
# nyc test coverage |
||||
|
.nyc_output |
||||
|
|
||||
|
# Dependency directories |
||||
|
jspm_packages/ |
||||
|
|
||||
|
# Optional npm cache directory |
||||
|
.npm |
||||
|
|
||||
|
# Optional eslint cache |
||||
|
.eslintcache |
||||
|
|
||||
|
# Microbundle cache |
||||
|
.rpt2_cache/ |
||||
|
.rts2_cache_cjs/ |
||||
|
.rts2_cache_es/ |
||||
|
.rts2_cache_umd/ |
||||
|
|
||||
|
# Optional REPL history |
||||
|
.node_repl_history |
||||
|
|
||||
|
# Output of 'npm pack' |
||||
|
*.tgz |
||||
|
|
||||
|
# Yarn Integrity file |
||||
|
.yarn-integrity |
||||
|
|
||||
|
# dotenv environment variables file |
||||
|
.env |
||||
|
.env.test |
||||
|
.env.local |
||||
|
.env.development.local |
||||
|
.env.test.local |
||||
|
.env.production.local |
||||
|
|
||||
|
# parcel-bundler cache (https://parceljs.org/) |
||||
|
.cache |
||||
|
.parcel-cache |
||||
|
|
||||
|
# next.js build output |
||||
|
.next |
||||
|
|
||||
|
# nuxt.js build output |
||||
|
.nuxt |
||||
|
|
||||
|
# vuepress build output |
||||
|
.vuepress/dist |
||||
|
|
||||
|
# Serverless directories |
||||
|
.serverless/ |
||||
|
|
||||
|
# FuseBox cache |
||||
|
.fusebox/ |
||||
|
|
||||
|
# DynamoDB Local files |
||||
|
.dynamodb/ |
||||
|
|
||||
|
# TernJS port file |
||||
|
.tern-port |
@ -0,0 +1,25 @@ |
|||||
|
import { CapacitorConfig } from '@capacitor/cli'; |
||||
|
|
||||
|
const config: CapacitorConfig = { |
||||
|
appId: 'com.timesafari.dailynotification.iostest', |
||||
|
appName: 'Daily Notification iOS Test', |
||||
|
webDir: 'dist', |
||||
|
server: { |
||||
|
iosScheme: 'capacitor' |
||||
|
}, |
||||
|
plugins: { |
||||
|
DailyNotification: { |
||||
|
storage: 'shared', |
||||
|
ttlSeconds: 1800, |
||||
|
prefetchLeadMinutes: 15, |
||||
|
enableETagSupport: true, |
||||
|
enableErrorHandling: true, |
||||
|
enablePerformanceOptimization: true |
||||
|
} |
||||
|
}, |
||||
|
ios: { |
||||
|
scheme: 'Daily Notification iOS Test' |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export default config; |
@ -0,0 +1,29 @@ |
|||||
|
{ |
||||
|
"name": "daily-notification-ios-test", |
||||
|
"version": "1.0.0", |
||||
|
"description": "Minimal iOS test app for Daily Notification Plugin", |
||||
|
"main": "index.js", |
||||
|
"scripts": { |
||||
|
"build": "webpack --mode=production", |
||||
|
"dev": "webpack serve --mode=development", |
||||
|
"ios": "npx cap run ios", |
||||
|
"sync": "npx cap sync ios", |
||||
|
"open": "npx cap open ios" |
||||
|
}, |
||||
|
"keywords": ["capacitor", "ios", "notifications", "test"], |
||||
|
"author": "Matthew Raymer", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"@capacitor/core": "^5.0.0", |
||||
|
"@capacitor/ios": "^5.0.0", |
||||
|
"@capacitor/cli": "^5.0.0" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"webpack": "^5.88.0", |
||||
|
"webpack-cli": "^5.1.0", |
||||
|
"webpack-dev-server": "^4.15.0", |
||||
|
"html-webpack-plugin": "^5.5.0", |
||||
|
"typescript": "^5.0.0", |
||||
|
"ts-loader": "^9.4.0" |
||||
|
} |
||||
|
} |
@ -0,0 +1,108 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>Daily Notification - iOS Test</title> |
||||
|
<style> |
||||
|
body { |
||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; |
||||
|
margin: 0; |
||||
|
padding: 20px; |
||||
|
background-color: #f5f5f5; |
||||
|
} |
||||
|
.container { |
||||
|
max-width: 600px; |
||||
|
margin: 0 auto; |
||||
|
background: white; |
||||
|
border-radius: 12px; |
||||
|
padding: 20px; |
||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1); |
||||
|
} |
||||
|
h1 { |
||||
|
color: #333; |
||||
|
text-align: center; |
||||
|
margin-bottom: 30px; |
||||
|
} |
||||
|
.status { |
||||
|
background: #e8f5e8; |
||||
|
padding: 15px; |
||||
|
border-radius: 8px; |
||||
|
margin-bottom: 20px; |
||||
|
text-align: center; |
||||
|
font-weight: bold; |
||||
|
color: #2e7d32; |
||||
|
} |
||||
|
.button-grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
gap: 10px; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
button { |
||||
|
background: #2e7d32; |
||||
|
color: white; |
||||
|
border: none; |
||||
|
padding: 12px 16px; |
||||
|
border-radius: 6px; |
||||
|
cursor: pointer; |
||||
|
font-size: 14px; |
||||
|
transition: background-color 0.2s; |
||||
|
} |
||||
|
button:hover { |
||||
|
background: #1b5e20; |
||||
|
} |
||||
|
button:disabled { |
||||
|
background: #ccc; |
||||
|
cursor: not-allowed; |
||||
|
} |
||||
|
.log-container { |
||||
|
background: #f8f9fa; |
||||
|
border: 1px solid #dee2e6; |
||||
|
border-radius: 6px; |
||||
|
padding: 15px; |
||||
|
height: 300px; |
||||
|
overflow-y: auto; |
||||
|
font-family: 'Courier New', monospace; |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
.timestamp { |
||||
|
color: #666; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
pre { |
||||
|
background: #e9ecef; |
||||
|
padding: 8px; |
||||
|
border-radius: 4px; |
||||
|
margin: 5px 0; |
||||
|
overflow-x: auto; |
||||
|
} |
||||
|
.clear-button { |
||||
|
background: #dc3545; |
||||
|
margin-top: 10px; |
||||
|
width: 100%; |
||||
|
} |
||||
|
.clear-button:hover { |
||||
|
background: #c82333; |
||||
|
} |
||||
|
</style> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div class="container"> |
||||
|
<h1>🍎 Daily Notification Plugin - iOS Test</h1> |
||||
|
|
||||
|
<div class="status" id="status">Ready</div> |
||||
|
|
||||
|
<div class="button-grid"> |
||||
|
<button id="configure">Configure Plugin</button> |
||||
|
<button id="schedule">Schedule Notification</button> |
||||
|
<button id="rolling-window">Maintain Window</button> |
||||
|
<button id="window-stats">Window Stats</button> |
||||
|
<button id="performance">Performance Metrics</button> |
||||
|
</div> |
||||
|
|
||||
|
<div class="log-container" id="log"></div> |
||||
|
<button class="clear-button" id="clear-log">Clear Log</button> |
||||
|
</div> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,153 @@ |
|||||
|
import { Capacitor } from '@capacitor/core'; |
||||
|
|
||||
|
// Mock plugin for development
|
||||
|
const DailyNotification = { |
||||
|
async configure(options: any) { |
||||
|
console.log('Configure called:', options); |
||||
|
return Promise.resolve(); |
||||
|
}, |
||||
|
async scheduleDailyNotification(options: any) { |
||||
|
console.log('Schedule called:', options); |
||||
|
return Promise.resolve(); |
||||
|
}, |
||||
|
async maintainRollingWindow() { |
||||
|
console.log('Maintain rolling window called'); |
||||
|
return Promise.resolve(); |
||||
|
}, |
||||
|
async getRollingWindowStats() { |
||||
|
return Promise.resolve({ |
||||
|
stats: '64 pending notifications, 20 daily limit', |
||||
|
maintenanceNeeded: false, |
||||
|
timeUntilNextMaintenance: 900000 |
||||
|
}); |
||||
|
}, |
||||
|
async getPerformanceMetrics() { |
||||
|
return Promise.resolve({ |
||||
|
overallScore: 88, |
||||
|
databasePerformance: 92, |
||||
|
memoryEfficiency: 85, |
||||
|
batteryEfficiency: 90, |
||||
|
objectPoolEfficiency: 88, |
||||
|
totalDatabaseQueries: 120, |
||||
|
averageMemoryUsage: 22.3, |
||||
|
objectPoolHits: 38, |
||||
|
backgroundCpuUsage: 1.8, |
||||
|
totalNetworkRequests: 8, |
||||
|
recommendations: ['Enable background tasks', 'Optimize memory usage'] |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// Test interface
|
||||
|
class TestApp { |
||||
|
private statusElement: HTMLElement; |
||||
|
private logElement: HTMLElement; |
||||
|
|
||||
|
constructor() { |
||||
|
this.statusElement = document.getElementById('status')!; |
||||
|
this.logElement = document.getElementById('log')!; |
||||
|
this.setupEventListeners(); |
||||
|
this.log('iOS Test app initialized'); |
||||
|
} |
||||
|
|
||||
|
private setupEventListeners() { |
||||
|
document.getElementById('configure')?.addEventListener('click', () => this.testConfigure()); |
||||
|
document.getElementById('schedule')?.addEventListener('click', () => this.testSchedule()); |
||||
|
document.getElementById('rolling-window')?.addEventListener('click', () => this.testRollingWindow()); |
||||
|
document.getElementById('window-stats')?.addEventListener('click', () => this.testWindowStats()); |
||||
|
document.getElementById('performance')?.addEventListener('click', () => this.testPerformance()); |
||||
|
document.getElementById('clear-log')?.addEventListener('click', () => this.clearLog()); |
||||
|
} |
||||
|
|
||||
|
private async testConfigure() { |
||||
|
try { |
||||
|
this.log('Testing iOS configuration...'); |
||||
|
await DailyNotification.configure({ |
||||
|
storage: 'shared', |
||||
|
ttlSeconds: 1800, |
||||
|
prefetchLeadMinutes: 15, |
||||
|
enableETagSupport: true, |
||||
|
enableErrorHandling: true, |
||||
|
enablePerformanceOptimization: true |
||||
|
}); |
||||
|
this.log('✅ iOS Configuration successful'); |
||||
|
this.updateStatus('Configured'); |
||||
|
} catch (error) { |
||||
|
this.log(`❌ Configuration failed: ${error}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async testSchedule() { |
||||
|
try { |
||||
|
this.log('Testing iOS notification scheduling...'); |
||||
|
await DailyNotification.scheduleDailyNotification({ |
||||
|
url: 'https://api.example.com/daily-content', |
||||
|
time: '09:00', |
||||
|
title: 'Daily iOS Test Notification', |
||||
|
body: 'This is a test notification from the iOS test app' |
||||
|
}); |
||||
|
this.log('✅ iOS Notification scheduled successfully'); |
||||
|
this.updateStatus('Scheduled'); |
||||
|
} catch (error) { |
||||
|
this.log(`❌ iOS Scheduling failed: ${error}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async testRollingWindow() { |
||||
|
try { |
||||
|
this.log('Testing iOS rolling window maintenance...'); |
||||
|
await DailyNotification.maintainRollingWindow(); |
||||
|
this.log('✅ Rolling window maintenance completed'); |
||||
|
this.updateStatus('Rolling Window Maintained'); |
||||
|
} catch (error) { |
||||
|
this.log(`❌ Rolling window maintenance failed: ${error}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async testWindowStats() { |
||||
|
try { |
||||
|
this.log('Testing iOS rolling window stats...'); |
||||
|
const stats = await DailyNotification.getRollingWindowStats(); |
||||
|
this.log(`📊 Rolling Window Stats:`, stats); |
||||
|
this.updateStatus(`Window: ${stats.maintenanceNeeded ? 'Needs Maintenance' : 'OK'}`); |
||||
|
} catch (error) { |
||||
|
this.log(`❌ Window stats check failed: ${error}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async testPerformance() { |
||||
|
try { |
||||
|
this.log('Testing iOS performance metrics...'); |
||||
|
const metrics = await DailyNotification.getPerformanceMetrics(); |
||||
|
this.log(`📊 iOS Performance Metrics:`, metrics); |
||||
|
this.updateStatus(`Performance: ${metrics.overallScore}/100`); |
||||
|
} catch (error) { |
||||
|
this.log(`❌ Performance check failed: ${error}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private log(message: string, data?: any) { |
||||
|
const timestamp = new Date().toLocaleTimeString(); |
||||
|
const logEntry = document.createElement('div'); |
||||
|
logEntry.innerHTML = `<span class="timestamp">[${timestamp}]</span> ${message}`; |
||||
|
if (data) { |
||||
|
logEntry.innerHTML += `<pre>${JSON.stringify(data, null, 2)}</pre>`; |
||||
|
} |
||||
|
this.logElement.appendChild(logEntry); |
||||
|
this.logElement.scrollTop = this.logElement.scrollHeight; |
||||
|
} |
||||
|
|
||||
|
private clearLog() { |
||||
|
this.logElement.innerHTML = ''; |
||||
|
this.log('Log cleared'); |
||||
|
} |
||||
|
|
||||
|
private updateStatus(status: string) { |
||||
|
this.statusElement.textContent = status; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Initialize app when DOM is ready
|
||||
|
document.addEventListener('DOMContentLoaded', () => { |
||||
|
new TestApp(); |
||||
|
}); |
@ -0,0 +1,15 @@ |
|||||
|
{ |
||||
|
"compilerOptions": { |
||||
|
"target": "ES2020", |
||||
|
"module": "ES2020", |
||||
|
"moduleResolution": "node", |
||||
|
"strict": true, |
||||
|
"esModuleInterop": true, |
||||
|
"skipLibCheck": true, |
||||
|
"forceConsistentCasingInFileNames": true, |
||||
|
"outDir": "./dist", |
||||
|
"rootDir": "./src" |
||||
|
}, |
||||
|
"include": ["src/**/*"], |
||||
|
"exclude": ["node_modules", "dist"] |
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
const path = require('path'); |
||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin'); |
||||
|
|
||||
|
module.exports = { |
||||
|
entry: './src/index.ts', |
||||
|
module: { |
||||
|
rules: [ |
||||
|
{ |
||||
|
test: /\.ts$/, |
||||
|
use: 'ts-loader', |
||||
|
exclude: /node_modules/, |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
resolve: { |
||||
|
extensions: ['.ts', '.js'], |
||||
|
}, |
||||
|
output: { |
||||
|
filename: 'bundle.js', |
||||
|
path: path.resolve(__dirname, 'dist'), |
||||
|
clean: true, |
||||
|
}, |
||||
|
plugins: [ |
||||
|
new HtmlWebpackPlugin({ |
||||
|
template: './src/index.html', |
||||
|
}), |
||||
|
], |
||||
|
devServer: { |
||||
|
static: './dist', |
||||
|
port: 3001, |
||||
|
hot: true, |
||||
|
}, |
||||
|
}; |
@ -0,0 +1,39 @@ |
|||||
|
#!/bin/bash |
||||
|
|
||||
|
# Android Test App Setup Script |
||||
|
echo "🚀 Setting up Android Test App..." |
||||
|
|
||||
|
cd android-test |
||||
|
|
||||
|
# Install dependencies |
||||
|
echo "📦 Installing dependencies..." |
||||
|
npm install |
||||
|
|
||||
|
# Install Capacitor CLI globally if not present |
||||
|
if ! command -v cap &> /dev/null; then |
||||
|
echo "🔧 Installing Capacitor CLI globally..." |
||||
|
npm install -g @capacitor/cli |
||||
|
fi |
||||
|
|
||||
|
# Initialize Capacitor |
||||
|
echo "⚡ Initializing Capacitor..." |
||||
|
npx cap init "Daily Notification Android Test" "com.timesafari.dailynotification.androidtest" |
||||
|
|
||||
|
# Add Android platform |
||||
|
echo "📱 Adding Android platform..." |
||||
|
npx cap add android |
||||
|
|
||||
|
# Build web assets |
||||
|
echo "🔨 Building web assets..." |
||||
|
npm run build |
||||
|
|
||||
|
# Sync to native |
||||
|
echo "🔄 Syncing to native..." |
||||
|
npx cap sync android |
||||
|
|
||||
|
echo "✅ Android test app setup complete!" |
||||
|
echo "" |
||||
|
echo "Next steps:" |
||||
|
echo "1. Open Android Studio: npx cap open android" |
||||
|
echo "2. Run on device/emulator: npx cap run android" |
||||
|
echo "3. Or build web version: npm run dev" |
@ -0,0 +1,21 @@ |
|||||
|
#!/bin/bash |
||||
|
|
||||
|
# Electron Test App Setup Script |
||||
|
echo "🚀 Setting up Electron Test App..." |
||||
|
|
||||
|
cd electron-test |
||||
|
|
||||
|
# Install dependencies |
||||
|
echo "📦 Installing dependencies..." |
||||
|
npm install |
||||
|
|
||||
|
# Build web assets |
||||
|
echo "🔨 Building web assets..." |
||||
|
npm run build-web |
||||
|
|
||||
|
echo "✅ Electron test app setup complete!" |
||||
|
echo "" |
||||
|
echo "Next steps:" |
||||
|
echo "1. Run Electron app: npm start" |
||||
|
echo "2. Run in dev mode: npm run dev" |
||||
|
echo "3. Build and run: npm run electron" |
@ -0,0 +1,39 @@ |
|||||
|
#!/bin/bash |
||||
|
|
||||
|
# iOS Test App Setup Script |
||||
|
echo "🚀 Setting up iOS Test App..." |
||||
|
|
||||
|
cd ios-test |
||||
|
|
||||
|
# Install dependencies |
||||
|
echo "📦 Installing dependencies..." |
||||
|
npm install |
||||
|
|
||||
|
# Install Capacitor CLI globally if not present |
||||
|
if ! command -v cap &> /dev/null; then |
||||
|
echo "🔧 Installing Capacitor CLI globally..." |
||||
|
npm install -g @capacitor/cli |
||||
|
fi |
||||
|
|
||||
|
# Initialize Capacitor |
||||
|
echo "⚡ Initializing Capacitor..." |
||||
|
npx cap init "Daily Notification iOS Test" "com.timesafari.dailynotification.iostest" |
||||
|
|
||||
|
# Add iOS platform |
||||
|
echo "🍎 Adding iOS platform..." |
||||
|
npx cap add ios |
||||
|
|
||||
|
# Build web assets |
||||
|
echo "🔨 Building web assets..." |
||||
|
npm run build |
||||
|
|
||||
|
# Sync to native |
||||
|
echo "🔄 Syncing to native..." |
||||
|
npx cap sync ios |
||||
|
|
||||
|
echo "✅ iOS test app setup complete!" |
||||
|
echo "" |
||||
|
echo "Next steps:" |
||||
|
echo "1. Open Xcode: npx cap open ios" |
||||
|
echo "2. Run on device/simulator: npx cap run ios" |
||||
|
echo "3. Or build web version: npm run dev" |
Loading…
Reference in new issue