Floating player position-listener fixes (#289)

## Issues
- 251 Dragging the floating player is super laggy

Recent changes and/or refactoring combined the effects or added dependancies into the effects, causing them to re-run excessively.

## Changes
- Restored effects to their original behavior.
- Don't perform the position check when dragging -- only do it when released.
- Do proper debouncing for the 'resize' listener -- the previous method was incorrect as a new function is created on each render.
This commit is contained in:
infinite-persistence 2021-11-15 13:13:48 -08:00 committed by GitHub
parent cb104017ad
commit c8ad9718bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -19,9 +19,64 @@ import { isURIEqual } from 'util/lbryURI';
import AutoplayCountdown from 'component/autoplayCountdown'; import AutoplayCountdown from 'component/autoplayCountdown';
const IS_DESKTOP_MAC = typeof process === 'object' ? process.platform === 'darwin' : false; 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'; 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 = { type Props = {
isFloating: boolean, isFloating: boolean,
fileInfo: FileListItem, fileInfo: FileListItem,
@ -84,10 +139,7 @@ export default function FileRenderFloating(props: Props) {
x: -25, x: -25,
y: window.innerHeight - 400, y: window.innerHeight - 400,
}); });
const [relativePos, setRelativePos] = useState({ const relativePosRef = React.useRef({ x: 0, y: 0 });
x: 0,
y: 0,
});
const navigateUrl = const navigateUrl =
playingUri && 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.") ? __("It looks like you deleted or moved this file. We're rebuilding it now. It will only take a few seconds.")
: __('Loading'); : __('Loading');
function getScreenWidth() { function restoreToRelativePosition() {
if (document && document.documentElement) { const newX = Math.round(relativePosRef.current.x * getScreenWidth());
return document.documentElement.clientWidth; const newY = Math.round(relativePosRef.current.y * getScreenHeight());
} else { setPosition(clampRectToScreen(newX, newY, getFloatingPlayerRect()));
return window.innerWidth;
}
} }
function getScreenHeight() { const clampToScreenOnResize = React.useCallback(
if (document && document.documentElement) { debounce(() => {
return document.documentElement.clientHeight; restoreToRelativePosition();
} else { }, DEBOUNCE_WINDOW_RESIZE_HANDLER_MS),
return window.innerHeight; []
} );
}
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(() => { 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) { if (isFloating) {
// When the player begins floating, remove the comment source // When the player begins floating, remove the comment source
// so that it doesn't try to resize again in case of going back // 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 // to the origin's comment section and fail to position correctly
if (isComment && playingUri) clearSecondarySource(playingUri.uri); 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 });
}
} }
}, [ }, [isFloating, isComment, clearSecondarySource, playingUri]);
clampToScreen,
clearSecondarySource, // Initial update for relativePosRef:
isComment, useEffect(() => {
isFloating, relativePosRef.current = calculateRelativePos(position.x, position.y);
playingUri, }, []); // eslint-disable-line react-hooks/exhaustive-deps
position.x,
position.y, // Ensure player is within screen when 'isFloating' changes.
setPosition, useEffect(() => {
stringifiedPosition, if (isFloating) {
]); restoreToRelativePosition();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isFloating]);
// Listen to main-window resizing and adjust the fp position accordingly: // Listen to main-window resizing and adjust the fp position accordingly:
useEffect(() => { useEffect(() => {
const handleMainWindowResize = debounce(() => { if (isFloating) {
let newPos = { window.addEventListener('resize', clampToScreenOnResize);
x: Math.round(relativePos.x * getScreenWidth()), return () => window.removeEventListener('resize', clampToScreenOnResize);
y: Math.round(relativePos.y * getScreenHeight()), }
}; }, [isFloating, clampToScreenOnResize]);
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]);
const handleResize = React.useCallback(() => { const handleResize = React.useCallback(() => {
const element = mainFilePlaying const element = mainFilePlaying
@ -283,25 +291,25 @@ export default function FileRenderFloating(props: Props) {
function handleDragMove(e, ui) { function handleDragMove(e, ui) {
const { x, y } = position; const { x, y } = position;
const newX = x + ui.deltaX; const newX = ui.x;
const newY = y + ui.deltaY; const newY = ui.y;
// Mark as dragging if the position changed and we were not dragging before. // Mark as dragging if the position changed and we were not dragging before.
if (!wasDragging && (newX !== x || newY !== y)) { if (!wasDragging && (newX !== x || newY !== y)) {
setWasDragging(true); setWasDragging(true);
} }
setPosition({
x: newX,
y: newY,
});
} }
function handleDragStop(e) { function handleDragStop(e, ui) {
if (wasDragging) { if (wasDragging) {
setWasDragging(false); 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);
} }
} }