Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/orange-tips-doubt.md
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -123,6 +123,10 @@ const ExportMessages = () => {
if (type === 'email') {
setValue('format', 'html');
}

if (type === 'download') {
setValue('format', 'json');
}
}, [type, setValue]);

useEffect(() => {
Expand All @@ -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<void> => {
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,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,53 @@
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';

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'][]) => {
Expand Down Expand Up @@ -48,12 +83,21 @@ export const useExportMessagesAsPDFMutation = () => {
{messages.map((message) => {
const dateTime = formatDateAndTime(message.ts);
return (
<Text key={message._id} style={{ marginBottom: 5 }}>
<Text style={{ color: '#555', fontSize: 14 }}>{message.u.username}</Text>{' '}
<Text style={{ color: '#aaa', fontSize: 12 }}>{dateTime}</Text>
<Text>{'\n'}</Text>
{parseMessage(message)}
</Text>
<View key={message._id} style={{ marginBottom: 10 }}>
<View style={pdfStyles.messageHeader}>
<Text style={{ ...(message.tmid ? leftTab : {}), ...pdfStyles.username }}>{message.u.username}</Text>
<Text style={pdfStyles.dateTime}>{dateTime}</Text>
{message.tcount && <Text style={pdfStyles.threadMessagesCount}>{`${message.tcount} ${t('thread_messages')}`}</Text>}
</View>
<Text style={message.tmid ? pdfStyles.threadMessage : pdfStyles.message}>{parseMessage(message)}</Text>
{message.attachments?.map((attachment: MessageAttachmentDefault, index) => (
<View key={index}>
{attachment.description && <Text style={pdfStyles.message}>{attachment.description}</Text>}
{attachment.image_url && <Image src={attachment.title_link} style={attachment.image_dimensions} />}
<Text style={pdfStyles.message}>{attachment.title}</Text>
</View>
))}
</View>
);
})}
</View>
Expand All @@ -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<void>((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') });
},
});
};
1 change: 1 addition & 0 deletions packages/i18n/src/locales/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading