diff --git a/.changeset/orange-tips-doubt.md b/.changeset/orange-tips-doubt.md new file mode 100644 index 0000000000000..30d3c55c7b2db --- /dev/null +++ b/.changeset/orange-tips-doubt.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": minor +"@rocket.chat/i18n": minor +--- + +Improves the message exportation content for PDF method including images and information about threads and files diff --git a/apps/meteor/client/views/room/contextualBar/ExportMessages/ExportMessages.tsx b/apps/meteor/client/views/room/contextualBar/ExportMessages/ExportMessages.tsx index 037bf3db5bb50..58b6e58567ae7 100644 --- a/apps/meteor/client/views/room/contextualBar/ExportMessages/ExportMessages.tsx +++ b/apps/meteor/client/views/room/contextualBar/ExportMessages/ExportMessages.tsx @@ -111,8 +111,8 @@ const ExportMessages = () => { // Remove HTML from download options const downloadOutputOptions = outputOptions.slice(1); - const roomExportMutation = useRoomExportMutation(); - const downloadExportMutation = useDownloadExportMutation(); + const { mutateAsync: exportRoom } = useRoomExportMutation(); + const { mutateAsync: exportAndDownload } = useDownloadExportMutation(); const { selectedMessageStore } = useContext(SelectedMessageContext); const messageCount = useCountSelected(); @@ -123,6 +123,10 @@ const ExportMessages = () => { if (type === 'email') { setValue('format', 'html'); } + + if (type === 'download') { + setValue('format', 'json'); + } }, [type, setValue]); useEffect(() => { @@ -139,34 +143,45 @@ const ExportMessages = () => { setValue('messagesCount', messageCount, { shouldDirty: true }); }, [messageCount, setValue]); - const { mutate: exportAsPDF } = useExportMessagesAsPDFMutation(); + const { mutateAsync: exportAsPDF } = useExportMessagesAsPDFMutation(); - const handleExport = async ({ type, toUsers, dateFrom, dateTo, format, subject, additionalEmails }: ExportMessagesFormValues) => { + const handleExport = async ({ + type, + toUsers, + dateFrom, + dateTo, + format, + subject, + additionalEmails, + }: ExportMessagesFormValues): Promise => { const messages = selectedMessageStore.getSelectedMessages(); if (type === 'download') { if (format === 'pdf') { - return exportAsPDF(messages); + await exportAsPDF(messages); + return; } if (format === 'json') { - return downloadExportMutation.mutateAsync({ + await exportAndDownload({ mids: messages, }); + return; } } if (type === 'file') { - return roomExportMutation.mutateAsync({ + await exportRoom({ rid: room._id, type: 'file', ...(dateFrom && { dateFrom }), ...(dateTo && { dateTo }), format: format as 'html' | 'json', }); + return; } - roomExportMutation.mutateAsync({ + await exportRoom({ rid: room._id, type: 'email', toUsers, diff --git a/apps/meteor/client/views/room/contextualBar/ExportMessages/useExportMessagesAsPDFMutation.tsx b/apps/meteor/client/views/room/contextualBar/ExportMessages/useExportMessagesAsPDFMutation.tsx index 7422c6ee80a89..7c0d2a90aa107 100644 --- a/apps/meteor/client/views/room/contextualBar/ExportMessages/useExportMessagesAsPDFMutation.tsx +++ b/apps/meteor/client/views/room/contextualBar/ExportMessages/useExportMessagesAsPDFMutation.tsx @@ -1,7 +1,7 @@ -import { Document, Page, pdf, Text, View } from '@react-pdf/renderer'; -import type { IMessage } from '@rocket.chat/core-typings'; +import { Document, Image, Page, pdf, StyleSheet, Text, View } from '@react-pdf/renderer'; +import type { IMessage, MessageAttachmentDefault } from '@rocket.chat/core-typings'; import { escapeHTML } from '@rocket.chat/string-helpers'; -import { useSetting } from '@rocket.chat/ui-contexts'; +import { useSetting, useToastMessageDispatch } from '@rocket.chat/ui-contexts'; import { useMutation } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; @@ -9,10 +9,45 @@ import { Messages } from '../../../../../app/models/client'; import { MessageTypes } from '../../../../../app/ui-utils/lib/MessageTypes'; import { useFormatDateAndTime } from '../../../../hooks/useFormatDateAndTime'; +const leftTab = { + marginLeft: 20, +}; + +const pdfStyles = StyleSheet.create({ + messageHeader: { + display: 'flex', + flexDirection: 'row', + alignItems: 'flex-end', + gap: 10, + }, + username: { + color: '#000', + fontSize: 14, + }, + dateTime: { + color: '#aaa', + fontSize: 12, + }, + threadMessagesCount: { + color: '#000', + fontSize: 14, + }, + threadMessage: { + color: '#555', + fontSize: 12, + ...leftTab, + }, + message: { + color: '#555', + fontSize: 14, + }, +}); + export const useExportMessagesAsPDFMutation = () => { const { t } = useTranslation(); const chatopsUsername = useSetting('Chatops_Username'); const formatDateAndTime = useFormatDateAndTime(); + const dispatchToastMessage = useToastMessageDispatch(); return useMutation({ mutationFn: async (messageIds: IMessage['_id'][]) => { @@ -48,12 +83,21 @@ export const useExportMessagesAsPDFMutation = () => { {messages.map((message) => { const dateTime = formatDateAndTime(message.ts); return ( - - {message.u.username}{' '} - {dateTime} - {'\n'} - {parseMessage(message)} - + + + {message.u.username} + {dateTime} + {message.tcount && {`${message.tcount} ${t('thread_messages')}`}} + + {parseMessage(message)} + {message.attachments?.map((attachment: MessageAttachmentDefault, index) => ( + + {attachment.description && {attachment.description}} + {attachment.image_url && } + {attachment.title} + + ))} + ); })} @@ -63,22 +107,33 @@ export const useExportMessagesAsPDFMutation = () => { const instance = pdf(); - const callback = async () => { - const link = document.createElement('a'); - link.href = URL.createObjectURL(await instance.toBlob()); - link.download = `exportedMessages-${new Date().toISOString()}.pdf`; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - URL.revokeObjectURL(link.href); - }; + await new Promise((resolve, reject) => { + const callback = async () => { + const link = document.createElement('a'); + link.href = URL.createObjectURL(await instance.toBlob()); + link.download = `exportedMessages-${new Date().toISOString()}.pdf`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(link.href); + resolve(); + }; - try { - instance.on('change', callback); - instance.updateContainer(jsx); - } finally { - instance.removeListener('change', callback); - } + try { + instance.on('change', callback); + instance.updateContainer(jsx); + } catch (error) { + reject(error); + } finally { + instance.removeListener('change', callback); + } + }); + }, + onError: (error) => { + dispatchToastMessage({ type: 'error', message: error }); + }, + onSuccess: () => { + dispatchToastMessage({ type: 'success', message: t('Messages_exported_successfully') }); }, }); }; diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 9b134dac289aa..f994907692a9b 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -6694,6 +6694,7 @@ "this_app_is_included_with_subscription": "This app is included with {{bundleName}} plans", "thread": "thread", "thread_message": "thread message", + "thread_messages": "thread messages", "thread_message_preview": "thread message preview", "threads_counter_one": "{{count}} unread threaded message", "threads_counter_other": "{{count}} unread threaded messages",