import React, { useCallback, useEffect, useState } from 'react'; import { Badge, Box, Button, Icon, Icons, Spinner, Text, Tooltip, TooltipProvider, as, } from 'folds'; import classNames from 'classnames'; import { BlurhashCanvas } from 'react-blurhash'; import { EncryptedAttachmentInfo } from 'browser-encrypt-attachment'; import { IThumbnailContent, IVideoInfo, MATRIX_BLUR_HASH_PROPERTY_NAME, } from '../../../../types/matrix/common'; import * as css from './styles.css'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback'; import { getFileSrcUrl } from './util'; import { Image, Video } from '../../../components/media'; import { bytesToSize } from '../../../../util/common'; import { millisecondsToMinutesAndSeconds } from '../../../utils/common'; export type VideoContentProps = { body: string; mimeType: string; url: string; info: IVideoInfo & IThumbnailContent; encInfo?: EncryptedAttachmentInfo; autoPlay?: boolean; loadThumbnail?: boolean; }; export const VideoContent = as<'div', VideoContentProps>( ({ className, body, mimeType, url, info, encInfo, autoPlay, loadThumbnail, ...props }, ref) => { const mx = useMatrixClient(); const blurHash = info.thumbnail_info?.[MATRIX_BLUR_HASH_PROPERTY_NAME]; const [load, setLoad] = useState(false); const [error, setError] = useState(false); const [srcState, loadSrc] = useAsyncCallback( useCallback( () => getFileSrcUrl(mx.mxcUrlToHttp(url) ?? '', mimeType, encInfo), [mx, url, mimeType, encInfo] ) ); const [thumbSrcState, loadThumbSrc] = useAsyncCallback( useCallback(() => { const thumbInfo = info.thumbnail_info; const thumbMxcUrl = info.thumbnail_file?.url ?? info.thumbnail_url; if (typeof thumbMxcUrl !== 'string' || typeof thumbInfo?.mimetype !== 'string') { throw new Error('Failed to load thumbnail'); } return getFileSrcUrl( mx.mxcUrlToHttp(thumbMxcUrl) ?? '', thumbInfo.mimetype, info.thumbnail_file ); }, [mx, info]) ); const handleLoad = () => { setLoad(true); }; const handleError = () => { setLoad(false); setError(true); }; const handleRetry = () => { setError(false); loadSrc(); }; useEffect(() => { if (autoPlay) loadSrc(); }, [autoPlay, loadSrc]); useEffect(() => { if (loadThumbnail) loadThumbSrc(); }, [loadThumbnail, loadThumbSrc]); return ( {typeof blurHash === 'string' && !load && ( )} {thumbSrcState.status === AsyncStatus.Success && !load && ( {body} )} {!autoPlay && srcState.status === AsyncStatus.Idle && ( )} {srcState.status === AsyncStatus.Success && ( )} {(srcState.status === AsyncStatus.Loading || srcState.status === AsyncStatus.Success) && !load && ( )} {(error || srcState.status === AsyncStatus.Error) && ( Failed to load video! } position="Top" align="Center" > {(triggerRef) => ( )} )} {!load && typeof info.size === 'number' && ( {millisecondsToMinutesAndSeconds(info.duration ?? 0)} {bytesToSize(info.size)} )} ); } );