202 lines
5.1 KiB
TypeScript
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.
|
|
*/
|