import clsx from 'clsx'; import throttle from 'lodash/throttle'; 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'; import { useSettings } from 'soapbox/hooks'; interface IScrollTopButton { /** Callback when clicked, and also when scrolled to the top. */ onClick: () => void /** Number of unread items. */ count: number /** Message to display in the button (should contain a `{count}` value). */ message: MessageDescriptor /** Distance from the top of the screen (scrolling down) before the button appears. */ threshold?: number /** Distance from the top of the screen (scrolling up) before the action is triggered. */ autoloadThreshold?: number } /** Floating new post counter above timelines, clicked to scroll to top. */ const ScrollTopButton: React.FC = ({ onClick, count, message, threshold = 400, autoloadThreshold = 50, }) => { const intl = useIntl(); const settings = useSettings(); const [scrolled, setScrolled] = useState(false); const autoload = settings.get('autoloadTimelines') === true; const visible = count > 0 && scrolled; const classes = clsx('fixed left-1/2 top-20 z-50 -translate-x-1/2', { 'hidden': !visible, }); const getScrollTop = (): number => { return (document.scrollingElement || document.documentElement).scrollTop; }; const maybeUnload = () => { if (autoload && getScrollTop() <= autoloadThreshold) { onClick(); } }; const handleScroll = useCallback(throttle(() => { maybeUnload(); if (getScrollTop() > threshold) { setScrolled(true); } else { setScrolled(false); } }, 150, { trailing: true }), [autoload, threshold, autoloadThreshold, onClick]); const scrollUp = () => { window.scrollTo({ top: 0 }); }; const handleClick: React.MouseEventHandler = () => { setTimeout(scrollUp, 10); onClick(); }; useEffect(() => { window.addEventListener('scroll', handleScroll); return () => { window.removeEventListener('scroll', handleScroll); }; }, [onClick]); useEffect(() => { maybeUnload(); }, [count]); return (
{(count > 0) && ( {intl.formatMessage(message, { count })} )}
); }; export default ScrollTopButton;