expo-popcore-old/components/sker/video-player/video-player.tsx

121 lines
2.8 KiB
TypeScript

import { Image } from 'expo-image';
import { useVideoPlayer, VideoView } from 'expo-video';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { StyleSheet, View } from 'react-native';
import type { VideoPlayerProps } from './types';
import { useSharedVisibility } from './useSharedVisibility';
import { getVideoThumbnail, getVideoUrl } from '@/lib/utils/media';
const videoViewStyle = {
width: '100%',
height: '100%',
position: 'absolute' as const,
top: 0,
left: 0,
right: 0,
bottom: 0
};
const originalImageStyle = {
width: 64,
height: 64,
position: 'absolute' as const,
bottom: 8,
left: 8,
borderRadius: 12,
borderWidth: 1,
borderColor: "#ffffff",
zIndex: 99
};
const imageStyle = {
width: '100%',
borderWidth: 0
};
function VideoPlayerComponent({
source,
originalImage,
style,
dwellTime = 500,
threshold = 0.1
}: VideoPlayerProps) {
const [imageAspectRatio, setImageAspectRatio] = useState(1);
const { ref: viewRef, isVisible, shouldPlay } = useSharedVisibility(threshold, dwellTime);
const url = useMemo(() => {
return source?.uri
}, [source])
const isVideo = useMemo(() => {
return /\.(mp4|webm|ogg|mov|avi)$/i.test(url || ``)
}, [url])
const thumbnailUrl = useMemo(() => {
if (!isVideo) return url;
return getVideoThumbnail(url!, { time: '0s', width: 600, height: 600, fit: 'cover' });
}, [url, isVideo]);
const player = useVideoPlayer(
isVideo ? { ...source, uri: getVideoUrl(source.uri!) } : null,
(p) => {
p.loop = true;
p.muted = true;
}
)
useEffect(() => {
if (!isVideo || !player) return;
try {
shouldPlay ? player.play() : player.pause();
} catch { }
}, [shouldPlay, player, isVideo]);
const handleImageLoad = useCallback((e: any) => {
const { width, height } = e.source;
if (width && height) {
setImageAspectRatio(width / height);
}
}, []);
const element = useMemo(() => {
if (!isVisible) return null;
if (isVideo && player) {
return <>
<VideoView
player={player}
contentFit="cover"
nativeControls={false}
style={videoViewStyle}
/>
{originalImage && <Image source={{ uri: originalImage }} style={originalImageStyle} />}
</>
}
else {
return <Image
source={{ uri: thumbnailUrl }}
contentFit="cover"
onLoad={handleImageLoad}
style={[imageStyle, { aspectRatio: imageAspectRatio }]}
/>
}
}, [isVideo, isVisible, imageAspectRatio, player, thumbnailUrl, originalImage, handleImageLoad])
return (
<View ref={viewRef as any} style={[styles.container, style]}>
{element}
</View>
);
}
const styles = StyleSheet.create({
container: {
width: '100%',
height: '100%',
overflow: 'hidden'
}
});
export default memo(VideoPlayerComponent);