about summary refs log tree commit diff
path: root/src/view/com/util/post-embeds/ActiveVideoContext.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/view/com/util/post-embeds/ActiveVideoContext.tsx')
-rw-r--r--src/view/com/util/post-embeds/ActiveVideoContext.tsx89
1 files changed, 80 insertions, 9 deletions
diff --git a/src/view/com/util/post-embeds/ActiveVideoContext.tsx b/src/view/com/util/post-embeds/ActiveVideoContext.tsx
index 6804436a7..d18dfc090 100644
--- a/src/view/com/util/post-embeds/ActiveVideoContext.tsx
+++ b/src/view/com/util/post-embeds/ActiveVideoContext.tsx
@@ -1,37 +1,103 @@
-import React, {useCallback, useId, useMemo, useState} from 'react'
+import React, {
+  useCallback,
+  useEffect,
+  useId,
+  useMemo,
+  useRef,
+  useState,
+} from 'react'
+import {useWindowDimensions} from 'react-native'
 
+import {isNative} from '#/platform/detection'
 import {VideoPlayerProvider} from './VideoPlayerContext'
 
 const ActiveVideoContext = React.createContext<{
   activeViewId: string | null
   setActiveView: (viewId: string, src: string) => void
+  sendViewPosition: (viewId: string, y: number) => void
 } | null>(null)
 
 export function ActiveVideoProvider({children}: {children: React.ReactNode}) {
   const [activeViewId, setActiveViewId] = useState<string | null>(null)
+  const activeViewLocationRef = useRef(Infinity)
   const [source, setSource] = useState<string | null>(null)
+  const {height: windowHeight} = useWindowDimensions()
+
+  // minimising re-renders by using refs
+  const manuallySetRef = useRef(false)
+  const activeViewIdRef = useRef(activeViewId)
+  useEffect(() => {
+    activeViewIdRef.current = activeViewId
+  }, [activeViewId])
+
+  const setActiveView = useCallback(
+    (viewId: string, src: string) => {
+      setActiveViewId(viewId)
+      setSource(src)
+      manuallySetRef.current = true
+      // we don't know the exact position, but it's definitely on screen
+      // so just guess that it's in the middle. Any value is fine
+      // so long as it's not offscreen
+      activeViewLocationRef.current = windowHeight / 2
+    },
+    [windowHeight],
+  )
+
+  const sendViewPosition = useCallback(
+    (viewId: string, y: number) => {
+      if (isNative) return
+
+      if (viewId === activeViewIdRef.current) {
+        activeViewLocationRef.current = y
+      } else {
+        if (
+          distanceToIdealPosition(y) <
+          distanceToIdealPosition(activeViewLocationRef.current)
+        ) {
+          // if the old view was manually set, only usurp if the old view is offscreen
+          if (
+            manuallySetRef.current &&
+            withinViewport(activeViewLocationRef.current)
+          ) {
+            return
+          }
+
+          setActiveViewId(viewId)
+          activeViewLocationRef.current = y
+          manuallySetRef.current = false
+        }
+      }
+
+      function distanceToIdealPosition(yPos: number) {
+        return Math.abs(yPos - windowHeight / 2.5)
+      }
+
+      function withinViewport(yPos: number) {
+        return yPos > 0 && yPos < windowHeight
+      }
+    },
+    [windowHeight],
+  )
 
   const value = useMemo(
     () => ({
       activeViewId,
-      setActiveView: (viewId: string, src: string) => {
-        setActiveViewId(viewId)
-        setSource(src)
-      },
+      setActiveView,
+      sendViewPosition,
     }),
-    [activeViewId],
+    [activeViewId, setActiveView, sendViewPosition],
   )
 
   return (
     <ActiveVideoContext.Provider value={value}>
-      <VideoPlayerProvider source={source ?? ''} viewId={activeViewId}>
+      <VideoPlayerProvider source={source ?? ''}>
         {children}
       </VideoPlayerProvider>
     </ActiveVideoContext.Provider>
   )
 }
 
-export function useActiveVideoView() {
+export function useActiveVideoView({source}: {source: string}) {
   const context = React.useContext(ActiveVideoContext)
   if (!context) {
     throw new Error('useActiveVideo must be used within a ActiveVideoProvider')
@@ -41,7 +107,12 @@ export function useActiveVideoView() {
   return {
     active: context.activeViewId === id,
     setActive: useCallback(
-      (source: string) => context.setActiveView(id, source),
+      () => context.setActiveView(id, source),
+      [context, id, source],
+    ),
+    currentActiveView: context.activeViewId,
+    sendPosition: useCallback(
+      (y: number) => context.sendViewPosition(id, y),
       [context, id],
     ),
   }