Message List: Support copying multiple messages (#1724)
This commit is contained in:
parent
abd10ba9b8
commit
9c789cdcb8
@ -48,6 +48,7 @@ import renderText from '../common/helpers/renderText';
|
|||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
import useWindowSize from '../../hooks/useWindowSize';
|
import useWindowSize from '../../hooks/useWindowSize';
|
||||||
import useInterval from '../../hooks/useInterval';
|
import useInterval from '../../hooks/useInterval';
|
||||||
|
import useNativeCopySelectedMessages from '../../hooks/useNativeCopySelectedMessages';
|
||||||
|
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
import MessageListContent from './MessageListContent';
|
import MessageListContent from './MessageListContent';
|
||||||
@ -138,7 +139,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
withBottomShift,
|
withBottomShift,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
loadViewportMessages, setScrollOffset, loadSponsoredMessages, loadMessageReactions,
|
loadViewportMessages, setScrollOffset, loadSponsoredMessages, loadMessageReactions, copyMessagesByIds,
|
||||||
} = getDispatch();
|
} = getDispatch();
|
||||||
|
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
@ -191,6 +192,8 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
memoFocusingIdRef.current = focusingId;
|
memoFocusingIdRef.current = focusingId;
|
||||||
}, [focusingId]);
|
}, [focusingId]);
|
||||||
|
|
||||||
|
useNativeCopySelectedMessages(copyMessagesByIds);
|
||||||
|
|
||||||
const messageGroups = useMemo(() => {
|
const messageGroups = useMemo(() => {
|
||||||
if (!messageIds || !messagesById) {
|
if (!messageIds || !messagesById) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@ -64,7 +64,7 @@
|
|||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
max-width: 32rem;
|
max-width: calc(100% * var(--composer-hidden-scale));
|
||||||
width: 100%;
|
width: 100%;
|
||||||
left: auto;
|
left: auto;
|
||||||
right: auto;
|
right: auto;
|
||||||
@ -72,7 +72,7 @@
|
|||||||
|
|
||||||
&-inner {
|
&-inner {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 32rem;
|
max-width: calc(100% * var(--composer-hidden-scale));
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0.25rem;
|
padding: 0.25rem;
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import captureKeyboardListeners from '../../util/captureKeyboardListeners';
|
|||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import usePrevious from '../../hooks/usePrevious';
|
import usePrevious from '../../hooks/usePrevious';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import useCopySelectedMessages from './hooks/useCopySelectedMessages';
|
||||||
|
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
|
|
||||||
@ -58,11 +59,13 @@ const MessageSelectToolbar: FC<OwnProps & StateProps> = ({
|
|||||||
exitMessageSelectMode,
|
exitMessageSelectMode,
|
||||||
openForwardMenuForSelectedMessages,
|
openForwardMenuForSelectedMessages,
|
||||||
downloadSelectedMessages,
|
downloadSelectedMessages,
|
||||||
|
copySelectedMessages,
|
||||||
} = getDispatch();
|
} = getDispatch();
|
||||||
|
|
||||||
const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag();
|
const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag();
|
||||||
const [isReportModalOpen, openReportModal, closeReportModal] = useFlag();
|
const [isReportModalOpen, openReportModal, closeReportModal] = useFlag();
|
||||||
|
|
||||||
|
useCopySelectedMessages(Boolean(isActive), copySelectedMessages);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return isActive && !isDeleteModalOpen && !isReportModalOpen
|
return isActive && !isDeleteModalOpen && !isReportModalOpen
|
||||||
? captureKeyboardListeners({
|
? captureKeyboardListeners({
|
||||||
@ -73,6 +76,11 @@ const MessageSelectToolbar: FC<OwnProps & StateProps> = ({
|
|||||||
: undefined;
|
: undefined;
|
||||||
}, [isActive, isDeleteModalOpen, isReportModalOpen, openDeleteModal, exitMessageSelectMode]);
|
}, [isActive, isDeleteModalOpen, isReportModalOpen, openDeleteModal, exitMessageSelectMode]);
|
||||||
|
|
||||||
|
const handleCopy = useCallback(() => {
|
||||||
|
copySelectedMessages();
|
||||||
|
exitMessageSelectMode();
|
||||||
|
}, [copySelectedMessages, exitMessageSelectMode]);
|
||||||
|
|
||||||
const handleDownload = useCallback(() => {
|
const handleDownload = useCallback(() => {
|
||||||
downloadSelectedMessages();
|
downloadSelectedMessages();
|
||||||
exitMessageSelectMode();
|
exitMessageSelectMode();
|
||||||
@ -139,6 +147,7 @@ const MessageSelectToolbar: FC<OwnProps & StateProps> = ({
|
|||||||
{canDownloadMessages && (
|
{canDownloadMessages && (
|
||||||
renderButton('download', lang('lng_media_download'), handleDownload, hasProtectedMessage)
|
renderButton('download', lang('lng_media_download'), handleDownload, hasProtectedMessage)
|
||||||
)}
|
)}
|
||||||
|
{renderButton('copy', lang('lng_context_copy_selected_items'), handleCopy, hasProtectedMessage)}
|
||||||
{renderButton('delete', lang('EditAdminGroupDeleteMessages'), openDeleteModal, !canDeleteMessages, true)}
|
{renderButton('delete', lang('EditAdminGroupDeleteMessages'), openDeleteModal, !canDeleteMessages, true)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
24
src/components/middle/hooks/useCopySelectedMessages.ts
Normal file
24
src/components/middle/hooks/useCopySelectedMessages.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { useEffect } from '../../../lib/teact/teact';
|
||||||
|
import { IS_MAC_OS } from '../../../util/environment';
|
||||||
|
import getKeyFromEvent from '../../../util/getKeyFromEvent';
|
||||||
|
|
||||||
|
const useCopySelectedMessages = (isActive: boolean, copySelectedMessages: NoneToVoidFunction) => {
|
||||||
|
useEffect(() => {
|
||||||
|
function handleCopy(e: KeyboardEvent) {
|
||||||
|
if (((IS_MAC_OS && e.metaKey) || (!IS_MAC_OS && e.ctrlKey)) && getKeyFromEvent(e) === 'c') {
|
||||||
|
e.preventDefault();
|
||||||
|
copySelectedMessages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isActive) {
|
||||||
|
document.addEventListener('keydown', handleCopy, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', handleCopy, false);
|
||||||
|
};
|
||||||
|
}, [copySelectedMessages, isActive]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useCopySelectedMessages;
|
||||||
@ -125,6 +125,7 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
openReactorListModal,
|
openReactorListModal,
|
||||||
loadFullChat,
|
loadFullChat,
|
||||||
loadReactors,
|
loadReactors,
|
||||||
|
copyMessagesByIds,
|
||||||
} = getDispatch();
|
} = getDispatch();
|
||||||
|
|
||||||
const { transitionClassNames } = useShowTransition(isOpen, onCloseAnimationEnd, undefined, false);
|
const { transitionClassNames } = useShowTransition(isOpen, onCloseAnimationEnd, undefined, false);
|
||||||
@ -288,6 +289,11 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
}, [message.chatId, message.id, rescheduleMessage]);
|
}, [message.chatId, message.id, rescheduleMessage]);
|
||||||
|
|
||||||
|
const handleCopyMessages = useCallback((messageIds: number[]) => {
|
||||||
|
copyMessagesByIds({ messageIds });
|
||||||
|
closeMenu();
|
||||||
|
}, [closeMenu, copyMessagesByIds]);
|
||||||
|
|
||||||
const handleCopyLink = useCallback(() => {
|
const handleCopyLink = useCallback(() => {
|
||||||
copyTextToClipboard(`https://t.me/${chatUsername || `c/${message.chatId.replace('-', '')}`}/${message.id}`);
|
copyTextToClipboard(`https://t.me/${chatUsername || `c/${message.chatId.replace('-', '')}`}/${message.id}`);
|
||||||
closeMenu();
|
closeMenu();
|
||||||
@ -366,6 +372,7 @@ const ContextMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
onReschedule={handleOpenCalendar}
|
onReschedule={handleOpenCalendar}
|
||||||
onClose={closeMenu}
|
onClose={closeMenu}
|
||||||
onCopyLink={handleCopyLink}
|
onCopyLink={handleCopyLink}
|
||||||
|
onCopyMessages={handleCopyMessages}
|
||||||
onDownload={handleDownloadClick}
|
onDownload={handleDownloadClick}
|
||||||
onShowSeenBy={handleOpenSeenByModal}
|
onShowSeenBy={handleOpenSeenByModal}
|
||||||
onSendReaction={handleSendReaction}
|
onSendReaction={handleSendReaction}
|
||||||
|
|||||||
@ -63,6 +63,7 @@ type OwnProps = {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onCloseAnimationEnd?: () => void;
|
onCloseAnimationEnd?: () => void;
|
||||||
onCopyLink?: () => void;
|
onCopyLink?: () => void;
|
||||||
|
onCopyMessages?: (messageIds: number[]) => void;
|
||||||
onDownload?: () => void;
|
onDownload?: () => void;
|
||||||
onShowSeenBy?: () => void;
|
onShowSeenBy?: () => void;
|
||||||
onShowReactors?: () => void;
|
onShowReactors?: () => void;
|
||||||
@ -120,12 +121,13 @@ const MessageContextMenu: FC<OwnProps> = ({
|
|||||||
onShowSeenBy,
|
onShowSeenBy,
|
||||||
onShowReactors,
|
onShowReactors,
|
||||||
onSendReaction,
|
onSendReaction,
|
||||||
|
onCopyMessages,
|
||||||
}) => {
|
}) => {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const scrollableRef = useRef<HTMLDivElement>(null);
|
const scrollableRef = useRef<HTMLDivElement>(null);
|
||||||
const copyOptions = getMessageCopyOptions(message, onClose, canCopyLink ? onCopyLink : undefined);
|
const copyOptions = getMessageCopyOptions(message, onClose, canCopyLink ? onCopyLink : undefined, onCopyMessages);
|
||||||
const noReactions = !isPrivate && !enabledReactions?.length;
|
const noReactions = !isPrivate && !enabledReactions?.length;
|
||||||
const withReactions = canShowReactionList && !noReactions;
|
const withReactions = canShowReactionList && !noReactions;
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
hasMessageLocalBlobUrl,
|
hasMessageLocalBlobUrl,
|
||||||
} from '../../../../modules/helpers';
|
} from '../../../../modules/helpers';
|
||||||
import { CLIPBOARD_ITEM_SUPPORTED, copyImageToClipboard, copyTextToClipboard } from '../../../../util/clipboard';
|
import { CLIPBOARD_ITEM_SUPPORTED, copyImageToClipboard, copyTextToClipboard } from '../../../../util/clipboard';
|
||||||
|
import getMessageIdsForSelectedText from '../../../../util/getMessageIdsForSelectedText';
|
||||||
|
|
||||||
type ICopyOptions = {
|
type ICopyOptions = {
|
||||||
label: string;
|
label: string;
|
||||||
@ -18,7 +19,10 @@ type ICopyOptions = {
|
|||||||
}[];
|
}[];
|
||||||
|
|
||||||
export function getMessageCopyOptions(
|
export function getMessageCopyOptions(
|
||||||
message: ApiMessage, afterEffect?: () => void, onCopyLink?: () => void,
|
message: ApiMessage,
|
||||||
|
afterEffect?: () => void,
|
||||||
|
onCopyLink?: () => void,
|
||||||
|
onCopyMessages?: (messageIds: number[]) => void,
|
||||||
): ICopyOptions {
|
): ICopyOptions {
|
||||||
const options: ICopyOptions = [];
|
const options: ICopyOptions = [];
|
||||||
const text = getMessageText(message);
|
const text = getMessageText(message);
|
||||||
@ -53,8 +57,13 @@ export function getMessageCopyOptions(
|
|||||||
options.push({
|
options.push({
|
||||||
label: getCopyLabel(hasSelection),
|
label: getCopyLabel(hasSelection),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
const clipboardText = hasSelection && selection ? selection.toString() : getMessageTextWithSpoilers(message)!;
|
const messageIds = getMessageIdsForSelectedText();
|
||||||
copyTextToClipboard(clipboardText);
|
if (messageIds?.length && onCopyMessages) {
|
||||||
|
onCopyMessages(messageIds);
|
||||||
|
} else {
|
||||||
|
const clipboardText = hasSelection && selection ? selection.toString() : getMessageTextWithSpoilers(message)!;
|
||||||
|
copyTextToClipboard(clipboardText);
|
||||||
|
}
|
||||||
|
|
||||||
if (afterEffect) {
|
if (afterEffect) {
|
||||||
afterEffect();
|
afterEffect();
|
||||||
|
|||||||
@ -533,7 +533,7 @@ export type ActionTypes = (
|
|||||||
'loadSponsoredMessages' | 'viewSponsoredMessage' | 'loadSendAs' | 'saveDefaultSendAs' | 'loadAvailableReactions' |
|
'loadSponsoredMessages' | 'viewSponsoredMessage' | 'loadSendAs' | 'saveDefaultSendAs' | 'loadAvailableReactions' |
|
||||||
'stopActiveEmojiInteraction' | 'interactWithAnimatedEmoji' | 'loadReactors' | 'setDefaultReaction' |
|
'stopActiveEmojiInteraction' | 'interactWithAnimatedEmoji' | 'loadReactors' | 'setDefaultReaction' |
|
||||||
'sendDefaultReaction' | 'sendEmojiInteraction' | 'sendWatchingEmojiInteraction' | 'loadMessageReactions' |
|
'sendDefaultReaction' | 'sendEmojiInteraction' | 'sendWatchingEmojiInteraction' | 'loadMessageReactions' |
|
||||||
'stopActiveReaction' | 'startActiveReaction' |
|
'stopActiveReaction' | 'startActiveReaction' | 'copySelectedMessages' | 'copyMessagesByIds' |
|
||||||
// downloads
|
// downloads
|
||||||
'downloadSelectedMessages' | 'downloadMessageMedia' | 'cancelMessageMediaDownload' |
|
'downloadSelectedMessages' | 'downloadMessageMedia' | 'cancelMessageMediaDownload' |
|
||||||
// scheduled messages
|
// scheduled messages
|
||||||
|
|||||||
27
src/hooks/useNativeCopySelectedMessages.ts
Normal file
27
src/hooks/useNativeCopySelectedMessages.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { useEffect } from '../lib/teact/teact';
|
||||||
|
import { IS_MAC_OS } from '../util/environment';
|
||||||
|
import getKeyFromEvent from '../util/getKeyFromEvent';
|
||||||
|
import getMessageIdsForSelectedText from '../util/getMessageIdsForSelectedText';
|
||||||
|
|
||||||
|
const useNativeCopySelectedMessages = (copyMessagesByIds: ({ messageIds }: { messageIds?: number[] }) => void) => {
|
||||||
|
useEffect(() => {
|
||||||
|
function handleCopy(e: KeyboardEvent) {
|
||||||
|
if (((IS_MAC_OS && e.metaKey) || (!IS_MAC_OS && e.ctrlKey)) && getKeyFromEvent(e) === 'c') {
|
||||||
|
const messageIds = getMessageIdsForSelectedText();
|
||||||
|
|
||||||
|
if (messageIds && messageIds.length > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
copyMessagesByIds({ messageIds });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('keydown', handleCopy, false);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', handleCopy, false);
|
||||||
|
};
|
||||||
|
}, [copyMessagesByIds]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useNativeCopySelectedMessages;
|
||||||
@ -32,12 +32,17 @@ import {
|
|||||||
selectIsViewportNewest,
|
selectIsViewportNewest,
|
||||||
selectReplyingToId,
|
selectReplyingToId,
|
||||||
selectReplyStack,
|
selectReplyStack,
|
||||||
|
selectSender,
|
||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
import { findLast } from '../../../util/iteratees';
|
import { findLast } from '../../../util/iteratees';
|
||||||
import { getServerTime } from '../../../util/serverTime';
|
import { getServerTime } from '../../../util/serverTime';
|
||||||
|
|
||||||
import versionNotification from '../../../versionNotification.txt';
|
import versionNotification from '../../../versionNotification.txt';
|
||||||
import parseMessageInput from '../../../util/parseMessageInput';
|
import parseMessageInput from '../../../util/parseMessageInput';
|
||||||
|
import { getMessageSummaryText, getSenderTitle } from '../../helpers';
|
||||||
|
import * as langProvider from '../../../util/langProvider';
|
||||||
|
import { copyTextToClipboard } from '../../../util/clipboard';
|
||||||
|
import { GlobalState } from '../../../global/types';
|
||||||
|
|
||||||
const FOCUS_DURATION = 1500;
|
const FOCUS_DURATION = 1500;
|
||||||
const FOCUS_NO_HIGHLIGHT_DURATION = FAST_SMOOTH_MAX_DURATION + ANIMATION_END_DELAY;
|
const FOCUS_NO_HIGHLIGHT_DURATION = FAST_SMOOTH_MAX_DURATION + ANIMATION_END_DELAY;
|
||||||
@ -672,3 +677,45 @@ addReducer('closeSeenByModal', (global) => {
|
|||||||
seenByModal: undefined,
|
seenByModal: undefined,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addReducer('copySelectedMessages', (global) => {
|
||||||
|
if (!global.selectedMessages) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { chatId, messageIds } = global.selectedMessages;
|
||||||
|
copyTextForMessages(global, chatId, messageIds);
|
||||||
|
});
|
||||||
|
|
||||||
|
addReducer('copyMessagesByIds', (global, actions, payload: { messageIds?: number[] } ) => {
|
||||||
|
const { messageIds } = payload;
|
||||||
|
const chat = selectCurrentChat(global);
|
||||||
|
if (!messageIds || messageIds.length === 0 || !chat) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
copyTextForMessages(global, chat.id, messageIds);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function copyTextForMessages(global: GlobalState, chatId: string, messageIds: number[]) {
|
||||||
|
const { threadId } = selectCurrentMessageList(global) || {};
|
||||||
|
const lang = langProvider.getTranslation;
|
||||||
|
|
||||||
|
const chatMessages = selectChatMessages(global, chatId);
|
||||||
|
if (!chatMessages || !threadId) return;
|
||||||
|
const messages = messageIds
|
||||||
|
.map((id) => chatMessages[id])
|
||||||
|
.filter((message) => selectAllowedMessageActions(global, message, threadId).canCopy)
|
||||||
|
.sort((message1, message2) => message1.id - message2.id);
|
||||||
|
|
||||||
|
const result = messages.reduce((acc, message) => {
|
||||||
|
const sender = selectSender(global, message);
|
||||||
|
acc.push(`> ${sender ? getSenderTitle(lang, sender) : ''}:`);
|
||||||
|
acc.push(getMessageSummaryText(lang, message, false, 0, undefined, true) + '\n');
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, [] as string[]);
|
||||||
|
|
||||||
|
copyTextToClipboard(result.join('\n'));
|
||||||
|
}
|
||||||
|
|||||||
@ -16,11 +16,12 @@ export function getMessageSummaryText(
|
|||||||
noEmoji = false,
|
noEmoji = false,
|
||||||
truncateLength = TRUNCATED_SUMMARY_LENGTH,
|
truncateLength = TRUNCATED_SUMMARY_LENGTH,
|
||||||
noReactions = true,
|
noReactions = true,
|
||||||
|
isExtended = false,
|
||||||
) {
|
) {
|
||||||
const emoji = !noEmoji && getMessageSummaryEmoji(message, noReactions);
|
const emoji = !noEmoji && getMessageSummaryEmoji(message, noReactions);
|
||||||
const emojiWithSpace = emoji ? `${emoji} ` : '';
|
const emojiWithSpace = emoji ? `${emoji} ` : '';
|
||||||
const text = trimText(getMessageTextWithSpoilers(message), truncateLength);
|
const text = trimText(getMessageTextWithSpoilers(message), truncateLength);
|
||||||
const description = getMessageSummaryDescription(lang, message, text, noReactions);
|
const description = getMessageSummaryDescription(lang, message, text, noReactions, isExtended);
|
||||||
|
|
||||||
return `${emojiWithSpace}${description}`;
|
return `${emojiWithSpace}${description}`;
|
||||||
}
|
}
|
||||||
@ -104,6 +105,7 @@ export function getMessageSummaryDescription(
|
|||||||
message: ApiMessage,
|
message: ApiMessage,
|
||||||
truncatedText?: string | TextPart[],
|
truncatedText?: string | TextPart[],
|
||||||
noReactions = true,
|
noReactions = true,
|
||||||
|
isExtended = false,
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
text,
|
text,
|
||||||
@ -133,8 +135,7 @@ export function getMessageSummaryDescription(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sticker) {
|
if (sticker) {
|
||||||
summary = lang('AttachSticker')
|
summary = lang('AttachSticker').trim();
|
||||||
.trim();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio) {
|
if (audio) {
|
||||||
@ -146,7 +147,7 @@ export function getMessageSummaryDescription(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (document) {
|
if (document) {
|
||||||
summary = truncatedText || document.fileName;
|
summary = isExtended ? document.fileName : (truncatedText || document.fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contact) {
|
if (contact) {
|
||||||
@ -158,11 +159,15 @@ export function getMessageSummaryDescription(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (invoice) {
|
if (invoice) {
|
||||||
summary = 'Invoice';
|
summary = lang('PaymentInvoice') + ': ' + invoice.text;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text) {
|
if (text) {
|
||||||
summary = truncatedText;
|
if (isExtended && summary) {
|
||||||
|
summary += '\n' + truncatedText;
|
||||||
|
} else {
|
||||||
|
summary = truncatedText;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const reaction = !noReactions && getMessageRecentReaction(message);
|
const reaction = !noReactions && getMessageRecentReaction(message);
|
||||||
|
|||||||
29
src/util/getMessageIdsForSelectedText.ts
Normal file
29
src/util/getMessageIdsForSelectedText.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
const ELEMENT_NODE = 1;
|
||||||
|
|
||||||
|
export default function getMessageIdsForSelectedText() {
|
||||||
|
let selectedFragments = window.getSelection()?.getRangeAt(0).cloneContents();
|
||||||
|
if (!selectedFragments || selectedFragments.childElementCount === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageIds = Array.from(selectedFragments.children)
|
||||||
|
.reduce((result, node) => {
|
||||||
|
if (node.nodeType === ELEMENT_NODE && node.classList.contains('message-date-group')) {
|
||||||
|
return Array.from(node.querySelectorAll('.Message'))
|
||||||
|
.reduce((acc, messageEl) => acc.concat(Number((messageEl as HTMLElement).dataset.messageId)), result);
|
||||||
|
} else if (node.nodeType === ELEMENT_NODE && node.classList.contains('Message')) {
|
||||||
|
return result.concat(Number((node as HTMLElement).dataset.messageId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}, [] as number[]);
|
||||||
|
|
||||||
|
|
||||||
|
// Cleanup a document fragment because it is playing media content in the background
|
||||||
|
while (selectedFragments.firstChild) {
|
||||||
|
selectedFragments.removeChild(selectedFragments.firstChild);
|
||||||
|
}
|
||||||
|
selectedFragments = undefined;
|
||||||
|
|
||||||
|
return messageIds;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user