import React, {useRef} from 'react' import {Pressable, StyleProp, View, ViewStyle} from 'react-native' import Animated, {FadeInDown} from 'react-native-reanimated' import {AppBskyEmbedVideo} from '@atproto/api' import {BlueskyVideoView} from '@haileyok/bluesky-video' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {HITSLOP_30} from '#/lib/constants' import {clamp} from '#/lib/numbers' import {useAutoplayDisabled} from '#/state/preferences' import {useVideoMuteState} from 'view/com/util/post-embeds/VideoVolumeContext' import {atoms as a, useTheme} from '#/alf' import {useIsWithinMessage} from '#/components/dms/MessageContext' import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute' import {Pause_Filled_Corner0_Rounded as PauseIcon} from '#/components/icons/Pause' import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play' import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {TimeIndicator} from './TimeIndicator' export const VideoEmbedInnerNative = React.forwardRef( function VideoEmbedInnerNative( { embed, setStatus, setIsLoading, setIsActive, }: { embed: AppBskyEmbedVideo.View setStatus: (status: 'playing' | 'paused') => void setIsLoading: (isLoading: boolean) => void setIsActive: (isActive: boolean) => void }, ref: React.Ref<{togglePlayback: () => void}>, ) { const {_} = useLingui() const videoRef = useRef(null) const autoplayDisabled = useAutoplayDisabled() const isWithinMessage = useIsWithinMessage() const [muted, setMuted] = useVideoMuteState() const [isPlaying, setIsPlaying] = React.useState(false) const [timeRemaining, setTimeRemaining] = React.useState(0) const [error, setError] = React.useState() React.useImperativeHandle(ref, () => ({ togglePlayback: () => { videoRef.current?.togglePlayback() }, })) if (error) { throw new Error(error) } let aspectRatio = 16 / 9 if (embed.aspectRatio) { const {width, height} = embed.aspectRatio aspectRatio = width / height aspectRatio = clamp(aspectRatio, 1 / 1, 3 / 1) } return ( { setIsActive(e.nativeEvent.isActive) }} onLoadingChange={e => { setIsLoading(e.nativeEvent.isLoading) }} onMutedChange={e => { setMuted(e.nativeEvent.isMuted) }} onStatusChange={e => { setStatus(e.nativeEvent.status) setIsPlaying(e.nativeEvent.status === 'playing') }} onTimeRemainingChange={e => { setTimeRemaining(e.nativeEvent.timeRemaining) }} onError={e => { setError(e.nativeEvent.error) }} ref={videoRef} accessibilityLabel={ embed.alt ? _(msg`Video: ${embed.alt}`) : _(msg`Video`) } accessibilityHint="" /> { videoRef.current?.enterFullscreen() }} toggleMuted={() => { videoRef.current?.toggleMuted() }} togglePlayback={() => { videoRef.current?.togglePlayback() }} isPlaying={isPlaying} timeRemaining={timeRemaining} /> ) }, ) function VideoControls({ enterFullscreen, toggleMuted, togglePlayback, timeRemaining, isPlaying, }: { enterFullscreen: () => void toggleMuted: () => void togglePlayback: () => void timeRemaining: number isPlaying: boolean }) { const {_} = useLingui() const t = useTheme() const [muted] = useVideoMuteState() // show countdown when: // 1. timeRemaining is a number - was seeing NaNs // 2. duration is greater than 0 - means metadata has loaded // 3. we're less than 5 second into the video const showTime = !isNaN(timeRemaining) return ( {isPlaying ? ( ) : ( )} {showTime && } {muted ? ( ) : ( )} ) } function ControlButton({ onPress, children, label, accessibilityHint, style, }: { onPress: () => void children: React.ReactNode label: string accessibilityHint: string style?: StyleProp }) { return ( {children} ) }