DRP_Sajidaat/DRP-App/app/ImageViewerModal.tsx

182 lines
5.1 KiB
TypeScript

// ImageViewerModal.tsx
import React, { useState, useRef, useEffect } from 'react';
import {
Modal,
View,
Text,
Image,
TouchableOpacity,
StyleSheet,
Dimensions,
FlatList,
SafeAreaView,
Platform
} from 'react-native';
export interface ImageObject {
url: string;
note?: string;
}
const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
interface ImageViewerModalProps {
visible: boolean;
images: ImageObject[];
initialIndex?: number;
onClose: () => void;
}
const ImageViewerModal: React.FC<ImageViewerModalProps> = ({
visible,
images,
initialIndex = 0,
onClose,
}) => {
const [currentIndex, setCurrentIndex] = useState(initialIndex);
const flatListRef = useRef<FlatList<ImageObject>>(null);
// Effect to scroll to initialIndex when modal becomes visible or initialIndex prop changes
useEffect(() => {
if (visible && images && images.length > 0) {
const validInitialIndex = Math.max(0, Math.min(initialIndex, images.length - 1));
setCurrentIndex(validInitialIndex);
// setTimeout is a common workaround for FlatList not scrolling immediately
// to initialScrollIndex on mount or when data changes if modal was hidden.
setTimeout(() => {
flatListRef.current?.scrollToIndex({
animated: false,
index: validInitialIndex,
});
}, 100);
}
}, [visible, initialIndex, images]);
const onViewableItemsChanged = useRef(
({ viewableItems }: { viewableItems: Array<{ item: ImageObject; index: number | null }> }) => {
if (viewableItems.length > 0 && viewableItems[0].index !== null) {
setCurrentIndex(viewableItems[0].index);
}
}
).current;
const viewabilityConfig = useRef({ itemVisiblePercentThreshold: 50 }).current;
if (!images || images.length === 0) {
return null; // Don't render modal if no images
}
const renderImageItem = ({ item }: { item: ImageObject }) => (
<View style={styles.slide}>
<Image source={{ uri: item.url }} style={styles.largeImage} resizeMode="contain" />
</View>
);
return (
<Modal
animationType="slide"
transparent={false}
visible={visible}
onRequestClose={onClose} // For Android back button
>
<SafeAreaView style={styles.modalContainer}>
<FlatList
ref={flatListRef}
data={images}
renderItem={renderImageItem}
keyExtractor={(item, index) => `${item.url}-${index}`} // Ensure unique key
horizontal
pagingEnabled
showsHorizontalScrollIndicator={false}
onViewableItemsChanged={onViewableItemsChanged}
viewabilityConfig={viewabilityConfig}
// initialScrollIndex={currentIndex} // Let useEffect handle initial scroll
getItemLayout={(data, index) => (
{ length: screenWidth, offset: screenWidth * index, index }
)}
/>
{images[currentIndex]?.note && (
<View style={styles.noteContainer}>
<Text style={styles.noteText}>{images[currentIndex].note}</Text>
</View>
)}
<TouchableOpacity style={styles.closeButton} onPress={onClose}>
<Text style={styles.closeButtonText}></Text>
</TouchableOpacity>
{images.length > 1 && (
<View style={styles.pagination}>
<Text style={styles.paginationText}>{currentIndex + 1} / {images.length}</Text>
</View>
)}
</SafeAreaView>
</Modal>
);
};
const styles = StyleSheet.create({
modalContainer: {
flex: 1,
backgroundColor: 'black',
},
slide: {
width: screenWidth,
height: '100%', // Make slide take full height of SafeAreaView content area
justifyContent: 'center',
alignItems: 'center',
},
largeImage: {
width: screenWidth, // Image takes full width of its slide
height: '80%', // Image takes 80% height of its slide, adjust as needed
},
noteContainer: {
position: 'absolute',
bottom: Platform.OS === 'ios' ? 20 : 40, // Adjust for different OS safe areas or tab bars if modal is within tabs
left: 0,
right: 0,
paddingHorizontal: 20,
paddingVertical: 10,
backgroundColor: 'rgba(0,0,0,0.7)',
},
noteText: {
color: 'white',
textAlign: 'center',
fontSize: 16,
},
closeButton: {
position: 'absolute',
top: Platform.OS === 'ios' ? 50 : 20, // Safer position for status bar, adjust if using custom header
right: 15,
backgroundColor: 'rgba(0,0,0,0.5)',
width: 40,
height: 40,
borderRadius: 20,
justifyContent: 'center',
alignItems: 'center',
zIndex: 10, // Ensure it's above other elements
},
closeButtonText: {
color: 'white',
fontSize: 18,
fontWeight: 'bold',
lineHeight: 20, // Helps center the X
},
pagination: {
position: 'absolute',
top: Platform.OS === 'ios' ? 55 : 25,
left: 15,
backgroundColor: 'rgba(0,0,0,0.5)',
paddingHorizontal: 10,
paddingVertical: 5,
borderRadius: 15,
zIndex: 10,
},
paginationText: {
color: 'white',
fontSize: 14,
},
});
export default ImageViewerModal;