Files
daily-notification-plugin/www/index.html
Matthew Raymer 8ec63a7876 feat(www): show prefetch and notification times in schedule success message
Update www/index.html scheduleNotification() function to calculate and display
both prefetch time (5 minutes before) and notification time in the success message,
matching the behavior added to the development app assets version.

This provides users with clear visibility into when the PBS prefetch will run
and when the notification will actually fire.
2025-10-30 07:19:54 +00:00

644 lines
28 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>DailyNotification Plugin Test</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: white;
}
.container {
max-width: 800px;
margin: 0 auto;
}
h1 {
text-align: center;
margin-bottom: 30px;
font-size: 2.5em;
}
.section {
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
padding: 20px;
margin: 20px 0;
}
.section h2 {
margin-top: 0;
color: #ffd700;
}
.button {
background: rgba(255, 255, 255, 0.2);
border: 2px solid rgba(255, 255, 255, 0.3);
color: white;
padding: 12px 24px;
margin: 8px;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
display: inline-block;
}
.button:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.status {
margin-top: 15px;
padding: 15px;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
font-family: monospace;
font-size: 12px;
white-space: pre-wrap;
max-height: 200px;
overflow-y: auto;
}
.success { color: #4CAF50; }
.error { color: #f44336; }
.warning { color: #ff9800; }
.info { color: #2196F3; }
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 15px 0;
}
.input-group {
margin: 10px 0;
}
.input-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.input-group input, .input-group select {
width: 100%;
padding: 8px;
border-radius: 5px;
border: 1px solid rgba(255, 255, 255, 0.3);
background: rgba(255, 255, 255, 0.1);
color: white;
}
.input-group input::placeholder {
color: rgba(255, 255, 255, 0.7);
}
</style>
</head>
<body>
<div class="container">
<h1>🔔 DailyNotification Plugin Test</h1>
<!-- Plugin Status Section -->
<div class="section">
<h2>📊 Plugin Status</h2>
<div class="grid">
<button class="button" onclick="checkPluginAvailability()">Check Availability</button>
<button class="button" onclick="getNotificationStatus()">Get Status</button>
<button class="button" onclick="checkPermissions()">Check Permissions</button>
<button class="button" onclick="getBatteryStatus()">Battery Status</button>
</div>
<div id="status" class="status">Ready to test...</div>
</div>
<!-- Permission Management Section -->
<div class="section">
<h2>🔐 Permission Management</h2>
<div class="grid">
<button class="button" onclick="requestPermissions()">Request Permissions</button>
<button class="button" onclick="requestExactAlarmPermission()">Request Exact Alarm</button>
<button class="button" onclick="openExactAlarmSettings()">Open Settings</button>
<button class="button" onclick="requestBatteryOptimizationExemption()">Battery Exemption</button>
</div>
</div>
<!-- Notification Scheduling Section -->
<div class="section">
<h2>⏰ Notification Scheduling</h2>
<div class="input-group">
<label for="notificationUrl">Content URL:</label>
<input type="text" id="notificationUrl" placeholder="https://api.example.com/daily-content" value="https://api.example.com/daily-content">
</div>
<div class="input-group">
<label for="notificationTime">Schedule Time:</label>
<input type="time" id="notificationTime" value="09:00">
</div>
<div class="input-group">
<label for="notificationTitle">Title:</label>
<input type="text" id="notificationTitle" placeholder="Daily Notification" value="Daily Notification">
</div>
<div class="input-group">
<label for="notificationBody">Body:</label>
<input type="text" id="notificationBody" placeholder="Your daily content is ready!" value="Your daily content is ready!">
</div>
<div class="grid">
<button class="button" onclick="scheduleNotification()">Schedule Notification</button>
<button class="button" onclick="cancelAllNotifications()">Cancel All</button>
<button class="button" onclick="getLastNotification()">Get Last</button>
</div>
</div>
<!-- Configuration Section -->
<div class="section">
<h2>⚙️ Plugin Configuration</h2>
<div class="input-group">
<label for="configUrl">Fetch URL:</label>
<input type="text" id="configUrl" placeholder="https://api.example.com/content" value="https://api.example.com/content">
</div>
<div class="input-group">
<label for="configTime">Schedule Time:</label>
<input type="time" id="configTime" value="09:00">
</div>
<div class="input-group">
<label for="configRetryCount">Retry Count:</label>
<input type="number" id="configRetryCount" value="3" min="0" max="10">
</div>
<div class="grid">
<button class="button" onclick="configurePlugin()">Configure Plugin</button>
<button class="button" onclick="updateSettings()">Update Settings</button>
</div>
</div>
<!-- Advanced Features Section -->
<div class="section">
<h2>🚀 Advanced Features</h2>
<div class="grid">
<button class="button" onclick="getExactAlarmStatus()">Exact Alarm Status</button>
<button class="button" onclick="getRebootRecoveryStatus()">Reboot Recovery</button>
<button class="button" onclick="getRollingWindowStats()">Rolling Window</button>
<button class="button" onclick="maintainRollingWindow()">Maintain Window</button>
<button class="button" onclick="getContentCache()">Content Cache</button>
<button class="button" onclick="clearContentCache()">Clear Cache</button>
<button class="button" onclick="getContentHistory()">Content History</button>
<button class="button" onclick="getDualScheduleStatus()">Dual Schedule</button>
</div>
</div>
</div>
<script>
console.log('🔔 DailyNotification Plugin Test Interface Loading...');
// Global variables
let plugin = null;
let isPluginAvailable = false;
// Initialize plugin on page load
document.addEventListener('DOMContentLoaded', async function() {
console.log('📱 DOM loaded, initializing plugin...');
await initializePlugin();
});
// Initialize the real DailyNotification plugin
async function initializePlugin() {
try {
// Try to access the real plugin through Capacitor
if (window.Capacitor && window.Capacitor.Plugins && window.Capacitor.Plugins.DailyNotification) {
plugin = window.Capacitor.Plugins.DailyNotification;
isPluginAvailable = true;
console.log('✅ Real DailyNotification plugin found!');
updateStatus('success', '✅ Real DailyNotification plugin loaded successfully!');
} else {
// Fallback to mock for development
console.log('⚠️ Real plugin not available, using mock for development');
plugin = createMockPlugin();
isPluginAvailable = false;
updateStatus('warning', '⚠️ Using mock plugin (real plugin not available)');
}
} catch (error) {
console.error('❌ Plugin initialization failed:', error);
updateStatus('error', `❌ Plugin initialization failed: ${error.message}`);
}
}
// Create mock plugin for development/testing
function createMockPlugin() {
return {
configure: async (options) => {
console.log('Mock configure called with:', options);
return Promise.resolve();
},
getNotificationStatus: async () => {
return Promise.resolve({
isEnabled: true,
isScheduled: true,
lastNotificationTime: Date.now() - 86400000,
nextNotificationTime: Date.now() + 3600000,
pending: 1,
settings: { url: 'https://api.example.com/content', time: '09:00' },
error: null
});
},
checkPermissions: async () => {
return Promise.resolve({
notifications: 'granted',
backgroundRefresh: 'granted',
alert: true,
badge: true,
sound: true
});
},
requestPermissions: async () => {
return Promise.resolve({
notifications: 'granted',
backgroundRefresh: 'granted',
alert: true,
badge: true,
sound: true
});
},
scheduleDailyNotification: async (options) => {
console.log('Mock scheduleDailyNotification called with:', options);
return Promise.resolve();
},
cancelAllNotifications: async () => {
console.log('Mock cancelAllNotifications called');
return Promise.resolve();
},
getLastNotification: async () => {
return Promise.resolve({
id: 'mock-123',
title: 'Mock Notification',
body: 'This is a mock notification',
timestamp: Date.now() - 3600000,
url: 'https://example.com'
});
},
getBatteryStatus: async () => {
return Promise.resolve({
level: 85,
isCharging: false,
powerState: 1,
isOptimizationExempt: false
});
},
getExactAlarmStatus: async () => {
return Promise.resolve({
supported: true,
enabled: true,
canSchedule: true,
fallbackWindow: '±15 minutes'
});
},
requestExactAlarmPermission: async () => {
console.log('Mock requestExactAlarmPermission called');
return Promise.resolve();
},
openExactAlarmSettings: async () => {
console.log('Mock openExactAlarmSettings called');
return Promise.resolve();
},
requestBatteryOptimizationExemption: async () => {
console.log('Mock requestBatteryOptimizationExemption called');
return Promise.resolve();
},
getRebootRecoveryStatus: async () => {
return Promise.resolve({
inProgress: false,
lastRecoveryTime: Date.now() - 86400000,
timeSinceLastRecovery: 86400000,
recoveryNeeded: false
});
},
getRollingWindowStats: async () => {
return Promise.resolve({
stats: 'Window: 7 days, Notifications: 5, Success rate: 100%',
maintenanceNeeded: false,
timeUntilNextMaintenance: 3600000
});
},
maintainRollingWindow: async () => {
console.log('Mock maintainRollingWindow called');
return Promise.resolve();
},
getContentCache: async () => {
return Promise.resolve({
'cache-key-1': { content: 'Mock cached content', timestamp: Date.now() },
'cache-key-2': { content: 'Another mock item', timestamp: Date.now() - 3600000 }
});
},
clearContentCache: async () => {
console.log('Mock clearContentCache called');
return Promise.resolve();
},
getContentHistory: async () => {
return Promise.resolve([
{ id: '1', timestamp: Date.now() - 86400000, success: true, content: 'Mock content 1' },
{ id: '2', timestamp: Date.now() - 172800000, success: true, content: 'Mock content 2' }
]);
},
getDualScheduleStatus: async () => {
return Promise.resolve({
isActive: true,
contentSchedule: { nextRun: Date.now() + 3600000, isEnabled: true },
userSchedule: { nextRun: Date.now() + 7200000, isEnabled: true },
lastContentFetch: Date.now() - 3600000,
lastUserNotification: Date.now() - 7200000
});
}
};
}
// Utility function to update status display
function updateStatus(type, message) {
const statusEl = document.getElementById('status');
statusEl.className = `status ${type}`;
statusEl.textContent = message;
console.log(`[${type.toUpperCase()}] ${message}`);
}
// Plugin availability check
async function checkPluginAvailability() {
updateStatus('info', '🔍 Checking plugin availability...');
try {
if (plugin) {
updateStatus('success', `✅ Plugin available: ${isPluginAvailable ? 'Real plugin' : 'Mock plugin'}`);
} else {
updateStatus('error', '❌ Plugin not available');
}
} catch (error) {
updateStatus('error', `❌ Availability check failed: ${error.message}`);
}
}
// Get notification status
async function getNotificationStatus() {
updateStatus('info', '📊 Getting notification status...');
try {
const status = await plugin.getNotificationStatus();
updateStatus('success', `📊 Status: ${JSON.stringify(status, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Status check failed: ${error.message}`);
}
}
// Check permissions
async function checkPermissions() {
updateStatus('info', '🔐 Checking permissions...');
try {
const permissions = await plugin.checkPermissions();
updateStatus('success', `🔐 Permissions: ${JSON.stringify(permissions, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Permission check failed: ${error.message}`);
}
}
// Request permissions
async function requestPermissions() {
updateStatus('info', '🔐 Requesting permissions...');
try {
const result = await plugin.requestPermissions();
updateStatus('success', `🔐 Permission result: ${JSON.stringify(result, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Permission request failed: ${error.message}`);
}
}
// Get battery status
async function getBatteryStatus() {
updateStatus('info', '🔋 Getting battery status...');
try {
const battery = await plugin.getBatteryStatus();
updateStatus('success', `🔋 Battery: ${JSON.stringify(battery, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Battery check failed: ${error.message}`);
}
}
// Schedule notification
async function scheduleNotification() {
updateStatus('info', '⏰ Scheduling notification...');
try {
const timeInput = document.getElementById('notificationTime').value;
const [hours, minutes] = timeInput.split(':');
const now = new Date();
const scheduledTime = new Date();
scheduledTime.setHours(parseInt(hours), parseInt(minutes), 0, 0);
// If scheduled time is in the past, schedule for tomorrow
if (scheduledTime <= now) {
scheduledTime.setDate(scheduledTime.getDate() + 1);
}
// Calculate prefetch time (5 minutes before notification)
const prefetchTime = new Date(scheduledTime.getTime() - 300000); // 5 minutes
const prefetchTimeReadable = prefetchTime.toLocaleTimeString();
const notificationTimeReadable = scheduledTime.toLocaleTimeString();
const prefetchTimeString = prefetchTime.getHours().toString().padStart(2, '0') + ':' +
prefetchTime.getMinutes().toString().padStart(2, '0');
const notificationTimeString = scheduledTime.getHours().toString().padStart(2, '0') + ':' +
scheduledTime.getMinutes().toString().padStart(2, '0');
const options = {
url: document.getElementById('notificationUrl').value,
time: timeInput,
title: document.getElementById('notificationTitle').value,
body: document.getElementById('notificationBody').value,
sound: true,
priority: 'high'
};
await plugin.scheduleDailyNotification(options);
updateStatus('success', `✅ Notification scheduled!<br>` +
`📥 Prefetch: ${prefetchTimeReadable} (${prefetchTimeString})<br>` +
`🔔 Notification: ${notificationTimeReadable} (${notificationTimeString})`);
} catch (error) {
updateStatus('error', `❌ Scheduling failed: ${error.message}`);
}
}
// Cancel all notifications
async function cancelAllNotifications() {
updateStatus('info', '❌ Cancelling all notifications...');
try {
await plugin.cancelAllNotifications();
updateStatus('success', '❌ All notifications cancelled');
} catch (error) {
updateStatus('error', `❌ Cancel failed: ${error.message}`);
}
}
// Get last notification
async function getLastNotification() {
updateStatus('info', '📱 Getting last notification...');
try {
const notification = await plugin.getLastNotification();
updateStatus('success', `📱 Last notification: ${JSON.stringify(notification, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Get last notification failed: ${error.message}`);
}
}
// Configure plugin
async function configurePlugin() {
updateStatus('info', '⚙️ Configuring plugin...');
try {
const config = {
fetchUrl: document.getElementById('configUrl').value,
scheduleTime: document.getElementById('configTime').value,
retryCount: parseInt(document.getElementById('configRetryCount').value),
enableNotifications: true,
offlineFallback: true
};
await plugin.configure(config);
updateStatus('success', `⚙️ Plugin configured: ${JSON.stringify(config, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Configuration failed: ${error.message}`);
}
}
// Update settings
async function updateSettings() {
updateStatus('info', '⚙️ Updating settings...');
try {
const settings = {
url: document.getElementById('configUrl').value,
time: document.getElementById('configTime').value,
retryCount: parseInt(document.getElementById('configRetryCount').value),
sound: true,
priority: 'high'
};
await plugin.updateSettings(settings);
updateStatus('success', `⚙️ Settings updated: ${JSON.stringify(settings, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Settings update failed: ${error.message}`);
}
}
// Get exact alarm status
async function getExactAlarmStatus() {
updateStatus('info', '⏰ Getting exact alarm status...');
try {
const status = await plugin.getExactAlarmStatus();
updateStatus('success', `⏰ Exact alarm status: ${JSON.stringify(status, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Exact alarm check failed: ${error.message}`);
}
}
// Request exact alarm permission
async function requestExactAlarmPermission() {
updateStatus('info', '⏰ Requesting exact alarm permission...');
try {
await plugin.requestExactAlarmPermission();
updateStatus('success', '⏰ Exact alarm permission requested');
} catch (error) {
updateStatus('error', `❌ Exact alarm permission request failed: ${error.message}`);
}
}
// Open exact alarm settings
async function openExactAlarmSettings() {
updateStatus('info', '⚙️ Opening exact alarm settings...');
try {
await plugin.openExactAlarmSettings();
updateStatus('success', '⚙️ Exact alarm settings opened');
} catch (error) {
updateStatus('error', `❌ Open settings failed: ${error.message}`);
}
}
// Request battery optimization exemption
async function requestBatteryOptimizationExemption() {
updateStatus('info', '🔋 Requesting battery optimization exemption...');
try {
await plugin.requestBatteryOptimizationExemption();
updateStatus('success', '🔋 Battery optimization exemption requested');
} catch (error) {
updateStatus('error', `❌ Battery exemption request failed: ${error.message}`);
}
}
// Get reboot recovery status
async function getRebootRecoveryStatus() {
updateStatus('info', '🔄 Getting reboot recovery status...');
try {
const status = await plugin.getRebootRecoveryStatus();
updateStatus('success', `🔄 Reboot recovery status: ${JSON.stringify(status, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Reboot recovery check failed: ${error.message}`);
}
}
// Get rolling window stats
async function getRollingWindowStats() {
updateStatus('info', '📊 Getting rolling window stats...');
try {
const stats = await plugin.getRollingWindowStats();
updateStatus('success', `📊 Rolling window stats: ${JSON.stringify(stats, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Rolling window stats failed: ${error.message}`);
}
}
// Maintain rolling window
async function maintainRollingWindow() {
updateStatus('info', '🔧 Maintaining rolling window...');
try {
await plugin.maintainRollingWindow();
updateStatus('success', '🔧 Rolling window maintenance completed');
} catch (error) {
updateStatus('error', `❌ Rolling window maintenance failed: ${error.message}`);
}
}
// Get content cache
async function getContentCache() {
updateStatus('info', '💾 Getting content cache...');
try {
const cache = await plugin.getContentCache();
updateStatus('success', `💾 Content cache: ${JSON.stringify(cache, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Content cache check failed: ${error.message}`);
}
}
// Clear content cache
async function clearContentCache() {
updateStatus('info', '🗑️ Clearing content cache...');
try {
await plugin.clearContentCache();
updateStatus('success', '🗑️ Content cache cleared');
} catch (error) {
updateStatus('error', `❌ Clear cache failed: ${error.message}`);
}
}
// Get content history
async function getContentHistory() {
updateStatus('info', '📚 Getting content history...');
try {
const history = await plugin.getContentHistory();
updateStatus('success', `📚 Content history: ${JSON.stringify(history, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Content history check failed: ${error.message}`);
}
}
// Get dual schedule status
async function getDualScheduleStatus() {
updateStatus('info', '🔄 Getting dual schedule status...');
try {
const status = await plugin.getDualScheduleStatus();
updateStatus('success', `🔄 Dual schedule status: ${JSON.stringify(status, null, 2)}`);
} catch (error) {
updateStatus('error', `❌ Dual schedule check failed: ${error.message}`);
}
}
console.log('🔔 DailyNotification Plugin Test Interface Loaded Successfully!');
</script>
</body>
</html>