From 5ae0d40a14e7015daa0161e7e9d877690f8a339e Mon Sep 17 00:00:00 2001 From: Hailey Date: Wed, 28 Aug 2024 08:46:47 -0700 Subject: [Video] 🫧 Move logic around by platform (#5003) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.native.tsx | 2 +- src/App.web.tsx | 2 +- .../com/util/post-embeds/ActiveVideoContext.tsx | 119 --------------------- .../util/post-embeds/ActiveVideoNativeContext.tsx | 40 +++++++ .../com/util/post-embeds/ActiveVideoWebContext.tsx | 114 ++++++++++++++++++++ src/view/com/util/post-embeds/VideoEmbed.tsx | 13 ++- src/view/com/util/post-embeds/VideoEmbed.web.tsx | 4 +- .../VideoEmbedInner/VideoEmbedInnerNative.tsx | 4 +- .../com/util/post-embeds/VideoPlayerContext.tsx | 47 -------- .../util/post-embeds/VideoPlayerContext.web.tsx | 9 -- 10 files changed, 168 insertions(+), 186 deletions(-) delete mode 100644 src/view/com/util/post-embeds/ActiveVideoContext.tsx create mode 100644 src/view/com/util/post-embeds/ActiveVideoNativeContext.tsx create mode 100644 src/view/com/util/post-embeds/ActiveVideoWebContext.tsx delete mode 100644 src/view/com/util/post-embeds/VideoPlayerContext.tsx delete mode 100644 src/view/com/util/post-embeds/VideoPlayerContext.web.tsx (limited to 'src') 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/ActiveVideoContext.tsx b/src/view/com/util/post-embeds/ActiveVideoContext.tsx deleted file mode 100644 index d18dfc090..000000000 --- a/src/view/com/util/post-embeds/ActiveVideoContext.tsx +++ /dev/null @@ -1,119 +0,0 @@ -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(null) - const activeViewLocationRef = useRef(Infinity) - const [source, setSource] = useState(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, - sendViewPosition, - }), - [activeViewId, setActiveView, sendViewPosition], - ) - - return ( - - - {children} - - - ) -} - -export function useActiveVideoView({source}: {source: string}) { - const context = React.useContext(ActiveVideoContext) - if (!context) { - throw new Error('useActiveVideo must be used within a ActiveVideoProvider') - } - 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], - ), - } -} 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 ( + + {children} + + ) +} + +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/ActiveVideoWebContext.tsx b/src/view/com/util/post-embeds/ActiveVideoWebContext.tsx new file mode 100644 index 000000000..bc43e997c --- /dev/null +++ b/src/view/com/util/post-embeds/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 onl be used on web.') + } + + const [activeViewId, setActiveViewId] = useState(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 {children} +} + +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), + } +} 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 ? ( ) : (