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:
parent
cb104017ad
commit
c8ad9718bb
1 changed files with 98 additions and 90 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue