about summary refs log tree commit diff
path: root/src/components/Post/Embed/VideoEmbed/ActiveVideoWebContext.tsx
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2025-06-13 12:05:41 -0500
committerGitHub <noreply@github.com>2025-06-13 12:05:41 -0500
commit45f0f7eefecae1922c2f30d4e7760d2b93b1ae56 (patch)
treea2fd6917867f18fe334b54dd3289775c2930bc85 /src/components/Post/Embed/VideoEmbed/ActiveVideoWebContext.tsx
parentba0f5a9bdef5bd0447ded23cab1af222b65511cc (diff)
downloadvoidsky-45f0f7eefecae1922c2f30d4e7760d2b93b1ae56.tar.zst
Port post embeds to new arch (#7408)
* Direct port of embeds to new arch

(cherry picked from commit cc3fa1f6cea396dd9222486c633a508bfee1ecd6)

* Re-org

* Split out ListEmbed and FeedEmbed

* Split out ImageEmbed

* DRY up a bit

* Port over ExternalLinkEmbed

* Port over Player and Gif embeds

* Migrate ComposerReplyTo

* Replace other usages of old post-embeds

* Migrate view contexts

* Copy pasta VideoEmbed

* Copy pasta GifEmbed

* Swap in new file location

* Clean up

* Fix up native

* Add back in correct moderation on List and Feed embeds

* Format

* Prettier

* delete old video utils

* move bandwidth-estimate.ts

* Remove log

* Add LazyQuoteEmbed for composer use

* Clean up unused things

* Remove remaining items

* Prettier

* Fix imports

* Handle nested quotes same as prod

* Add back silenced error handling

* Fix lint

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
Diffstat (limited to 'src/components/Post/Embed/VideoEmbed/ActiveVideoWebContext.tsx')
-rw-r--r--src/components/Post/Embed/VideoEmbed/ActiveVideoWebContext.tsx114
1 files changed, 114 insertions, 0 deletions
diff --git a/src/components/Post/Embed/VideoEmbed/ActiveVideoWebContext.tsx b/src/components/Post/Embed/VideoEmbed/ActiveVideoWebContext.tsx
new file mode 100644
index 000000000..a038403b2
--- /dev/null
+++ b/src/components/Post/Embed/VideoEmbed/ActiveVideoWebContext.tsx
@@ -0,0 +1,114 @@
+import React, {
+  useCallback,
+  useEffect,
+  useId,
+  useMemo,
+  useRef,
+  useState,
+} from 'react'
+import {useWindowDimensions} from 'react-native'
+
+import {isNative, isWeb} from '#/platform/detection'
+
+const Context = React.createContext<{
+  activeViewId: string | null
+  setActiveView: (viewId: string) => void
+  sendViewPosition: (viewId: string, y: number) => void
+} | null>(null)
+
+export function Provider({children}: {children: React.ReactNode}) {
+  if (!isWeb) {
+    throw new Error('ActiveVideoWebContext may only be used on web.')
+  }
+
+  const [activeViewId, setActiveViewId] = useState<string | null>(null)
+  const activeViewLocationRef = useRef(Infinity)
+  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) => {
+      setActiveViewId(viewId)
+      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,
+      sendViewPosition,
+    }),
+    [activeViewId, setActiveView, sendViewPosition],
+  )
+
+  return <Context.Provider value={value}>{children}</Context.Provider>
+}
+
+export function useActiveVideoWeb() {
+  const context = React.useContext(Context)
+  if (!context) {
+    throw new Error(
+      'useActiveVideoWeb must be used within a ActiveVideoWebProvider',
+    )
+  }
+
+  const {activeViewId, setActiveView, sendViewPosition} = context
+  const id = useId()
+
+  return {
+    active: activeViewId === id,
+    setActive: () => {
+      setActiveView(id)
+    },
+    currentActiveView: activeViewId,
+    sendPosition: (y: number) => sendViewPosition(id, y),
+  }
+}