import React, { memo, useCallback, useEffect, useRef, } from '../../../lib/teact/teact'; import { getActions } from '../../../global'; import type { FC } from '../../../lib/teact/teact'; import type { ApiAvailableReaction, ApiChatReactions, ApiMessage, ApiReaction, ApiSponsoredMessage, ApiStickerSet, ApiUser, } from '../../../api/types'; import type { IAnchorPosition } from '../../../types'; import { getMessageCopyOptions } from './helpers/copyOptions'; import { disableScrolling, enableScrolling } from '../../../util/scrollLock'; import { getUserFullName } from '../../../global/helpers'; import buildClassName from '../../../util/buildClassName'; import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment'; import renderText from '../../common/helpers/renderText'; import useFlag from '../../../hooks/useFlag'; import useContextMenuPosition from '../../../hooks/useContextMenuPosition'; import useLang from '../../../hooks/useLang'; import Menu from '../../ui/Menu'; import MenuItem from '../../ui/MenuItem'; import MenuSeparator from '../../ui/MenuSeparator'; import Skeleton from '../../ui/Skeleton'; import Avatar from '../../common/Avatar'; import ReactionSelector from './ReactionSelector'; import './MessageContextMenu.scss'; type OwnProps = { availableReactions?: ApiAvailableReaction[]; isOpen: boolean; anchor: IAnchorPosition; message: ApiMessage | ApiSponsoredMessage; canSendNow?: boolean; enabledReactions?: ApiChatReactions; maxUniqueReactions?: number; canReschedule?: boolean; canReply?: boolean; canPin?: boolean; canUnpin?: boolean; canDelete?: boolean; canReport?: boolean; canShowReactionsCount?: boolean; canShowReactionList?: boolean; canBuyPremium?: boolean; canEdit?: boolean; canForward?: boolean; canFaveSticker?: boolean; canUnfaveSticker?: boolean; canCopy?: boolean; canCopyLink?: boolean; canSelect?: boolean; isPrivate?: boolean; isCurrentUserPremium?: boolean; canDownload?: boolean; canSaveGif?: boolean; canRevote?: boolean; canClosePoll?: boolean; isDownloading?: boolean; canShowSeenBy?: boolean; seenByRecentUsers?: ApiUser[]; hasCustomEmoji?: boolean; customEmojiSets?: ApiStickerSet[]; onReply?: () => void; onEdit?: () => void; onPin?: () => void; onUnpin?: () => void; onForward?: () => void; onDelete?: () => void; onReport?: () => void; onFaveSticker?: () => void; onUnfaveSticker?: () => void; onSelect?: () => void; onSend?: () => void; onReschedule?: () => void; onClose: () => void; onCloseAnimationEnd?: () => void; onCopyLink?: () => void; onCopyMessages?: (messageIds: number[]) => void; onCopyNumber?: () => void; onDownload?: () => void; onSaveGif?: () => void; onCancelVote?: () => void; onClosePoll?: () => void; onShowSeenBy?: () => void; onShowReactors?: () => void; onAboutAds?: () => void; onSponsoredHide?: () => void; onToggleReaction?: (reaction: ApiReaction) => void; }; const SCROLLBAR_WIDTH = 10; const REACTION_BUBBLE_EXTRA_WIDTH = 32; const ANIMATION_DURATION = 200; const MessageContextMenu: FC = ({ availableReactions, isOpen, message, isPrivate, isCurrentUserPremium, enabledReactions, maxUniqueReactions, anchor, canSendNow, canReschedule, canBuyPremium, canReply, canEdit, canPin, canUnpin, canDelete, canReport, canForward, canFaveSticker, canUnfaveSticker, canCopy, canCopyLink, canSelect, canDownload, canSaveGif, canRevote, canClosePoll, isDownloading, canShowSeenBy, canShowReactionsCount, canShowReactionList, seenByRecentUsers, hasCustomEmoji, customEmojiSets, onReply, onEdit, onPin, onUnpin, onForward, onDelete, onReport, onFaveSticker, onUnfaveSticker, onSelect, onSend, onReschedule, onClose, onCloseAnimationEnd, onCopyLink, onCopyNumber, onDownload, onSaveGif, onCancelVote, onClosePoll, onShowSeenBy, onShowReactors, onToggleReaction, onCopyMessages, onAboutAds, onSponsoredHide, }) => { const { showNotification, openStickerSet, openCustomEmojiSets } = getActions(); // eslint-disable-next-line no-null/no-null const menuRef = useRef(null); // eslint-disable-next-line no-null/no-null const scrollableRef = useRef(null); const lang = useLang(); const noReactions = !isPrivate && !enabledReactions; const withReactions = canShowReactionList && !noReactions; const isSponsoredMessage = !('id' in message); const messageId = !isSponsoredMessage ? message.id : ''; const [isReady, markIsReady, unmarkIsReady] = useFlag(); const handleAfterCopy = useCallback(() => { showNotification({ message: lang('Share.Link.Copied'), }); onClose(); }, [lang, onClose, showNotification]); const handleOpenCustomEmojiSets = useCallback(() => { if (!customEmojiSets) return; if (customEmojiSets.length === 1) { openStickerSet({ stickerSetInfo: { shortName: customEmojiSets[0].shortName, }, }); } else { openCustomEmojiSets({ setIds: customEmojiSets.map((set) => set.id), }); } onClose(); }, [customEmojiSets, onClose, openCustomEmojiSets, openStickerSet]); const copyOptions = isSponsoredMessage ? [] : getMessageCopyOptions( message, handleAfterCopy, canCopyLink ? onCopyLink : undefined, onCopyMessages, onCopyNumber, ); const getTriggerElement = useCallback(() => { return isSponsoredMessage ? document.querySelector('.Transition__slide--active > .MessageList .SponsoredMessage') : document.querySelector(`.Transition__slide--active > .MessageList div[data-message-id="${messageId}"]`); }, [isSponsoredMessage, messageId]); const getRootElement = useCallback( () => document.querySelector('.Transition__slide--active > .MessageList'), [], ); const getMenuElement = useCallback( () => document.querySelector('.MessageContextMenu .bubble'), [], ); const getLayout = useCallback(() => { const extraHeightAudioPlayer = (IS_SINGLE_COLUMN_LAYOUT && (document.querySelector('.AudioPlayer-content'))?.offsetHeight) || 0; const pinnedElement = document.querySelector('.HeaderPinnedMessage-wrapper'); const extraHeightPinned = (((IS_SINGLE_COLUMN_LAYOUT && !extraHeightAudioPlayer) || (!IS_SINGLE_COLUMN_LAYOUT && pinnedElement?.classList.contains('full-width'))) && pinnedElement?.offsetHeight) || 0; return { extraPaddingX: SCROLLBAR_WIDTH, extraTopPadding: (document.querySelector('.MiddleHeader')!).offsetHeight, marginSides: withReactions ? REACTION_BUBBLE_EXTRA_WIDTH : undefined, extraMarginTop: extraHeightPinned + extraHeightAudioPlayer, }; }, [withReactions]); useEffect(() => { if (!isOpen) { unmarkIsReady(); return; } setTimeout(() => { markIsReady(); }, ANIMATION_DURATION); }, [isOpen, markIsReady, unmarkIsReady]); const { positionX, positionY, transformOriginX, transformOriginY, style, menuStyle, withScroll, } = useContextMenuPosition(anchor, getTriggerElement, getRootElement, getMenuElement, getLayout); useEffect(() => { disableScrolling(withScroll ? scrollableRef.current : undefined, '.ReactionSelector'); return enableScrolling; }, [withScroll]); return ( {withReactions && ( )}
{canSendNow && {lang('MessageScheduleSend')}} {canReschedule && ( {lang('MessageScheduleEditTime')} )} {canReply && {lang('Reply')}} {canEdit && {lang('Edit')}} {canFaveSticker && ( {lang('AddToFavorites')} )} {canUnfaveSticker && ( {lang('Stickers.RemoveFromFavorites')} )} {canCopy && copyOptions.map((option) => ( {lang(option.label)} ))} {canPin && {lang('DialogPin')}} {canUnpin && {lang('DialogUnpin')}} {canSaveGif && {lang('lng_context_save_gif')}} {canRevote && {lang('lng_polls_retract')}} {canClosePoll && {lang('lng_polls_stop')}} {canDownload && ( {isDownloading ? lang('lng_context_cancel_download') : lang('lng_media_download')} )} {canForward && {lang('Forward')}} {canSelect && {lang('Common.Select')}} {canReport && {lang('lng_context_report_msg')}} {(canShowSeenBy || canShowReactionsCount) && !isSponsoredMessage && ( {canShowReactionsCount && message.reactors?.count ? ( canShowSeenBy && message.seenByUserIds?.length ? lang( 'Chat.OutgoingContextMixedReactionCount', [message.reactors.count, message.seenByUserIds.length], ) : lang('Chat.ContextReactionCount', message.reactors.count, 'i') ) : ( message.seenByUserIds?.length === 1 && seenByRecentUsers ? renderText(getUserFullName(seenByRecentUsers[0])!) : (message.seenByUserIds?.length ? lang('Conversation.ContextMenuSeen', message.seenByUserIds.length, 'i') : lang('Conversation.ContextMenuNoViews') ) )}
{seenByRecentUsers?.map((user) => ( ))}
)} {canDelete && {lang('Delete')}} {hasCustomEmoji && ( <> {!customEmojiSets && ( <> )} {customEmojiSets && customEmojiSets.length === 1 && ( {renderText(lang('MessageContainsEmojiPack', customEmojiSets[0].title), ['simple_markdown', 'emoji'])} )} {customEmojiSets && customEmojiSets.length > 1 && ( {renderText(lang('MessageContainsEmojiPacks', customEmojiSets.length), ['simple_markdown'])} )} )} {isSponsoredMessage && {lang('SponsoredMessageInfo')}} {isSponsoredMessage && onSponsoredHide && ( {lang('HideAd')} )}
); }; export default memo(MessageContextMenu);