import type { ReactElement } from 'react';
import { Modal, Pressable, StyleSheet, View } from 'react-native';
import { Image } from 'expo-image';
import { useVideoPlayer, VideoView } from 'expo-video';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
useAnimatedStyle,
useSharedValue,
withTiming,
} from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Icon } from '@/components';
export interface ViewerSource {
url: string;
isVideo: boolean;
}
interface Props {
source: ViewerSource | null;
onClose: () => void;
}
const ZoomableImage = ({ url }: { url: string }): ReactElement => {
const scale = useSharedValue(1);
const savedScale = useSharedValue(1);
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const pinch = Gesture.Pinch()
.onUpdate((event) => {
scale.value = Math.max(1, savedScale.value * event.scale);
})
.onEnd(() => {
savedScale.value = scale.value;
if (scale.value <= 1) {
translateX.value = withTiming(0);
translateY.value = withTiming(0);
}
});
const pan = Gesture.Pan()
.onUpdate((event) => {
if (scale.value > 1) {
translateX.value = event.translationX;
translateY.value = event.translationY;
}
});
const composed = Gesture.Simultaneous(pinch, pan);
const animatedStyle = useAnimatedStyle(() => ({
transform: [
{ translateX: translateX.value },
{ translateY: translateY.value },
{ scale: scale.value },
],
}));
return (
);
};
const ViewerVideo = ({ url }: { url: string }): ReactElement => {
const player = useVideoPlayer({ uri: url }, (instance) => {
instance.play();
});
return ;
};
// Full-screen image (pinch-zoom) / video overlay opened from a media bubble.
export const MediaViewer = ({ source, onClose }: Props): ReactElement => {
const insets = useSafeAreaInsets();
return (
{source !== null ? (
source.isVideo ? (
) : (
)
) : null}
);
};
const styles = StyleSheet.create({
backdrop: { flex: 1, backgroundColor: '#000' },
fill: { flex: 1, width: '100%', height: '100%' },
close: {
position: 'absolute',
right: 16,
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: 'rgba(0,0,0,0.5)',
alignItems: 'center',
justifyContent: 'center',
},
});