import {useCallback, useEffect, useRef, 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 {isAndroid} from '#/platform/detection' import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {ConstrainedImage} from '#/view/com/util/images/AutoSizedImage' import {atoms as a, platform, useTheme} from '#/alf' import {Button} from '#/components/Button' import {useThrottledValue} from '#/components/hooks/useThrottledValue' import {MediaInsetBorder} from '#/components/MediaInsetBorder' 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 = useRef<{togglePlayback: () => void}>(null) const [status, setStatus] = useState<'playing' | 'paused' | 'pending'>( 'pending', ) const [isLoading, setIsLoading] = useState(false) const [activeState, setActiveState] = useState< 'active' | 'inactive' | 'staged' >('inactive') const showSpinner = useThrottledValue( activeState === 'active' && isLoading, 100, ) const showOverlay = activeState === 'inactive' || (activeState === 'active' && (isLoading || status === 'pending')) useEffect(() => { if (isAndroid) { if (activeState === 'inactive' && status !== 'pending') { setStatus('pending') } } }, [activeState, status]) return ( <> {showOverlay && ( )} ) } function VideoError({retry}: {error: unknown; retry: () => void}) { return ( An error occurred while loading the video. Please try again later. ) }