click to steal active video

This commit is contained in:
Samuel Newman
2024-07-04 19:57:09 +01:00
parent bf71a5c80a
commit cec2438f6a
5 changed files with 77 additions and 7 deletions
@@ -22,6 +22,7 @@ const ActiveVideoContext = React.createContext<{
export function ActiveVideoProvider({children}: {children: React.ReactNode}) {
const [activeViewId, setActiveViewId] = useState<string | null>(null)
const [source, setSource] = useState<string | null>(null)
const [manuallySet, setManuallySet] = useState(false)
const {height: windowHeight} = useWindowDimensions()
const measurementCallbacks = useRef<Record<string, () => DOMRect | void>>({})
@@ -46,7 +47,37 @@ export function ActiveVideoProvider({children}: {children: React.ReactNode}) {
const active = closestToMiddle(windowHeight, videosInView)
if (active) {
setActiveViewId(active.id)
// change the active view if it's not already active
// if the user has manually set the active view, don't change it
// i.e. if the user clicks on a video at the bottom of the screen
// it takes precidence over the video that is closest to the middle
setActiveViewId(activeView => {
if (activeView !== active.id) {
if (manuallySet && videosInView.find(({id}) => id === activeView)) {
return activeView
}
setManuallySet(false)
return active.id
}
return activeView
})
} else {
// if no videos are in view, unset the active view
// if the active view is partially in view, keep it active
setActiveViewId(activeView => {
const activeRect = locations.find(({id}) => id === activeView)?.rect
if (activeRect) {
const topCond = activeRect.top + activeRect.height / 2 >= 0
const bottomCond =
activeRect.bottom - activeRect.height / 2 <= windowHeight
if (topCond && bottomCond) {
return activeView
}
}
return null
})
}
}
findAndActivateVideo()
@@ -55,7 +86,7 @@ export function ActiveVideoProvider({children}: {children: React.ReactNode}) {
return () => {
clearInterval(interval)
}
}, [activeViewId, windowHeight])
}, [manuallySet, windowHeight])
const value = useMemo(
() => ({
@@ -63,6 +94,7 @@ export function ActiveVideoProvider({children}: {children: React.ReactNode}) {
setActiveView: (viewId: string, src: string) => {
setActiveViewId(viewId)
setSource(src)
setManuallySet(true)
},
registerMeasurementCallback: (viewId: string, callback: () => void) => {
measurementCallbacks.current[viewId] = callback
+5 -1
View File
@@ -27,7 +27,11 @@ export function VideoEmbed({source}: {source: string}) {
a.my_xs,
]}>
{active ? (
<VideoEmbedInner source={source} active={active} />
<VideoEmbedInner
source={source}
active={active}
setActive={setActive}
/>
) : (
<Button
style={[a.flex_1, t.atoms.bg_contrast_25]}
@@ -43,7 +43,11 @@ export function VideoEmbed({source}: {source: string}) {
]}>
<div ref={ref} style={{display: 'flex', flex: 1}}>
{hasBeenActive ? (
<VideoEmbedInner source={source} active={active} />
<VideoEmbedInner
source={source}
active={active}
setActive={setActive}
/>
) : (
<Button
style={[a.flex_1, t.atoms.bg_contrast_25]}
@@ -13,7 +13,11 @@ import {atoms as a} from '#/alf'
import {Text} from '#/components/Typography'
import {useVideoPlayer} from './VideoPlayerContext'
export function VideoEmbedInner({}: {source: string; active: boolean}) {
export function VideoEmbedInner({}: {
source: string
active: boolean
setActive: () => void
}) {
const player = useVideoPlayer()
const aref = useAnimatedRef<Animated.View>()
const {height: windowHeight} = useWindowDimensions()
@@ -7,12 +7,15 @@ import {atoms as a, useTheme} from '#/alf'
export function VideoEmbedInner({
source,
active,
setActive,
}: {
source: string
active: boolean
setActive: () => void
}) {
const [hls] = useState(() => new Hls())
const ref = useRef<HTMLVideoElement>(null)
const [focused, setFocused] = useState(false)
const t = useTheme()
useEffect(() => {
@@ -38,6 +41,15 @@ export function VideoEmbedInner({
}
}, [source, hls])
useEffect(() => {
if (active) {
ref.current?.play()
} else {
ref.current?.pause()
setFocused(false)
}
}, [active])
return (
<View
style={[
@@ -55,8 +67,22 @@ export function VideoEmbedInner({
playsInline
preload="none"
loop
muted
autoPlay={active}
muted={!focused}
onClick={evt => {
evt.stopPropagation()
if (focused) {
if (ref.current?.paused) {
ref.current.play()
} else {
ref.current?.pause()
}
} else {
if (!active) {
setActive()
}
setFocused(true)
}
}}
/>
</View>
)