about summary refs log tree commit diff
path: root/src/view/com/util/post-embeds/VideoEmbed.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/util/post-embeds/VideoEmbed.tsx')
-rw-r--r--src/view/com/util/post-embeds/VideoEmbed.tsx142
1 files changed, 31 insertions, 111 deletions
diff --git a/src/view/com/util/post-embeds/VideoEmbed.tsx b/src/view/com/util/post-embeds/VideoEmbed.tsx
index a672830db..267b5d184 100644
--- a/src/view/com/util/post-embeds/VideoEmbed.tsx
+++ b/src/view/com/util/post-embeds/VideoEmbed.tsx
@@ -1,22 +1,18 @@
-import React, {useCallback, useEffect, useId, useState} from 'react'
+import React, {useCallback, useState} from 'react'
 import {View} from 'react-native'
 import {ImageBackground} from 'expo-image'
-import {PlayerError, VideoPlayerStatus} from 'expo-video'
 import {AppBskyEmbedVideo} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
 import {clamp} from '#/lib/numbers'
-import {useAutoplayDisabled} from 'state/preferences'
 import {VideoEmbedInnerNative} from '#/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative'
 import {atoms as a} from '#/alf'
 import {Button} from '#/components/Button'
-import {useIsWithinMessage} from '#/components/dms/MessageContext'
+import {useThrottledValue} from '#/components/hooks/useThrottledValue'
 import {Loader} from '#/components/Loader'
 import {PlayButtonIcon} from '#/components/video/PlayButtonIcon'
-import {VisibilityView} from '../../../../../modules/expo-bluesky-swiss-army'
 import {ErrorBoundary} from '../ErrorBoundary'
-import {useActiveVideoNative} from './ActiveVideoNativeContext'
 import * as VideoFallback from './VideoEmbedInner/VideoFallback'
 
 interface Props {
@@ -59,113 +55,36 @@ export function VideoEmbed({embed}: Props) {
 
 function InnerWrapper({embed}: Props) {
   const {_} = useLingui()
-  const {activeSource, activeViewId, setActiveSource, player} =
-    useActiveVideoNative()
-  const viewId = useId()
+  const ref = React.useRef<{togglePlayback: () => void}>(null)
 
-  const [playerStatus, setPlayerStatus] = useState<
-    VideoPlayerStatus | 'paused'
-  >('paused')
-  const [isMuted, setIsMuted] = useState(player.muted)
-  const [isFullscreen, setIsFullscreen] = React.useState(false)
-  const [timeRemaining, setTimeRemaining] = React.useState(0)
-  const isWithinMessage = useIsWithinMessage()
-  const disableAutoplay = useAutoplayDisabled() || isWithinMessage
-  const isActive = embed.playlist === activeSource && activeViewId === viewId
-  // There are some different loading states that we should pay attention to and show a spinner for
-  const isLoading =
-    isActive &&
-    (playerStatus === 'waitingToPlayAtSpecifiedRate' ||
-      playerStatus === 'loading')
-  // This happens whenever the visibility view decides that another video should start playing
-  const showOverlay = !isActive || isLoading || playerStatus === 'paused'
-
-  // send error up to error boundary
-  const [error, setError] = useState<Error | PlayerError | null>(null)
-  if (error) {
-    throw error
-  }
-
-  useEffect(() => {
-    if (isActive) {
-      // eslint-disable-next-line @typescript-eslint/no-shadow
-      const volumeSub = player.addListener('volumeChange', ({isMuted}) => {
-        setIsMuted(isMuted)
-      })
-      const timeSub = player.addListener(
-        'timeRemainingChange',
-        secondsRemaining => {
-          setTimeRemaining(secondsRemaining)
-        },
-      )
-      const statusSub = player.addListener(
-        'statusChange',
-        (status, oldStatus, playerError) => {
-          setPlayerStatus(status)
-          if (status === 'error') {
-            setError(playerError ?? new Error('Unknown player error'))
-          }
-          if (status === 'readyToPlay' && oldStatus !== 'readyToPlay') {
-            player.play()
-          }
-        },
-      )
-      return () => {
-        volumeSub.remove()
-        timeSub.remove()
-        statusSub.remove()
-      }
-    }
-  }, [player, isActive, disableAutoplay])
-
-  // The source might already be active (for example, if you are scrolling a list of quotes and its all the same
-  // video). In those cases, just start playing. Otherwise, setting the active source will result in the video
-  // start playback immediately
-  const startPlaying = (ignoreAutoplayPreference: boolean) => {
-    if (disableAutoplay && !ignoreAutoplayPreference) {
-      return
-    }
+  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)
 
-    if (isActive) {
-      player.play()
-    } else {
-      setActiveSource(embed.playlist, viewId)
-    }
-  }
+  const showOverlay =
+    !isActive ||
+    isLoading ||
+    (status === 'paused' && !isActive) ||
+    status === 'pending'
 
-  const onVisibilityStatusChange = (isVisible: boolean) => {
-    // When `isFullscreen` is true, it means we're actually still exiting the fullscreen player. Ignore these change
-    // events
-    if (isFullscreen) {
-      return
+  React.useEffect(() => {
+    if (!isActive && status !== 'pending') {
+      setStatus('pending')
     }
-    if (isVisible) {
-      startPlaying(false)
-    } else {
-      // Clear the active source so the video view unmounts when autoplay is disabled. Otherwise, leave it mounted
-      // until it gets replaced by another video
-      if (disableAutoplay) {
-        setActiveSource(null, null)
-      } else {
-        player.muted = true
-        if (player.playing) {
-          player.pause()
-        }
-      }
-    }
-  }
+  }, [isActive, status])
 
   return (
-    <VisibilityView enabled={true} onChangeStatus={onVisibilityStatusChange}>
-      {isActive ? (
-        <VideoEmbedInnerNative
-          embed={embed}
-          timeRemaining={timeRemaining}
-          isMuted={isMuted}
-          isFullscreen={isFullscreen}
-          setIsFullscreen={setIsFullscreen}
-        />
-      ) : null}
+    <>
+      <VideoEmbedInnerNative
+        embed={embed}
+        setStatus={setStatus}
+        setIsLoading={setIsLoading}
+        setIsActive={setIsActive}
+        ref={ref}
+      />
       <ImageBackground
         source={{uri: embed.thumbnail}}
         accessibilityIgnoresInvertColors
@@ -185,17 +104,18 @@ function InnerWrapper({embed}: Props) {
       >
         <Button
           style={[a.flex_1, a.align_center, a.justify_center]}
-          onPress={() => startPlaying(true)}
+          onPress={() => {
+            ref.current?.togglePlayback()
+          }}
           label={_(msg`Play video`)}
           color="secondary">
-          {isLoading ? (
+          {showSpinner ? (
             <View
               style={[
                 a.rounded_full,
                 a.p_xs,
                 a.align_center,
                 a.justify_center,
-                {backgroundColor: 'rgba(0,0,0,0.5)'},
               ]}>
               <Loader size="2xl" style={{color: 'white'}} />
             </View>
@@ -204,7 +124,7 @@ function InnerWrapper({embed}: Props) {
           )}
         </Button>
       </ImageBackground>
-    </VisibilityView>
+    </>
   )
 }