import React, { useContext, useEffect, useRef, useState } from 'react'; import { StyleProp, StyleSheet, TextStyle, View, Text } from 'react-native'; import { dequal } from 'dequal'; import Touchable from './Touchable'; import Markdown from '../markdown'; import { isIOS } from '../../lib/methods/helpers'; import { CustomIcon } from '../CustomIcon'; import { themes } from '../../lib/constants'; import MessageContext from './Context'; import { fileDownload } from './helpers/fileDownload'; import EventEmitter from '../../lib/methods/helpers/events'; import { LISTENER } from '../Toast'; import I18n from '../../i18n'; import { IAttachment } from '../../definitions/IAttachment'; import RCActivityIndicator from '../ActivityIndicator'; import { TGetCustomEmoji } from '../../definitions/IEmoji'; import { useTheme } from '../../theme'; import { formatAttachmentUrl } from '../../lib/methods/helpers/formatAttachmentUrl'; import { LOCAL_DOCUMENT_PATH, cancelDownload, downloadMediaFile, isDownloadActive, searchMediaFileAsync } from '../../lib/methods/handleMediaDownload'; import { fetchAutoDownloadEnabled } from '../../lib/methods/autoDownloadPreference'; import sharedStyles from '../../views/Styles'; const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])]; const isTypeSupported = (type: string) => SUPPORTED_TYPES.indexOf(type) !== -1; const styles = StyleSheet.create({ button: { flex: 1, borderRadius: 4, height: 150, marginBottom: 6, alignItems: 'center', justifyContent: 'center' }, cancelContainer: { position: 'absolute', top: 8, right: 8 }, text: { ...sharedStyles.textRegular, fontSize: 12 } }); interface IMessageVideo { file: IAttachment; showAttachment?: (file: IAttachment) => void; getCustomEmoji: TGetCustomEmoji; style?: StyleProp[]; isReply?: boolean; } const DownloadIndicator = ({ handleCancelDownload }: { handleCancelDownload(): void }) => { const { colors } = useTheme(); return ( <> {I18n.t('Cancel')} ); }; const Video = React.memo( ({ file, showAttachment, getCustomEmoji, style, isReply }: IMessageVideo) => { const [videoCached, setVideoCached] = useState(file); const [loading, setLoading] = useState(false); const { baseUrl, user } = useContext(MessageContext); const { theme } = useTheme(); const filePath = useRef(''); const video = formatAttachmentUrl(file.video_url, user.id, user.token, baseUrl); useEffect(() => { const handleVideoSearchAndDownload = async () => { if (video) { const cachedVideoResult = await searchMediaFileAsync({ type: 'video', mimeType: file.video_type, urlToCache: video }); filePath.current = cachedVideoResult.filePath; const downloadActive = isDownloadActive('video', video); if (cachedVideoResult.file?.exists) { setVideoCached(prev => ({ ...prev, video_url: cachedVideoResult.file?.uri })); if (downloadActive) { cancelDownload('video', video); } return; } if (downloadActive) return setLoading(true); await handleAutoDownload(); } }; handleVideoSearchAndDownload(); }, []); if (!baseUrl) { return null; } const handleAutoDownload = async () => { const isAutoDownloadEnabled = fetchAutoDownloadEnabled('videoPreferenceDownload'); if (isAutoDownloadEnabled) { await handleDownload(); } }; const handleDownload = async () => { setLoading(true); try { const videoUri = await downloadMediaFile({ downloadUrl: video, mediaType: 'video', path: filePath.current }); setVideoCached(prev => ({ ...prev, video_url: videoUri })); } finally { setLoading(false); } }; const onPress = async () => { if (file.video_type && isTypeSupported(file.video_type) && showAttachment) { if (!videoCached.video_url?.startsWith(LOCAL_DOCUMENT_PATH) && !loading) { // Keep the video downloading while showing the video buffering handleDownload(); } return showAttachment(videoCached); } if (!isIOS && file.video_url) { await downloadVideoToGallery(video); return; } EventEmitter.emit(LISTENER, { message: I18n.t('Unsupported_format') }); }; const handleCancelDownload = () => { if (loading) { cancelDownload('video', video); return setLoading(false); } }; const downloadVideoToGallery = async (uri: string) => { setLoading(true); const fileDownloaded = await fileDownload(uri, file); setLoading(false); if (fileDownloaded) { EventEmitter.emit(LISTENER, { message: I18n.t('saved_to_gallery') }); return; } EventEmitter.emit(LISTENER, { message: I18n.t('error-save-video') }); }; return ( <> {loading ? ( ) : ( )} ); }, (prevProps, nextProps) => dequal(prevProps.file, nextProps.file) ); export default Video;