/* * File: DicomViewer.tsx * Description: DICOM viewer component using WebView for medical imaging * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. */ import React, { useRef, useEffect, useState } from 'react'; import { WebView, WebViewMessageEvent } from 'react-native-webview'; import { Platform, View, Text, StyleSheet, ActivityIndicator, TouchableOpacity } from 'react-native'; // Interface for component props interface DicomViewerProps { dicomUrl: string; onError?: (error: string) => void; onLoad?: () => void; debugMode?: boolean; } // Interface for WebView reference interface WebViewRef { postMessage: (message: string) => void; reload: () => void; } export default function DicomViewer({ dicomUrl, onError, onLoad, debugMode = false }: DicomViewerProps): React.ReactElement { const webViewRef = useRef(null); const [isLoading, setIsLoading] = useState(true); const [hasError, setHasError] = useState(false); const [debugInfo, setDebugInfo] = useState([]); const [webViewReady, setWebViewReady] = useState(false); // Debug logging function const debugLog = (message: string) => { if (debugMode) { const timestamp = new Date().toLocaleTimeString(); const logMessage = `[${timestamp}] ${message}`; console.log(logMessage); setDebugInfo(prev => [...prev.slice(-9), logMessage]); // Keep last 10 messages } }; // Handle WebView load events const handleLoadStart = () => { debugLog('WebView load started'); setIsLoading(true); setHasError(false); }; const handleLoadEnd = () => { debugLog('WebView load ended'); setIsLoading(false); setWebViewReady(true); onLoad?.(); }; const handleError = (error: any) => { debugLog(`WebView error: ${JSON.stringify(error)}`); setIsLoading(false); setHasError(true); onError?.(error?.nativeEvent?.description || 'Failed to load DICOM viewer'); }; const handleMessage = (event: WebViewMessageEvent) => { try { const message = event.nativeEvent.data; debugLog(`Message from WebView: ${message}`); // Try to parse JSON message if (typeof message === 'string') { try { const parsedMessage = JSON.parse(message); debugLog(`Parsed message: ${JSON.stringify(parsedMessage)}`); if (parsedMessage.type === 'error') { setHasError(true); onError?.(parsedMessage.message); } else if (parsedMessage.type === 'success') { setHasError(false); } } catch (parseError) { debugLog(`Failed to parse message as JSON: ${parseError}`); } } } catch (error) { debugLog(`Error handling WebView message: ${error}`); } }; // Send DICOM URL to WebView when component mounts or URL changes useEffect(() => { if (webViewRef.current && dicomUrl && webViewReady) { debugLog(`Sending DICOM URL to WebView: ${dicomUrl}`); // Wait a bit for WebView to be ready const timer = setTimeout(() => { if (webViewRef.current) { try { webViewRef.current.postMessage(dicomUrl); debugLog('DICOM URL sent successfully'); } catch (error) { debugLog(`Failed to send DICOM URL: ${error}`); } } }, 1000); return () => clearTimeout(timer); } }, [dicomUrl, webViewReady]); // Reload WebView if there's an error const handleRetry = () => { debugLog('Retrying WebView load'); if (webViewRef.current) { setHasError(false); setIsLoading(true); setWebViewReady(false); webViewRef.current.reload(); } }; // Clear debug info const clearDebugInfo = () => { setDebugInfo([]); }; return ( ( Loading DICOM Viewer... )} /> {hasError && ( Failed to load DICOM viewer URL: {dicomUrl} Tap to retry )} {debugMode && ( Debug Info Clear {debugInfo.map((info, index) => ( {info} ))} WebView Ready: {webViewReady ? 'Yes' : 'No'} Loading: {isLoading ? 'Yes' : 'No'} Error: {hasError ? 'Yes' : 'No'} )} ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#000', }, webview: { flex: 1, backgroundColor: '#000', }, loadingContainer: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center', backgroundColor: '#000', }, loadingText: { color: '#FFF', marginTop: 16, fontSize: 16, }, errorContainer: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center', backgroundColor: '#000', padding: 20, }, errorText: { color: '#F44336', fontSize: 18, textAlign: 'center', marginBottom: 16, fontWeight: '600', }, errorDetails: { color: '#FF9800', fontSize: 14, textAlign: 'center', marginBottom: 20, fontFamily: 'monospace', }, retryButton: { backgroundColor: '#2196F3', paddingHorizontal: 24, paddingVertical: 12, borderRadius: 8, }, retryButtonText: { color: '#FFFFFF', fontSize: 16, fontWeight: '600', }, debugContainer: { position: 'absolute', top: 10, right: 10, backgroundColor: 'rgba(0,0,0,0.9)', borderRadius: 8, padding: 10, maxWidth: 300, maxHeight: 400, }, debugHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8, }, debugTitle: { color: '#FFFFFF', fontSize: 14, fontWeight: '600', }, clearButton: { color: '#2196F3', fontSize: 12, textDecorationLine: 'underline', }, debugContent: { maxHeight: 200, }, debugText: { color: '#FFFFFF', fontSize: 10, fontFamily: 'monospace', marginBottom: 2, }, debugStatus: { marginTop: 8, paddingTop: 8, borderTopColor: '#333', borderTopWidth: 1, }, debugStatusText: { color: '#CCC', fontSize: 10, marginBottom: 2, }, }); /* * End of File: DicomViewer.tsx * Design & Developed by Tech4Biz Solutions * Copyright (c) Spurrin Innovations. All rights reserved. */