TelegramPWA/src/components/story/StoryToggler.tsx

151 lines
4.6 KiB
TypeScript

import React, { memo, useEffect, useMemo } from '../../lib/teact/teact';
import { getActions, getGlobal, withGlobal } from '../../global';
import type { GlobalState } from '../../global/types';
import { ANIMATION_END_DELAY, PREVIEW_AVATAR_COUNT } from '../../config';
import { selectIsForumPanelOpen, selectPerformanceSettingsValue, selectTabState } from '../../global/selectors';
import { animateClosing, animateOpening, ANIMATION_DURATION } from './helpers/ribbonAnimation';
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
import useOldLang from '../../hooks/useOldLang';
import useShowTransition from '../../hooks/useShowTransition';
import useStoryPreloader from './hooks/useStoryPreloader';
import Avatar from '../common/Avatar';
import styles from './StoryToggler.module.scss';
interface OwnProps {
isArchived?: boolean;
canShow?: boolean;
}
interface StateProps {
currentUserId: string;
orderedPeerIds: string[];
isShown: boolean;
isForumPanelOpen?: boolean;
withAnimation?: boolean;
peerStories: GlobalState['stories']['byPeerId'];
}
const PRELOAD_PEERS = 5;
function StoryToggler({
currentUserId,
orderedPeerIds,
canShow,
isShown,
isForumPanelOpen,
isArchived,
withAnimation,
peerStories,
}: OwnProps & StateProps) {
const { toggleStoryRibbon } = getActions();
const lang = useOldLang();
const peers = useMemo(() => {
// No need for expensive global updates on users, so we avoid them
const usersById = getGlobal().users.byId;
const chatsById = getGlobal().chats.byId;
if (orderedPeerIds.length === 1) {
return [usersById[orderedPeerIds[0]] || chatsById[orderedPeerIds[0]]];
}
return orderedPeerIds
.map((id) => usersById[id] || chatsById[id])
.filter((peer) => peer && peer.id !== currentUserId)
.slice(0, PREVIEW_AVATAR_COUNT)
.reverse();
}, [currentUserId, orderedPeerIds]);
const closeFriends = useMemo(() => {
if (!peers?.length) return {};
return peers.reduce((acc, peer) => {
const stories = peerStories[peer.id];
if (!stories) return acc;
const isCloseFriend = stories.orderedIds.some((id) => {
const story = stories.byId[id];
if (!story || !('isForCloseFriends' in story)) return false;
const isRead = stories.lastReadId && story.id <= stories.lastReadId;
return story.isForCloseFriends && !isRead;
});
acc[peer.id] = isCloseFriend;
return acc;
}, {} as Record<string, boolean>);
}, [peerStories, peers]);
const preloadPeerIds = useMemo(() => {
return orderedPeerIds.slice(0, PRELOAD_PEERS);
}, [orderedPeerIds]);
useStoryPreloader(preloadPeerIds);
const isVisible = canShow && isShown;
// For some reason, setting 'slow' here also fixes scroll freezes on iOS when collapsing Story Ribbon
const { ref, shouldRender } = useShowTransition<HTMLButtonElement>({
isOpen: isVisible,
className: 'slow',
withShouldRender: true,
});
useEffect(() => {
if (!withAnimation || isForumPanelOpen) return;
if (isVisible) {
dispatchHeavyAnimationEvent(ANIMATION_DURATION + ANIMATION_END_DELAY);
animateClosing(isArchived);
} else {
dispatchHeavyAnimationEvent(ANIMATION_DURATION + ANIMATION_END_DELAY);
animateOpening(isArchived);
}
}, [isArchived, isVisible, withAnimation, isForumPanelOpen]);
if (!shouldRender) {
return undefined;
}
return (
<button
ref={ref}
type="button"
id="StoryToggler"
className={styles.root}
aria-label={lang('Chat.Context.Peer.OpenStory')}
onClick={() => toggleStoryRibbon({ isShown: true, isArchived })}
dir={lang.isRtl ? 'rtl' : undefined}
>
{peers.map((peer) => (
<Avatar
key={peer.id}
peer={peer}
size="tiny"
className={styles.avatar}
withStorySolid
forceFriendStorySolid={closeFriends[peer.id]}
/>
))}
</button>
);
}
export default memo(withGlobal<OwnProps>((global, { isArchived }): StateProps => {
const { orderedPeerIds: { archived, active }, byPeerId } = global.stories;
const { storyViewer: { isRibbonShown, isArchivedRibbonShown } } = selectTabState(global);
const isForumPanelOpen = selectIsForumPanelOpen(global);
const withAnimation = selectPerformanceSettingsValue(global, 'storyRibbonAnimations');
return {
currentUserId: global.currentUserId!,
orderedPeerIds: isArchived ? archived : active,
isShown: isArchived ? !isArchivedRibbonShown : !isRibbonShown,
isForumPanelOpen,
withAnimation,
peerStories: byPeerId,
};
})(StoryToggler));