Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IMPROVE] Add interpreter to message md #27938

Merged
merged 1 commit into from
Feb 2, 2023
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
8 changes: 5 additions & 3 deletions ee/apps/omnichannel-transcript/src/OmnichannelTranscript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ type WorkDetailsWithSource = WorkDetails & {
from: string;
};

type MessageWithFiles = Pick<IMessage, '_id' | 'ts' | 'u' | 'msg'> & { files: ({ name?: string; buffer: Buffer | null } | undefined)[] };
type MessageWithFiles = Pick<IMessage, '_id' | 'ts' | 'u' | 'msg' | 'md'> & {
files: ({ name?: string; buffer: Buffer | null } | undefined)[];
};

type WorkerData = {
siteName: string;
Expand Down Expand Up @@ -79,7 +81,7 @@ export class OmnichannelTranscript extends ServiceClass implements IOmnichannelT
// Closing message should not appear :)
return Messages.findLivechatMessagesWithoutClosing(rid, {
sort: { ts: 1 },
projection: { _id: 1, msg: 1, u: 1, t: 1, ts: 1, attachments: 1, files: 1 },
projection: { _id: 1, msg: 1, u: 1, t: 1, ts: 1, attachments: 1, files: 1, md: 1 },
}).toArray();
}

Expand Down Expand Up @@ -121,7 +123,7 @@ export class OmnichannelTranscript extends ServiceClass implements IOmnichannelT
messages.map(async (message: IMessage) => {
if (!message.attachments || !message.attachments.length) {
// If there's no attachment and no message, what was sent? lol
return { _id: message._id, files: [], ts: message.ts, u: message.u, msg: message.msg };
return { _id: message._id, files: [], ts: message.ts, u: message.u, msg: message.msg, md: message.md };
}

const files = await Promise.all(
Expand Down
3 changes: 3 additions & 0 deletions packages/pdf-worker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@storybook/testing-library": "~0.0.13",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "~13.4.0",
"@types/emojione": "^2.2.6",
"@types/jest": "^27.4.1",
"@types/react-dom": "^18",
"@types/testing-library__jest-dom": "^5",
Expand All @@ -40,6 +41,8 @@
"@react-pdf/renderer": "^3.0.1",
"@rocket.chat/core-typings": "workspace:^",
"@types/react": "^18.0.26",
"emoji-assets": "^7.0.1",
"emojione": "^4.5.0",
"react": "^18.2.0"
}
}
Binary file added packages/pdf-worker/public/inter400-italic.ttf
Binary file not shown.
Binary file added packages/pdf-worker/public/inter500-italic.ttf
Binary file not shown.
Binary file added packages/pdf-worker/public/inter700-italic.ttf
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,21 @@ export const exampleData = {
name: 'Christian Castro',
username: 'cristiano.castro',
},
md: [
{
type: 'UNORDERED_LIST',
value: [
{ type: 'LIST_ITEM', value: [{ type: 'PLAIN_TEXT', value: 'I am having trouble with my account;' }] },
{
type: 'LIST_ITEM',
value: [
{ type: 'PLAIN_TEXT', value: 'I am having trouble with my password. ' },
{ type: 'EMOJI', value: undefined, unicode: '🙂' },
],
},
],
},
],
},
{
msg: 'Can you please provide your account email?',
Expand Down Expand Up @@ -142,13 +157,21 @@ export const exampleData = {
files: [invalidFile],
},
{
msg: 'I have fixed the issue, is there anything else I can help you with?',
ts: '2022-11-21T16:00:00.000Z',
u: {
_id: '123',
name: 'Juanito De Ponce',
username: 'juanito.ponce',
},
md: [
{
type: 'PARAGRAPH',
value: [
{ type: 'PLAIN_TEXT', value: 'I have fixed the issue, is there anything else I can help you with? ' },
{ type: 'EMOJI', value: { type: 'PLAIN_TEXT', value: 'smile' } },
],
},
],
},
{
msg: 'No, that is all. Thank you for your help.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ import { ChatTranscriptPDF } from '.';

Font.register({
family: 'Inter',
fonts: [{ src: '/inter400.ttf' }, { src: '/inter500.ttf', fontWeight: 500 }, { src: '/inter700.ttf', fontWeight: 700 }],
fonts: [
{ src: '/inter400.ttf' },
{ src: '/inter400-italic.ttf', fontStyle: 'italic' },
{ src: '/inter500.ttf', fontWeight: 500 },
{ src: '/inter500-italic.ttf', fontWeight: 500, fontStyle: 'italic' },
{ src: '/inter700.ttf', fontWeight: 700 },
{ src: '/inter700-italic.ttf', fontWeight: 700, fontStyle: 'italic' },
],
});

Font.registerHyphenationCallback((word) => [word]);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { View, StyleSheet } from '@react-pdf/renderer';
import { Text, View, StyleSheet } from '@react-pdf/renderer';
import { fontScales } from '@rocket.chat/fuselage-tokens/typography.json';

import { Divider } from './Divider';
import { MessageHeader } from './MessageHeader';
import { Files } from './Files';
import { MessageContent } from './MessageContent';
import type { ChatTranscriptData } from '..';
import { Markup } from '../markup';

const styles = StyleSheet.create({
wrapper: {
marginBottom: 16,
paddingHorizontal: 32,
},
message: {
marginTop: 1,
fontSize: fontScales.p2.fontSize,
textAlign: 'justify',
},
});

export const MessageList = ({ messages, invalidFileMessage }: { messages: ChatTranscriptData['messages']; invalidFileMessage: string }) => (
Expand All @@ -19,7 +25,7 @@ export const MessageList = ({ messages, invalidFileMessage }: { messages: ChatTr
<View style={styles.wrapper} key={index} wrap={false}>
{message.divider && <Divider divider={message.divider} />}
<MessageHeader name={message.u.name || message.u.username} time={message.ts} />
<MessageContent message={message.msg} />
<View style={styles.message}>{message.md ? <Markup tokens={message.md} /> : <Text>{message.msg}</Text>}</View>
{message.files && <Files files={message.files} invalidMessage={invalidFileMessage} />}
</View>
))}
Expand Down
5 changes: 4 additions & 1 deletion packages/pdf-worker/src/templates/ChatTranscript/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const FONT_PATH = path.resolve(__dirname, '../../../public');

export type PDFFile = { name?: string; buffer: Buffer | null };

export type PDFMessage = Serialized<Omit<Pick<IMessage, 'msg' | 'u' | 'ts'>, 'files'>> & {
export type PDFMessage = Serialized<Omit<Pick<IMessage, 'msg' | 'u' | 'ts' | 'md'>, 'files'>> & {
files?: PDFFile[];
} & { divider?: string };

Expand Down Expand Up @@ -72,8 +72,11 @@ export default async (data: ChatTranscriptData): Promise<NodeJS.ReadableStream>
family: 'Inter',
fonts: [
{ src: `${FONT_PATH}/inter400.ttf` },
{ src: `${FONT_PATH}/inter400-italic.ttf`, fontStyle: 'italic' },
{ src: `${FONT_PATH}/inter500.ttf`, fontWeight: 500 },
{ src: `${FONT_PATH}/inter500-italic.ttf`, fontWeight: 500, fontStyle: 'italic' },
{ src: `${FONT_PATH}/inter700.ttf`, fontWeight: 700 },
{ src: `${FONT_PATH}/inter700-italic.ttf`, fontWeight: 700, fontStyle: 'italic' },
],
});
Font.registerHyphenationCallback((word) => [word]);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Text, View } from '@react-pdf/renderer';
import type * as MessageParser from '@rocket.chat/message-parser';
import emojione from 'emojione';
import type { ReactElement } from 'react';

type BigEmojiBlockProps = {
emoji: MessageParser.Emoji[];
};

const BigEmojiBlock = ({ emoji }: BigEmojiBlockProps): ReactElement => (
<View>
{emoji.map((emoji, index) => (
<Text key={index}>{emoji.value !== undefined ? `:${emoji.value?.value}:` : emojione.toShort(emoji.unicode)}</Text>
))}
</View>
);

export default BigEmojiBlock;
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type * as MessageParser from '@rocket.chat/message-parser';
import { Text, View } from '@react-pdf/renderer';
import { fontScales } from '@rocket.chat/fuselage-tokens/typography.json';

type HeadingBlockProps = {
items?: MessageParser.Plain[];
level?: 1 | 2 | 3 | 4;
};

const Header = ({ items = [], level = 1 }: HeadingBlockProps) => (
<View style={{ fontSize: fontScales[`h${level}`].fontSize, fontWeight: fontScales[`h${level}`].fontWeight }}>
{items.map((block, index) => (
<Text key={index}>{block.value}</Text>
))}
</View>
);

export default Header;
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { StyleSheet, Text, View } from '@react-pdf/renderer';
import type * as MessageParser from '@rocket.chat/message-parser';

import InlineElements from '../elements/InlineElements';

const styles = StyleSheet.create({
wrapper: {
marginTop: 4,
},
list: {
flexDirection: 'row',
},
number: {
fontWeight: 700,
marginHorizontal: 4,
},
});

type OrderedListBlockProps = {
items: MessageParser.ListItem[];
};

const OrderedListBlock = ({ items }: OrderedListBlockProps) => (
<View style={styles.wrapper}>
{items.map(({ value, number }, index) => (
<Text style={styles.list} key={index}>
<Text style={styles.number}>{number}.</Text> <InlineElements children={value} />
</Text>
))}
</View>
);

export default OrderedListBlock;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { View } from '@react-pdf/renderer';
import type * as MessageParser from '@rocket.chat/message-parser';

import InlineElements from '../elements/InlineElements';

type ParagraphBlockProps = {
items: MessageParser.Inlines[];
};

const ParagraphBlock = ({ items }: ParagraphBlockProps) => (
<View>
<InlineElements children={items} />
</View>
);

export default ParagraphBlock;
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { StyleSheet, Text, View } from '@react-pdf/renderer';
import type * as MessageParser from '@rocket.chat/message-parser';

import InlineElements from '../elements/InlineElements';

const styles = StyleSheet.create({
wrapper: {
marginTop: 4,
},
list: {
flexDirection: 'row',
},
bullet: {
marginHorizontal: 4,
},
});

type UnorderedListBlockProps = {
items: MessageParser.ListItem[];
};
const UnorderedListBlock = ({ items }: UnorderedListBlockProps) => (
<View style={styles.wrapper}>
{items.map(({ value }, index) => (
<View style={styles.list} key={index}>
<Text style={styles.bullet}>•</Text>
<InlineElements children={value} />
</View>
))}
</View>
);

export default UnorderedListBlock;
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { StyleSheet, View, Text } from '@react-pdf/renderer';
import type * as MessageParser from '@rocket.chat/message-parser';

import ItalicSpan from './ItalicSpan';
import LinkSpan from './LinkSpan';
import StrikeSpan from './StrikeSpan';

const styles = StyleSheet.create({
bold: {
fontWeight: 700,
},
});

type BoldSpanProps = {
children: (MessageParser.Link | MessageParser.MarkupExcluding<MessageParser.Bold>)[];
};

const BoldSpan = ({ children }: BoldSpanProps) => (
<View style={styles.bold}>
{children.map((child, index) => {
switch (child.type) {
case 'LINK':
return <LinkSpan key={index} label={Array.isArray(child.value.label) ? child.value.label : [child.value.label]} />;

case 'PLAIN_TEXT':
return <Text key={index}>{child.value}</Text>;

case 'STRIKE':
return <StrikeSpan key={index} children={child.value} />;

case 'ITALIC':
return <ItalicSpan key={index} children={child.value} />;

default:
return null;
}
})}
</View>
);

export default BoldSpan;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Text } from '@react-pdf/renderer';
import type * as MessageParser from '@rocket.chat/message-parser';
import emojione from 'emojione';

type EmojiSpanProps = MessageParser.Emoji;

const EmojiSpan = (emoji: EmojiSpanProps) => (
<Text>{emoji.value !== undefined ? `:${emoji.value?.value}:` : emojione.toShort(emoji.unicode)}</Text>
);

export default EmojiSpan;
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Text } from '@react-pdf/renderer';
import type * as MessageParser from '@rocket.chat/message-parser';

import BoldSpan from './BoldSpan';
import ItalicSpan from './ItalicSpan';
import LinkSpan from './LinkSpan';
import StrikeSpan from './StrikeSpan';
import EmojiSpan from './EmojiSpan';

type InlineElementsProps = {
children: MessageParser.Inlines[];
};

const InlineElements = ({ children }: InlineElementsProps) => (
<Text>
{children.map((child, index) => {
switch (child.type) {
case 'BOLD':
return <BoldSpan key={index} children={child.value} />;

case 'STRIKE':
return <StrikeSpan key={index} children={child.value} />;

case 'ITALIC':
return <ItalicSpan key={index} children={child.value} />;

case 'LINK':
return <LinkSpan key={index} label={Array.isArray(child.value.label) ? child.value.label : [child.value.label]} />;

case 'PLAIN_TEXT':
return <Text key={index}>{child.value}</Text>;

case 'EMOJI':
return <EmojiSpan key={index} {...child} />;

case 'MENTION_USER':
case 'MENTION_CHANNEL':
return <Text key={index}>{child.value?.value}</Text>;

default:
return null;
}
aleksandernsilva marked this conversation as resolved.
Show resolved Hide resolved
})}
</Text>
);

export default InlineElements;
Loading