import React, {useCallback, useEffect, useRef, useState} from 'react' import {Pressable, View} from 'react-native' import Animated, {FadeInDown} from 'react-native-reanimated' import {VideoPlayer, VideoView} from 'expo-video' import {AppBskyEmbedVideo} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {HITSLOP_30} from '#/lib/constants' import {clamp} from '#/lib/numbers' import {useActiveVideoNative} from 'view/com/util/post-embeds/ActiveVideoNativeContext' import {atoms as a, useTheme} from '#/alf' import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute' import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' import { AudioCategory, PlatformInfo, } from '../../../../../../modules/expo-bluesky-swiss-army' import {TimeIndicator} from './TimeIndicator' export function VideoEmbedInnerNative({ embed, }: { embed: AppBskyEmbedVideo.View }) { const {_} = useLingui() const {player} = useActiveVideoNative() const ref = useRef(null) const enterFullscreen = useCallback(() => { ref.current?.enterFullscreen() }, []) let aspectRatio = 16 / 9 if (embed.aspectRatio) { const {width, height} = embed.aspectRatio aspectRatio = width / height aspectRatio = clamp(aspectRatio, 1 / 1, 3 / 1) } return ( { PlatformInfo.setAudioCategory(AudioCategory.Playback) PlatformInfo.setAudioActive(true) player.muted = false }} onExitFullscreen={() => { PlatformInfo.setAudioCategory(AudioCategory.Ambient) PlatformInfo.setAudioActive(false) player.muted = true if (!player.playing) { player.play() } }} accessibilityLabel={ embed.alt ? _(msg`Video: ${embed.alt}`) : _(msg`Video`) } accessibilityHint="" /> ) } function VideoControls({ player, enterFullscreen, }: { player: VideoPlayer enterFullscreen: () => void }) { const {_} = useLingui() const t = useTheme() const [isMuted, setIsMuted] = useState(player.muted) const [timeRemaining, setTimeRemaining] = React.useState(0) useEffect(() => { // eslint-disable-next-line @typescript-eslint/no-shadow const volumeSub = player.addListener('volumeChange', ({isMuted}) => { setIsMuted(isMuted) }) const timeSub = player.addListener( 'timeRemainingChange', secondsRemaining => { setTimeRemaining(secondsRemaining) }, ) return () => { volumeSub.remove() timeSub.remove() } }, [player]) const onPressFullscreen = useCallback(() => { switch (player.status) { case 'idle': case 'loading': case 'readyToPlay': { if (!player.playing) player.play() enterFullscreen() break } case 'error': { player.replay() break } } }, [player, enterFullscreen]) const toggleMuted = useCallback(() => { const muted = !player.muted // We want to set this to the _inverse_ of the new value, because we actually want for the audio to be mixed when // the video is muted, and vice versa. const mix = !muted const category = muted ? AudioCategory.Ambient : AudioCategory.Playback PlatformInfo.setAudioCategory(category) PlatformInfo.setAudioActive(mix) player.muted = muted }, [player]) // 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 ( {showTime && } {isMuted ? ( ) : ( )} ) }