diff --git a/src/components/middle/hooks/useScrollHooks.ts b/src/components/middle/hooks/useScrollHooks.ts index 4c880dc58..2bb7251eb 100644 --- a/src/components/middle/hooks/useScrollHooks.ts +++ b/src/components/middle/hooks/useScrollHooks.ts @@ -11,6 +11,7 @@ import { MESSAGE_LIST_SENSITIVE_AREA } from '../../../util/browser/windowEnviron import { debounce } from '../../../util/schedulers'; import { useDebouncedSignal } from '../../../hooks/useAsyncResolvers'; +import useDebouncedCallback from '../../../hooks/useDebouncedCallback'; import { useIntersectionObserver, useOnIntersect } from '../../../hooks/useIntersectionObserver'; import useLastCallback from '../../../hooks/useLastCallback'; import { useSignalEffect } from '../../../hooks/useSignalEffect'; @@ -19,6 +20,7 @@ import useSyncEffect from '../../../hooks/useSyncEffect'; const FAB_THRESHOLD = 50; const NOTCH_THRESHOLD = 1; // Notch has zero height so we at least need a 1px margin to intersect const CONTAINER_HEIGHT_DEBOUNCE = 200; +const SCROLL_TOOLS_DEBOUNCE = 100; const TOOLS_FREEZE_TIMEOUT = 350; // Approximate message sending animation duration export default function useScrollHooks({ @@ -57,19 +59,26 @@ export default function useScrollHooks({ const forwardsTriggerRef = useRef(); const fabTriggerRef = useRef(); - const toggleScrollTools = useLastCallback(() => { + const toggleScrollTools = useLastCallback((scrollDown: boolean, notch: boolean) => { + onScrollDownToggle?.(scrollDown); + onNotchToggle?.(notch); + }); + + const toggleScrollToolsDebounced = useDebouncedCallback( + toggleScrollTools, [toggleScrollTools], SCROLL_TOOLS_DEBOUNCE, true, false, + ); + + const updateScrollTools = useLastCallback(() => { if (!isReady) return; if (!messageIds?.length) { - onScrollDownToggle?.(false); - onNotchToggle?.(false); + toggleScrollTools(false, false); return; } if (!isViewportNewest) { - onScrollDownToggle?.(true); - onNotchToggle?.(true); + toggleScrollToolsDebounced(true, true); return; } @@ -86,8 +95,7 @@ export default function useScrollHooks({ if (scrollHeight === 0) return; - onScrollDownToggle?.(isUnread ? !isAtBottom : !isNearBottom); - onNotchToggle?.(!isAtBottom); + toggleScrollToolsDebounced(isUnread ? !isAtBottom : !isNearBottom, !isAtBottom); }); const { @@ -126,7 +134,7 @@ export default function useScrollHooks({ rootRef: containerRef, margin: FAB_THRESHOLD * 2, throttleScheduler: requestMeasure, - }, toggleScrollTools); + }, updateScrollTools); useOnIntersect(fabTriggerRef, observeIntersectionForFab); @@ -138,13 +146,13 @@ export default function useScrollHooks({ rootRef: containerRef, margin: NOTCH_THRESHOLD, throttleScheduler: requestMeasure, - }, toggleScrollTools); + }, updateScrollTools); useOnIntersect(fabTriggerRef, observeIntersectionForNotch); useEffect(() => { if (isReady) { - toggleScrollTools(); + updateScrollTools(); } }, [isReady]); @@ -152,10 +160,10 @@ export default function useScrollHooks({ const container = containerRef.current; if (!container) return; - container.addEventListener('scrollend', toggleScrollTools); + container.addEventListener('scrollend', updateScrollTools); return () => { - container.removeEventListener('scrollend', toggleScrollTools); + container.removeEventListener('scrollend', updateScrollTools); }; }, [containerRef]);