import { useCallback } from '../../../lib/teact/teact'; import { fastRaf } from '../../../util/schedulers'; import useRunDebounced from '../../../hooks/useRunDebounced'; import useFlag from '../../../hooks/useFlag'; const DEBOUNCE = 1000; const STICKY_TOP = 10; const STICKY_TOP_WITH_TOOLS = 60; export default function useStickyDates() { // For some reason we can not synchronously hide a sticky element (from `useLayoutEffect`) when chat opens // so we will add `position: sticky` only after first scroll. There would be no animation on the first show though. const [isScrolled, markIsScrolled] = useFlag(false); const runDebounced = useRunDebounced(DEBOUNCE, true); const updateStickyDates = useCallback((container: HTMLDivElement, hasTools?: boolean) => { markIsScrolled(); if (!document.body.classList.contains('is-scrolling-messages')) { fastRaf(() => { document.body.classList.add('is-scrolling-messages'); }); } runDebounced(() => { fastRaf(() => { const currentStuck = document.querySelector('.stuck'); if (currentStuck) { currentStuck.classList.remove('stuck'); } const stuckDateEl = findStuckDate(container, hasTools); if (stuckDateEl) { stuckDateEl.classList.add('stuck'); } document.body.classList.remove('is-scrolling-messages'); }); }); }, [markIsScrolled, runDebounced]); return { isScrolled, updateStickyDates, }; } function findStuckDate(container: HTMLElement, hasTools?: boolean) { const allElements = container.querySelectorAll('.sticky-date'); const containerTop = container.scrollTop; return Array.from(allElements).find((el) => { const { offsetTop, offsetHeight } = el; const top = offsetTop - containerTop; return -offsetHeight <= top && top <= (hasTools ? STICKY_TOP_WITH_TOOLS : STICKY_TOP); }); }