Floating player position-listener fixes (#289)

- 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.

- 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 jessopb
parent ba07cdeaa2
commit c6322ddb24

View file

@ -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,
@ -85,10 +140,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 &&
@ -103,93 +155,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
@ -295,24 +303,27 @@ export default function FileRenderFloating(props: Props) {
}
function handleDragMove(e, ui) {
setWasDragging(true);
const { x, y } = position;
const newX = x + ui.deltaX;
const newY = y + ui.deltaY;
setPosition({
x: newX,
y: newY,
});
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);
}
}
function handleDragStop(e) {
function handleDragStop(e, ui) {
if (wasDragging) {
e.stopPropagation();
// e.stopPropagation();
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);
}
}
@ -334,6 +345,7 @@ export default function FileRenderFloating(props: Props) {
'content__viewer--inline': !isFloating,
'content__viewer--secondary': isComment,
'content__viewer--theater-mode': !isFloating && videoTheaterMode,
'content__viewer--disable-click': wasDragging,
})}
style={
!isFloating && fileViewerRect
@ -363,13 +375,7 @@ export default function FileRenderFloating(props: Props) {
)}
{isReadyToPlay ? (
<FileRender
className="draggable"
uri={uri}
// @if TARGET='app'
desktopPlayStartTime={desktopPlayStartTime}
// @endif
/>
<FileRender className="draggable" uri={uri} desktopPlayStartTime={desktopPlayStartTime} />
) : (
<>
{collectionId && !canViewFile ? (