NeoScan_Physician/app/shared/components/DicomViewer.tsx
2025-08-22 00:24:24 +05:30

202 lines
5.1 KiB
TypeScript

/*
* 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;
}
// Interface for WebView reference
interface WebViewRef {
postMessage: (message: string) => void;
reload: () => void;
}
export default function DicomViewer({ dicomUrl, onError, onLoad }: DicomViewerProps): React.ReactElement {
const webViewRef = useRef<WebViewRef>(null);
const [hasError, setHasError] = useState(false);
const [webViewReady, setWebViewReady] = useState(false);
// Handle WebView load events
const handleLoadStart = () => {
setHasError(false);
};
const handleLoadEnd = () => {
setWebViewReady(true);
onLoad?.();
};
const handleError = (error: any) => {
setHasError(true);
onError?.(error?.nativeEvent?.description || 'Failed to load DICOM viewer');
};
const handleMessage = (event: WebViewMessageEvent) => {
try {
const message = event.nativeEvent.data;
// Try to parse JSON message
if (typeof message === 'string') {
try {
const parsedMessage = JSON.parse(message);
if (parsedMessage.type === 'error') {
setHasError(true);
onError?.(parsedMessage.message);
} else if (parsedMessage.type === 'success') {
setHasError(false);
}
} catch (parseError) {
// Failed to parse message as JSON
}
}
} catch (error) {
// Error handling WebView message
}
};
// Send DICOM URL to WebView when component mounts or URL changes
useEffect(() => {
if (webViewRef.current && dicomUrl && webViewReady) {
// Wait a bit for WebView to be ready
const timer = setTimeout(() => {
if (webViewRef.current) {
try {
// Send the URL directly as a string message
webViewRef.current.postMessage(dicomUrl);
// Also try sending as a structured message
setTimeout(() => {
if (webViewRef.current) {
const structuredMessage = JSON.stringify({
type: 'loadDicom',
data: dicomUrl
});
webViewRef.current.postMessage(structuredMessage);
}
}, 500);
} catch (error) {
// Failed to send DICOM URL
}
}
}, 1000);
return () => clearTimeout(timer);
}
}, [dicomUrl, webViewReady]);
// Reload WebView if there's an error
const handleRetry = () => {
if (webViewRef.current) {
setHasError(false);
setWebViewReady(false);
webViewRef.current.reload();
}
};
return (
<View style={styles.container}>
<WebView
ref={webViewRef as any}
source={{ uri: 'file:///android_asset/dicom-viewer.html' }}
originWhitelist={['*']}
javaScriptEnabled
domStorageEnabled
allowFileAccess
allowUniversalAccessFromFileURLs
allowFileAccessFromFileURLs
onLoadStart={handleLoadStart}
onLoadEnd={handleLoadEnd}
onError={handleError}
onMessage={handleMessage}
style={styles.webview}
mixedContentMode="always"
/>
{hasError && (
<View style={styles.errorContainer}>
<Text style={styles.errorText}>Failed to load DICOM viewer</Text>
<Text style={styles.errorDetails}>
URL: {dicomUrl}
</Text>
<TouchableOpacity style={styles.retryButton} onPress={handleRetry}>
<Text style={styles.retryButtonText}>Tap to retry</Text>
</TouchableOpacity>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000',
},
webview: {
flex: 1,
backgroundColor: '#000',
},
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',
},
});
/*
* End of File: DicomViewer.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/