Skip to content

Commit

Permalink
[Video] Fix fullscreen on Chrome (#5246)
Browse files Browse the repository at this point in the history
  • Loading branch information
mozzius authored Sep 10, 2024
1 parent 0f6be24 commit 66239ba
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 30 deletions.
53 changes: 53 additions & 0 deletions src/components/hooks/useFullscreen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
useCallback,
useEffect,
useRef,
useState,
useSyncExternalStore,
} from 'react'

import {isFirefox, isSafari} from '#/lib/browser'
import {isWeb} from '#/platform/detection'

function fullscreenSubscribe(onChange: () => void) {
document.addEventListener('fullscreenchange', onChange)
return () => document.removeEventListener('fullscreenchange', onChange)
}

export function useFullscreen(ref?: React.RefObject<HTMLElement>) {
if (!isWeb) throw new Error("'useFullscreen' is a web-only hook")
const isFullscreen = useSyncExternalStore(fullscreenSubscribe, () =>
Boolean(document.fullscreenElement),
)
const scrollYRef = useRef<null | number>(null)
const [prevIsFullscreen, setPrevIsFullscreen] = useState(isFullscreen)

const toggleFullscreen = useCallback(() => {
if (isFullscreen) {
document.exitFullscreen()
} else {
if (!ref) throw new Error('No ref provided')
if (!ref.current) return
scrollYRef.current = window.scrollY
ref.current.requestFullscreen()
}
}, [isFullscreen, ref])

useEffect(() => {
if (prevIsFullscreen === isFullscreen) return
setPrevIsFullscreen(isFullscreen)

// Chrome has an issue where it doesn't scroll back to the top after exiting fullscreen
// Let's play it safe and do it if not FF or Safari, since anything else will probably be chromium
if (prevIsFullscreen && !isFirefox && !isSafari) {
setTimeout(() => {
if (scrollYRef.current !== null) {
window.scrollTo(0, scrollYRef.current)
scrollYRef.current = null
}
}, 100)
}
}, [isFullscreen, prevIsFullscreen])

return [isFullscreen, toggleFullscreen] as const
}
13 changes: 12 additions & 1 deletion src/view/com/util/post-embeds/VideoEmbed.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
VideoNotFoundError,
} from '#/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerWeb'
import {atoms as a} from '#/alf'
import {useFullscreen} from '#/components/hooks/useFullscreen'
import {ErrorBoundary} from '../ErrorBoundary'
import {useActiveVideoWeb} from './ActiveVideoWebContext'
import * as VideoFallback from './VideoEmbedInner/VideoFallback'
Expand Down Expand Up @@ -106,6 +107,8 @@ function ViewportObserver({
}) {
const ref = useRef<HTMLDivElement>(null)
const [nearScreen, setNearScreen] = useState(false)
const [isFullscreen] = useFullscreen()
const [nearScreenOrFullscreen, setNearScreenOrFullscreen] = useState(false)

// Send position when scrolling. This is done with an IntersectionObserver
// observing a div of 100vh height
Expand Down Expand Up @@ -135,9 +138,17 @@ function ViewportObserver({
}
}, [isAnyViewActive, sendPosition])

// disguesting effect - it should be `nearScreen` except when fullscreen
// when it should be whatever it was before fullscreen changed
useEffect(() => {
if (!isFullscreen) {
setNearScreenOrFullscreen(nearScreen)
}
}, [isFullscreen, nearScreen])

return (
<View style={[a.flex_1, a.flex_row]}>
{nearScreen && children}
{nearScreenOrFullscreen && children}
<div
ref={ref}
style={{
Expand Down
31 changes: 2 additions & 29 deletions src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import React, {
useCallback,
useEffect,
useRef,
useState,
useSyncExternalStore,
} from 'react'
import React, {useCallback, useEffect, useRef, useState} from 'react'
import {Pressable, View} from 'react-native'
import {SvgProps} from 'react-native-svg'
import {msg, Trans} from '@lingui/macro'
Expand All @@ -21,6 +15,7 @@ import {
} from '#/state/preferences'
import {atoms as a, useTheme, web} from '#/alf'
import {Button} from '#/components/Button'
import {useFullscreen} from '#/components/hooks/useFullscreen'
import {useInteractionState} from '#/components/hooks/useInteractionState'
import {
ArrowsDiagonalIn_Stroke2_Corner0_Rounded as ArrowsInIcon,
Expand Down Expand Up @@ -850,25 +845,3 @@ function useVideoUtils(ref: React.RefObject<HTMLVideoElement>) {
canPlay,
}
}

function fullscreenSubscribe(onChange: () => void) {
document.addEventListener('fullscreenchange', onChange)
return () => document.removeEventListener('fullscreenchange', onChange)
}

function useFullscreen(ref: React.RefObject<HTMLElement>) {
const isFullscreen = useSyncExternalStore(fullscreenSubscribe, () =>
Boolean(document.fullscreenElement),
)

const toggleFullscreen = useCallback(() => {
if (isFullscreen) {
document.exitFullscreen()
} else {
if (!ref.current) return
ref.current.requestFullscreen()
}
}, [isFullscreen, ref])

return [isFullscreen, toggleFullscreen] as const
}

0 comments on commit 66239ba

Please sign in to comment.