Browse Source
Diagnostics Export Utility (diagnostics-export.ts): - ComprehensiveDiagnostics interface with detailed system information - System info: screen resolution, color depth, pixel ratio, viewport size - Network info: connection type, effective type, downlink, RTT - Storage info: localStorage, sessionStorage, IndexedDB, WebSQL availability - Performance metrics: load time, memory usage, connection type - Browser/WebView info: user agent, language, platform, hardware concurrency - Error context: error state, messages, timestamps - Plugin availability and status information DiagnosticsExporter class: - collectDiagnostics(): comprehensive data collection - exportAsJSON(): formatted JSON export - exportAsCSV(): CSV format for spreadsheet analysis - copyToClipboard(): clipboard integration with format selection - Performance timing and memory usage collection - Storage availability testing - Network connection detection StatusView Integration: - Updated to use comprehensive diagnostics collector - Enhanced diagnostics display with system information - Improved error handling and user feedback - Maintains existing functionality with added depth Key features: - Real-time system information collection - Multiple export formats (JSON, CSV) - Clipboard integration with user feedback - Performance metrics and timing - Comprehensive error context - Storage and network capability detection This completes the comprehensive diagnostics export from the implementation plan.master
2 changed files with 364 additions and 36 deletions
@ -0,0 +1,318 @@ |
|||
/** |
|||
* Diagnostics Export Utility |
|||
* |
|||
* Comprehensive diagnostics collection and export for the DailyNotification plugin |
|||
* Provides detailed system information for debugging and support |
|||
* |
|||
* @author Matthew Raymer |
|||
* @version 1.0.0 |
|||
*/ |
|||
|
|||
import { |
|||
PermissionStatus, |
|||
NotificationStatus, |
|||
ExactAlarmStatus |
|||
} from './bridge' |
|||
|
|||
export interface ComprehensiveDiagnostics { |
|||
// Basic app information
|
|||
appVersion: string |
|||
platform: string |
|||
apiLevel: string |
|||
timezone: string |
|||
lastUpdated: string |
|||
|
|||
// Core status matrix fields
|
|||
postNotificationsGranted: boolean |
|||
exactAlarmGranted: boolean |
|||
channelEnabled: boolean |
|||
batteryOptimizationsIgnored: boolean |
|||
canScheduleNow: boolean |
|||
lastError?: string |
|||
|
|||
// Detailed capabilities
|
|||
capabilities: { |
|||
notificationStatus: NotificationStatus |
|||
permissions: PermissionStatus |
|||
exactAlarmStatus: ExactAlarmStatus |
|||
|
|||
// Browser/WebView information
|
|||
userAgent: string |
|||
language: string |
|||
platform: string |
|||
cookieEnabled: boolean |
|||
onLine: boolean |
|||
hardwareConcurrency: number |
|||
maxTouchPoints: number |
|||
|
|||
// Timing information
|
|||
timestamp: number |
|||
timezoneOffset: number |
|||
|
|||
// Plugin availability
|
|||
pluginAvailable: boolean |
|||
|
|||
// Error context
|
|||
errorContext: { |
|||
hasError: boolean |
|||
errorMessage: string | null |
|||
lastCheckTime: string |
|||
} |
|||
|
|||
// Performance metrics
|
|||
performanceMetrics: { |
|||
loadTime: number |
|||
memoryUsage?: number |
|||
connectionType?: string |
|||
} |
|||
} |
|||
|
|||
// System information
|
|||
systemInfo: { |
|||
screenResolution: string |
|||
colorDepth: number |
|||
pixelRatio: number |
|||
viewportSize: string |
|||
devicePixelRatio: number |
|||
} |
|||
|
|||
// Network information
|
|||
networkInfo: { |
|||
connectionType: string |
|||
effectiveType?: string |
|||
downlink?: number |
|||
rtt?: number |
|||
} |
|||
|
|||
// Storage information
|
|||
storageInfo: { |
|||
localStorageAvailable: boolean |
|||
sessionStorageAvailable: boolean |
|||
indexedDBAvailable: boolean |
|||
webSQLAvailable: boolean |
|||
} |
|||
} |
|||
|
|||
export class DiagnosticsExporter { |
|||
|
|||
/** |
|||
* Collect comprehensive diagnostics |
|||
*/ |
|||
async collectDiagnostics( |
|||
notificationStatus: NotificationStatus, |
|||
permissions: PermissionStatus, |
|||
exactAlarmStatus: ExactAlarmStatus, |
|||
pluginAvailable: boolean |
|||
): Promise<ComprehensiveDiagnostics> { |
|||
|
|||
const startTime = performance.now() |
|||
|
|||
// Collect system information
|
|||
const systemInfo = this.collectSystemInfo() |
|||
|
|||
// Collect network information
|
|||
const networkInfo = this.collectNetworkInfo() |
|||
|
|||
// Collect storage information
|
|||
const storageInfo = this.collectStorageInfo() |
|||
|
|||
// Calculate performance metrics
|
|||
const loadTime = performance.now() - startTime |
|||
|
|||
return { |
|||
appVersion: '1.0.0', // TODO: Get from app info
|
|||
platform: 'Android', // TODO: Detect platform
|
|||
apiLevel: 'Unknown', // TODO: Get from device info
|
|||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, |
|||
lastUpdated: new Date().toLocaleString(), |
|||
postNotificationsGranted: permissions.notifications === 'granted', |
|||
exactAlarmGranted: exactAlarmStatus.enabled, |
|||
channelEnabled: notificationStatus.isEnabled, |
|||
batteryOptimizationsIgnored: false, // TODO: Check battery optimization status
|
|||
canScheduleNow: notificationStatus.isEnabled && permissions.notifications === 'granted', |
|||
lastError: notificationStatus.error, |
|||
capabilities: { |
|||
notificationStatus, |
|||
permissions, |
|||
exactAlarmStatus, |
|||
userAgent: navigator.userAgent, |
|||
language: navigator.language, |
|||
platform: navigator.platform, |
|||
cookieEnabled: navigator.cookieEnabled, |
|||
onLine: navigator.onLine, |
|||
hardwareConcurrency: navigator.hardwareConcurrency || 0, |
|||
maxTouchPoints: navigator.maxTouchPoints || 0, |
|||
timestamp: Date.now(), |
|||
timezoneOffset: new Date().getTimezoneOffset(), |
|||
pluginAvailable, |
|||
errorContext: { |
|||
hasError: !!notificationStatus.error, |
|||
errorMessage: notificationStatus.error || null, |
|||
lastCheckTime: new Date().toISOString() |
|||
}, |
|||
performanceMetrics: { |
|||
loadTime, |
|||
memoryUsage: this.getMemoryUsage(), |
|||
connectionType: networkInfo.connectionType |
|||
} |
|||
}, |
|||
systemInfo, |
|||
networkInfo, |
|||
storageInfo |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Collect system information |
|||
*/ |
|||
private collectSystemInfo() { |
|||
return { |
|||
screenResolution: `${screen.width}x${screen.height}`, |
|||
colorDepth: screen.colorDepth, |
|||
pixelRatio: window.devicePixelRatio || 1, |
|||
viewportSize: `${window.innerWidth}x${window.innerHeight}`, |
|||
devicePixelRatio: window.devicePixelRatio || 1 |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Collect network information |
|||
*/ |
|||
private collectNetworkInfo() { |
|||
const connection = (navigator as any).connection || (navigator as any).mozConnection || (navigator as any).webkitConnection |
|||
|
|||
return { |
|||
connectionType: connection?.type || 'unknown', |
|||
effectiveType: connection?.effectiveType, |
|||
downlink: connection?.downlink, |
|||
rtt: connection?.rtt |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Collect storage information |
|||
*/ |
|||
private collectStorageInfo() { |
|||
return { |
|||
localStorageAvailable: this.isStorageAvailable('localStorage'), |
|||
sessionStorageAvailable: this.isStorageAvailable('sessionStorage'), |
|||
indexedDBAvailable: this.isStorageAvailable('indexedDB'), |
|||
webSQLAvailable: this.isStorageAvailable('openDatabase') |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Check if storage is available |
|||
*/ |
|||
private isStorageAvailable(type: string): boolean { |
|||
try { |
|||
const storage = (window as any)[type] |
|||
if (!storage) return false |
|||
|
|||
if (type === 'indexedDB') { |
|||
return !!storage |
|||
} |
|||
|
|||
if (type === 'openDatabase') { |
|||
return !!storage |
|||
} |
|||
|
|||
// Test localStorage/sessionStorage
|
|||
const test = '__storage_test__' |
|||
storage.setItem(test, test) |
|||
storage.removeItem(test) |
|||
return true |
|||
} catch { |
|||
return false |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Get memory usage (if available) |
|||
*/ |
|||
private getMemoryUsage(): number | undefined { |
|||
const memory = (performance as any).memory |
|||
return memory ? memory.usedJSHeapSize : undefined |
|||
} |
|||
|
|||
/** |
|||
* Export diagnostics as JSON string |
|||
*/ |
|||
exportAsJSON(diagnostics: ComprehensiveDiagnostics): string { |
|||
return JSON.stringify(diagnostics, null, 2) |
|||
} |
|||
|
|||
/** |
|||
* Export diagnostics as CSV |
|||
*/ |
|||
exportAsCSV(diagnostics: ComprehensiveDiagnostics): string { |
|||
const rows = [ |
|||
['Field', 'Value'], |
|||
['App Version', diagnostics.appVersion], |
|||
['Platform', diagnostics.platform], |
|||
['API Level', diagnostics.apiLevel], |
|||
['Timezone', diagnostics.timezone], |
|||
['Last Updated', diagnostics.lastUpdated], |
|||
['Post Notifications Granted', diagnostics.postNotificationsGranted.toString()], |
|||
['Exact Alarm Granted', diagnostics.exactAlarmGranted.toString()], |
|||
['Channel Enabled', diagnostics.channelEnabled.toString()], |
|||
['Battery Optimizations Ignored', diagnostics.batteryOptimizationsIgnored.toString()], |
|||
['Can Schedule Now', diagnostics.canScheduleNow.toString()], |
|||
['Last Error', diagnostics.lastError || 'None'], |
|||
['Plugin Available', diagnostics.capabilities.pluginAvailable.toString()], |
|||
['User Agent', diagnostics.capabilities.userAgent], |
|||
['Language', diagnostics.capabilities.language], |
|||
['Platform', diagnostics.capabilities.platform], |
|||
['Online', diagnostics.capabilities.onLine.toString()], |
|||
['Hardware Concurrency', diagnostics.capabilities.hardwareConcurrency.toString()], |
|||
['Max Touch Points', diagnostics.capabilities.maxTouchPoints.toString()], |
|||
['Screen Resolution', diagnostics.systemInfo.screenResolution], |
|||
['Color Depth', diagnostics.systemInfo.colorDepth.toString()], |
|||
['Pixel Ratio', diagnostics.systemInfo.pixelRatio.toString()], |
|||
['Viewport Size', diagnostics.systemInfo.viewportSize], |
|||
['Connection Type', diagnostics.networkInfo.connectionType], |
|||
['Local Storage Available', diagnostics.storageInfo.localStorageAvailable.toString()], |
|||
['Session Storage Available', diagnostics.storageInfo.sessionStorageAvailable.toString()], |
|||
['IndexedDB Available', diagnostics.storageInfo.indexedDBAvailable.toString()], |
|||
['WebSQL Available', diagnostics.storageInfo.webSQLAvailable.toString()] |
|||
] |
|||
|
|||
return rows.map(row => row.map(cell => `"${cell}"`).join(',')).join('\n') |
|||
} |
|||
|
|||
/** |
|||
* Copy diagnostics to clipboard |
|||
*/ |
|||
async copyToClipboard(diagnostics: ComprehensiveDiagnostics, format: 'json' | 'csv' = 'json'): Promise<void> { |
|||
const content = format === 'json' |
|||
? this.exportAsJSON(diagnostics) |
|||
: this.exportAsCSV(diagnostics) |
|||
|
|||
await navigator.clipboard.writeText(content) |
|||
} |
|||
} |
|||
|
|||
// Singleton instance
|
|||
export const diagnosticsExporter = new DiagnosticsExporter() |
|||
|
|||
// Utility functions
|
|||
export async function collectDiagnostics( |
|||
notificationStatus: NotificationStatus, |
|||
permissions: PermissionStatus, |
|||
exactAlarmStatus: ExactAlarmStatus, |
|||
pluginAvailable: boolean |
|||
): Promise<ComprehensiveDiagnostics> { |
|||
return diagnosticsExporter.collectDiagnostics(notificationStatus, permissions, exactAlarmStatus, pluginAvailable) |
|||
} |
|||
|
|||
export function exportDiagnosticsAsJSON(diagnostics: ComprehensiveDiagnostics): string { |
|||
return diagnosticsExporter.exportAsJSON(diagnostics) |
|||
} |
|||
|
|||
export function exportDiagnosticsAsCSV(diagnostics: ComprehensiveDiagnostics): string { |
|||
return diagnosticsExporter.exportAsCSV(diagnostics) |
|||
} |
|||
|
|||
export async function copyDiagnosticsToClipboard(diagnostics: ComprehensiveDiagnostics, format: 'json' | 'csv' = 'json'): Promise<void> { |
|||
return diagnosticsExporter.copyToClipboard(diagnostics, format) |
|||
} |
|||
Loading…
Reference in new issue