about summary refs log tree commit diff
diff options
context:
space:
mode:
authorHailey <me@haileyok.com>2024-08-28 08:46:47 -0700
committerGitHub <noreply@github.com>2024-08-28 08:46:47 -0700
commit5ae0d40a14e7015daa0161e7e9d877690f8a339e (patch)
treee8be0c39c72ef55ae65b69f3cf8cb34b902bdbff
parent9aa2b2d14e833a7efa8a22688ac8bd80f8f57dac (diff)
downloadvoidsky-5ae0d40a14e7015daa0161e7e9d877690f8a339e.tar.zst
[Video] 🫧 Move logic around by platform (#5003)
-rw-r--r--src/App.native.tsx2
-rw-r--r--src/App.web.tsx2
-rw-r--r--src/view/com/util/post-embeds/ActiveVideoNativeContext.tsx40
-rw-r--r--src/view/com/util/post-embeds/ActiveVideoWebContext.tsx (renamed from src/view/com/util/post-embeds/ActiveVideoContext.tsx)51
-rw-r--r--src/view/com/util/post-embeds/VideoEmbed.tsx13
-rw-r--r--src/view/com/util/post-embeds/VideoEmbed.web.tsx4
-rw-r--r--src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.tsx4
-rw-r--r--src/view/com/util/post-embeds/VideoPlayerContext.tsx47
-rw-r--r--src/view/com/util/post-embeds/VideoPlayerContext.web.tsx9
9 files changed, 77 insertions, 95 deletions
diff --git a/src/App.native.tsx b/src/App.native.tsx
index 69c7629bf..a4282e7fb 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -52,7 +52,7 @@ import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
 import {Provider as StarterPackProvider} from '#/state/shell/starter-pack'
 import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies'
 import {TestCtrls} from '#/view/com/testing/TestCtrls'
-import {ActiveVideoProvider} from '#/view/com/util/post-embeds/ActiveVideoContext'
+import {Provider as ActiveVideoProvider} from '#/view/com/util/post-embeds/ActiveVideoNativeContext'
 import * as Toast from '#/view/com/util/Toast'
 import {Shell} from '#/view/shell'
 import {ThemeProvider as Alf} from '#/alf'
diff --git a/src/App.web.tsx b/src/App.web.tsx
index 9ec792530..69a8020c2 100644
--- a/src/App.web.tsx
+++ b/src/App.web.tsx
@@ -40,7 +40,7 @@ import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide'
 import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
 import {Provider as StarterPackProvider} from '#/state/shell/starter-pack'
 import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies'
-import {ActiveVideoProvider} from '#/view/com/util/post-embeds/ActiveVideoContext'
+import {Provider as ActiveVideoProvider} from '#/view/com/util/post-embeds/ActiveVideoWebContext'
 import * as Toast from '#/view/com/util/Toast'
 import {ToastContainer} from '#/view/com/util/Toast.web'
 import {Shell} from '#/view/shell/index'
diff --git a/src/view/com/util/post-embeds/ActiveVideoNativeContext.tsx b/src/view/com/util/post-embeds/ActiveVideoNativeContext.tsx
new file mode 100644
index 000000000..77616d788
--- /dev/null
+++ b/src/view/com/util/post-embeds/ActiveVideoNativeContext.tsx
@@ -0,0 +1,40 @@
+import React from 'react'
+import {useVideoPlayer, VideoPlayer} from 'expo-video'
+
+import {isNative} from '#/platform/detection'
+
+const Context = React.createContext<{
+  activeSource: string | null
+  setActiveSource: (src: string) => void
+  player: VideoPlayer
+} | null>(null)
+
+export function Provider({children}: {children: React.ReactNode}) {
+  if (!isNative) {
+    throw new Error('ActiveVideoProvider may only be used on native.')
+  }
+
+  const [activeSource, setActiveSource] = React.useState('')
+
+  const player = useVideoPlayer(activeSource, p => {
+    p.muted = true
+    p.loop = true
+    p.play()
+  })
+
+  return (
+    <Context.Provider value={{activeSource, setActiveSource, player}}>
+      {children}
+    </Context.Provider>
+  )
+}
+
+export function useActiveVideoNative() {
+  const context = React.useContext(Context)
+  if (!context) {
+    throw new Error(
+      'useActiveVideoNative must be used within a ActiveVideoNativeProvider',
+    )
+  }
+  return context
+}
diff --git a/src/view/com/util/post-embeds/ActiveVideoContext.tsx b/src/view/com/util/post-embeds/ActiveVideoWebContext.tsx
index d18dfc090..bc43e997c 100644
--- a/src/view/com/util/post-embeds/ActiveVideoContext.tsx
+++ b/src/view/com/util/post-embeds/ActiveVideoWebContext.tsx
@@ -8,19 +8,21 @@ import React, {
 } from 'react'
 import {useWindowDimensions} from 'react-native'
 
-import {isNative} from '#/platform/detection'
-import {VideoPlayerProvider} from './VideoPlayerContext'
+import {isNative, isWeb} from '#/platform/detection'
 
-const ActiveVideoContext = React.createContext<{
+const Context = React.createContext<{
   activeViewId: string | null
-  setActiveView: (viewId: string, src: string) => void
+  setActiveView: (viewId: string) => void
   sendViewPosition: (viewId: string, y: number) => void
 } | null>(null)
 
-export function ActiveVideoProvider({children}: {children: React.ReactNode}) {
+export function Provider({children}: {children: React.ReactNode}) {
+  if (!isWeb) {
+    throw new Error('ActiveVideoWebContext may onl be used on web.')
+  }
+
   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
@@ -31,9 +33,8 @@ export function ActiveVideoProvider({children}: {children: React.ReactNode}) {
   }, [activeViewId])
 
   const setActiveView = useCallback(
-    (viewId: string, src: string) => {
+    (viewId: 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
@@ -88,32 +89,26 @@ export function ActiveVideoProvider({children}: {children: React.ReactNode}) {
     [activeViewId, setActiveView, sendViewPosition],
   )
 
-  return (
-    <ActiveVideoContext.Provider value={value}>
-      <VideoPlayerProvider source={source ?? ''}>
-        {children}
-      </VideoPlayerProvider>
-    </ActiveVideoContext.Provider>
-  )
+  return <Context.Provider value={value}>{children}</Context.Provider>
 }
 
-export function useActiveVideoView({source}: {source: string}) {
-  const context = React.useContext(ActiveVideoContext)
+export function useActiveVideoWeb() {
+  const context = React.useContext(Context)
   if (!context) {
-    throw new Error('useActiveVideo must be used within a ActiveVideoProvider')
+    throw new Error(
+      'useActiveVideoWeb must be used within a ActiveVideoWebProvider',
+    )
   }
+
+  const {activeViewId, setActiveView, sendViewPosition} = context
   const id = useId()
 
   return {
-    active: context.activeViewId === id,
-    setActive: useCallback(
-      () => context.setActiveView(id, source),
-      [context, id, source],
-    ),
-    currentActiveView: context.activeViewId,
-    sendPosition: useCallback(
-      (y: number) => context.sendViewPosition(id, y),
-      [context, id],
-    ),
+    active: activeViewId === id,
+    setActive: () => {
+      setActiveView(id)
+    },
+    currentActiveView: activeViewId,
+    sendPosition: (y: number) => sendViewPosition(id, y),
   }
 }
diff --git a/src/view/com/util/post-embeds/VideoEmbed.tsx b/src/view/com/util/post-embeds/VideoEmbed.tsx
index 4e2909f40..b2bcd8511 100644
--- a/src/view/com/util/post-embeds/VideoEmbed.tsx
+++ b/src/view/com/util/post-embeds/VideoEmbed.tsx
@@ -9,12 +9,13 @@ import {Button, ButtonIcon} from '#/components/Button'
 import {Play_Filled_Corner2_Rounded as PlayIcon} from '#/components/icons/Play'
 import {VisibilityView} from '../../../../../modules/expo-bluesky-swiss-army'
 import {ErrorBoundary} from '../ErrorBoundary'
-import {useActiveVideoView} from './ActiveVideoContext'
+import {useActiveVideoNative} from './ActiveVideoNativeContext'
 import * as VideoFallback from './VideoEmbedInner/VideoFallback'
 
 export function VideoEmbed({source}: {source: string}) {
   const t = useTheme()
-  const {active, setActive} = useActiveVideoView({source})
+  const {activeSource, setActiveSource} = useActiveVideoNative()
+  const isActive = source === activeSource
   const {_} = useLingui()
 
   const [key, setKey] = useState(0)
@@ -40,15 +41,17 @@ export function VideoEmbed({source}: {source: string}) {
           enabled={true}
           onChangeStatus={isActive => {
             if (isActive) {
-              setActive()
+              setActiveSource(source)
             }
           }}>
-          {active ? (
+          {isActive ? (
             <VideoEmbedInnerNative />
           ) : (
             <Button
               style={[a.flex_1, t.atoms.bg_contrast_25]}
-              onPress={setActive}
+              onPress={() => {
+                setActiveSource(source)
+              }}
               label={_(msg`Play video`)}
               variant="ghost"
               color="secondary"
diff --git a/src/view/com/util/post-embeds/VideoEmbed.web.tsx b/src/view/com/util/post-embeds/VideoEmbed.web.tsx
index 5803b836d..c0d774abe 100644
--- a/src/view/com/util/post-embeds/VideoEmbed.web.tsx
+++ b/src/view/com/util/post-embeds/VideoEmbed.web.tsx
@@ -8,14 +8,14 @@ import {
 } from 'view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb'
 import {atoms as a, useTheme} from '#/alf'
 import {ErrorBoundary} from '../ErrorBoundary'
-import {useActiveVideoView} from './ActiveVideoContext'
+import {useActiveVideoWeb} from './ActiveVideoWebContext'
 import * as VideoFallback from './VideoEmbedInner/VideoFallback'
 
 export function VideoEmbed({source}: {source: string}) {
   const t = useTheme()
   const ref = useRef<HTMLDivElement>(null)
   const {active, setActive, sendPosition, currentActiveView} =
-    useActiveVideoView({source})
+    useActiveVideoWeb()
   const [onScreen, setOnScreen] = useState(false)
 
   useEffect(() => {
diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.tsx
index 8cbf32a83..ea56f2997 100644
--- a/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.tsx
+++ b/src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.tsx
@@ -9,7 +9,7 @@ import {useIsFocused} from '@react-navigation/native'
 import {HITSLOP_30} from '#/lib/constants'
 import {useAppState} from '#/lib/hooks/useAppState'
 import {logger} from '#/logger'
-import {useVideoPlayer} from '#/view/com/util/post-embeds/VideoPlayerContext'
+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'
@@ -20,7 +20,7 @@ import {
 import {TimeIndicator} from './TimeIndicator'
 
 export function VideoEmbedInnerNative() {
-  const player = useVideoPlayer()
+  const {player} = useActiveVideoNative()
   const ref = useRef<VideoView>(null)
   const isScreenFocused = useIsFocused()
   const isAppFocused = useAppState()
diff --git a/src/view/com/util/post-embeds/VideoPlayerContext.tsx b/src/view/com/util/post-embeds/VideoPlayerContext.tsx
deleted file mode 100644
index 95511099e..000000000
--- a/src/view/com/util/post-embeds/VideoPlayerContext.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import React, {useContext} from 'react'
-import type {VideoPlayer} from 'expo-video'
-import {useVideoPlayer as useExpoVideoPlayer} from 'expo-video'
-
-import {logger} from '#/logger'
-import {
-  AudioCategory,
-  PlatformInfo,
-} from '../../../../../modules/expo-bluesky-swiss-army'
-
-const VideoPlayerContext = React.createContext<VideoPlayer | null>(null)
-
-export function VideoPlayerProvider({
-  source,
-  children,
-}: {
-  source: string
-  children: React.ReactNode
-}) {
-  // eslint-disable-next-line @typescript-eslint/no-shadow
-  const player = useExpoVideoPlayer(source, player => {
-    try {
-      PlatformInfo.setAudioCategory(AudioCategory.Ambient)
-      PlatformInfo.setAudioActive(false)
-
-      player.loop = true
-      player.muted = true
-      player.play()
-    } catch (err) {
-      logger.error('Failed to init video player', {safeMessage: err})
-    }
-  })
-
-  return (
-    <VideoPlayerContext.Provider value={player}>
-      {children}
-    </VideoPlayerContext.Provider>
-  )
-}
-
-export function useVideoPlayer() {
-  const context = useContext(VideoPlayerContext)
-  if (!context) {
-    throw new Error('useVideoPlayer must be used within a VideoPlayerProvider')
-  }
-  return context
-}
diff --git a/src/view/com/util/post-embeds/VideoPlayerContext.web.tsx b/src/view/com/util/post-embeds/VideoPlayerContext.web.tsx
deleted file mode 100644
index 329fb1206..000000000
--- a/src/view/com/util/post-embeds/VideoPlayerContext.web.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react'
-
-export function VideoPlayerProvider({children}: {children: React.ReactNode}) {
-  return children
-}
-
-export function useVideoPlayer() {
-  throw new Error('useVideoPlayer must not be used on web')
-}