Fix fullscreen infinite render loop in video embeds (#10807)
This commit is contained in:
@@ -650,14 +650,6 @@
|
|||||||
"count": 2
|
"count": 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/components/hooks/useFullscreen.ts": {
|
|
||||||
"@typescript-eslint/no-floating-promises": {
|
|
||||||
"count": 2
|
|
||||||
},
|
|
||||||
"react-hooks/set-state-in-effect": {
|
|
||||||
"count": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"src/components/hooks/useLandingEntry.native.ts": {
|
"src/components/hooks/useLandingEntry.native.ts": {
|
||||||
"react-hooks/set-state-in-effect": {
|
"react-hooks/set-state-in-effect": {
|
||||||
"count": 1
|
"count": 1
|
||||||
|
|||||||
@@ -108,10 +108,15 @@ export function useActiveVideoWeb() {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
active: activeViewId === id,
|
active: activeViewId === id,
|
||||||
setActive: () => {
|
setActive: useCallback(() => {
|
||||||
setActiveView(id)
|
setActiveView(id)
|
||||||
},
|
}, [setActiveView, id]),
|
||||||
currentActiveView: activeViewId,
|
currentActiveView: activeViewId,
|
||||||
sendPosition: (y: number) => sendViewPosition(id, y),
|
sendPosition: useCallback(
|
||||||
|
(y: number) => {
|
||||||
|
sendViewPosition(id, y)
|
||||||
|
},
|
||||||
|
[sendViewPosition, id],
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
import {
|
import {useCallback, useEffect, useRef, useSyncExternalStore} from 'react'
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
useSyncExternalStore,
|
|
||||||
} from 'react'
|
|
||||||
|
|
||||||
import {IS_WEB, IS_WEB_FIREFOX, IS_WEB_SAFARI} from '#/env'
|
import {IS_WEB, IS_WEB_FIREFOX, IS_WEB_SAFARI} from '#/env'
|
||||||
|
|
||||||
@@ -13,28 +7,38 @@ function fullscreenSubscribe(onChange: () => void) {
|
|||||||
return () => document.removeEventListener('fullscreenchange', onChange)
|
return () => document.removeEventListener('fullscreenchange', onChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFullscreenSnapshot() {
|
||||||
|
return Boolean(document.fullscreenElement)
|
||||||
|
}
|
||||||
|
|
||||||
export function useFullscreen(ref?: React.RefObject<HTMLElement | null>) {
|
export function useFullscreen(ref?: React.RefObject<HTMLElement | null>) {
|
||||||
if (!IS_WEB) throw new Error("'useFullscreen' is a web-only hook")
|
if (!IS_WEB) throw new Error("'useFullscreen' is a web-only hook")
|
||||||
const isFullscreen = useSyncExternalStore(fullscreenSubscribe, () =>
|
const isFullscreen = useSyncExternalStore(
|
||||||
Boolean(document.fullscreenElement),
|
fullscreenSubscribe,
|
||||||
|
getFullscreenSnapshot,
|
||||||
)
|
)
|
||||||
const scrollYRef = useRef<null | number>(null)
|
const scrollYRef = useRef<null | number>(null)
|
||||||
const [prevIsFullscreen, setPrevIsFullscreen] = useState(isFullscreen)
|
// Tracked via a ref rather than state so that reacting to a fullscreen change
|
||||||
|
// never schedules its own render. Scheduling a render in response to the
|
||||||
|
// external store value (the old `setPrevIsFullscreen` pattern) was a seed for
|
||||||
|
// the commit-phase update loop reported in APP-315 / APP-5PP / APP-7ZB.
|
||||||
|
const prevIsFullscreenRef = useRef(isFullscreen)
|
||||||
|
|
||||||
const toggleFullscreen = useCallback(() => {
|
const toggleFullscreen = useCallback(() => {
|
||||||
if (isFullscreen) {
|
if (isFullscreen) {
|
||||||
document.exitFullscreen()
|
void document.exitFullscreen()
|
||||||
} else {
|
} else {
|
||||||
if (!ref) throw new Error('No ref provided')
|
if (!ref) throw new Error('No ref provided')
|
||||||
if (!ref.current) return
|
if (!ref.current) return
|
||||||
scrollYRef.current = window.scrollY
|
scrollYRef.current = window.scrollY
|
||||||
ref.current.requestFullscreen()
|
void ref.current.requestFullscreen()
|
||||||
}
|
}
|
||||||
}, [isFullscreen, ref])
|
}, [isFullscreen, ref])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const prevIsFullscreen = prevIsFullscreenRef.current
|
||||||
if (prevIsFullscreen === isFullscreen) return
|
if (prevIsFullscreen === isFullscreen) return
|
||||||
setPrevIsFullscreen(isFullscreen)
|
prevIsFullscreenRef.current = isFullscreen
|
||||||
|
|
||||||
// Chrome has an issue where it doesn't scroll back to the top after exiting fullscreen
|
// 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
|
// Let's play it safe and do it if not FF or Safari, since anything else will probably be chromium
|
||||||
@@ -46,7 +50,7 @@ export function useFullscreen(ref?: React.RefObject<HTMLElement | null>) {
|
|||||||
}
|
}
|
||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
}, [isFullscreen, prevIsFullscreen])
|
}, [isFullscreen])
|
||||||
|
|
||||||
return [isFullscreen, toggleFullscreen] as const
|
return [isFullscreen, toggleFullscreen] as const
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user