docs: add comprehensive static daily reminders documentation
- Add static daily reminders to README.md core features and API reference - Create detailed usage guide in USAGE.md with examples and best practices - Add version 2.1.0 changelog entry documenting new reminder functionality - Create examples/static-daily-reminders.ts with complete usage examples - Update test-apps README to include reminder testing capabilities The static daily reminder feature provides simple daily notifications without network content dependency, supporting cross-platform scheduling with rich customization options.
This commit is contained in:
34
CHANGELOG.md
34
CHANGELOG.md
@@ -5,6 +5,40 @@ All notable changes to the Daily Notification Plugin will be documented in this
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [2.1.0] - 2025-01-02
|
||||
|
||||
### Added
|
||||
|
||||
- **Static Daily Reminders**: New functionality for simple daily notifications without network content
|
||||
- **Cross-Platform Reminder API**: Consistent reminder management across Android, iOS, and Web
|
||||
- **Reminder Management**: Full CRUD operations for reminder scheduling and management
|
||||
- **Offline Reminder Support**: Reminders work completely offline without content caching
|
||||
- **Rich Reminder Customization**: Support for custom titles, bodies, sounds, vibration, and priorities
|
||||
- **Persistent Reminder Storage**: Reminders survive app restarts and device reboots
|
||||
|
||||
### New Methods
|
||||
|
||||
- `scheduleDailyReminder(options)`: Schedule a simple daily reminder
|
||||
- `cancelDailyReminder(reminderId)`: Cancel a specific reminder
|
||||
- `getScheduledReminders()`: Get all scheduled reminders
|
||||
- `updateDailyReminder(reminderId, options)`: Update an existing reminder
|
||||
|
||||
### Features
|
||||
|
||||
- **No Network Dependency**: Static reminders work completely offline
|
||||
- **Simple Time Format**: Easy HH:mm time format (e.g., "09:00")
|
||||
- **Priority Levels**: Support for low, normal, and high priority notifications
|
||||
- **Repeat Options**: Configurable daily repetition
|
||||
- **Platform Integration**: Native notification channels and categories
|
||||
- **Test App Integration**: Complete test app support for reminder functionality
|
||||
|
||||
### Documentation
|
||||
|
||||
- Updated README.md with static reminder examples and API reference
|
||||
- Added comprehensive usage examples in USAGE.md
|
||||
- Created detailed example file: `examples/static-daily-reminders.ts`
|
||||
- Enhanced test apps with reminder management UI
|
||||
|
||||
## [1.0.0] - 2024-03-20
|
||||
|
||||
### Added
|
||||
|
||||
98
README.md
98
README.md
@@ -39,6 +39,7 @@ The Daily Notification Plugin is a comprehensive Capacitor plugin that provides
|
||||
- **TTL-at-Fire Logic**: Content validity checking at notification time
|
||||
- **Callback System**: HTTP, local, and queue callback support
|
||||
- **Circuit Breaker Pattern**: Automatic failure detection and recovery
|
||||
- **Static Daily Reminders**: Simple daily notifications without network content
|
||||
- **Cross-Platform**: Android, iOS, and Web implementations
|
||||
|
||||
### 📱 **Platform Support**
|
||||
@@ -54,6 +55,15 @@ The Daily Notification Plugin is a comprehensive Capacitor plugin that provides
|
||||
- **Error Handling**: Exponential backoff and retry logic
|
||||
- **Security**: Encrypted storage and secure callback handling
|
||||
|
||||
### ⏰ **Static Daily Reminders**
|
||||
|
||||
- **No Network Required**: Completely offline reminder notifications
|
||||
- **Simple Scheduling**: Easy daily reminder setup with HH:mm time format
|
||||
- **Rich Customization**: Customizable title, body, sound, vibration, and priority
|
||||
- **Persistent Storage**: Survives app restarts and device reboots
|
||||
- **Cross-Platform**: Consistent API across Android, iOS, and Web
|
||||
- **Management**: Full CRUD operations for reminder management
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
@@ -130,6 +140,45 @@ function saveToDatabase(event: CallbackEvent) {
|
||||
}
|
||||
```
|
||||
|
||||
### Static Daily Reminders
|
||||
|
||||
For simple daily reminders that don't require network content:
|
||||
|
||||
```typescript
|
||||
import { DailyNotification } from '@timesafari/daily-notification-plugin';
|
||||
|
||||
// Schedule a simple daily reminder
|
||||
await DailyNotification.scheduleDailyReminder({
|
||||
id: 'morning_checkin',
|
||||
title: 'Good Morning!',
|
||||
body: 'Time to check your TimeSafari community updates',
|
||||
time: '09:00', // HH:mm format
|
||||
sound: true,
|
||||
vibration: true,
|
||||
priority: 'normal',
|
||||
repeatDaily: true
|
||||
});
|
||||
|
||||
// Get all scheduled reminders
|
||||
const result = await DailyNotification.getScheduledReminders();
|
||||
console.log('Scheduled reminders:', result.reminders);
|
||||
|
||||
// Update an existing reminder
|
||||
await DailyNotification.updateDailyReminder('morning_checkin', {
|
||||
title: 'Updated Morning Reminder',
|
||||
time: '08:30'
|
||||
});
|
||||
|
||||
// Cancel a reminder
|
||||
await DailyNotification.cancelDailyReminder('morning_checkin');
|
||||
```
|
||||
|
||||
**Key Benefits of Static Reminders:**
|
||||
- ✅ **No network dependency** - works completely offline
|
||||
- ✅ **No content cache required** - direct notification display
|
||||
- ✅ **Simple API** - easy to use for basic reminder functionality
|
||||
- ✅ **Persistent** - survives app restarts and device reboots
|
||||
|
||||
## API Reference
|
||||
|
||||
### Core Methods
|
||||
@@ -257,6 +306,55 @@ const status = await DailyNotification.getDualScheduleStatus();
|
||||
- **HTTPS**: Required for Service Worker and push notifications
|
||||
- **Browser Support**: Chrome 40+, Firefox 44+, Safari 11.1+
|
||||
|
||||
### Static Daily Reminder Methods
|
||||
|
||||
#### `scheduleDailyReminder(options)`
|
||||
|
||||
Schedule a simple daily reminder without network content.
|
||||
|
||||
```typescript
|
||||
await DailyNotification.scheduleDailyReminder({
|
||||
id: string; // Unique reminder identifier
|
||||
title: string; // Notification title
|
||||
body: string; // Notification body
|
||||
time: string; // Time in HH:mm format (e.g., "09:00")
|
||||
sound?: boolean; // Enable sound (default: true)
|
||||
vibration?: boolean; // Enable vibration (default: true)
|
||||
priority?: 'low' | 'normal' | 'high'; // Priority level (default: 'normal')
|
||||
repeatDaily?: boolean; // Repeat daily (default: true)
|
||||
timezone?: string; // Optional timezone
|
||||
});
|
||||
```
|
||||
|
||||
#### `cancelDailyReminder(reminderId)`
|
||||
|
||||
Cancel a scheduled daily reminder.
|
||||
|
||||
```typescript
|
||||
await DailyNotification.cancelDailyReminder('morning_checkin');
|
||||
```
|
||||
|
||||
#### `getScheduledReminders()`
|
||||
|
||||
Get all scheduled reminders.
|
||||
|
||||
```typescript
|
||||
const result = await DailyNotification.getScheduledReminders();
|
||||
console.log('Reminders:', result.reminders);
|
||||
```
|
||||
|
||||
#### `updateDailyReminder(reminderId, options)`
|
||||
|
||||
Update an existing daily reminder.
|
||||
|
||||
```typescript
|
||||
await DailyNotification.updateDailyReminder('morning_checkin', {
|
||||
title: 'Updated Title',
|
||||
time: '08:30',
|
||||
priority: 'high'
|
||||
});
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Android Configuration
|
||||
|
||||
65
USAGE.md
65
USAGE.md
@@ -39,6 +39,71 @@ await DailyNotification.scheduleDailyNotification({
|
||||
- **`enableErrorHandling`**: Advanced retry logic with exponential backoff
|
||||
- **`enablePerformanceOptimization`**: Database indexes, memory management, object pooling
|
||||
|
||||
## Static Daily Reminders
|
||||
|
||||
For simple daily reminders that don't require network content or content caching:
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```typescript
|
||||
// Schedule a simple daily reminder
|
||||
await DailyNotification.scheduleDailyReminder({
|
||||
id: 'morning_checkin',
|
||||
title: 'Good Morning!',
|
||||
body: 'Time to check your TimeSafari community updates',
|
||||
time: '09:00' // HH:mm format
|
||||
});
|
||||
```
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
```typescript
|
||||
// Schedule a customized reminder
|
||||
await DailyNotification.scheduleDailyReminder({
|
||||
id: 'evening_reflection',
|
||||
title: 'Evening Reflection',
|
||||
body: 'Take a moment to reflect on your day',
|
||||
time: '20:00',
|
||||
sound: true,
|
||||
vibration: true,
|
||||
priority: 'high',
|
||||
repeatDaily: true,
|
||||
timezone: 'America/New_York'
|
||||
});
|
||||
```
|
||||
|
||||
### Reminder Management
|
||||
|
||||
```typescript
|
||||
// Get all scheduled reminders
|
||||
const result = await DailyNotification.getScheduledReminders();
|
||||
console.log('Scheduled reminders:', result.reminders);
|
||||
|
||||
// Update an existing reminder
|
||||
await DailyNotification.updateDailyReminder('morning_checkin', {
|
||||
title: 'Updated Morning Check-in',
|
||||
time: '08:30',
|
||||
priority: 'high'
|
||||
});
|
||||
|
||||
// Cancel a specific reminder
|
||||
await DailyNotification.cancelDailyReminder('evening_reflection');
|
||||
```
|
||||
|
||||
### Key Benefits
|
||||
|
||||
- ✅ **No Network Required**: Works completely offline
|
||||
- ✅ **No Content Cache**: Direct notification display without caching
|
||||
- ✅ **Simple API**: Easy-to-use methods for basic reminder functionality
|
||||
- ✅ **Persistent**: Survives app restarts and device reboots
|
||||
- ✅ **Cross-Platform**: Consistent behavior across Android, iOS, and Web
|
||||
|
||||
### Time Format
|
||||
|
||||
Use 24-hour format with leading zeros:
|
||||
- ✅ Valid: `"09:00"`, `"12:30"`, `"23:59"`
|
||||
- ❌ Invalid: `"9:00"`, `"24:00"`, `"12:60"`
|
||||
|
||||
## Platform-Specific Features
|
||||
|
||||
### Android
|
||||
|
||||
@@ -94,6 +94,26 @@ class NotifyReceiver : BroadcastReceiver() {
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
// Check if this is a static reminder (no content dependency)
|
||||
val isStaticReminder = intent?.getBooleanExtra("is_static_reminder", false) ?: false
|
||||
|
||||
if (isStaticReminder) {
|
||||
// Handle static reminder without content cache
|
||||
val title = intent?.getStringExtra("title") ?: "Daily Reminder"
|
||||
val body = intent?.getStringExtra("body") ?: "Don't forget your daily check-in!"
|
||||
val sound = intent?.getBooleanExtra("sound", true) ?: true
|
||||
val vibration = intent?.getBooleanExtra("vibration", true) ?: true
|
||||
val priority = intent?.getStringExtra("priority") ?: "normal"
|
||||
val reminderId = intent?.getStringExtra("reminder_id") ?: "unknown"
|
||||
|
||||
showStaticReminderNotification(context, title, body, sound, vibration, priority, reminderId)
|
||||
|
||||
// Record reminder trigger in database
|
||||
recordReminderTrigger(context, reminderId)
|
||||
return@launch
|
||||
}
|
||||
|
||||
// Existing cached content logic for regular notifications
|
||||
val db = DailyNotificationDatabase.getDatabase(context)
|
||||
val latestCache = db.contentCacheDao().getLatest()
|
||||
|
||||
@@ -250,4 +270,67 @@ class NotifyReceiver : BroadcastReceiver() {
|
||||
// Local callback implementation would go here
|
||||
Log.i(TAG, "Local callback fired: ${callback.id} for event: $eventType")
|
||||
}
|
||||
|
||||
// Static Reminder Helper Methods
|
||||
private fun showStaticReminderNotification(
|
||||
context: Context,
|
||||
title: String,
|
||||
body: String,
|
||||
sound: Boolean,
|
||||
vibration: Boolean,
|
||||
priority: String,
|
||||
reminderId: String
|
||||
) {
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
// Create notification channel for reminders
|
||||
createReminderNotificationChannel(context, notificationManager)
|
||||
|
||||
val notification = NotificationCompat.Builder(context, "daily_reminders")
|
||||
.setSmallIcon(android.R.drawable.ic_dialog_info)
|
||||
.setContentTitle(title)
|
||||
.setContentText(body)
|
||||
.setPriority(
|
||||
when (priority) {
|
||||
"high" -> NotificationCompat.PRIORITY_HIGH
|
||||
"low" -> NotificationCompat.PRIORITY_LOW
|
||||
else -> NotificationCompat.PRIORITY_DEFAULT
|
||||
}
|
||||
)
|
||||
.setSound(if (sound) null else null) // Use default sound if enabled
|
||||
.setAutoCancel(true)
|
||||
.setVibrate(if (vibration) longArrayOf(0, 250, 250, 250) else null)
|
||||
.setCategory(NotificationCompat.CATEGORY_REMINDER)
|
||||
.build()
|
||||
|
||||
notificationManager.notify(reminderId.hashCode(), notification)
|
||||
Log.i(TAG, "Static reminder displayed: $title (ID: $reminderId)")
|
||||
}
|
||||
|
||||
private fun createReminderNotificationChannel(context: Context, notificationManager: NotificationManager) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel(
|
||||
"daily_reminders",
|
||||
"Daily Reminders",
|
||||
NotificationManager.IMPORTANCE_DEFAULT
|
||||
).apply {
|
||||
description = "Daily reminder notifications"
|
||||
enableVibration(true)
|
||||
setShowBadge(true)
|
||||
}
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
}
|
||||
|
||||
private fun recordReminderTrigger(context: Context, reminderId: String) {
|
||||
try {
|
||||
val prefs = context.getSharedPreferences("daily_reminders", Context.MODE_PRIVATE)
|
||||
val editor = prefs.edit()
|
||||
editor.putLong("${reminderId}_lastTriggered", System.currentTimeMillis())
|
||||
editor.apply()
|
||||
Log.d(TAG, "Reminder trigger recorded: $reminderId")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error recording reminder trigger", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
342
examples/static-daily-reminders.ts
Normal file
342
examples/static-daily-reminders.ts
Normal file
@@ -0,0 +1,342 @@
|
||||
/**
|
||||
* 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();');
|
||||
}
|
||||
@@ -176,4 +176,305 @@ public class DailyNotificationPlugin: CAPPlugin {
|
||||
// For now, return next day at 9 AM
|
||||
return 86400 // 24 hours
|
||||
}
|
||||
|
||||
// MARK: - Static Daily Reminder Methods
|
||||
|
||||
@objc func scheduleDailyReminder(_ call: CAPPluginCall) {
|
||||
guard let id = call.getString("id"),
|
||||
let title = call.getString("title"),
|
||||
let body = call.getString("body"),
|
||||
let time = call.getString("time") else {
|
||||
call.reject("Missing required parameters: id, title, body, time")
|
||||
return
|
||||
}
|
||||
|
||||
let sound = call.getBool("sound", true)
|
||||
let vibration = call.getBool("vibration", true)
|
||||
let priority = call.getString("priority", "normal")
|
||||
let repeatDaily = call.getBool("repeatDaily", true)
|
||||
let timezone = call.getString("timezone")
|
||||
|
||||
print("DNP-REMINDER: Scheduling daily reminder: \(id)")
|
||||
|
||||
// Parse time (HH:mm format)
|
||||
let timeComponents = time.components(separatedBy: ":")
|
||||
guard timeComponents.count == 2,
|
||||
let hour = Int(timeComponents[0]),
|
||||
let minute = Int(timeComponents[1]),
|
||||
hour >= 0 && hour <= 23,
|
||||
minute >= 0 && minute <= 59 else {
|
||||
call.reject("Invalid time format. Use HH:mm (e.g., 09:00)")
|
||||
return
|
||||
}
|
||||
|
||||
// Create notification content
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = title
|
||||
content.body = body
|
||||
content.sound = sound ? .default : nil
|
||||
content.categoryIdentifier = "DAILY_REMINDER"
|
||||
|
||||
// Set priority
|
||||
switch priority {
|
||||
case "high":
|
||||
content.interruptionLevel = .critical
|
||||
case "low":
|
||||
content.interruptionLevel = .passive
|
||||
default:
|
||||
content.interruptionLevel = .active
|
||||
}
|
||||
|
||||
// Create date components for daily trigger
|
||||
var dateComponents = DateComponents()
|
||||
dateComponents.hour = hour
|
||||
dateComponents.minute = minute
|
||||
|
||||
let trigger = UNCalendarNotificationTrigger(
|
||||
dateMatching: dateComponents,
|
||||
repeats: repeatDaily
|
||||
)
|
||||
|
||||
let request = UNNotificationRequest(
|
||||
identifier: "reminder_\(id)",
|
||||
content: content,
|
||||
trigger: trigger
|
||||
)
|
||||
|
||||
// Store reminder in UserDefaults
|
||||
storeReminderInUserDefaults(
|
||||
id: id,
|
||||
title: title,
|
||||
body: body,
|
||||
time: time,
|
||||
sound: sound,
|
||||
vibration: vibration,
|
||||
priority: priority,
|
||||
repeatDaily: repeatDaily,
|
||||
timezone: timezone
|
||||
)
|
||||
|
||||
// Schedule the notification
|
||||
notificationCenter.add(request) { error in
|
||||
DispatchQueue.main.async {
|
||||
if let error = error {
|
||||
print("DNP-REMINDER: Failed to schedule reminder: \(error)")
|
||||
call.reject("Failed to schedule daily reminder: \(error.localizedDescription)")
|
||||
} else {
|
||||
print("DNP-REMINDER: Daily reminder scheduled successfully: \(id)")
|
||||
call.resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func cancelDailyReminder(_ call: CAPPluginCall) {
|
||||
guard let reminderId = call.getString("reminderId") else {
|
||||
call.reject("Missing reminderId parameter")
|
||||
return
|
||||
}
|
||||
|
||||
print("DNP-REMINDER: Cancelling daily reminder: \(reminderId)")
|
||||
|
||||
// Cancel the notification
|
||||
notificationCenter.removePendingNotificationRequests(withIdentifiers: ["reminder_\(reminderId)"])
|
||||
|
||||
// Remove from UserDefaults
|
||||
removeReminderFromUserDefaults(id: reminderId)
|
||||
|
||||
call.resolve()
|
||||
}
|
||||
|
||||
@objc func getScheduledReminders(_ call: CAPPluginCall) {
|
||||
print("DNP-REMINDER: Getting scheduled reminders")
|
||||
|
||||
// Get pending notifications
|
||||
notificationCenter.getPendingNotificationRequests { requests in
|
||||
let reminderRequests = requests.filter { $0.identifier.hasPrefix("reminder_") }
|
||||
|
||||
// Get stored reminder data from UserDefaults
|
||||
let reminders = self.getRemindersFromUserDefaults()
|
||||
|
||||
var result: [[String: Any]] = []
|
||||
for reminder in reminders {
|
||||
let isScheduled = reminderRequests.contains { $0.identifier == "reminder_\(reminder["id"] as! String)" }
|
||||
var reminderInfo = reminder
|
||||
reminderInfo["isScheduled"] = isScheduled
|
||||
result.append(reminderInfo)
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
call.resolve(["reminders": result])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func updateDailyReminder(_ call: CAPPluginCall) {
|
||||
guard let reminderId = call.getString("reminderId") else {
|
||||
call.reject("Missing reminderId parameter")
|
||||
return
|
||||
}
|
||||
|
||||
print("DNP-REMINDER: Updating daily reminder: \(reminderId)")
|
||||
|
||||
// Cancel existing reminder
|
||||
notificationCenter.removePendingNotificationRequests(withIdentifiers: ["reminder_\(reminderId)"])
|
||||
|
||||
// Update in UserDefaults
|
||||
let title = call.getString("title")
|
||||
let body = call.getString("body")
|
||||
let time = call.getString("time")
|
||||
let sound = call.getBool("sound")
|
||||
let vibration = call.getBool("vibration")
|
||||
let priority = call.getString("priority")
|
||||
let repeatDaily = call.getBool("repeatDaily")
|
||||
let timezone = call.getString("timezone")
|
||||
|
||||
updateReminderInUserDefaults(
|
||||
id: reminderId,
|
||||
title: title,
|
||||
body: body,
|
||||
time: time,
|
||||
sound: sound,
|
||||
vibration: vibration,
|
||||
priority: priority,
|
||||
repeatDaily: repeatDaily,
|
||||
timezone: timezone
|
||||
)
|
||||
|
||||
// Reschedule with new settings if all required fields are provided
|
||||
if let title = title, let body = body, let time = time {
|
||||
// Parse time
|
||||
let timeComponents = time.components(separatedBy: ":")
|
||||
guard timeComponents.count == 2,
|
||||
let hour = Int(timeComponents[0]),
|
||||
let minute = Int(timeComponents[1]) else {
|
||||
call.reject("Invalid time format")
|
||||
return
|
||||
}
|
||||
|
||||
// Create new notification content
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = title
|
||||
content.body = body
|
||||
content.sound = (sound ?? true) ? .default : nil
|
||||
content.categoryIdentifier = "DAILY_REMINDER"
|
||||
|
||||
// Set priority
|
||||
let finalPriority = priority ?? "normal"
|
||||
switch finalPriority {
|
||||
case "high":
|
||||
content.interruptionLevel = .critical
|
||||
case "low":
|
||||
content.interruptionLevel = .passive
|
||||
default:
|
||||
content.interruptionLevel = .active
|
||||
}
|
||||
|
||||
// Create date components for daily trigger
|
||||
var dateComponents = DateComponents()
|
||||
dateComponents.hour = hour
|
||||
dateComponents.minute = minute
|
||||
|
||||
let trigger = UNCalendarNotificationTrigger(
|
||||
dateMatching: dateComponents,
|
||||
repeats: repeatDaily ?? true
|
||||
)
|
||||
|
||||
let request = UNNotificationRequest(
|
||||
identifier: "reminder_\(reminderId)",
|
||||
content: content,
|
||||
trigger: trigger
|
||||
)
|
||||
|
||||
// Schedule the updated notification
|
||||
notificationCenter.add(request) { error in
|
||||
DispatchQueue.main.async {
|
||||
if let error = error {
|
||||
call.reject("Failed to reschedule updated reminder: \(error.localizedDescription)")
|
||||
} else {
|
||||
call.resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
call.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helper Methods for Reminder Storage
|
||||
|
||||
private func storeReminderInUserDefaults(
|
||||
id: String,
|
||||
title: String,
|
||||
body: String,
|
||||
time: String,
|
||||
sound: Bool,
|
||||
vibration: Bool,
|
||||
priority: String,
|
||||
repeatDaily: Bool,
|
||||
timezone: String?
|
||||
) {
|
||||
let reminderData: [String: Any] = [
|
||||
"id": id,
|
||||
"title": title,
|
||||
"body": body,
|
||||
"time": time,
|
||||
"sound": sound,
|
||||
"vibration": vibration,
|
||||
"priority": priority,
|
||||
"repeatDaily": repeatDaily,
|
||||
"timezone": timezone ?? "",
|
||||
"createdAt": Date().timeIntervalSince1970,
|
||||
"lastTriggered": 0
|
||||
]
|
||||
|
||||
var reminders = UserDefaults.standard.array(forKey: "daily_reminders") as? [[String: Any]] ?? []
|
||||
reminders.append(reminderData)
|
||||
UserDefaults.standard.set(reminders, forKey: "daily_reminders")
|
||||
|
||||
print("DNP-REMINDER: Reminder stored: \(id)")
|
||||
}
|
||||
|
||||
private func removeReminderFromUserDefaults(id: String) {
|
||||
var reminders = UserDefaults.standard.array(forKey: "daily_reminders") as? [[String: Any]] ?? []
|
||||
reminders.removeAll { ($0["id"] as? String) == id }
|
||||
UserDefaults.standard.set(reminders, forKey: "daily_reminders")
|
||||
|
||||
print("DNP-REMINDER: Reminder removed: \(id)")
|
||||
}
|
||||
|
||||
private func getRemindersFromUserDefaults() -> [[String: Any]] {
|
||||
return UserDefaults.standard.array(forKey: "daily_reminders") as? [[String: Any]] ?? []
|
||||
}
|
||||
|
||||
private func updateReminderInUserDefaults(
|
||||
id: String,
|
||||
title: String?,
|
||||
body: String?,
|
||||
time: String?,
|
||||
sound: Bool?,
|
||||
vibration: Bool?,
|
||||
priority: String?,
|
||||
repeatDaily: Bool?,
|
||||
timezone: String?
|
||||
) {
|
||||
var reminders = UserDefaults.standard.array(forKey: "daily_reminders") as? [[String: Any]] ?? []
|
||||
|
||||
for i in 0..<reminders.count {
|
||||
if reminders[i]["id"] as? String == id {
|
||||
if let title = title { reminders[i]["title"] = title }
|
||||
if let body = body { reminders[i]["body"] = body }
|
||||
if let time = time { reminders[i]["time"] = time }
|
||||
if let sound = sound { reminders[i]["sound"] = sound }
|
||||
if let vibration = vibration { reminders[i]["vibration"] = vibration }
|
||||
if let priority = priority { reminders[i]["priority"] = priority }
|
||||
if let repeatDaily = repeatDaily { reminders[i]["repeatDaily"] = repeatDaily }
|
||||
if let timezone = timezone { reminders[i]["timezone"] = timezone }
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
UserDefaults.standard.set(reminders, forKey: "daily_reminders")
|
||||
print("DNP-REMINDER: Reminder updated: \(id)")
|
||||
}
|
||||
}
|
||||
@@ -1583,4 +1583,353 @@ public class DailyNotificationPlugin extends Plugin {
|
||||
call.reject("Coordination status retrieval failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Static Daily Reminder Methods
|
||||
@PluginMethod
|
||||
public void scheduleDailyReminder(PluginCall call) {
|
||||
try {
|
||||
Log.d(TAG, "Scheduling daily reminder");
|
||||
|
||||
// Extract reminder options
|
||||
String id = call.getString("id");
|
||||
String title = call.getString("title");
|
||||
String body = call.getString("body");
|
||||
String time = call.getString("time");
|
||||
boolean sound = call.getBoolean("sound", true);
|
||||
boolean vibration = call.getBoolean("vibration", true);
|
||||
String priority = call.getString("priority", "normal");
|
||||
boolean repeatDaily = call.getBoolean("repeatDaily", true);
|
||||
String timezone = call.getString("timezone");
|
||||
|
||||
// Validate required parameters
|
||||
if (id == null || title == null || body == null || time == null) {
|
||||
call.reject("Missing required parameters: id, title, body, time");
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse time (HH:mm format)
|
||||
String[] timeParts = time.split(":");
|
||||
if (timeParts.length != 2) {
|
||||
call.reject("Invalid time format. Use HH:mm (e.g., 09:00)");
|
||||
return;
|
||||
}
|
||||
|
||||
int hour = Integer.parseInt(timeParts[0]);
|
||||
int minute = Integer.parseInt(timeParts[1]);
|
||||
|
||||
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
|
||||
call.reject("Invalid time values. Hour must be 0-23, minute must be 0-59");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create reminder content
|
||||
NotificationContent reminderContent = new NotificationContent();
|
||||
reminderContent.setId("reminder_" + id); // Prefix to identify as reminder
|
||||
reminderContent.setTitle(title);
|
||||
reminderContent.setBody(body);
|
||||
reminderContent.setSound(sound);
|
||||
reminderContent.setPriority(priority);
|
||||
reminderContent.setFetchTime(System.currentTimeMillis());
|
||||
|
||||
// Calculate next trigger time
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.HOUR_OF_DAY, hour);
|
||||
calendar.set(Calendar.MINUTE, minute);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
// If time has passed today, schedule for tomorrow
|
||||
if (calendar.getTimeInMillis() <= System.currentTimeMillis()) {
|
||||
calendar.add(Calendar.DAY_OF_MONTH, 1);
|
||||
}
|
||||
|
||||
reminderContent.setScheduledTime(calendar.getTimeInMillis());
|
||||
|
||||
// Store reminder in database
|
||||
storeReminderInDatabase(id, title, body, time, sound, vibration, priority, repeatDaily, timezone);
|
||||
|
||||
// Schedule the notification
|
||||
boolean scheduled = scheduler.scheduleNotification(reminderContent);
|
||||
|
||||
if (scheduled) {
|
||||
Log.i(TAG, "Daily reminder scheduled successfully: " + id);
|
||||
call.resolve();
|
||||
} else {
|
||||
call.reject("Failed to schedule daily reminder");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error scheduling daily reminder", e);
|
||||
call.reject("Daily reminder scheduling failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void cancelDailyReminder(PluginCall call) {
|
||||
try {
|
||||
Log.d(TAG, "Cancelling daily reminder");
|
||||
|
||||
String reminderId = call.getString("reminderId");
|
||||
if (reminderId == null) {
|
||||
call.reject("Missing reminderId parameter");
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel the scheduled notification (use prefixed ID)
|
||||
scheduler.cancelNotification("reminder_" + reminderId);
|
||||
|
||||
// Remove from database
|
||||
removeReminderFromDatabase(reminderId);
|
||||
|
||||
Log.i(TAG, "Daily reminder cancelled: " + reminderId);
|
||||
call.resolve();
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error cancelling daily reminder", e);
|
||||
call.reject("Daily reminder cancellation failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void getScheduledReminders(PluginCall call) {
|
||||
try {
|
||||
Log.d(TAG, "Getting scheduled reminders");
|
||||
|
||||
// Get reminders from database
|
||||
java.util.List<DailyReminderInfo> reminders = getRemindersFromDatabase();
|
||||
|
||||
// Convert to JSObject array
|
||||
JSObject result = new JSObject();
|
||||
result.put("reminders", reminders);
|
||||
|
||||
call.resolve(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error getting scheduled reminders", e);
|
||||
call.reject("Failed to get scheduled reminders: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void updateDailyReminder(PluginCall call) {
|
||||
try {
|
||||
Log.d(TAG, "Updating daily reminder");
|
||||
|
||||
String reminderId = call.getString("reminderId");
|
||||
if (reminderId == null) {
|
||||
call.reject("Missing reminderId parameter");
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract updated options
|
||||
String title = call.getString("title");
|
||||
String body = call.getString("body");
|
||||
String time = call.getString("time");
|
||||
Boolean sound = call.getBoolean("sound");
|
||||
Boolean vibration = call.getBoolean("vibration");
|
||||
String priority = call.getString("priority");
|
||||
Boolean repeatDaily = call.getBoolean("repeatDaily");
|
||||
String timezone = call.getString("timezone");
|
||||
|
||||
// Cancel existing reminder (use prefixed ID)
|
||||
scheduler.cancelNotification("reminder_" + reminderId);
|
||||
|
||||
// Update in database
|
||||
updateReminderInDatabase(reminderId, title, body, time, sound, vibration, priority, repeatDaily, timezone);
|
||||
|
||||
// Reschedule with new settings
|
||||
if (title != null && body != null && time != null) {
|
||||
// Create new reminder content
|
||||
NotificationContent reminderContent = new NotificationContent();
|
||||
reminderContent.setId("reminder_" + reminderId); // Prefix to identify as reminder
|
||||
reminderContent.setTitle(title);
|
||||
reminderContent.setBody(body);
|
||||
reminderContent.setSound(sound != null ? sound : true);
|
||||
reminderContent.setPriority(priority != null ? priority : "normal");
|
||||
reminderContent.setFetchTime(System.currentTimeMillis());
|
||||
|
||||
// Calculate next trigger time
|
||||
String[] timeParts = time.split(":");
|
||||
int hour = Integer.parseInt(timeParts[0]);
|
||||
int minute = Integer.parseInt(timeParts[1]);
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.HOUR_OF_DAY, hour);
|
||||
calendar.set(Calendar.MINUTE, minute);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
if (calendar.getTimeInMillis() <= System.currentTimeMillis()) {
|
||||
calendar.add(Calendar.DAY_OF_MONTH, 1);
|
||||
}
|
||||
|
||||
reminderContent.setScheduledTime(calendar.getTimeInMillis());
|
||||
|
||||
// Schedule the updated notification
|
||||
boolean scheduled = scheduler.scheduleNotification(reminderContent);
|
||||
|
||||
if (!scheduled) {
|
||||
call.reject("Failed to reschedule updated reminder");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Log.i(TAG, "Daily reminder updated: " + reminderId);
|
||||
call.resolve();
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error updating daily reminder", e);
|
||||
call.reject("Daily reminder update failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Helper methods for reminder database operations
|
||||
private void storeReminderInDatabase(String id, String title, String body, String time,
|
||||
boolean sound, boolean vibration, String priority,
|
||||
boolean repeatDaily, String timezone) {
|
||||
try {
|
||||
SharedPreferences prefs = getContext().getSharedPreferences("daily_reminders", Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
|
||||
editor.putString(id + "_title", title);
|
||||
editor.putString(id + "_body", body);
|
||||
editor.putString(id + "_time", time);
|
||||
editor.putBoolean(id + "_sound", sound);
|
||||
editor.putBoolean(id + "_vibration", vibration);
|
||||
editor.putString(id + "_priority", priority);
|
||||
editor.putBoolean(id + "_repeatDaily", repeatDaily);
|
||||
editor.putString(id + "_timezone", timezone);
|
||||
editor.putLong(id + "_createdAt", System.currentTimeMillis());
|
||||
editor.putBoolean(id + "_isScheduled", true);
|
||||
|
||||
editor.apply();
|
||||
Log.d(TAG, "Reminder stored in database: " + id);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error storing reminder in database", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeReminderFromDatabase(String id) {
|
||||
try {
|
||||
SharedPreferences prefs = getContext().getSharedPreferences("daily_reminders", Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
|
||||
editor.remove(id + "_title");
|
||||
editor.remove(id + "_body");
|
||||
editor.remove(id + "_time");
|
||||
editor.remove(id + "_sound");
|
||||
editor.remove(id + "_vibration");
|
||||
editor.remove(id + "_priority");
|
||||
editor.remove(id + "_repeatDaily");
|
||||
editor.remove(id + "_timezone");
|
||||
editor.remove(id + "_createdAt");
|
||||
editor.remove(id + "_isScheduled");
|
||||
editor.remove(id + "_lastTriggered");
|
||||
|
||||
editor.apply();
|
||||
Log.d(TAG, "Reminder removed from database: " + id);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error removing reminder from database", e);
|
||||
}
|
||||
}
|
||||
|
||||
private java.util.List<DailyReminderInfo> getRemindersFromDatabase() {
|
||||
java.util.List<DailyReminderInfo> reminders = new java.util.ArrayList<>();
|
||||
|
||||
try {
|
||||
SharedPreferences prefs = getContext().getSharedPreferences("daily_reminders", Context.MODE_PRIVATE);
|
||||
java.util.Map<String, ?> allEntries = prefs.getAll();
|
||||
|
||||
java.util.Set<String> reminderIds = new java.util.HashSet<>();
|
||||
for (String key : allEntries.keySet()) {
|
||||
if (key.endsWith("_title")) {
|
||||
String id = key.substring(0, key.length() - 6); // Remove "_title"
|
||||
reminderIds.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
for (String id : reminderIds) {
|
||||
DailyReminderInfo reminder = new DailyReminderInfo();
|
||||
reminder.id = id;
|
||||
reminder.title = prefs.getString(id + "_title", "");
|
||||
reminder.body = prefs.getString(id + "_body", "");
|
||||
reminder.time = prefs.getString(id + "_time", "");
|
||||
reminder.sound = prefs.getBoolean(id + "_sound", true);
|
||||
reminder.vibration = prefs.getBoolean(id + "_vibration", true);
|
||||
reminder.priority = prefs.getString(id + "_priority", "normal");
|
||||
reminder.repeatDaily = prefs.getBoolean(id + "_repeatDaily", true);
|
||||
reminder.timezone = prefs.getString(id + "_timezone", null);
|
||||
reminder.isScheduled = prefs.getBoolean(id + "_isScheduled", false);
|
||||
reminder.createdAt = prefs.getLong(id + "_createdAt", 0);
|
||||
reminder.lastTriggered = prefs.getLong(id + "_lastTriggered", 0);
|
||||
|
||||
// Calculate next trigger time
|
||||
String[] timeParts = reminder.time.split(":");
|
||||
int hour = Integer.parseInt(timeParts[0]);
|
||||
int minute = Integer.parseInt(timeParts[1]);
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.HOUR_OF_DAY, hour);
|
||||
calendar.set(Calendar.MINUTE, minute);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
calendar.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
if (calendar.getTimeInMillis() <= System.currentTimeMillis()) {
|
||||
calendar.add(Calendar.DAY_OF_MONTH, 1);
|
||||
}
|
||||
|
||||
reminder.nextTriggerTime = calendar.getTimeInMillis();
|
||||
|
||||
reminders.add(reminder);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error getting reminders from database", e);
|
||||
}
|
||||
|
||||
return reminders;
|
||||
}
|
||||
|
||||
private void updateReminderInDatabase(String id, String title, String body, String time,
|
||||
Boolean sound, Boolean vibration, String priority,
|
||||
Boolean repeatDaily, String timezone) {
|
||||
try {
|
||||
SharedPreferences prefs = getContext().getSharedPreferences("daily_reminders", Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
|
||||
if (title != null) editor.putString(id + "_title", title);
|
||||
if (body != null) editor.putString(id + "_body", body);
|
||||
if (time != null) editor.putString(id + "_time", time);
|
||||
if (sound != null) editor.putBoolean(id + "_sound", sound);
|
||||
if (vibration != null) editor.putBoolean(id + "_vibration", vibration);
|
||||
if (priority != null) editor.putString(id + "_priority", priority);
|
||||
if (repeatDaily != null) editor.putBoolean(id + "_repeatDaily", repeatDaily);
|
||||
if (timezone != null) editor.putString(id + "_timezone", timezone);
|
||||
|
||||
editor.apply();
|
||||
Log.d(TAG, "Reminder updated in database: " + id);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error updating reminder in database", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Data class for reminder info
|
||||
public static class DailyReminderInfo {
|
||||
public String id;
|
||||
public String title;
|
||||
public String body;
|
||||
public String time;
|
||||
public boolean sound;
|
||||
public boolean vibration;
|
||||
public String priority;
|
||||
public boolean repeatDaily;
|
||||
public String timezone;
|
||||
public boolean isScheduled;
|
||||
public long nextTriggerTime;
|
||||
public long createdAt;
|
||||
public long lastTriggered;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +108,17 @@ public class DailyNotificationScheduler {
|
||||
intent.setAction(ACTION_NOTIFICATION);
|
||||
intent.putExtra(EXTRA_NOTIFICATION_ID, content.getId());
|
||||
|
||||
// Check if this is a static reminder
|
||||
if (content.getId().startsWith("reminder_") || content.getId().contains("_reminder")) {
|
||||
intent.putExtra("is_static_reminder", true);
|
||||
intent.putExtra("reminder_id", content.getId());
|
||||
intent.putExtra("title", content.getTitle());
|
||||
intent.putExtra("body", content.getBody());
|
||||
intent.putExtra("sound", content.isSound());
|
||||
intent.putExtra("vibration", true); // Default to true for reminders
|
||||
intent.putExtra("priority", content.getPriority());
|
||||
}
|
||||
|
||||
// Create pending intent with unique request code
|
||||
int requestCode = content.getId().hashCode();
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(
|
||||
|
||||
@@ -103,6 +103,35 @@ export interface PermissionStatus {
|
||||
carPlay?: boolean;
|
||||
}
|
||||
|
||||
// Static Daily Reminder Interfaces
|
||||
export interface DailyReminderOptions {
|
||||
id: string;
|
||||
title: string;
|
||||
body: string;
|
||||
time: string; // HH:mm format (e.g., "09:00")
|
||||
sound?: boolean;
|
||||
vibration?: boolean;
|
||||
priority?: 'low' | 'normal' | 'high';
|
||||
repeatDaily?: boolean;
|
||||
timezone?: string;
|
||||
}
|
||||
|
||||
export interface DailyReminderInfo {
|
||||
id: string;
|
||||
title: string;
|
||||
body: string;
|
||||
time: string;
|
||||
sound: boolean;
|
||||
vibration: boolean;
|
||||
priority: 'low' | 'normal' | 'high';
|
||||
repeatDaily: boolean;
|
||||
timezone?: string;
|
||||
isScheduled: boolean;
|
||||
nextTriggerTime?: number;
|
||||
createdAt: number;
|
||||
lastTriggered?: number;
|
||||
}
|
||||
|
||||
export type PermissionState = 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied' | 'provisional' | 'ephemeral' | 'unknown';
|
||||
|
||||
// Additional interfaces for enhanced functionality
|
||||
@@ -364,6 +393,28 @@ export interface DailyNotificationPlugin {
|
||||
refreshAuthenticationForNewIdentity(activeDid: string): Promise<void>;
|
||||
clearCacheForNewIdentity(): Promise<void>;
|
||||
updateBackgroundTaskIdentity(activeDid: string): Promise<void>;
|
||||
|
||||
// Static Daily Reminder Methods
|
||||
/**
|
||||
* Schedule a simple daily reminder notification
|
||||
* No network content required - just static text
|
||||
*/
|
||||
scheduleDailyReminder(options: DailyReminderOptions): Promise<void>;
|
||||
|
||||
/**
|
||||
* Cancel a daily reminder notification
|
||||
*/
|
||||
cancelDailyReminder(reminderId: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Get all scheduled daily reminders
|
||||
*/
|
||||
getScheduledReminders(): Promise<DailyReminderInfo[]>;
|
||||
|
||||
/**
|
||||
* Update an existing daily reminder
|
||||
*/
|
||||
updateDailyReminder(reminderId: string, options: DailyReminderOptions): Promise<void>;
|
||||
}
|
||||
|
||||
// Phase 1: TimeSafari Endorser.ch API Interfaces
|
||||
|
||||
314
src/web.ts
314
src/web.ts
@@ -811,4 +811,318 @@ export class DailyNotificationWeb extends WebPlugin implements DailyNotification
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Static Daily Reminder Methods
|
||||
async scheduleDailyReminder(options: any): Promise<void> {
|
||||
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<void> {
|
||||
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<any> {
|
||||
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<void> {
|
||||
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<string, number> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ Each test app includes comprehensive UI patterns and testing capabilities:
|
||||
- **TimeSafari Configuration**: Test community-focused notification settings
|
||||
- **Endorser.ch API Integration**: Test real API patterns with pagination
|
||||
- **Community Notification Scheduling**: Test offers, projects, people, and items notifications
|
||||
- **Static Daily Reminders**: Test simple daily notifications without network content
|
||||
- **Performance Monitoring**: Metrics collection and display
|
||||
- **Error Handling**: Comprehensive error testing
|
||||
- **Debug Information**: Platform-specific debug data
|
||||
|
||||
@@ -402,6 +402,57 @@
|
||||
<button id="test-phase4-integration" class="btn-secondary">Test Complete Integration</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Static Daily Reminders Testing -->
|
||||
<div class="ui-section">
|
||||
<h3>⏰ Static Daily Reminders</h3>
|
||||
<div class="button-grid">
|
||||
<button id="schedule-reminder" class="btn-primary">Schedule Daily Reminder</button>
|
||||
<button id="cancel-reminder" class="btn-primary">Cancel Reminder</button>
|
||||
<button id="get-reminders" class="btn-primary">Get Scheduled Reminders</button>
|
||||
<button id="update-reminder" class="btn-primary">Update Reminder</button>
|
||||
</div>
|
||||
<div class="form-section">
|
||||
<h4>Reminder Configuration</h4>
|
||||
<div class="form-group">
|
||||
<label for="reminder-id">Reminder ID:</label>
|
||||
<input type="text" id="reminder-id" value="morning_checkin" placeholder="e.g., morning_checkin">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reminder-title">Title:</label>
|
||||
<input type="text" id="reminder-title" value="Good Morning!" placeholder="Reminder title">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reminder-body">Body:</label>
|
||||
<input type="text" id="reminder-body" value="Time to check your TimeSafari community updates" placeholder="Reminder message">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reminder-time">Time (HH:mm):</label>
|
||||
<input type="text" id="reminder-time" value="09:00" placeholder="09:00">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="reminder-sound" checked> Sound
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" id="reminder-vibration" checked> Vibration
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reminder-priority">Priority:</label>
|
||||
<select id="reminder-priority">
|
||||
<option value="low">Low</option>
|
||||
<option value="normal" selected>Normal</option>
|
||||
<option value="high">High</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="reminder-repeat" checked> Repeat Daily
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error Handling Section -->
|
||||
<div class="ui-section">
|
||||
|
||||
@@ -469,6 +469,12 @@ class TimeSafariAndroidTestApp {
|
||||
document.getElementById('test-endorser-api-client')?.addEventListener('click', () => this.testEndorserAPIClient());
|
||||
document.getElementById('test-notification-manager')?.addEventListener('click', () => this.testTimeSafariNotificationManager());
|
||||
document.getElementById('test-phase4-integration')?.addEventListener('click', () => this.testPhase4Integration());
|
||||
|
||||
// Static Daily Reminder event listeners
|
||||
document.getElementById('schedule-reminder')?.addEventListener('click', () => this.scheduleDailyReminder());
|
||||
document.getElementById('cancel-reminder')?.addEventListener('click', () => this.cancelDailyReminder());
|
||||
document.getElementById('get-reminders')?.addEventListener('click', () => this.getScheduledReminders());
|
||||
document.getElementById('update-reminder')?.addEventListener('click', () => this.updateDailyReminder());
|
||||
}
|
||||
|
||||
private async initializeUI(): Promise<void> {
|
||||
@@ -1116,6 +1122,98 @@ class TimeSafariAndroidTestApp {
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
// Static Daily Reminder Methods
|
||||
private async scheduleDailyReminder(): Promise<void> {
|
||||
try {
|
||||
this.log('Scheduling daily reminder...');
|
||||
|
||||
const reminderOptions = {
|
||||
id: (document.getElementById('reminder-id') as HTMLInputElement)?.value || 'morning_checkin',
|
||||
title: (document.getElementById('reminder-title') as HTMLInputElement)?.value || 'Good Morning!',
|
||||
body: (document.getElementById('reminder-body') as HTMLInputElement)?.value || 'Time to check your TimeSafari community updates',
|
||||
time: (document.getElementById('reminder-time') as HTMLInputElement)?.value || '09:00',
|
||||
sound: (document.getElementById('reminder-sound') as HTMLInputElement)?.checked ?? true,
|
||||
vibration: (document.getElementById('reminder-vibration') as HTMLInputElement)?.checked ?? true,
|
||||
priority: (document.getElementById('reminder-priority') as HTMLSelectElement)?.value || 'normal',
|
||||
repeatDaily: (document.getElementById('reminder-repeat') as HTMLInputElement)?.checked ?? true
|
||||
};
|
||||
|
||||
this.log('Reminder options:', reminderOptions);
|
||||
|
||||
await this.plugin.scheduleDailyReminder(reminderOptions);
|
||||
this.log('✅ Daily reminder scheduled successfully');
|
||||
this.errorDisplay.showSuccess('Daily reminder scheduled successfully!');
|
||||
|
||||
} catch (error) {
|
||||
this.log('❌ Failed to schedule daily reminder:', error);
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async cancelDailyReminder(): Promise<void> {
|
||||
try {
|
||||
this.log('Cancelling daily reminder...');
|
||||
|
||||
const reminderId = (document.getElementById('reminder-id') as HTMLInputElement)?.value || 'morning_checkin';
|
||||
|
||||
await this.plugin.cancelDailyReminder(reminderId);
|
||||
this.log('✅ Daily reminder cancelled successfully');
|
||||
this.errorDisplay.showSuccess('Daily reminder cancelled successfully!');
|
||||
|
||||
} catch (error) {
|
||||
this.log('❌ Failed to cancel daily reminder:', error);
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async getScheduledReminders(): Promise<void> {
|
||||
try {
|
||||
this.log('Getting scheduled reminders...');
|
||||
|
||||
const result = await this.plugin.getScheduledReminders();
|
||||
this.log('✅ Scheduled reminders retrieved:', result);
|
||||
|
||||
if (result.reminders && result.reminders.length > 0) {
|
||||
this.errorDisplay.showSuccess(`Found ${result.reminders.length} scheduled reminders`);
|
||||
console.table(result.reminders);
|
||||
} else {
|
||||
this.errorDisplay.showInfo('No scheduled reminders found');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
this.log('❌ Failed to get scheduled reminders:', error);
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async updateDailyReminder(): Promise<void> {
|
||||
try {
|
||||
this.log('Updating daily reminder...');
|
||||
|
||||
const reminderId = (document.getElementById('reminder-id') as HTMLInputElement)?.value || 'morning_checkin';
|
||||
|
||||
const updateOptions = {
|
||||
title: (document.getElementById('reminder-title') as HTMLInputElement)?.value,
|
||||
body: (document.getElementById('reminder-body') as HTMLInputElement)?.value,
|
||||
time: (document.getElementById('reminder-time') as HTMLInputElement)?.value,
|
||||
sound: (document.getElementById('reminder-sound') as HTMLInputElement)?.checked,
|
||||
vibration: (document.getElementById('reminder-vibration') as HTMLInputElement)?.checked,
|
||||
priority: (document.getElementById('reminder-priority') as HTMLSelectElement)?.value,
|
||||
repeatDaily: (document.getElementById('reminder-repeat') as HTMLInputElement)?.checked
|
||||
};
|
||||
|
||||
this.log('Update options:', updateOptions);
|
||||
|
||||
await this.plugin.updateDailyReminder(reminderId, updateOptions);
|
||||
this.log('✅ Daily reminder updated successfully');
|
||||
this.errorDisplay.showSuccess('Daily reminder updated successfully!');
|
||||
|
||||
} catch (error) {
|
||||
this.log('❌ Failed to update daily reminder:', error);
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize app when DOM is ready
|
||||
|
||||
@@ -402,6 +402,57 @@
|
||||
<button id="test-phase4-integration" class="btn-secondary">Test Complete Integration</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Static Daily Reminders Testing -->
|
||||
<div class="ui-section">
|
||||
<h3>⏰ Static Daily Reminders</h3>
|
||||
<div class="button-grid">
|
||||
<button id="schedule-reminder" class="btn-primary">Schedule Daily Reminder</button>
|
||||
<button id="cancel-reminder" class="btn-primary">Cancel Reminder</button>
|
||||
<button id="get-reminders" class="btn-primary">Get Scheduled Reminders</button>
|
||||
<button id="update-reminder" class="btn-primary">Update Reminder</button>
|
||||
</div>
|
||||
<div class="form-section">
|
||||
<h4>Reminder Configuration</h4>
|
||||
<div class="form-group">
|
||||
<label for="reminder-id">Reminder ID:</label>
|
||||
<input type="text" id="reminder-id" value="morning_checkin" placeholder="e.g., morning_checkin">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reminder-title">Title:</label>
|
||||
<input type="text" id="reminder-title" value="Good Morning!" placeholder="Reminder title">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reminder-body">Body:</label>
|
||||
<input type="text" id="reminder-body" value="Time to check your TimeSafari community updates" placeholder="Reminder message">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reminder-time">Time (HH:mm):</label>
|
||||
<input type="text" id="reminder-time" value="09:00" placeholder="09:00">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="reminder-sound" checked> Sound
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" id="reminder-vibration" checked> Vibration
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reminder-priority">Priority:</label>
|
||||
<select id="reminder-priority">
|
||||
<option value="low">Low</option>
|
||||
<option value="normal" selected>Normal</option>
|
||||
<option value="high">High</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="reminder-repeat" checked> Repeat Daily
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error Handling Section -->
|
||||
<div class="ui-section">
|
||||
|
||||
@@ -318,6 +318,12 @@ class TimeSafariElectronTestApp {
|
||||
document.getElementById('test-endorser-api-client')?.addEventListener('click', () => this.testEndorserAPIClient());
|
||||
document.getElementById('test-notification-manager')?.addEventListener('click', () => this.testTimeSafariNotificationManager());
|
||||
document.getElementById('test-phase4-integration')?.addEventListener('click', () => this.testPhase4Integration());
|
||||
|
||||
// Static Daily Reminder event listeners
|
||||
document.getElementById('schedule-reminder')?.addEventListener('click', () => this.scheduleDailyReminder());
|
||||
document.getElementById('cancel-reminder')?.addEventListener('click', () => this.cancelDailyReminder());
|
||||
document.getElementById('get-reminders')?.addEventListener('click', () => this.getScheduledReminders());
|
||||
document.getElementById('update-reminder')?.addEventListener('click', () => this.updateDailyReminder());
|
||||
}
|
||||
|
||||
private async initializeUI(): Promise<void> {
|
||||
@@ -963,6 +969,98 @@ class TimeSafariElectronTestApp {
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
// Static Daily Reminder Methods
|
||||
private async scheduleDailyReminder(): Promise<void> {
|
||||
try {
|
||||
this.log('Scheduling daily reminder...');
|
||||
|
||||
const reminderOptions = {
|
||||
id: (document.getElementById('reminder-id') as HTMLInputElement)?.value || 'morning_checkin',
|
||||
title: (document.getElementById('reminder-title') as HTMLInputElement)?.value || 'Good Morning!',
|
||||
body: (document.getElementById('reminder-body') as HTMLInputElement)?.value || 'Time to check your TimeSafari community updates',
|
||||
time: (document.getElementById('reminder-time') as HTMLInputElement)?.value || '09:00',
|
||||
sound: (document.getElementById('reminder-sound') as HTMLInputElement)?.checked ?? true,
|
||||
vibration: (document.getElementById('reminder-vibration') as HTMLInputElement)?.checked ?? true,
|
||||
priority: (document.getElementById('reminder-priority') as HTMLSelectElement)?.value || 'normal',
|
||||
repeatDaily: (document.getElementById('reminder-repeat') as HTMLInputElement)?.checked ?? true
|
||||
};
|
||||
|
||||
this.log('Reminder options:', reminderOptions);
|
||||
|
||||
await this.plugin.scheduleDailyReminder(reminderOptions);
|
||||
this.log('✅ Daily reminder scheduled successfully');
|
||||
this.errorDisplay.showSuccess('Daily reminder scheduled successfully!');
|
||||
|
||||
} catch (error) {
|
||||
this.log('❌ Failed to schedule daily reminder:', error);
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async cancelDailyReminder(): Promise<void> {
|
||||
try {
|
||||
this.log('Cancelling daily reminder...');
|
||||
|
||||
const reminderId = (document.getElementById('reminder-id') as HTMLInputElement)?.value || 'morning_checkin';
|
||||
|
||||
await this.plugin.cancelDailyReminder(reminderId);
|
||||
this.log('✅ Daily reminder cancelled successfully');
|
||||
this.errorDisplay.showSuccess('Daily reminder cancelled successfully!');
|
||||
|
||||
} catch (error) {
|
||||
this.log('❌ Failed to cancel daily reminder:', error);
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async getScheduledReminders(): Promise<void> {
|
||||
try {
|
||||
this.log('Getting scheduled reminders...');
|
||||
|
||||
const result = await this.plugin.getScheduledReminders();
|
||||
this.log('✅ Scheduled reminders retrieved:', result);
|
||||
|
||||
if (result.reminders && result.reminders.length > 0) {
|
||||
this.errorDisplay.showSuccess(`Found ${result.reminders.length} scheduled reminders`);
|
||||
console.table(result.reminders);
|
||||
} else {
|
||||
this.errorDisplay.showInfo('No scheduled reminders found');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
this.log('❌ Failed to get scheduled reminders:', error);
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async updateDailyReminder(): Promise<void> {
|
||||
try {
|
||||
this.log('Updating daily reminder...');
|
||||
|
||||
const reminderId = (document.getElementById('reminder-id') as HTMLInputElement)?.value || 'morning_checkin';
|
||||
|
||||
const updateOptions = {
|
||||
title: (document.getElementById('reminder-title') as HTMLInputElement)?.value,
|
||||
body: (document.getElementById('reminder-body') as HTMLInputElement)?.value,
|
||||
time: (document.getElementById('reminder-time') as HTMLInputElement)?.value,
|
||||
sound: (document.getElementById('reminder-sound') as HTMLInputElement)?.checked,
|
||||
vibration: (document.getElementById('reminder-vibration') as HTMLInputElement)?.checked,
|
||||
priority: (document.getElementById('reminder-priority') as HTMLSelectElement)?.value,
|
||||
repeatDaily: (document.getElementById('reminder-repeat') as HTMLInputElement)?.checked
|
||||
};
|
||||
|
||||
this.log('Update options:', updateOptions);
|
||||
|
||||
await this.plugin.updateDailyReminder(reminderId, updateOptions);
|
||||
this.log('✅ Daily reminder updated successfully');
|
||||
this.errorDisplay.showSuccess('Daily reminder updated successfully!');
|
||||
|
||||
} catch (error) {
|
||||
this.log('❌ Failed to update daily reminder:', error);
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize app when DOM is ready
|
||||
|
||||
@@ -402,6 +402,57 @@
|
||||
<button id="test-phase4-integration" class="btn-secondary">Test Complete Integration</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Static Daily Reminders Testing -->
|
||||
<div class="ui-section">
|
||||
<h3>⏰ Static Daily Reminders</h3>
|
||||
<div class="button-grid">
|
||||
<button id="schedule-reminder" class="btn-primary">Schedule Daily Reminder</button>
|
||||
<button id="cancel-reminder" class="btn-primary">Cancel Reminder</button>
|
||||
<button id="get-reminders" class="btn-primary">Get Scheduled Reminders</button>
|
||||
<button id="update-reminder" class="btn-primary">Update Reminder</button>
|
||||
</div>
|
||||
<div class="form-section">
|
||||
<h4>Reminder Configuration</h4>
|
||||
<div class="form-group">
|
||||
<label for="reminder-id">Reminder ID:</label>
|
||||
<input type="text" id="reminder-id" value="morning_checkin" placeholder="e.g., morning_checkin">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reminder-title">Title:</label>
|
||||
<input type="text" id="reminder-title" value="Good Morning!" placeholder="Reminder title">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reminder-body">Body:</label>
|
||||
<input type="text" id="reminder-body" value="Time to check your TimeSafari community updates" placeholder="Reminder message">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reminder-time">Time (HH:mm):</label>
|
||||
<input type="text" id="reminder-time" value="09:00" placeholder="09:00">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="reminder-sound" checked> Sound
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" id="reminder-vibration" checked> Vibration
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="reminder-priority">Priority:</label>
|
||||
<select id="reminder-priority">
|
||||
<option value="low">Low</option>
|
||||
<option value="normal" selected>Normal</option>
|
||||
<option value="high">High</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" id="reminder-repeat" checked> Repeat Daily
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error Handling Section -->
|
||||
<div class="ui-section">
|
||||
|
||||
@@ -318,6 +318,12 @@ class TimeSafariIOSTestApp {
|
||||
document.getElementById('test-endorser-api-client')?.addEventListener('click', () => this.testEndorserAPIClient());
|
||||
document.getElementById('test-notification-manager')?.addEventListener('click', () => this.testTimeSafariNotificationManager());
|
||||
document.getElementById('test-phase4-integration')?.addEventListener('click', () => this.testPhase4Integration());
|
||||
|
||||
// Static Daily Reminder event listeners
|
||||
document.getElementById('schedule-reminder')?.addEventListener('click', () => this.scheduleDailyReminder());
|
||||
document.getElementById('cancel-reminder')?.addEventListener('click', () => this.cancelDailyReminder());
|
||||
document.getElementById('get-reminders')?.addEventListener('click', () => this.getScheduledReminders());
|
||||
document.getElementById('update-reminder')?.addEventListener('click', () => this.updateDailyReminder());
|
||||
}
|
||||
|
||||
private async initializeUI(): Promise<void> {
|
||||
@@ -960,6 +966,98 @@ class TimeSafariIOSTestApp {
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
// Static Daily Reminder Methods
|
||||
private async scheduleDailyReminder(): Promise<void> {
|
||||
try {
|
||||
this.log('Scheduling daily reminder...');
|
||||
|
||||
const reminderOptions = {
|
||||
id: (document.getElementById('reminder-id') as HTMLInputElement)?.value || 'morning_checkin',
|
||||
title: (document.getElementById('reminder-title') as HTMLInputElement)?.value || 'Good Morning!',
|
||||
body: (document.getElementById('reminder-body') as HTMLInputElement)?.value || 'Time to check your TimeSafari community updates',
|
||||
time: (document.getElementById('reminder-time') as HTMLInputElement)?.value || '09:00',
|
||||
sound: (document.getElementById('reminder-sound') as HTMLInputElement)?.checked ?? true,
|
||||
vibration: (document.getElementById('reminder-vibration') as HTMLInputElement)?.checked ?? true,
|
||||
priority: (document.getElementById('reminder-priority') as HTMLSelectElement)?.value || 'normal',
|
||||
repeatDaily: (document.getElementById('reminder-repeat') as HTMLInputElement)?.checked ?? true
|
||||
};
|
||||
|
||||
this.log('Reminder options:', reminderOptions);
|
||||
|
||||
await this.plugin.scheduleDailyReminder(reminderOptions);
|
||||
this.log('✅ Daily reminder scheduled successfully');
|
||||
this.errorDisplay.showSuccess('Daily reminder scheduled successfully!');
|
||||
|
||||
} catch (error) {
|
||||
this.log('❌ Failed to schedule daily reminder:', error);
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async cancelDailyReminder(): Promise<void> {
|
||||
try {
|
||||
this.log('Cancelling daily reminder...');
|
||||
|
||||
const reminderId = (document.getElementById('reminder-id') as HTMLInputElement)?.value || 'morning_checkin';
|
||||
|
||||
await this.plugin.cancelDailyReminder(reminderId);
|
||||
this.log('✅ Daily reminder cancelled successfully');
|
||||
this.errorDisplay.showSuccess('Daily reminder cancelled successfully!');
|
||||
|
||||
} catch (error) {
|
||||
this.log('❌ Failed to cancel daily reminder:', error);
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async getScheduledReminders(): Promise<void> {
|
||||
try {
|
||||
this.log('Getting scheduled reminders...');
|
||||
|
||||
const result = await this.plugin.getScheduledReminders();
|
||||
this.log('✅ Scheduled reminders retrieved:', result);
|
||||
|
||||
if (result.reminders && result.reminders.length > 0) {
|
||||
this.errorDisplay.showSuccess(`Found ${result.reminders.length} scheduled reminders`);
|
||||
console.table(result.reminders);
|
||||
} else {
|
||||
this.errorDisplay.showInfo('No scheduled reminders found');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
this.log('❌ Failed to get scheduled reminders:', error);
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
private async updateDailyReminder(): Promise<void> {
|
||||
try {
|
||||
this.log('Updating daily reminder...');
|
||||
|
||||
const reminderId = (document.getElementById('reminder-id') as HTMLInputElement)?.value || 'morning_checkin';
|
||||
|
||||
const updateOptions = {
|
||||
title: (document.getElementById('reminder-title') as HTMLInputElement)?.value,
|
||||
body: (document.getElementById('reminder-body') as HTMLInputElement)?.value,
|
||||
time: (document.getElementById('reminder-time') as HTMLInputElement)?.value,
|
||||
sound: (document.getElementById('reminder-sound') as HTMLInputElement)?.checked,
|
||||
vibration: (document.getElementById('reminder-vibration') as HTMLInputElement)?.checked,
|
||||
priority: (document.getElementById('reminder-priority') as HTMLSelectElement)?.value,
|
||||
repeatDaily: (document.getElementById('reminder-repeat') as HTMLInputElement)?.checked
|
||||
};
|
||||
|
||||
this.log('Update options:', updateOptions);
|
||||
|
||||
await this.plugin.updateDailyReminder(reminderId, updateOptions);
|
||||
this.log('✅ Daily reminder updated successfully');
|
||||
this.errorDisplay.showSuccess('Daily reminder updated successfully!');
|
||||
|
||||
} catch (error) {
|
||||
this.log('❌ Failed to update daily reminder:', error);
|
||||
this.errorDisplay.showError(error as Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize app when DOM is ready
|
||||
|
||||
@@ -56,6 +56,12 @@ describe('DailyNotification Advanced Scenarios', () => {
|
||||
refreshAuthenticationForNewIdentity: jest.fn(),
|
||||
clearCacheForNewIdentity: jest.fn(),
|
||||
updateBackgroundTaskIdentity: jest.fn(),
|
||||
|
||||
// Static Daily Reminder Methods
|
||||
scheduleDailyReminder: jest.fn(),
|
||||
cancelDailyReminder: jest.fn(),
|
||||
getScheduledReminders: jest.fn(),
|
||||
updateDailyReminder: jest.fn(),
|
||||
};
|
||||
plugin = new DailyNotification(mockPlugin);
|
||||
});
|
||||
|
||||
@@ -70,6 +70,12 @@ describe('DailyNotification Plugin', () => {
|
||||
refreshAuthenticationForNewIdentity: jest.fn(),
|
||||
clearCacheForNewIdentity: jest.fn(),
|
||||
updateBackgroundTaskIdentity: jest.fn(),
|
||||
|
||||
// Static Daily Reminder Methods
|
||||
scheduleDailyReminder: jest.fn(),
|
||||
cancelDailyReminder: jest.fn(),
|
||||
getScheduledReminders: jest.fn(),
|
||||
updateDailyReminder: jest.fn(),
|
||||
};
|
||||
|
||||
// Create plugin instance with mock
|
||||
|
||||
@@ -61,6 +61,12 @@ describe('DailyNotification Edge Cases', () => {
|
||||
refreshAuthenticationForNewIdentity: jest.fn(),
|
||||
clearCacheForNewIdentity: jest.fn(),
|
||||
updateBackgroundTaskIdentity: jest.fn(),
|
||||
|
||||
// Static Daily Reminder Methods
|
||||
scheduleDailyReminder: jest.fn(),
|
||||
cancelDailyReminder: jest.fn(),
|
||||
getScheduledReminders: jest.fn(),
|
||||
updateDailyReminder: jest.fn(),
|
||||
};
|
||||
plugin = new DailyNotification(mockPlugin);
|
||||
});
|
||||
|
||||
@@ -60,6 +60,12 @@ describe('DailyNotification Enterprise Scenarios', () => {
|
||||
refreshAuthenticationForNewIdentity: jest.fn(),
|
||||
clearCacheForNewIdentity: jest.fn(),
|
||||
updateBackgroundTaskIdentity: jest.fn(),
|
||||
|
||||
// Static Daily Reminder Methods
|
||||
scheduleDailyReminder: jest.fn(),
|
||||
cancelDailyReminder: jest.fn(),
|
||||
getScheduledReminders: jest.fn(),
|
||||
updateDailyReminder: jest.fn(),
|
||||
};
|
||||
plugin = new DailyNotification(mockPlugin);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user