diff --git a/ui/component/fileRenderFloating/view.jsx b/ui/component/fileRenderFloating/view.jsx index 2b67d0efa..cb7f3d0f2 100644 --- a/ui/component/fileRenderFloating/view.jsx +++ b/ui/component/fileRenderFloating/view.jsx @@ -19,9 +19,64 @@ import { isURIEqual } from 'util/lbryURI'; import AutoplayCountdown from 'component/autoplayCountdown'; const IS_DESKTOP_MAC = typeof process === 'object' ? process.platform === 'darwin' : false; -const DEBOUNCE_WINDOW_RESIZE_HANDLER_MS = 60; +const DEBOUNCE_WINDOW_RESIZE_HANDLER_MS = 100; export const INLINE_PLAYER_WRAPPER_CLASS = 'inline-player__wrapper'; +function getScreenWidth() { + if (document && document.documentElement) { + return document.documentElement.clientWidth; + } else { + return window.innerWidth; + } +} + +function getScreenHeight() { + if (document && document.documentElement) { + return document.documentElement.clientHeight; + } else { + return window.innerHeight; + } +} + +function getFloatingPlayerRect() { + // TODO: use 'fileViewerRect'? + const FLOATING_PLAYER_CLASS = 'content__viewer--floating'; + const elem = document.querySelector(`.${FLOATING_PLAYER_CLASS}`); + if (elem) { + return elem.getBoundingClientRect(); + } else { + return null; + } +} + +function clampRectToScreen(x, y, rect) { + if (rect) { + const ESTIMATED_SCROLL_BAR_PX = 50; + const screenW = getScreenWidth(); + const screenH = getScreenHeight(); + + if (x + rect.width > screenW - ESTIMATED_SCROLL_BAR_PX) { + x = screenW - rect.width - ESTIMATED_SCROLL_BAR_PX; + } + + if (y + rect.height > screenH) { + y = screenH - rect.height; + } + } + + return { x, y }; +} + +function calculateRelativePos(x, y) { + return { + x: x / getScreenWidth(), + y: y / getScreenHeight(), + }; +} + +// **************************************************************************** +// **************************************************************************** + type Props = { isFloating: boolean, fileInfo: FileListItem, @@ -84,10 +139,7 @@ export default function FileRenderFloating(props: Props) { x: -25, y: window.innerHeight - 400, }); - const [relativePos, setRelativePos] = useState({ - x: 0, - y: 0, - }); + const relativePosRef = React.useRef({ x: 0, y: 0 }); const navigateUrl = playingUri && @@ -102,93 +154,49 @@ export default function FileRenderFloating(props: Props) { ? __("It looks like you deleted or moved this file. We're rebuilding it now. It will only take a few seconds.") : __('Loading'); - function getScreenWidth() { - if (document && document.documentElement) { - return document.documentElement.clientWidth; - } else { - return window.innerWidth; - } + function restoreToRelativePosition() { + const newX = Math.round(relativePosRef.current.x * getScreenWidth()); + const newY = Math.round(relativePosRef.current.y * getScreenHeight()); + setPosition(clampRectToScreen(newX, newY, getFloatingPlayerRect())); } - function getScreenHeight() { - if (document && document.documentElement) { - return document.documentElement.clientHeight; - } else { - return window.innerHeight; - } - } + const clampToScreenOnResize = React.useCallback( + debounce(() => { + restoreToRelativePosition(); + }, DEBOUNCE_WINDOW_RESIZE_HANDLER_MS), + [] + ); - const clampToScreen = React.useCallback((pos) => { - const ESTIMATED_SCROLL_BAR_PX = 50; - const FLOATING_PLAYER_CLASS = 'content__viewer--floating'; - const fpPlayerElem = document.querySelector(`.${FLOATING_PLAYER_CLASS}`); - - if (fpPlayerElem) { - if (pos.x + fpPlayerElem.getBoundingClientRect().width > getScreenWidth() - ESTIMATED_SCROLL_BAR_PX) { - pos.x = getScreenWidth() - fpPlayerElem.getBoundingClientRect().width - ESTIMATED_SCROLL_BAR_PX; - } - if (pos.y + fpPlayerElem.getBoundingClientRect().height > getScreenHeight()) { - pos.y = getScreenHeight() - fpPlayerElem.getBoundingClientRect().height; - } - } - }, []); - - // Updated 'relativePos' based on persisted 'position': - const stringifiedPosition = JSON.stringify(position); + // ??? useEffect(() => { - const jsonPosition = JSON.parse(stringifiedPosition); - - setRelativePos({ - x: jsonPosition.x / getScreenWidth(), - y: jsonPosition.y / getScreenHeight(), - }); - }, [stringifiedPosition]); - - // Ensure player is within screen when 'isFloating' changes. - useEffect(() => { - const jsonPosition = JSON.parse(stringifiedPosition); - if (isFloating) { // When the player begins floating, remove the comment source // so that it doesn't try to resize again in case of going back // to the origin's comment section and fail to position correctly if (isComment && playingUri) clearSecondarySource(playingUri.uri); - - let pos = { x: jsonPosition.x, y: jsonPosition.y }; - clampToScreen(pos); - if (pos.x !== position.x || pos.y !== position.y) { - setPosition({ x: pos.x, y: pos.y }); - } } - }, [ - clampToScreen, - clearSecondarySource, - isComment, - isFloating, - playingUri, - position.x, - position.y, - setPosition, - stringifiedPosition, - ]); + }, [isFloating, isComment, clearSecondarySource, playingUri]); + + // Initial update for relativePosRef: + useEffect(() => { + relativePosRef.current = calculateRelativePos(position.x, position.y); + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + // Ensure player is within screen when 'isFloating' changes. + useEffect(() => { + if (isFloating) { + restoreToRelativePosition(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isFloating]); // Listen to main-window resizing and adjust the fp position accordingly: useEffect(() => { - const handleMainWindowResize = debounce(() => { - let newPos = { - x: Math.round(relativePos.x * getScreenWidth()), - y: Math.round(relativePos.y * getScreenHeight()), - }; - clampToScreen(newPos); - setPosition({ x: newPos.x, y: newPos.y }); - }, DEBOUNCE_WINDOW_RESIZE_HANDLER_MS); - - window.addEventListener('resize', handleMainWindowResize); - return () => window.removeEventListener('resize', handleMainWindowResize); - - // 'relativePos' is needed in the dependency list to avoid stale closure. - // Otherwise, this could just be changed to a one-time effect. - }, [clampToScreen, relativePos.x, relativePos.y, setPosition]); + if (isFloating) { + window.addEventListener('resize', clampToScreenOnResize); + return () => window.removeEventListener('resize', clampToScreenOnResize); + } + }, [isFloating, clampToScreenOnResize]); const handleResize = React.useCallback(() => { const element = mainFilePlaying @@ -283,25 +291,25 @@ export default function FileRenderFloating(props: Props) { function handleDragMove(e, ui) { const { x, y } = position; - const newX = x + ui.deltaX; - const newY = y + ui.deltaY; + const newX = ui.x; + const newY = ui.y; + // Mark as dragging if the position changed and we were not dragging before. if (!wasDragging && (newX !== x || newY !== y)) { setWasDragging(true); } - setPosition({ - x: newX, - y: newY, - }); } - function handleDragStop(e) { + function handleDragStop(e, ui) { if (wasDragging) { setWasDragging(false); - setRelativePos({ - x: position.x / getScreenWidth(), - y: position.y / getScreenHeight(), - }); + } + + let newPos = { x: ui.x, y: ui.y }; + if (newPos.x !== position.x || newPos.y !== position.y) { + newPos = clampRectToScreen(newPos.x, newPos.y, getFloatingPlayerRect()); + setPosition(newPos); + relativePosRef.current = calculateRelativePos(newPos.x, newPos.y); } }