TelegramPWA/src/components/right/hooks/useProfileState.ts
2024-12-29 11:59:16 +01:00

132 lines
3.7 KiB
TypeScript

import { useEffect } from '../../../lib/teact/teact';
import { ProfileState, type ProfileTabType } from '../../../types';
import animateScroll from '../../../util/animateScroll';
import { throttle } from '../../../util/schedulers';
import useEffectWithPrevDeps from '../../../hooks/useEffectWithPrevDeps';
import useLastCallback from '../../../hooks/useLastCallback';
const TRANSITION_DURATION = 300;
const PROGRAMMATIC_SCROLL_TIMEOUT_MS = 350;
const runThrottledForScroll = throttle((cb) => cb(), 250, false);
let isScrollingProgrammatically = false;
export default function useProfileState(
containerRef: { current: HTMLDivElement | null },
tabType: ProfileTabType,
profileState: ProfileState,
onProfileStateChange: (state: ProfileState) => void,
forceScrollProfileTab = false,
allowAutoScrollToTabs = false,
handleStopAutoScrollToTabs: () => void,
) {
// Scroll to tabs if needed
useEffectWithPrevDeps(([prevTabType]) => {
if ((prevTabType && prevTabType !== tabType && allowAutoScrollToTabs) || (tabType && forceScrollProfileTab)) {
const container = containerRef.current!;
const tabsEl = container.querySelector<HTMLDivElement>('.TabList')!;
handleStopAutoScrollToTabs();
if (container.scrollTop < tabsEl.offsetTop) {
onProfileStateChange(getStateFromTabType(tabType));
isScrollingProgrammatically = true;
animateScroll({
container,
element: tabsEl,
position: 'start',
forceDuration: TRANSITION_DURATION,
});
setTimeout(() => {
isScrollingProgrammatically = false;
}, PROGRAMMATIC_SCROLL_TIMEOUT_MS);
}
}
}, [tabType, onProfileStateChange, containerRef, forceScrollProfileTab,
allowAutoScrollToTabs, handleStopAutoScrollToTabs]);
// Scroll to top
useEffectWithPrevDeps(([prevProfileState]) => {
if (profileState !== ProfileState.Profile || profileState === prevProfileState) {
return;
}
const container = containerRef.current;
if (!container) {
return;
}
const tabListEl = container.querySelector<HTMLDivElement>('.TabList');
if (!tabListEl || tabListEl.offsetTop > container.scrollTop) {
return;
}
isScrollingProgrammatically = true;
animateScroll({
container,
element: container.firstElementChild as HTMLElement,
position: 'start',
maxDistance: container.offsetHeight * 2,
});
setTimeout(() => {
isScrollingProgrammatically = false;
}, PROGRAMMATIC_SCROLL_TIMEOUT_MS);
}, [profileState, containerRef]);
const determineProfileState = useLastCallback(() => {
const container = containerRef.current;
if (!container) {
return;
}
const tabListEl = container.querySelector<HTMLDivElement>('.TabList');
if (!tabListEl) {
return;
}
let state: ProfileState = ProfileState.Profile;
if (container.scrollTop >= tabListEl.offsetTop) {
state = getStateFromTabType(tabType);
}
onProfileStateChange(state);
});
// Determine profile state when switching tabs
useEffect(() => {
if (isScrollingProgrammatically) {
return;
}
determineProfileState();
}, [determineProfileState, tabType]);
// Determine profile state when scrolling
const handleScroll = useLastCallback(() => {
if (isScrollingProgrammatically) {
return;
}
runThrottledForScroll(determineProfileState);
});
return { handleScroll };
}
function getStateFromTabType(tabType: ProfileTabType) {
switch (tabType) {
case 'members':
return ProfileState.MemberList;
case 'stories':
return ProfileState.StoryList;
case 'dialogs':
return ProfileState.SavedDialogs;
default:
return ProfileState.SharedMedia;
}
}