[Glitch] Temporarily hold timeline if mouse moved recently

Port 6e72dda7ef to glitch-soc
lolsob-rspec
Thibaut Girka 2019-01-08 21:21:46 +01:00 committed by ThibG
parent cf48c0e6e1
commit f4191a8a00
1 changed files with 48 additions and 2 deletions

View File

@ -10,6 +10,8 @@ import classNames from 'classnames';
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from 'flavours/glitch/util/fullscreen'; import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from 'flavours/glitch/util/fullscreen';
import LoadingIndicator from './loading_indicator'; import LoadingIndicator from './loading_indicator';
const MOUSE_IDLE_DELAY = 300;
export default class ScrollableList extends PureComponent { export default class ScrollableList extends PureComponent {
static contextTypes = { static contextTypes = {
@ -38,6 +40,8 @@ export default class ScrollableList extends PureComponent {
state = { state = {
fullscreen: null, fullscreen: null,
mouseMovedRecently: false,
scrollToTopOnMouseIdle: false,
}; };
intersectionObserverWrapper = new IntersectionObserverWrapper(); intersectionObserverWrapper = new IntersectionObserverWrapper();
@ -61,6 +65,47 @@ export default class ScrollableList extends PureComponent {
trailing: true, trailing: true,
}); });
mouseIdleTimer = null;
clearMouseIdleTimer = () => {
if (this.mouseIdleTimer === null) {
return;
}
clearTimeout(this.mouseIdleTimer);
this.mouseIdleTimer = null;
};
handleMouseMove = throttle(() => {
// As long as the mouse keeps moving, clear and restart the idle timer.
this.clearMouseIdleTimer();
this.mouseIdleTimer =
setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY);
this.setState(({
mouseMovedRecently,
scrollToTopOnMouseIdle,
}) => ({
mouseMovedRecently: true,
// Only set scrollToTopOnMouseIdle if we just started moving and were
// scrolled to the top. Otherwise, just retain the previous state.
scrollToTopOnMouseIdle:
mouseMovedRecently
? scrollToTopOnMouseIdle
: (this.node.scrollTop === 0),
}));
}, MOUSE_IDLE_DELAY / 2);
handleMouseIdle = () => {
if (this.state.scrollToTopOnMouseIdle) {
this.node.scrollTop = 0;
this.props.onScrollToTop();
}
this.setState({
mouseMovedRecently: false,
scrollToTopOnMouseIdle: false,
});
}
componentDidMount () { componentDidMount () {
this.attachScrollListener(); this.attachScrollListener();
this.attachIntersectionObserver(); this.attachIntersectionObserver();
@ -90,7 +135,7 @@ export default class ScrollableList extends PureComponent {
const someItemInserted = React.Children.count(prevProps.children) > 0 && const someItemInserted = React.Children.count(prevProps.children) > 0 &&
React.Children.count(prevProps.children) < React.Children.count(this.props.children) && React.Children.count(prevProps.children) < React.Children.count(this.props.children) &&
this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props); this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props);
if (someItemInserted && this.node.scrollTop > 0) { if ((someItemInserted && this.node.scrollTop > 0) || this.state.mouseMovedRecently) {
return this.node.scrollHeight - this.node.scrollTop; return this.node.scrollHeight - this.node.scrollTop;
} else { } else {
return null; return null;
@ -104,6 +149,7 @@ export default class ScrollableList extends PureComponent {
} }
componentWillUnmount () { componentWillUnmount () {
this.clearMouseIdleTimer();
this.detachScrollListener(); this.detachScrollListener();
this.detachIntersectionObserver(); this.detachIntersectionObserver();
detachFullscreenListener(this.onFullScreenChange); detachFullscreenListener(this.onFullScreenChange);
@ -181,7 +227,7 @@ export default class ScrollableList extends PureComponent {
); );
} else if (isLoading || childrenCount > 0 || hasMore || !emptyMessage) { } else if (isLoading || childrenCount > 0 || hasMore || !emptyMessage) {
scrollableArea = ( scrollableArea = (
<div className={classNames('scrollable', { fullscreen })} ref={this.setRef}> <div className={classNames('scrollable', { fullscreen })} ref={this.setRef} onMouseMove={this.handleMouseMove}>
<div role='feed' className='item-list'> <div role='feed' className='item-list'>
{prepend} {prepend}