2022-06-01 18:47:28 -07:00
|
|
|
import classNames from 'classnames';
|
2022-06-16 12:32:17 -07:00
|
|
|
import throttle from 'lodash/throttle';
|
2022-06-01 18:47:28 -07:00
|
|
|
import React, { useState, useEffect, useCallback } from 'react';
|
|
|
|
import { useIntl, MessageDescriptor } from 'react-intl';
|
|
|
|
|
|
|
|
import Icon from 'soapbox/components/icon';
|
|
|
|
import { Text } from 'soapbox/components/ui';
|
2022-06-03 10:38:54 -07:00
|
|
|
import { useSettings } from 'soapbox/hooks';
|
2022-06-01 18:47:28 -07:00
|
|
|
|
2022-06-03 10:34:30 -07:00
|
|
|
interface IScrollTopButton {
|
2022-06-03 11:09:46 -07:00
|
|
|
/** Callback when clicked, and also when scrolled to the top. */
|
2022-06-01 18:47:28 -07:00
|
|
|
onClick: () => void,
|
2022-06-03 11:09:46 -07:00
|
|
|
/** Number of unread items. */
|
2022-06-03 10:38:54 -07:00
|
|
|
count: number,
|
2022-06-03 11:09:46 -07:00
|
|
|
/** Message to display in the button (should contain a `{count}` value). */
|
2022-06-01 18:47:28 -07:00
|
|
|
message: MessageDescriptor,
|
2022-06-03 11:09:46 -07:00
|
|
|
/** Distance from the top of the screen (scrolling down) before the button appears. */
|
2022-06-01 18:47:28 -07:00
|
|
|
threshold?: number,
|
2022-06-03 11:09:46 -07:00
|
|
|
/** Distance from the top of the screen (scrolling up) before the action is triggered. */
|
2022-06-01 18:47:28 -07:00
|
|
|
autoloadThreshold?: number,
|
|
|
|
}
|
|
|
|
|
2022-06-03 11:09:46 -07:00
|
|
|
/** Floating new post counter above timelines, clicked to scroll to top. */
|
2022-06-03 10:34:30 -07:00
|
|
|
const ScrollTopButton: React.FC<IScrollTopButton> = ({
|
2022-06-01 18:47:28 -07:00
|
|
|
onClick,
|
2022-06-03 10:38:54 -07:00
|
|
|
count,
|
2022-06-01 18:47:28 -07:00
|
|
|
message,
|
|
|
|
threshold = 400,
|
|
|
|
autoloadThreshold = 50,
|
|
|
|
}) => {
|
|
|
|
const intl = useIntl();
|
|
|
|
const settings = useSettings();
|
|
|
|
|
|
|
|
const [scrolled, setScrolled] = useState<boolean>(false);
|
|
|
|
const autoload = settings.get('autoloadTimelines') === true;
|
|
|
|
|
2022-07-01 13:07:01 -07:00
|
|
|
const visible = count > 0 && scrolled;
|
|
|
|
|
|
|
|
const classes = classNames('left-1/2 -translate-x-1/2 fixed top-20 z-50', {
|
|
|
|
'hidden': !visible,
|
|
|
|
});
|
|
|
|
|
2022-06-28 17:23:25 -07:00
|
|
|
const getScrollTop = (): number => {
|
|
|
|
return (document.scrollingElement || document.documentElement).scrollTop;
|
|
|
|
};
|
2022-06-01 18:47:28 -07:00
|
|
|
|
2022-06-28 17:23:25 -07:00
|
|
|
const maybeUnload = () => {
|
|
|
|
if (autoload && getScrollTop() <= autoloadThreshold) {
|
2022-06-01 18:47:28 -07:00
|
|
|
onClick();
|
|
|
|
}
|
2022-06-28 17:23:25 -07:00
|
|
|
};
|
2022-06-01 18:47:28 -07:00
|
|
|
|
2022-06-28 17:23:25 -07:00
|
|
|
const handleScroll = useCallback(throttle(() => {
|
|
|
|
maybeUnload();
|
|
|
|
|
|
|
|
if (getScrollTop() > threshold) {
|
2022-06-01 18:47:28 -07:00
|
|
|
setScrolled(true);
|
|
|
|
} else {
|
|
|
|
setScrolled(false);
|
|
|
|
}
|
2022-06-29 06:12:12 -07:00
|
|
|
}, 150, { trailing: true }), [autoload, threshold, autoloadThreshold, onClick]);
|
2022-06-01 18:47:28 -07:00
|
|
|
|
|
|
|
const scrollUp = () => {
|
2022-06-03 12:00:56 -07:00
|
|
|
window.scrollTo({ top: 0 });
|
2022-06-01 18:47:28 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
const handleClick: React.MouseEventHandler = () => {
|
|
|
|
setTimeout(scrollUp, 10);
|
|
|
|
onClick();
|
|
|
|
};
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
window.addEventListener('scroll', handleScroll);
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
window.removeEventListener('scroll', handleScroll);
|
|
|
|
};
|
2022-06-29 06:12:12 -07:00
|
|
|
}, [onClick]);
|
2022-06-01 18:47:28 -07:00
|
|
|
|
2022-06-28 17:23:25 -07:00
|
|
|
useEffect(() => {
|
|
|
|
maybeUnload();
|
|
|
|
}, [count]);
|
|
|
|
|
2022-06-01 18:47:28 -07:00
|
|
|
return (
|
|
|
|
<div className={classes}>
|
|
|
|
<a className='flex items-center bg-primary-600 hover:bg-primary-700 hover:scale-105 active:scale-100 transition-transform text-white rounded-full px-4 py-2 space-x-1.5 cursor-pointer whitespace-nowrap' onClick={handleClick}>
|
2022-07-09 09:20:02 -07:00
|
|
|
<Icon src={require('@tabler/icons/arrow-bar-to-up.svg')} />
|
2022-06-01 18:47:28 -07:00
|
|
|
|
|
|
|
{(count > 0) && (
|
|
|
|
<Text theme='inherit' size='sm'>
|
2022-06-03 11:16:22 -07:00
|
|
|
{intl.formatMessage(message, { count })}
|
2022-06-01 18:47:28 -07:00
|
|
|
</Text>
|
|
|
|
)}
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2022-06-03 10:34:30 -07:00
|
|
|
export default ScrollTopButton;
|