From 39cdd8e8e84dc24d4cce613e30582fd874a5e459 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Fri, 6 May 2022 17:55:55 +0100 Subject: [PATCH] Message / Contact: Support copying number, support non-registered (#1860) --- .../middle/MessageSelectToolbar.tsx | 9 ++++--- src/components/middle/message/Contact.tsx | 7 +++-- .../middle/message/ContextMenuContainer.tsx | 17 +++++++++--- .../middle/message/MessageContextMenu.tsx | 26 +++++++++++++----- .../middle/message/helpers/copyOptions.ts | 27 ++++++++++++------- 5 files changed, 62 insertions(+), 24 deletions(-) diff --git a/src/components/middle/MessageSelectToolbar.tsx b/src/components/middle/MessageSelectToolbar.tsx index 8d88b2276..abc074a96 100644 --- a/src/components/middle/MessageSelectToolbar.tsx +++ b/src/components/middle/MessageSelectToolbar.tsx @@ -60,7 +60,9 @@ const MessageSelectToolbar: FC = ({ openForwardMenuForSelectedMessages, downloadSelectedMessages, copySelectedMessages, + showNotification, } = getActions(); + const lang = useLang(); const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag(); const [isReportModalOpen, openReportModal, closeReportModal] = useFlag(); @@ -78,8 +80,11 @@ const MessageSelectToolbar: FC = ({ const handleCopy = useCallback(() => { copySelectedMessages(); + showNotification({ + message: lang('Share.Link.Copied'), + }); exitMessageSelectMode(); - }, [copySelectedMessages, exitMessageSelectMode]); + }, [copySelectedMessages, exitMessageSelectMode, lang, showNotification]); const handleDownload = useCallback(() => { downloadSelectedMessages(); @@ -89,8 +94,6 @@ const MessageSelectToolbar: FC = ({ const prevSelectedMessagesCount = usePrevious(selectedMessagesCount || undefined, true); const renderingSelectedMessagesCount = isActive ? selectedMessagesCount : prevSelectedMessagesCount; - const lang = useLang(); - const formattedMessagesCount = lang('VoiceOver.Chat.MessagesSelected', renderingSelectedMessagesCount, 'i'); const className = buildClassName( diff --git a/src/components/middle/message/Contact.tsx b/src/components/middle/message/Contact.tsx index 98a1abbcf..f8bff416a 100644 --- a/src/components/middle/message/Contact.tsx +++ b/src/components/middle/message/Contact.tsx @@ -20,6 +20,8 @@ type StateProps = { phoneCodeList: ApiCountryCode[]; }; +const UNREGISTERED_CONTACT_ID = '0'; + const Contact: FC = ({ contact, user, phoneCodeList, }) => { @@ -31,6 +33,7 @@ const Contact: FC = ({ phoneNumber, userId, } = contact; + const isRegistered = userId !== UNREGISTERED_CONTACT_ID; const handleClick = useCallback(() => { openChat({ id: userId }); @@ -38,8 +41,8 @@ const Contact: FC = ({ return (
diff --git a/src/components/middle/message/ContextMenuContainer.tsx b/src/components/middle/message/ContextMenuContainer.tsx index 8f868cf1d..855a0677f 100644 --- a/src/components/middle/message/ContextMenuContainer.tsx +++ b/src/components/middle/message/ContextMenuContainer.tsx @@ -6,6 +6,7 @@ import { getActions, getGlobal, withGlobal } from '../../../global'; import { MessageListType } from '../../../global/types'; import { ApiAvailableReaction, ApiMessage } from '../../../api/types'; import { IAlbum, IAnchorPosition } from '../../../types'; + import { selectActiveDownloadIds, selectAllowedMessageActions, @@ -19,17 +20,18 @@ import { } from '../../../global/helpers'; import { SERVICE_NOTIFICATIONS_USER_ID } from '../../../config'; import { getDayStartAt } from '../../../util/dateFormat'; +import buildClassName from '../../../util/buildClassName'; +import { REM } from '../../common/helpers/mediaDimensions'; + import { copyTextToClipboard } from '../../../util/clipboard'; import useShowTransition from '../../../hooks/useShowTransition'; import useFlag from '../../../hooks/useFlag'; -import { REM } from '../../common/helpers/mediaDimensions'; import DeleteMessageModal from '../../common/DeleteMessageModal'; import ReportModal from '../../common/ReportModal'; import PinMessageModal from '../../common/PinMessageModal'; import MessageContextMenu from './MessageContextMenu'; import CalendarModal from '../../common/CalendarModal'; -import buildClassName from '../../../util/buildClassName'; const START_SIZE = 2 * REM; @@ -300,7 +302,12 @@ const ContextMenuContainer: FC = ({ const handleCopyLink = useCallback(() => { copyTextToClipboard(`https://t.me/${chatUsername || `c/${message.chatId.replace('-', '')}`}/${message.id}`); closeMenu(); - }, [chatUsername, closeMenu, message.chatId, message.id]); + }, [chatUsername, closeMenu, message]); + + const handleCopyNumber = useCallback(() => { + copyTextToClipboard(message.content.contact!.phoneNumber); + closeMenu(); + }, [closeMenu, message]); const handleDownloadClick = useCallback(() => { (album?.messages || [message]).forEach((msg) => { @@ -383,6 +390,7 @@ const ContextMenuContainer: FC = ({ onClose={closeMenu} onCopyLink={handleCopyLink} onCopyMessages={handleCopyMessages} + onCopyNumber={handleCopyNumber} onDownload={handleDownloadClick} onSaveGif={handleSaveGif} onShowSeenBy={handleOpenSeenByModal} @@ -464,6 +472,7 @@ export default memo(withGlobal( && !areReactionsEmpty(message.reactions) && message.reactions.canSeeList; const canRemoveReaction = isPrivate && message.reactions?.results?.some((l) => l.isChosen); const isProtected = selectIsMessageProtected(global, message); + const canCopyNumber = Boolean(message.content.contact); return { availableReactions: global.availableReactions, @@ -479,7 +488,7 @@ export default memo(withGlobal( canForward: !isProtected && !isScheduled && canForward, canFaveSticker: !isScheduled && canFaveSticker, canUnfaveSticker: !isScheduled && canUnfaveSticker, - canCopy: !isProtected && canCopy, + canCopy: canCopyNumber || (!isProtected && canCopy), canCopyLink: !isProtected && !isScheduled && canCopyLink, canSelect, canDownload: !isProtected && canDownload, diff --git a/src/components/middle/message/MessageContextMenu.tsx b/src/components/middle/message/MessageContextMenu.tsx index cf14bd314..970c87090 100644 --- a/src/components/middle/message/MessageContextMenu.tsx +++ b/src/components/middle/message/MessageContextMenu.tsx @@ -1,6 +1,7 @@ import React, { FC, memo, useCallback, useEffect, useRef, } from '../../../lib/teact/teact'; +import { getActions } from '../../../global'; import { ApiAvailableReaction, ApiMessage, ApiUser } from '../../../api/types'; import { IAnchorPosition } from '../../../types'; @@ -8,11 +9,12 @@ import { 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 useFlag from '../../../hooks/useFlag'; import useContextMenuPosition from '../../../hooks/useContextMenuPosition'; import useLang from '../../../hooks/useLang'; -import buildClassName from '../../../util/buildClassName'; -import useFlag from '../../../hooks/useFlag'; -import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment'; import Menu from '../../ui/Menu'; import MenuItem from '../../ui/MenuItem'; @@ -66,6 +68,7 @@ type OwnProps = { onCloseAnimationEnd?: () => void; onCopyLink?: () => void; onCopyMessages?: (messageIds: number[]) => void; + onCopyNumber?: () => void; onDownload?: () => void; onSaveGif?: () => void; onShowSeenBy?: () => void; @@ -121,6 +124,7 @@ const MessageContextMenu: FC = ({ onClose, onCloseAnimationEnd, onCopyLink, + onCopyNumber, onDownload, onSaveGif, onShowSeenBy, @@ -128,16 +132,28 @@ const MessageContextMenu: FC = ({ onSendReaction, onCopyMessages, }) => { + const { showNotification } = 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 copyOptions = getMessageCopyOptions(message, onClose, canCopyLink ? onCopyLink : undefined, onCopyMessages); + const lang = useLang(); const noReactions = !isPrivate && !enabledReactions?.length; const withReactions = canShowReactionList && !noReactions; const [isReady, markIsReady, unmarkIsReady] = useFlag(); + const handleAfterCopy = useCallback(() => { + showNotification({ + message: lang('Share.Link.Copied'), + }); + onClose(); + }, [lang, onClose, showNotification]); + + const copyOptions = getMessageCopyOptions( + message, handleAfterCopy, canCopyLink ? onCopyLink : undefined, onCopyMessages, onCopyNumber, + ); + const getTriggerElement = useCallback(() => { return document.querySelector(`.Transition__slide--active > .MessageList div[data-message-id="${message.id}"]`); }, [message.id]); @@ -193,8 +209,6 @@ const MessageContextMenu: FC = ({ return enableScrolling; }, [withScroll]); - const lang = useLang(); - return ( void, onCopyLink?: () => void, onCopyMessages?: (messageIds: number[]) => void, + onCopyNumber?: () => void, ): ICopyOptions { const options: ICopyOptions = []; const text = getMessageText(message); const photo = getMessagePhoto(message) || (!getMessageWebPageVideo(message) ? getMessageWebPagePhoto(message) : undefined); + const contact = getMessageContact(message); const mediaHash = getMessageMediaHash(message, 'inline'); const canImageBeCopied = photo && (mediaHash || hasMessageLocalBlobUrl(message)) && CLIPBOARD_ITEM_SUPPORTED; const selection = window.getSelection(); @@ -41,9 +44,7 @@ export function getMessageCopyOptions( Promise.resolve(mediaHash ? mediaLoader.fetch(mediaHash, ApiMediaFormat.BlobUrl) : photo!.blobUrl) .then(copyImageToClipboard); - if (afterEffect) { - afterEffect(); - } + afterEffect?.(); }, }); } @@ -68,9 +69,7 @@ export function getMessageCopyOptions( copyTextToClipboard(clipboardText); } - if (afterEffect) { - afterEffect(); - } + afterEffect?.(); }, }); } @@ -82,9 +81,19 @@ export function getMessageCopyOptions( handler: () => { onCopyLink(); - if (afterEffect) { - afterEffect(); - } + afterEffect?.(); + }, + }); + } + + if (contact && onCopyNumber) { + options.push({ + label: 'lng_profile_copy_phone', + icon: 'copy', + handler: () => { + onCopyNumber(); + + afterEffect?.(); }, }); }