2026-03-31 11:31:16 +02:00

208 lines
5.9 KiB
TypeScript

import { memo, useMemo } from '../../lib/teact/teact';
import { getActions, withGlobal } from '../../global';
import type {
ApiAvailableReaction, ApiPeer, ApiTypeStoryView, ApiUser,
} from '../../api/types';
import type { IconName } from '../../types/icons';
import { getUserFullName } from '../../global/helpers';
import { selectPeer } from '../../global/selectors';
import buildClassName from '../../util/buildClassName';
import { formatDateAtTime } from '../../util/dates/oldDateFormat';
import { isUserId } from '../../util/entities/ids';
import { REM } from '../common/helpers/mediaDimensions';
import useLastCallback from '../../hooks/useLastCallback';
import useOldLang from '../../hooks/useOldLang';
import GroupChatInfo from '../common/GroupChatInfo';
import PrivateChatInfo from '../common/PrivateChatInfo';
import ReactionStaticEmoji from '../common/reactions/ReactionStaticEmoji';
import ListItem, { type MenuItemContextAction } from '../ui/ListItem';
import styles from './StoryViewModal.module.scss';
type OwnProps = {
storyView: ApiTypeStoryView;
};
type StateProps = {
peer?: ApiPeer;
availableReactions?: ApiAvailableReaction[];
};
const CLOSE_ANIMATION_DURATION = 100;
const DEFAULT_REACTION_SIZE = 1.5 * REM;
const BULLET = '\u2022';
const StoryView = ({
storyView,
peer,
availableReactions,
}: OwnProps & StateProps) => {
const {
openChat,
closeStoryViewer,
unblockUser,
blockUser,
deleteContact,
updateStoryView,
focusMessage,
openStoryViewer,
closeStoryViewModal,
} = getActions();
const lang = useOldLang();
const handleClick = useLastCallback(() => {
const { type } = storyView;
if (type === 'repost') {
closeStoryViewModal();
openStoryViewer({
peerId: storyView.peerId,
storyId: storyView.storyId,
});
return;
}
closeStoryViewer();
setTimeout(() => {
if (type === 'user') {
openChat({ id: storyView.peerId });
} else if (type === 'forward') {
focusMessage({ chatId: storyView.peerId, messageId: storyView.messageId });
}
}, CLOSE_ANIMATION_DURATION);
});
const contextActions = useMemo(() => {
if (!isUserId(storyView.peerId)) return undefined;
const { peerId, areStoriesBlocked, isUserBlocked } = storyView;
const user = peer as ApiUser;
const { isContact } = user || {};
const fullName = getUserFullName(user);
const actions: MenuItemContextAction[] = [];
if (!isUserBlocked) {
if (!areStoriesBlocked) {
actions.push({
handler: () => {
blockUser({ userId: peerId, isOnlyStories: true });
updateStoryView({ userId: peerId, areStoriesBlocked: true });
},
title: lang('StoryHideFrom', fullName),
icon: 'hand-stop',
});
} else {
actions.push({
handler: () => {
unblockUser({ userId: peerId, isOnlyStories: true });
updateStoryView({ userId: peerId, areStoriesBlocked: false });
},
title: lang('StoryShowBackTo', fullName),
icon: 'play-story',
});
}
}
if (isContact) {
actions.push({
handler: () => {
deleteContact({ userId: peerId });
},
title: lang('DeleteContact'),
icon: 'delete-user',
destructive: true,
});
} else {
actions.push({
handler: () => {
if (isUserBlocked) {
unblockUser({ userId: peerId });
updateStoryView({ userId: peerId, isUserBlocked: false });
} else {
blockUser({ userId: peerId });
updateStoryView({ userId: peerId, isUserBlocked: true });
}
},
title: lang(isUserBlocked ? 'Unblock' : 'BlockUser'),
icon: isUserBlocked ? 'user' : 'delete-user',
destructive: !isUserBlocked,
});
}
return actions;
}, [lang, storyView, peer]);
const statusIcon: IconName = storyView.type === 'user' ? 'message-read'
: storyView.type === 'forward' ? 'forward' : 'loop';
const shouldColorStatus = storyView.type === 'forward' || storyView.type === 'repost';
const status = useMemo(() => {
const isModified = storyView.type === 'repost' && storyView.story.forwardInfo?.isModified;
const parts = [formatDateAtTime(lang, storyView.date * 1000)];
if (isModified) {
parts.push(lang('lng_edited'));
}
return parts.join(` ${BULLET} `);
}, [lang, storyView]);
return (
<ListItem
key={storyView.peerId}
className={buildClassName(
'chat-item-clickable small-icon',
styles.opacityFadeIn,
(storyView.isUserBlocked || storyView.areStoriesBlocked) && styles.blocked,
)}
onClick={handleClick}
rightElement={storyView.type === 'user' && storyView.reaction ? (
<ReactionStaticEmoji
reaction={storyView.reaction}
className={styles.viewReaction}
size={DEFAULT_REACTION_SIZE}
availableReactions={availableReactions}
withIconHeart
/>
) : undefined}
contextActions={contextActions}
withPortalForMenu
menuBubbleClassName={styles.menuBubble}
>
{isUserId(storyView.peerId) ? (
<PrivateChatInfo
className={buildClassName(shouldColorStatus && styles.withColoredStatus)}
userId={storyView.peerId}
noStatusOrTyping
status={status}
statusIcon={statusIcon}
withStory
forceShowSelf
/>
) : (
<GroupChatInfo
className={buildClassName(shouldColorStatus && styles.withColoredStatus)}
chatId={storyView.peerId}
status={status}
statusIcon={statusIcon}
withStory
/>
)}
</ListItem>
);
};
export default memo(withGlobal<OwnProps>((global, { storyView }) => {
const peer = selectPeer(global, storyView.peerId);
return {
peer,
availableReactions: global.reactions.availableReactions,
};
})(StoryView));