unmount when far offscreen + round 2dp
This commit is contained in:
@@ -34,6 +34,7 @@ export function VideoEmbed({source}: {source: string}) {
|
|||||||
// web only
|
// web only
|
||||||
sendPosition={() => {}}
|
sendPosition={() => {}}
|
||||||
onScreen={true}
|
onScreen={true}
|
||||||
|
onGoFarOffScreen={() => {}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ export function VideoEmbed({source}: {source: string}) {
|
|||||||
return () => observer.disconnect()
|
return () => observer.disconnect()
|
||||||
}, [sendPosition])
|
}, [sendPosition])
|
||||||
|
|
||||||
|
const onGoFarOffScreen = useCallback(() => {
|
||||||
|
setOnScreen(false)
|
||||||
|
setHasBeenOnScreen(false)
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -60,6 +65,7 @@ export function VideoEmbed({source}: {source: string}) {
|
|||||||
sendPosition={sendPosition}
|
sendPosition={sendPosition}
|
||||||
onScreen={onScreen}
|
onScreen={onScreen}
|
||||||
isAnyViewActive={currentActiveView !== null}
|
isAnyViewActive={currentActiveView !== null}
|
||||||
|
onGoFarOffScreen={onGoFarOffScreen}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export function VideoEmbedInner({}: {
|
|||||||
sendPosition: (position: number) => void
|
sendPosition: (position: number) => void
|
||||||
onScreen: boolean
|
onScreen: boolean
|
||||||
isAnyViewActive?: boolean
|
isAnyViewActive?: boolean
|
||||||
|
onGoFarOffScreen: () => void
|
||||||
}) {
|
}) {
|
||||||
const player = useVideoPlayer()
|
const player = useVideoPlayer()
|
||||||
const aref = useAnimatedRef<Animated.View>()
|
const aref = useAnimatedRef<Animated.View>()
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {msg} from '@lingui/macro'
|
|||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import Hls from 'hls.js'
|
import Hls from 'hls.js'
|
||||||
|
|
||||||
|
import {useAutoplayDisabled} from '#/state/preferences'
|
||||||
import {atoms as a, useTheme, web} from '#/alf'
|
import {atoms as a, useTheme, web} from '#/alf'
|
||||||
import {Button} from '#/components/Button'
|
import {Button} from '#/components/Button'
|
||||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||||
@@ -22,6 +23,7 @@ export function VideoEmbedInner({
|
|||||||
active,
|
active,
|
||||||
sendPosition,
|
sendPosition,
|
||||||
isAnyViewActive,
|
isAnyViewActive,
|
||||||
|
onGoFarOffScreen,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
source: string
|
source: string
|
||||||
@@ -29,6 +31,7 @@ export function VideoEmbedInner({
|
|||||||
setActive: () => void
|
setActive: () => void
|
||||||
sendPosition: (position: number) => void
|
sendPosition: (position: number) => void
|
||||||
onScreen: boolean
|
onScreen: boolean
|
||||||
|
onGoFarOffScreen: () => void
|
||||||
isAnyViewActive?: boolean
|
isAnyViewActive?: boolean
|
||||||
}) {
|
}) {
|
||||||
const ref = useRef<HTMLDivElement>(null)
|
const ref = useRef<HTMLDivElement>(null)
|
||||||
@@ -44,12 +47,16 @@ export function VideoEmbedInner({
|
|||||||
const position =
|
const position =
|
||||||
entry.boundingClientRect.y + entry.boundingClientRect.height / 2
|
entry.boundingClientRect.y + entry.boundingClientRect.height / 2
|
||||||
sendPosition(position)
|
sendPosition(position)
|
||||||
|
if (!entry.isIntersecting) {
|
||||||
|
// unmounts the video player
|
||||||
|
onGoFarOffScreen()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{threshold: Array.from({length: 101}, (_, i) => i / 100)},
|
{threshold: Array.from({length: 101}, (_, i) => i / 100)},
|
||||||
)
|
)
|
||||||
observer.observe(ref.current)
|
observer.observe(ref.current)
|
||||||
return () => observer.disconnect()
|
return () => observer.disconnect()
|
||||||
}, [sendPosition])
|
}, [sendPosition, onGoFarOffScreen])
|
||||||
|
|
||||||
// In case scrolling hasn't started yet, send up the position
|
// In case scrolling hasn't started yet, send up the position
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -196,18 +203,34 @@ function Controls({
|
|||||||
onOut: onMouseLeave,
|
onOut: onMouseLeave,
|
||||||
} = useInteractionState()
|
} = useInteractionState()
|
||||||
const isFullscreen = useFullscreen()
|
const isFullscreen = useFullscreen()
|
||||||
const {state: hasFocus, onIn: onFocus, onOut: onBlur} = useInteractionState()
|
const {
|
||||||
|
state: hasFocus,
|
||||||
|
onIn: onFocus,
|
||||||
|
onOut: onUnfocus,
|
||||||
|
} = useInteractionState()
|
||||||
|
const [interactingViaKeypress, setInteractingViaKeypress] = useState(false)
|
||||||
|
const onKeyDown = useCallback(() => {
|
||||||
|
setInteractingViaKeypress(true)
|
||||||
|
}, [])
|
||||||
|
const onBlur = useCallback(() => {
|
||||||
|
setInteractingViaKeypress(false)
|
||||||
|
onUnfocus()
|
||||||
|
}, [onUnfocus])
|
||||||
|
|
||||||
|
const autoplayDisabled = useAutoplayDisabled()
|
||||||
|
|
||||||
|
// autoplay
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (active) {
|
if (active && !autoplayDisabled) {
|
||||||
play()
|
play()
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
pause()
|
pause()
|
||||||
setFocused(false)
|
setFocused(false)
|
||||||
}
|
}
|
||||||
}, [active, play, pause, setFocused])
|
}, [active, play, pause, setFocused, autoplayDisabled])
|
||||||
|
|
||||||
|
// pause when offscreen
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!onScreen) {
|
if (!onScreen) {
|
||||||
pause()
|
pause()
|
||||||
@@ -225,7 +248,8 @@ function Controls({
|
|||||||
}
|
}
|
||||||
}, [togglePlayPause, setActive, setFocused, active, focused])
|
}, [togglePlayPause, setActive, setFocused, active, focused])
|
||||||
|
|
||||||
const showControls = hovered || hasFocus || !playing
|
const showControls =
|
||||||
|
(focused && !playing) || (interactingViaKeypress ? hasFocus : hovered)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -236,10 +260,12 @@ function Controls({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
}}
|
}}
|
||||||
|
onClick={() => setInteractingViaKeypress(false)}
|
||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
onFocus={onFocus}
|
onFocus={onFocus}
|
||||||
onBlur={onBlur}>
|
onBlur={onBlur}
|
||||||
|
onKeyDown={onKeyDown}>
|
||||||
<Pressable
|
<Pressable
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityHint={_(
|
accessibilityHint={_(
|
||||||
@@ -360,6 +386,8 @@ function formatTime(time: number) {
|
|||||||
return '--'
|
return '--'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
time = Math.round(time)
|
||||||
|
|
||||||
const minutes = Math.floor(time / 60)
|
const minutes = Math.floor(time / 60)
|
||||||
const seconds = String(time % 60).padStart(2, '0')
|
const seconds = String(time % 60).padStart(2, '0')
|
||||||
|
|
||||||
@@ -377,20 +405,24 @@ function useVideoUtils(ref: React.RefObject<HTMLVideoElement>) {
|
|||||||
if (!ref.current) return
|
if (!ref.current) return
|
||||||
let current = ref.current
|
let current = ref.current
|
||||||
|
|
||||||
|
function round(num: number) {
|
||||||
|
return Math.round(num * 100) / 100
|
||||||
|
}
|
||||||
|
|
||||||
// Initial values
|
// Initial values
|
||||||
setCurrentTime(Math.round(current.currentTime) || 0)
|
setCurrentTime(round(current.currentTime) || 0)
|
||||||
setDuration(Math.round(current.duration) || 0)
|
setDuration(round(current.duration) || 0)
|
||||||
setMuted(current.muted)
|
setMuted(current.muted)
|
||||||
setPlaying(!current.paused)
|
setPlaying(!current.paused)
|
||||||
|
|
||||||
const handleTimeUpdate = () => {
|
const handleTimeUpdate = () => {
|
||||||
if (!current) return
|
if (!current) return
|
||||||
setCurrentTime(Math.round(current.currentTime) || 0)
|
setCurrentTime(round(current.currentTime) || 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDurationChange = () => {
|
const handleDurationChange = () => {
|
||||||
if (!current) return
|
if (!current) return
|
||||||
setDuration(Math.round(current.duration) || 0)
|
setDuration(round(current.duration) || 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePlay = () => {
|
const handlePlay = () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user