|
|
@ -1,7 +1,242 @@ |
|
|
|
import { Capacitor } from '@capacitor/core'; |
|
|
|
import { ConfigLoader, MockDailyNotificationService, TestLogger } from '../shared/config-loader'; |
|
|
|
|
|
|
|
// Test interface for TimeSafari Android integration
|
|
|
|
// Enhanced UI components for comprehensive testing
|
|
|
|
class PermissionManager { |
|
|
|
private container: HTMLElement; |
|
|
|
private dialogContainer: HTMLElement; |
|
|
|
|
|
|
|
constructor(container: HTMLElement, dialogContainer: HTMLElement) { |
|
|
|
this.container = container; |
|
|
|
this.dialogContainer = dialogContainer; |
|
|
|
} |
|
|
|
|
|
|
|
async updateStatus(): Promise<void> { |
|
|
|
// Mock permission status for testing
|
|
|
|
const mockStatus = { |
|
|
|
granted: true, |
|
|
|
notifications: 'granted' as const, |
|
|
|
backgroundRefresh: 'granted' as const |
|
|
|
}; |
|
|
|
|
|
|
|
this.renderStatus(mockStatus); |
|
|
|
} |
|
|
|
|
|
|
|
private renderStatus(status: any): void { |
|
|
|
const statusClass = status.granted ? 'status-granted' : 'status-denied'; |
|
|
|
const statusText = status.granted ? 'Granted' : 'Denied'; |
|
|
|
|
|
|
|
this.container.innerHTML = ` |
|
|
|
<div class="permission-status ${statusClass}"> |
|
|
|
<div class="status-indicator"> |
|
|
|
<span class="status-icon">${status.granted ? '✓' : '✗'}</span> |
|
|
|
<span class="status-text">${statusText}</span> |
|
|
|
</div> |
|
|
|
<div class="permission-details"> |
|
|
|
<div class="permission-item"> |
|
|
|
<span>Notifications:</span> |
|
|
|
<span class="${status.notifications === 'granted' ? 'granted' : 'denied'}"> |
|
|
|
${status.notifications} |
|
|
|
</span> |
|
|
|
</div> |
|
|
|
<div class="permission-item"> |
|
|
|
<span>Background Refresh:</span> |
|
|
|
<span class="${status.backgroundRefresh === 'granted' ? 'granted' : 'denied'}"> |
|
|
|
${status.backgroundRefresh} |
|
|
|
</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
`;
|
|
|
|
} |
|
|
|
|
|
|
|
showPermissionDialog(): void { |
|
|
|
const dialog = document.createElement('div'); |
|
|
|
dialog.className = 'dialog-overlay'; |
|
|
|
dialog.innerHTML = ` |
|
|
|
<div class="dialog-content"> |
|
|
|
<h2>Enable Daily Notifications</h2> |
|
|
|
<p>Get notified about new offers, projects, people, and items in your TimeSafari community.</p> |
|
|
|
<ul> |
|
|
|
<li>New offers directed to you</li> |
|
|
|
<li>Changes to your projects</li> |
|
|
|
<li>Updates from favorited people</li> |
|
|
|
<li>New items of interest</li> |
|
|
|
</ul> |
|
|
|
<div class="dialog-actions"> |
|
|
|
<button id="allow-permissions" class="btn-primary">Allow Notifications</button> |
|
|
|
<button id="deny-permissions" class="btn-secondary">Not Now</button> |
|
|
|
<button id="never-permissions" class="btn-tertiary">Never Ask Again</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
`;
|
|
|
|
|
|
|
|
this.dialogContainer.appendChild(dialog); |
|
|
|
|
|
|
|
dialog.querySelector('#allow-permissions')?.addEventListener('click', () => { |
|
|
|
this.hideDialog(); |
|
|
|
this.updateStatus(); |
|
|
|
}); |
|
|
|
|
|
|
|
dialog.querySelector('#deny-permissions')?.addEventListener('click', () => { |
|
|
|
this.hideDialog(); |
|
|
|
}); |
|
|
|
|
|
|
|
dialog.querySelector('#never-permissions')?.addEventListener('click', () => { |
|
|
|
this.hideDialog(); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
private hideDialog(): void { |
|
|
|
const dialog = this.dialogContainer.querySelector('.dialog-overlay'); |
|
|
|
if (dialog) { |
|
|
|
dialog.remove(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class SettingsPanel { |
|
|
|
private container: HTMLElement; |
|
|
|
|
|
|
|
constructor(container: HTMLElement) { |
|
|
|
this.container = container; |
|
|
|
} |
|
|
|
|
|
|
|
render(): void { |
|
|
|
this.container.innerHTML = ` |
|
|
|
<div class="settings-panel"> |
|
|
|
<div class="setting-group"> |
|
|
|
<label class="setting-label"> |
|
|
|
<input type="checkbox" id="enable-notifications" checked> |
|
|
|
Enable Daily Notifications |
|
|
|
</label> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="setting-group"> |
|
|
|
<label for="notification-time">Notification Time:</label> |
|
|
|
<input type="time" id="notification-time" value="09:00"> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="setting-group"> |
|
|
|
<label>Content Types:</label> |
|
|
|
<div class="checkbox-group"> |
|
|
|
<label><input type="checkbox" id="content-offers" checked> Offers</label> |
|
|
|
<label><input type="checkbox" id="content-projects" checked> Projects</label> |
|
|
|
<label><input type="checkbox" id="content-people" checked> People</label> |
|
|
|
<label><input type="checkbox" id="content-items" checked> Items</label> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="setting-group"> |
|
|
|
<label>Notification Preferences:</label> |
|
|
|
<div class="preference-grid"> |
|
|
|
<label> |
|
|
|
<input type="checkbox" id="sound-enabled" checked> |
|
|
|
Sound |
|
|
|
</label> |
|
|
|
<label> |
|
|
|
<input type="checkbox" id="vibration-enabled" checked> |
|
|
|
Vibration |
|
|
|
</label> |
|
|
|
<label> |
|
|
|
<select id="priority-level"> |
|
|
|
<option value="low">Low</option> |
|
|
|
<option value="normal" selected>Normal</option> |
|
|
|
<option value="high">High</option> |
|
|
|
</select> |
|
|
|
Priority |
|
|
|
</label> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="setting-actions"> |
|
|
|
<button id="save-settings" class="btn-primary">Save Settings</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
`;
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class StatusDashboard { |
|
|
|
private container: HTMLElement; |
|
|
|
|
|
|
|
constructor(container: HTMLElement) { |
|
|
|
this.container = container; |
|
|
|
} |
|
|
|
|
|
|
|
render(): void { |
|
|
|
this.container.innerHTML = ` |
|
|
|
<div class="status-dashboard"> |
|
|
|
<div class="status-grid"> |
|
|
|
<div class="status-item"> |
|
|
|
<div class="status-label">Overall Status</div> |
|
|
|
<div class="status-value active">Active</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="status-item"> |
|
|
|
<div class="status-label">Next Notification</div> |
|
|
|
<div class="status-value">2h 15m</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="status-item"> |
|
|
|
<div class="status-label">Last Outcome</div> |
|
|
|
<div class="status-value active">Success</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="status-item"> |
|
|
|
<div class="status-label">Cache Age</div> |
|
|
|
<div class="status-value">1h 23m</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="performance-metrics"> |
|
|
|
<h4>Performance</h4> |
|
|
|
<div class="metrics-grid"> |
|
|
|
<div class="metric-item"> |
|
|
|
<span class="metric-label">Success Rate:</span> |
|
|
|
<span class="metric-value">95%</span> |
|
|
|
</div> |
|
|
|
<div class="metric-item"> |
|
|
|
<span class="metric-label">Error Count:</span> |
|
|
|
<span class="metric-value">2</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
`;
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class ErrorDisplay { |
|
|
|
private container: HTMLElement; |
|
|
|
|
|
|
|
constructor(container: HTMLElement) { |
|
|
|
this.container = container; |
|
|
|
} |
|
|
|
|
|
|
|
showError(error: Error): void { |
|
|
|
this.container.innerHTML = ` |
|
|
|
<div class="error-display"> |
|
|
|
<div class="error-icon">⚠️</div> |
|
|
|
<div class="error-content"> |
|
|
|
<h3>Something went wrong</h3> |
|
|
|
<p class="error-message">${error.message}</p> |
|
|
|
<p class="error-code">Error Code: ${error.name}</p> |
|
|
|
</div> |
|
|
|
<div class="error-actions"> |
|
|
|
<button id="retry-action" class="btn-primary">Retry</button> |
|
|
|
<button id="reset-action" class="btn-secondary">Reset Configuration</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
`;
|
|
|
|
} |
|
|
|
|
|
|
|
hide(): void { |
|
|
|
this.container.innerHTML = ''; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Enhanced test interface for TimeSafari Android integration
|
|
|
|
class TimeSafariAndroidTestApp { |
|
|
|
private statusElement: HTMLElement; |
|
|
|
private logElement: HTMLElement; |
|
|
@ -9,24 +244,155 @@ class TimeSafariAndroidTestApp { |
|
|
|
private notificationService: MockDailyNotificationService; |
|
|
|
private logger: TestLogger; |
|
|
|
|
|
|
|
// UI Components
|
|
|
|
private permissionManager: PermissionManager; |
|
|
|
private settingsPanel: SettingsPanel; |
|
|
|
private statusDashboard: StatusDashboard; |
|
|
|
private errorDisplay: ErrorDisplay; |
|
|
|
|
|
|
|
constructor() { |
|
|
|
this.statusElement = document.getElementById('status')!; |
|
|
|
this.logElement = document.getElementById('log')!; |
|
|
|
this.configLoader = ConfigLoader.getInstance(); |
|
|
|
this.logger = new TestLogger('debug'); |
|
|
|
this.notificationService = new MockDailyNotificationService(this.configLoader.getConfig()); |
|
|
|
|
|
|
|
// Initialize UI components
|
|
|
|
this.permissionManager = new PermissionManager( |
|
|
|
document.getElementById('permission-status-container')!, |
|
|
|
document.getElementById('permission-dialog-container')! |
|
|
|
); |
|
|
|
this.settingsPanel = new SettingsPanel(document.getElementById('settings-container')!); |
|
|
|
this.statusDashboard = new StatusDashboard(document.getElementById('status-container')!); |
|
|
|
this.errorDisplay = new ErrorDisplay(document.getElementById('error-container')!); |
|
|
|
|
|
|
|
this.setupEventListeners(); |
|
|
|
this.log('TimeSafari Android Test app initialized'); |
|
|
|
this.initializeUI(); |
|
|
|
this.log('TimeSafari Android Test app initialized with enhanced UI'); |
|
|
|
} |
|
|
|
|
|
|
|
private setupEventListeners() { |
|
|
|
// Original test functionality
|
|
|
|
document.getElementById('configure')?.addEventListener('click', () => this.testConfigure()); |
|
|
|
document.getElementById('schedule')?.addEventListener('click', () => this.testSchedule()); |
|
|
|
document.getElementById('endorser-api')?.addEventListener('click', () => this.testEndorserAPI()); |
|
|
|
document.getElementById('callbacks')?.addEventListener('click', () => this.testCallbacks()); |
|
|
|
document.getElementById('status')?.addEventListener('click', () => this.testStatus()); |
|
|
|
document.getElementById('check-status')?.addEventListener('click', () => this.testStatus()); |
|
|
|
document.getElementById('performance')?.addEventListener('click', () => this.testPerformance()); |
|
|
|
document.getElementById('clear-log')?.addEventListener('click', () => this.clearLog()); |
|
|
|
|
|
|
|
// Enhanced UI functionality
|
|
|
|
document.getElementById('check-permissions')?.addEventListener('click', () => this.checkPermissions()); |
|
|
|
document.getElementById('request-permissions')?.addEventListener('click', () => this.requestPermissions()); |
|
|
|
document.getElementById('open-settings')?.addEventListener('click', () => this.openSettings()); |
|
|
|
document.getElementById('test-notification')?.addEventListener('click', () => this.testNotification()); |
|
|
|
document.getElementById('refresh-status')?.addEventListener('click', () => this.refreshStatus()); |
|
|
|
document.getElementById('battery-status')?.addEventListener('click', () => this.checkBatteryStatus()); |
|
|
|
document.getElementById('exact-alarm-status')?.addEventListener('click', () => this.checkExactAlarmStatus()); |
|
|
|
document.getElementById('reboot-recovery')?.addEventListener('click', () => this.checkRebootRecovery()); |
|
|
|
} |
|
|
|
|
|
|
|
private async initializeUI(): Promise<void> { |
|
|
|
try { |
|
|
|
// Initialize UI components
|
|
|
|
await this.permissionManager.updateStatus(); |
|
|
|
this.settingsPanel.render(); |
|
|
|
this.statusDashboard.render(); |
|
|
|
|
|
|
|
this.log('✅ Enhanced UI components initialized'); |
|
|
|
} catch (error) { |
|
|
|
this.log(`❌ UI initialization failed: ${error}`); |
|
|
|
this.errorDisplay.showError(error as Error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Enhanced UI methods
|
|
|
|
private async checkPermissions(): Promise<void> { |
|
|
|
try { |
|
|
|
this.log('Checking permissions...'); |
|
|
|
await this.permissionManager.updateStatus(); |
|
|
|
this.log('✅ Permission status updated'); |
|
|
|
} catch (error) { |
|
|
|
this.log(`❌ Permission check failed: ${error}`); |
|
|
|
this.errorDisplay.showError(error as Error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private async requestPermissions(): Promise<void> { |
|
|
|
try { |
|
|
|
this.log('Requesting permissions...'); |
|
|
|
this.permissionManager.showPermissionDialog(); |
|
|
|
this.log('✅ Permission dialog shown'); |
|
|
|
} catch (error) { |
|
|
|
this.log(`❌ Permission request failed: ${error}`); |
|
|
|
this.errorDisplay.showError(error as Error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private async openSettings(): Promise<void> { |
|
|
|
try { |
|
|
|
this.log('Opening settings...'); |
|
|
|
// Mock settings opening
|
|
|
|
this.log('✅ Settings opened (mock)'); |
|
|
|
} catch (error) { |
|
|
|
this.log(`❌ Settings open failed: ${error}`); |
|
|
|
this.errorDisplay.showError(error as Error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private async testNotification(): Promise<void> { |
|
|
|
try { |
|
|
|
this.log('Sending test notification...'); |
|
|
|
// Mock test notification
|
|
|
|
this.log('✅ Test notification sent (mock)'); |
|
|
|
} catch (error) { |
|
|
|
this.log(`❌ Test notification failed: ${error}`); |
|
|
|
this.errorDisplay.showError(error as Error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private async refreshStatus(): Promise<void> { |
|
|
|
try { |
|
|
|
this.log('Refreshing status...'); |
|
|
|
this.statusDashboard.render(); |
|
|
|
this.log('✅ Status refreshed'); |
|
|
|
} catch (error) { |
|
|
|
this.log(`❌ Status refresh failed: ${error}`); |
|
|
|
this.errorDisplay.showError(error as Error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private async checkBatteryStatus(): Promise<void> { |
|
|
|
try { |
|
|
|
this.log('Checking battery optimization status...'); |
|
|
|
// Mock battery status check
|
|
|
|
this.log('✅ Battery optimization: Not optimized (mock)'); |
|
|
|
} catch (error) { |
|
|
|
this.log(`❌ Battery status check failed: ${error}`); |
|
|
|
this.errorDisplay.showError(error as Error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private async checkExactAlarmStatus(): Promise<void> { |
|
|
|
try { |
|
|
|
this.log('Checking exact alarm permission...'); |
|
|
|
// Mock exact alarm status
|
|
|
|
this.log('✅ Exact alarm permission: Granted (mock)'); |
|
|
|
} catch (error) { |
|
|
|
this.log(`❌ Exact alarm check failed: ${error}`); |
|
|
|
this.errorDisplay.showError(error as Error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private async checkRebootRecovery(): Promise<void> { |
|
|
|
try { |
|
|
|
this.log('Checking reboot recovery status...'); |
|
|
|
// Mock reboot recovery status
|
|
|
|
this.log('✅ Reboot recovery: Active (mock)'); |
|
|
|
} catch (error) { |
|
|
|
this.log(`❌ Reboot recovery check failed: ${error}`); |
|
|
|
this.errorDisplay.showError(error as Error); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private async testConfigure() { |
|
|
|