Message / Contact: Support copying number, support non-registered (#1860)

This commit is contained in:
Alexander Zinchuk 2022-05-06 17:55:55 +01:00
parent 1f99bd3d49
commit 39cdd8e8e8
5 changed files with 62 additions and 24 deletions

View File

@ -60,7 +60,9 @@ const MessageSelectToolbar: FC<OwnProps & StateProps> = ({
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<OwnProps & StateProps> = ({
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<OwnProps & StateProps> = ({
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(

View File

@ -20,6 +20,8 @@ type StateProps = {
phoneCodeList: ApiCountryCode[];
};
const UNREGISTERED_CONTACT_ID = '0';
const Contact: FC<OwnProps & StateProps> = ({
contact, user, phoneCodeList,
}) => {
@ -31,6 +33,7 @@ const Contact: FC<OwnProps & StateProps> = ({
phoneNumber,
userId,
} = contact;
const isRegistered = userId !== UNREGISTERED_CONTACT_ID;
const handleClick = useCallback(() => {
openChat({ id: userId });
@ -38,8 +41,8 @@ const Contact: FC<OwnProps & StateProps> = ({
return (
<div
className={buildClassName('Contact', Boolean(userId) && 'interactive')}
onClick={userId ? handleClick : undefined}
className={buildClassName('Contact', isRegistered && 'interactive')}
onClick={isRegistered ? handleClick : undefined}
>
<Avatar size="large" user={user} text={firstName || lastName} />
<div className="contact-info">

View File

@ -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<OwnProps & StateProps> = ({
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<OwnProps & StateProps> = ({
onClose={closeMenu}
onCopyLink={handleCopyLink}
onCopyMessages={handleCopyMessages}
onCopyNumber={handleCopyNumber}
onDownload={handleDownloadClick}
onSaveGif={handleSaveGif}
onShowSeenBy={handleOpenSeenByModal}
@ -464,6 +472,7 @@ export default memo(withGlobal<OwnProps>(
&& !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<OwnProps>(
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,

View File

@ -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<OwnProps> = ({
onClose,
onCloseAnimationEnd,
onCopyLink,
onCopyNumber,
onDownload,
onSaveGif,
onShowSeenBy,
@ -128,16 +132,28 @@ const MessageContextMenu: FC<OwnProps> = ({
onSendReaction,
onCopyMessages,
}) => {
const { showNotification } = getActions();
// eslint-disable-next-line no-null/no-null
const menuRef = useRef<HTMLDivElement>(null);
// eslint-disable-next-line no-null/no-null
const scrollableRef = useRef<HTMLDivElement>(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<OwnProps> = ({
return enableScrolling;
}, [withScroll]);
const lang = useLang();
return (
<Menu
ref={menuRef}

View File

@ -2,6 +2,7 @@ import { ApiMediaFormat, ApiMessage } from '../../../../api/types';
import * as mediaLoader from '../../../../util/mediaLoader';
import {
getMessageContact,
getMessageMediaHash,
getMessagePhoto,
getMessageText,
@ -24,11 +25,13 @@ export function getMessageCopyOptions(
afterEffect?: () => 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?.();
},
});
}