import React, {useCallback, useState} from 'react' import {ActivityIndicator, View} from 'react-native' import {ImageBackground} from 'expo-image' import {type AppBskyEmbedVideo} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {ConstrainedImage} from '#/view/com/util/images/AutoSizedImage' import {atoms as a, useTheme} from '#/alf' import {Button} from '#/components/Button' import {useThrottledValue} from '#/components/hooks/useThrottledValue' import {PlayButtonIcon} from '#/components/video/PlayButtonIcon' import {VideoEmbedInnerNative} from './VideoEmbedInner/VideoEmbedInnerNative' import * as VideoFallback from './VideoEmbedInner/VideoFallback' interface Props { embed: AppBskyEmbedVideo.View crop?: 'none' | 'square' | 'constrained' } export function VideoEmbed({embed, crop}: Props) { const t = useTheme() const [key, setKey] = useState(0) const renderError = useCallback( (error: unknown) => ( setKey(key + 1)} /> ), [key], ) let aspectRatio: number | undefined const dims = embed.aspectRatio if (dims) { aspectRatio = dims.width / dims.height if (Number.isNaN(aspectRatio)) { aspectRatio = undefined } } let constrained: number | undefined let max: number | undefined if (aspectRatio !== undefined) { const ratio = 1 / 2 // max of 1:2 ratio in feeds constrained = Math.max(aspectRatio, ratio) max = Math.max(aspectRatio, 0.25) // max of 1:4 in thread } const cropDisabled = crop === 'none' const contents = ( ) return ( {cropDisabled ? ( {contents} ) : ( {contents} )} ) } function InnerWrapper({embed}: Props) { const {_} = useLingui() const ref = React.useRef<{togglePlayback: () => void}>(null) const [status, setStatus] = React.useState<'playing' | 'paused' | 'pending'>( 'pending', ) const [isLoading, setIsLoading] = React.useState(false) const [isActive, setIsActive] = React.useState(false) const showSpinner = useThrottledValue(isActive && isLoading, 100) const showOverlay = !isActive || isLoading || (status === 'paused' && !isActive) || status === 'pending' React.useEffect(() => { if (!isActive && status !== 'pending') { setStatus('pending') } }, [isActive, status]) return ( <> {showOverlay && ( )} ) } function VideoError({retry}: {error: unknown; retry: () => void}) { return ( An error occurred while loading the video. Please try again later. ) }