208 lines
5.9 KiB
TypeScript
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));
|