TelegramPWA/src/components/common/hooks/useStickerPickerObservers.ts

114 lines
3.3 KiB
TypeScript

import type { RefObject } from 'react';
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
import useSyncEffect from '../../../hooks/useSyncEffect';
import { useCallback, useRef, useState } from '../../../lib/teact/teact';
import { ANIMATION_END_DELAY } from '../../../config';
import animateScroll from '../../../util/animateScroll';
import { REM } from '../helpers/mediaDimensions';
const STICKER_INTERSECTION_THROTTLE = 200;
const STICKER_INTERSECTION_MARGIN = 100;
const SLIDE_TRANSITION_DURATION = 350 + ANIMATION_END_DELAY;
const SCROLL_MAX_DISTANCE_WHEN_CLOSE = 200;
const SCROLL_MAX_DISTANCE_WHEN_FAR = 80;
const FOCUS_MARGIN = 0.5 * REM;
export function useStickerPickerObservers(
containerRef: RefObject<HTMLDivElement>,
headerRef: RefObject<HTMLDivElement>,
idPrefix: string,
isHidden?: boolean,
) {
const stickerSetIntersectionsRef = useRef<boolean[]>([]);
const [activeSetIndex, setActiveSetIndex] = useState<number>(0);
const {
observe: observeIntersectionForSet,
freeze: freezeForSet,
unfreeze: unfreezeForSet,
} = useIntersectionObserver({
rootRef: containerRef,
}, (entries) => {
const stickerSetIntersections = stickerSetIntersectionsRef.current;
entries.forEach((entry) => {
const index = Number(entry.target.id.replace(`${idPrefix}-`, ''));
stickerSetIntersections[index] = entry.isIntersecting;
});
const minIntersectingIndex = stickerSetIntersections.reduce((lowestIndex, isIntersecting, index) => {
return isIntersecting && index < lowestIndex ? index : lowestIndex;
}, Infinity);
if (minIntersectingIndex === Infinity) {
return;
}
setActiveSetIndex(minIntersectingIndex);
});
const {
observe: observeIntersectionForShowingItems,
freeze: freezeForShowingItems,
unfreeze: unfreezeForShowingItems,
} = useIntersectionObserver({
rootRef: containerRef,
throttleMs: STICKER_INTERSECTION_THROTTLE,
margin: STICKER_INTERSECTION_MARGIN,
});
const {
observe: observeIntersectionForPlayingItems,
} = useIntersectionObserver({
rootRef: containerRef,
throttleMs: STICKER_INTERSECTION_THROTTLE,
margin: STICKER_INTERSECTION_MARGIN,
});
const {
observe: observeIntersectionForCovers,
} = useIntersectionObserver({
rootRef: headerRef,
});
useSyncEffect(() => {
if (isHidden) {
freezeForSet();
freezeForShowingItems();
} else {
setTimeout(() => {
unfreezeForShowingItems();
unfreezeForSet();
}, SLIDE_TRANSITION_DURATION);
}
}, [freezeForSet, freezeForShowingItems, isHidden, unfreezeForSet, unfreezeForShowingItems]);
const selectStickerSet = useCallback((index: number) => {
setActiveSetIndex((currentIndex) => {
const stickerSetEl = document.getElementById(`${idPrefix}-${index}`)!;
const isClose = Math.abs(currentIndex - index) === 1;
animateScroll(
containerRef.current!,
stickerSetEl,
'start',
FOCUS_MARGIN,
isClose ? SCROLL_MAX_DISTANCE_WHEN_CLOSE : SCROLL_MAX_DISTANCE_WHEN_FAR,
);
return index;
});
}, [containerRef, idPrefix]);
return {
activeSetIndex,
observeIntersectionForSet,
observeIntersectionForShowingItems,
observeIntersectionForPlayingItems,
observeIntersectionForCovers,
selectStickerSet,
};
}