299 lines
9.9 KiB
JavaScript
299 lines
9.9 KiB
JavaScript
// Import necessary dependencies
|
|
import React, { useState, useEffect } from 'react';
|
|
import { View, TouchableHighlight, StyleSheet, FlatList, TouchableWithoutFeedback, Share } from 'react-native';
|
|
import { Button, Text, ProgressBar } from 'react-native-paper';
|
|
import API from './../API.js';
|
|
import VideoPlayer from './VideoPlayer.js';
|
|
import VimeoPlayer from './VimeoPlayer.js';
|
|
import { WebView } from 'react-native-webview';
|
|
import { useSnapshot } from 'valtio';
|
|
import GlobalState from '../contexts/GlobalState.js';
|
|
import Moment from 'moment';
|
|
import { useNavigation } from '@react-navigation/native';
|
|
import { Image } from 'expo-image'; // Import Image from expo-image
|
|
import * as FileSystem from 'expo-file-system';
|
|
import * as Sharing from 'expo-sharing';
|
|
|
|
// Extract Vimeo video ID from content string
|
|
const videoIdF = (content) => {
|
|
let vimeoTag = content.match(/@vimeo:[0-9]+/);
|
|
if (!vimeoTag) return [];
|
|
let tag = vimeoTag;
|
|
tag = tag[0].substring(1);
|
|
return tag.split(':');
|
|
};
|
|
|
|
// Extract YouTube video ID from content string
|
|
const youtubeIdF = (content) => {
|
|
let youtubeTag = content.match(/@youtube:[0-z]+/);
|
|
if (!youtubeTag) return '';
|
|
let tag = youtubeTag;
|
|
tag = tag[0].substring(1);
|
|
return tag.split(':')[1];
|
|
};
|
|
|
|
// Extract HLS URL from content string
|
|
const hlsIdF = (content) => {
|
|
let hslTag = content.match(/@hls:.+\w/);
|
|
if (!hslTag) return '';
|
|
let tag = hslTag;
|
|
tag = tag[0].substring(5);
|
|
return tag;
|
|
};
|
|
|
|
// Extract image tags from content string
|
|
const imagesTagF = (content, width = 1000, height = 1000) => {
|
|
let images = content.match(/@image:[0-z|/|.|]+/g);
|
|
if (!images) return [];
|
|
let Tags = [];
|
|
images.forEach(i => {
|
|
let tag = i.substring(1);
|
|
let parts = [tag.substring(1, tag.indexOf(":")), tag.substring(tag.indexOf(":") + 1)];
|
|
if (parts[1].substring(0, 4) != "http") parts[1] = `https://social.emmint.com/${parts[1]}?width=${width}&height=${height}`;
|
|
Tags.push(parts);
|
|
});
|
|
return Tags;
|
|
};
|
|
|
|
// Extract iframe source from content string
|
|
const iframeTagF = (content) => {
|
|
let iframeMatch = content.match(/@iframe:.+\w/g);
|
|
if (!iframeMatch) return [];
|
|
let tag = iframeMatch[0].substring(1);
|
|
let parts = [tag.substring(1, tag.indexOf(":")), tag.substring(tag.indexOf(":") + 1)];
|
|
return parts;
|
|
};
|
|
|
|
// Media Component
|
|
let Media = (props) => {
|
|
const gState = useSnapshot(GlobalState);
|
|
const viewer = gState.me;
|
|
|
|
// Extracting tags from content
|
|
const imagesTag = imagesTagF(props.content, props.imageWidth || 1000, props.imageHeight || 1000);
|
|
const imagesTagLimited = imagesTag.slice(0, 10);
|
|
const imageStyle = imagesTag.length === 1 ? styles.image : styles.multipleImage;
|
|
const videosId = videoIdF(props.content);
|
|
const hlsUrl = hlsIdF(props.content);
|
|
const iframeSrc = iframeTagF(props.content) || [];
|
|
const youtubeId = youtubeIdF(props.content);
|
|
|
|
const [videosFiles, setVideosFiles] = useState([]);
|
|
const [poster, setPoster] = useState('');
|
|
const [loaded, setLoaded] = useState(false);
|
|
const navigation = useNavigation();
|
|
|
|
let interactive = props.interactive || true;
|
|
|
|
// Fetch video data from API
|
|
useEffect(() => {
|
|
let subscribed = true;
|
|
let getData = async () => {
|
|
if (!videosId[1]) return;
|
|
let videoObj = await API.getVideo(videosId[1]);
|
|
if (videoObj && videoObj.files && subscribed) {
|
|
setVideosFiles(videoObj.files);
|
|
setPoster(videoObj.pictures.sizes[4].link);
|
|
}
|
|
};
|
|
getData();
|
|
return () => {
|
|
subscribed = false;
|
|
};
|
|
}, [props.content]);
|
|
|
|
// Render video component
|
|
const renderVideo = () => {
|
|
if (videosFiles.length && !props.skiptVideo) {
|
|
return loaded ? (
|
|
<VideoPlayer
|
|
videosFiles={videosFiles}
|
|
poster={poster}
|
|
postId={props.postId}
|
|
profileId={props.post?.profileid}
|
|
videoId={videosId[1]}
|
|
/>
|
|
) : (
|
|
<TouchableHighlight onPress={() => setLoaded(true)}>
|
|
<Image source={poster ? { uri: poster } : {}} key={poster} style={styles.poster} cachePolicy="memory-disk" />
|
|
</TouchableHighlight>
|
|
);
|
|
}
|
|
if (videosId.length) {
|
|
return <VimeoPlayer videoId={videosId[1]} />;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
// Render HLS video component
|
|
const renderHlsVideo = () => {
|
|
if (hlsUrl && !props.skiptVideo) {
|
|
return loaded ? (
|
|
<VideoPlayer videoUrl={hlsUrl} postId={props.postId} profileId={props.post?.profileid} />
|
|
) : (
|
|
<TouchableHighlight onPress={() => {
|
|
GlobalState.currentMedia = hlsUrl;
|
|
GlobalState.mediaPost = props.post;
|
|
}}>
|
|
{poster ? (
|
|
<Image source={poster ? { uri: poster } : {}} key={poster} style={styles.poster} cachePolicy="memory-disk" />
|
|
) : (
|
|
<Button
|
|
icon={"subscriptions"}
|
|
labelStyle={{ fontSize: 58 }}
|
|
style={{ flow: 1 }}
|
|
/>
|
|
)}
|
|
</TouchableHighlight>
|
|
);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
// Render iframe component
|
|
const renderIframe = () => {
|
|
if (iframeSrc.length) {
|
|
return (
|
|
<WebView
|
|
style={styles.iframe}
|
|
source={{ uri: iframeSrc[1] }}
|
|
/>
|
|
);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
// Render YouTube embed component
|
|
const renderYouTubeEmbed = () => {
|
|
if (youtubeId.length) {
|
|
return (
|
|
<WebView
|
|
style={styles.iframe}
|
|
source={{ uri: "https://www.youtube.com/embed/" + youtubeId + "?fs=0" }}
|
|
/>
|
|
);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
// Render progress bar if available
|
|
const renderProgressBar = () => {
|
|
if (viewer.data && viewer.data[props.postId]) {
|
|
const percent = Math.round(viewer.data[props.postId].time / viewer.data[props.postId].duration * 100);
|
|
if (percent) {
|
|
return (
|
|
<>
|
|
<Text>{percent}% {Moment(viewer.data[props.postId].ts).fromNow()}</Text>
|
|
<ProgressBar progress={viewer.data[props.postId].time / viewer.data[props.postId].duration} />
|
|
</>
|
|
);
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
// Function to share an image
|
|
const shareImage = async (imageUrl) => {
|
|
if (!interactive) return;
|
|
try {
|
|
// Download the image to the device's file system
|
|
const fileUri = `${FileSystem.cacheDirectory}shared-image.jpg`;
|
|
const { uri } = await FileSystem.downloadAsync(imageUrl, fileUri);
|
|
|
|
// Share the downloaded image
|
|
await Sharing.shareAsync(uri);
|
|
} catch (error) {
|
|
console.error("Error sharing the image", error);
|
|
}
|
|
};
|
|
|
|
// Function to handle navigation to slideshow
|
|
const navigateToSlideshow = (index) => {
|
|
if (!interactive) return;
|
|
navigation.navigate('Slideshow', { images: imagesTag, startIndex: index });
|
|
};
|
|
|
|
// Render images in a FlatList
|
|
const renderImages = ({ item, index }) => (
|
|
<TouchableWithoutFeedback
|
|
style={styles.flatlistImages}
|
|
onPress={() => navigateToSlideshow(index)}
|
|
onLongPress={async () => {
|
|
await shareImage(item[1]);
|
|
}}
|
|
>
|
|
<Image source={{ uri: item[1] }} style={styles.flatlistImages} cachePolicy="memory-disk" />
|
|
</TouchableWithoutFeedback>
|
|
);
|
|
|
|
return (
|
|
<View style={{ paddingTop: 10, paddingBottom: 3 }}>
|
|
{imagesTag.length > 2 ? (
|
|
<FlatList
|
|
horizontal
|
|
data={imagesTagLimited}
|
|
renderItem={renderImages}
|
|
keyExtractor={(item) => item[1]}
|
|
initialNumToRender={2}
|
|
style={{
|
|
transform: [{ scale: 1.1 }],
|
|
paddingTop: 5,
|
|
paddingBottom: 10,
|
|
}}
|
|
showsHorizontalScrollIndicator={false}
|
|
/>
|
|
) : (
|
|
<View style={{ flexDirection: "row" }}>
|
|
{imagesTag.map((image, i) => (
|
|
<TouchableWithoutFeedback
|
|
key={image[1]}
|
|
onPress={() => navigateToSlideshow(i)}
|
|
onLongPress={async () => {
|
|
await shareImage(image[1]);
|
|
}}
|
|
>
|
|
<Image source={{ uri: image[1] }} style={imageStyle} cachePolicy="memory-disk" />
|
|
</TouchableWithoutFeedback>
|
|
))}
|
|
</View>
|
|
)}
|
|
{renderVideo()}
|
|
{renderHlsVideo()}
|
|
{renderIframe()}
|
|
{renderYouTubeEmbed()}
|
|
{renderProgressBar()}
|
|
</View>
|
|
);
|
|
};
|
|
|
|
export default Media;
|
|
|
|
// Styles for different media components
|
|
const styles = StyleSheet.create({
|
|
image: {
|
|
width: "100%",
|
|
aspectRatio: 1,
|
|
borderRadius: 15,
|
|
},
|
|
poster: {
|
|
width: "100%",
|
|
aspectRatio: 9 / 6,
|
|
},
|
|
multipleImage: {
|
|
width: "49%",
|
|
aspectRatio: 1,
|
|
margin: 3,
|
|
borderRadius: 15,
|
|
},
|
|
flatlistImages: {
|
|
width: 300,
|
|
aspectRatio: 1,
|
|
margin: 5,
|
|
borderRadius: 15,
|
|
},
|
|
iframe: {
|
|
width: "100%",
|
|
minHeight: 300,
|
|
}
|
|
});
|