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
32 changes: 0 additions & 32 deletions client/components/MarkdownText.js

This file was deleted.

96 changes: 96 additions & 0 deletions client/components/MarkdownText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { Box } from '@rocket.chat/fuselage';
import React, { FC, useMemo } from 'react';
import marked from 'marked';
import dompurify from 'dompurify';

type MarkdownTextParams = {
content: string;
variant: 'inline' | 'inlineWithoutBreaks' | 'document';
preserveHtml: boolean;
withTruncatedText: boolean;
};

const documentRenderer = new marked.Renderer();
const inlineRenderer = new marked.Renderer();
const inlineWithoutBreaks = new marked.Renderer();

marked.InlineLexer.rules.gfm = {
...marked.InlineLexer.rules.gfm,
strong: /^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,
em: /^__(?=\S)([\s\S]*?\S)__(?!_)|^_(?=\S)([\s\S]*?\S)_(?!_)/,
};

const linkMarked = (href: string | null, _title: string | null, text: string): string =>
`<a href="${ href }" target="_blank" rel="nofollow">${ text }</a> `;
const paragraphMarked = (text: string): string => text;
const brMarked = (): string => ' ';
const listItemMarked = (text: string): string => {
const cleanText = text.replace(/<p.*?>|<\/p>/ig, '');
return `<li>${ cleanText }</li>`;
};

documentRenderer.link = linkMarked;
documentRenderer.listitem = listItemMarked;

inlineRenderer.link = linkMarked;
inlineRenderer.paragraph = paragraphMarked;
inlineRenderer.listitem = listItemMarked;

inlineWithoutBreaks.link = linkMarked;
inlineWithoutBreaks.paragraph = paragraphMarked;
inlineWithoutBreaks.br = brMarked;
inlineWithoutBreaks.listitem = listItemMarked;

const defaultOptions = {
gfm: true,
headerIds: false,
};

const options = {
...defaultOptions,
renderer: documentRenderer,
};

const inlineOptions = {
...defaultOptions,
renderer: inlineRenderer,
};

const inlineWithoutBreaksOptions = {
...defaultOptions,
renderer: inlineWithoutBreaks,
};

const MarkdownText: FC<Partial<MarkdownTextParams>> = ({
content,
variant = 'document',
withTruncatedText = false,
preserveHtml = false,
...props
}) => {
const sanitizer = dompurify.sanitize;

let markedOptions: {};

const withRichContent = variant;
switch (variant) {
case 'inline':
markedOptions = inlineOptions;
break;
case 'inlineWithoutBreaks':
markedOptions = inlineWithoutBreaksOptions;
break;
case 'document':
default:
markedOptions = options;
}

const __html = useMemo(() => {
const html = content && typeof content === 'string' && marked(content, markedOptions);
return preserveHtml ? html : html && sanitizer(html, { ADD_ATTR: ['target'] });
}, [content, preserveHtml, sanitizer, markedOptions]);

return __html ? <Box dangerouslySetInnerHTML={{ __html }} withTruncatedText={withTruncatedText} withRichContent={withRichContent} {...props} /> : null;
};

export default MarkdownText;
4 changes: 2 additions & 2 deletions client/components/Message/Attachments/DefaultAttachment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export type DefaultAttachmentProps = {

const isActionAttachment = (attachment: AttachmentProps): attachment is ActionAttachmentProps => 'actions' in attachment;

const applyMarkdownIfRequires = (list: DefaultAttachmentProps['mrkdwn_in'] = ['text', 'pretext']) => (key: MarkdownFields, text: string): JSX.Element | string => (list?.includes(key) ? <MarkdownText withRichContent={undefined} content={text}/> : text);
const applyMarkdownIfRequires = (list: DefaultAttachmentProps['mrkdwn_in'] = ['text', 'pretext']) => (key: MarkdownFields, text: string): JSX.Element | string => (list?.includes(key) ? <MarkdownText variant='inline' content={text}/> : text);

export const DefaultAttachment: FC<DefaultAttachmentProps> = (attachment) => {
const applyMardownFor = applyMarkdownIfRequires(attachment.mrkdwn_in);
Expand All @@ -57,7 +57,7 @@ export const DefaultAttachment: FC<DefaultAttachmentProps> = (attachment) => {
{!collapsed && <>
{attachment.text && <Attachment.Text>{applyMardownFor('text', attachment.text)}</Attachment.Text>}
{/* {attachment.fields && <FieldsAttachment fields={attachment.mrkdwn_in?.includes('fields') ? attachment.fields.map(({ value, ...rest }) => ({ ...rest, value: <MarkdownText withRichContent={null} content={value} /> })) : attachment.fields} />} */}
{attachment.fields && <FieldsAttachment fields={attachment.fields.map(({ value, ...rest }) => ({ ...rest, value: <MarkdownText withRichContent={false} content={value} /> }))} />}
{attachment.fields && <FieldsAttachment fields={attachment.fields.map(({ value, ...rest }) => ({ ...rest, value: <MarkdownText variant='inline' content={value as string} /> }))} />}
{attachment.image_url && <Attachment.Image {...attachment.image_dimensions as any} src={attachment.image_url} />}
{/* DEPRECATED */}
{isActionAttachment(attachment) && <ActionAttachment {...attachment} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const GenericFileAttachment: FC<GenericFileAttachmentProps> = ({
// const [collapsed, collapse] = useCollapse(collapsedDefault);
const getURL = useMediaUrl();
return <Attachment>
{ description && <MarkdownText withRichContent={undefined} content={description} /> }
{ description && <MarkdownText content={description} /> }
<Attachment.Row>
{ hasDownload && link ? <Attachment.TitleLink link={getURL(link)} title={title} /> : <Attachment.Title>{title}</Attachment.Title> }
{size && <Attachment.Size size={size}/>}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const ImageAttachment: FC<ImageAttachmentProps> = ({
const [collapsed, collapse] = useCollapse(collapsedDefault);
const getURL = useMediaUrl();
return <Attachment>
<MarkdownText withRichContent={undefined} content={description} />
<MarkdownText variant='inline' content={description} />
<Attachment.Row>
<Attachment.Title>{title}</Attachment.Title>
{size && <Attachment.Size size={size}/>}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const PDFAttachment: FC<PDFAttachmentProps> = ({
const t = useTranslation();
const [collapsed, collapse] = useCollapse(collapsedDefault);
return <Attachment>
<MarkdownText withRichContent={undefined} content={description} />
<MarkdownText variant='inline' content={description} />
<Attachment.Row>
<Attachment.Title>{t('PDF')}</Attachment.Title>
{collapse}
Expand Down
2 changes: 1 addition & 1 deletion client/components/Message/Attachments/QuoteAttachment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const QuoteAttachment: FC<QuoteAttachmentProps> = ({ author_icon: url, au
<Attachment.AuthorName {...authorLink && { is: 'a', href: authorLink, target: '_blank', color: undefined }}>{name}</Attachment.AuthorName>
<Box fontScale='c1' {...messageLink ? { is: 'a', href: messageLink } : { color: 'hint' }}>{format(ts)}</Box>
</Attachment.Author>
<MarkdownText mb='neg-x16' content={text} />
<MarkdownText variant='inline' content={text} />
{attachments && <Attachment.Inner><Attachments attachments={attachments} /></Attachment.Inner>}
</Attachment.Details>
</Attachment.Content>
Expand Down
2 changes: 1 addition & 1 deletion client/components/UserCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const UserCard = forwardRef(({
<Username status={status} name={name} title={username !== name ? username : undefined} />
{nickname && <Box title={t('Nickname')} color='hint' mis='x8' fontScale='p1' withTruncatedText>({ nickname })</Box>}
</Box>
{ customStatus && <Info>{typeof customStatus === 'string' ? <MarkdownText content={customStatus} withRichContent={false}/> : customStatus}</Info> }
{ customStatus && <Info>{typeof customStatus === 'string' ? <MarkdownText content={customStatus} /> : customStatus}</Info> }
<Roles>{roles}</Roles>
<Info>{localTime}</Info>
{ bio && <Info withTruncatedText={false} style={clampStyle} height='x60'>{typeof bio === 'string' ? <MarkdownText content={bio}/> : bio}</Info> }
Expand Down
2 changes: 1 addition & 1 deletion client/sidebar/header/UserDropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ const UserDropdown = ({ user, onClose }) => {
</Margins>
</Box>
<Box color='hint' withTruncatedText display='inline-block'>
<MarkdownText content={statusText || t(status)} withRichContent={false}/>
<MarkdownText content={statusText || t(status)} variant='inlineWithoutBreaks'/>
</Box>
</Box>
</Box>
Expand Down
2 changes: 1 addition & 1 deletion client/types/fuselage.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ declare module '@rocket.chat/fuselage' {

elevation?: '0' | '1' | '2';
invisible?: boolean;
withRichContent?: boolean;
withRichContent?: boolean | string;
withTruncatedText?: boolean;
size?: CSSProperties['blockSize'];
minSize?: CSSProperties['blockSize'];
Expand Down
2 changes: 1 addition & 1 deletion client/views/admin/cloud/CopyStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const CopyStep: FC<CopyStepProps> = ({ onNextButtonClick }) => {
<Icon name='copy' /> {t('Copy')}
</Button>
</Box>
<MarkdownText is='p' preserveHtml={true} withRichContent content={t('Cloud_click_here', { cloudConsoleUrl })} />
<MarkdownText preserveHtml={true} content={t('Cloud_click_here', { cloudConsoleUrl })} />
</Modal.Content>
<Modal.Footer>
<ButtonGroup>
Expand Down
2 changes: 1 addition & 1 deletion client/views/directory/ChannelsTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ function ChannelsTable() {
<Box display='flex' alignItems='center'>
<Icon name={roomTypes.getIcon(room)} color='hint' /> <Box fontScale='p2' mi='x4'>{fname || name}</Box><RoomTags room={room} style={style} />
</Box>
{topic && <MarkdownText fontScale='p1' color='hint' style={style} withRichContent={false} content={topic} />}
{topic && <MarkdownText variant='inlineWithoutBreaks' fontScale='p1' color='hint' style={style} content={topic} />}
</Box>
</Box>
</Table.Cell>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { FC } from 'react';
import { Box } from '@rocket.chat/fuselage';
import { css } from '@rocket.chat/css-in-js';
import colors from '@rocket.chat/fuselage-tokens/colors';
Expand All @@ -8,7 +8,17 @@ import AnnouncementModal from './AnnouncementModal';
import { useSetModal } from '../../../contexts/ModalContext';
import MarkdownText from '../../../components/MarkdownText';

export const Announcement = ({ children, onClickOpen }) => {
type AnnouncementComponentParams = {
children: JSX.Element;
onClickOpen: (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void;
};

type AnnouncementParams = {
announcement: string;
announcementDetails: () => void;
}

export const AnnouncementComponent: FC<AnnouncementComponentParams> = ({ children, onClickOpen }) => {
const announcementBar = css`
background-color: ${ colors.b200 };
background-color: var(--rc-color-announcement-background, ${ colors.b200 });
Expand All @@ -35,21 +45,22 @@ export const Announcement = ({ children, onClickOpen }) => {
return <Box onClick={onClickOpen} height='x40' pi='x24' alignItems='center' display='flex' fontScale='p2' textAlign='center' className={announcementBar}><Box withTruncatedText w='none'>{children}</Box></Box>;
};

export default ({ announcement, announcementDetails }) => {
const Announcement: FC<AnnouncementParams> = ({ announcement, announcementDetails }) => {
const setModal = useSetModal();
const closeModal = useMutableCallback(() => setModal());
const handleClick = (e) => {
if (e.target.href) {
const closeModal = useMutableCallback(() => setModal(null));
const handleClick = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>): void => {
if ((e.target as HTMLAnchorElement).href) {
return;
}

if (window.getSelection().toString() !== '') {
if (window?.getSelection()?.toString() !== '') {
return;
}

announcementDetails ? announcementDetails() : setModal(<AnnouncementModal onClose={closeModal}>{announcement}</AnnouncementModal>);
};
const announcementWithoutBreaks = announcement && announcement.replace(/(\r\n|\n|\r)/gm, ' ');

return announcementWithoutBreaks ? <Announcement onClickOpen={handleClick}><MarkdownText content={announcementWithoutBreaks} /></Announcement> : false;
return announcement ? <AnnouncementComponent onClickOpen={(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>): void => handleClick(e)}><MarkdownText variant='inlineWithoutBreaks' content={announcement} withTruncatedText /></AnnouncementComponent> : null;
};

export default Announcement;
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import React from 'react';
import React, { FC } from 'react';
import { Button, ButtonGroup, Box, Modal } from '@rocket.chat/fuselage';

import { useTranslation } from '../../../contexts/TranslationContext';
import MarkdownText from '../../../components/MarkdownText';

export default ({
type AnnouncementModalParams = {
onClose: () => void;
confirmLabel?: string;
children?: string;
}

const AnnouncementModal: FC<AnnouncementModalParams> = ({
onClose,
confirmLabel = 'Close',
children,
Expand All @@ -19,7 +25,7 @@ export default ({
<Modal.Close onClick={onClose}/>
</Modal.Header>
<Modal.Content>
<Box textAlign='center'><MarkdownText content={children} /></Box>
<Box><MarkdownText content={children} /></Box>
</Modal.Content>
<Modal.Footer>
<ButtonGroup align='end'>
Expand All @@ -29,3 +35,5 @@ export default ({
</Modal>
);
};

export default AnnouncementModal;
2 changes: 1 addition & 1 deletion client/views/room/Header/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const RoomHeader = ({ room, topic }) => {
<Translate room={room} />
</Header.Content.Row>
<Header.Content.Row>
<Header.Subtitle>{topic && <MarkdownText withRichContent={false} content={topic}/>}</Header.Subtitle>
<Header.Subtitle>{topic && <MarkdownText variant='inlineWithoutBreaks' content={topic}/>}</Header.Subtitle>
</Header.Content.Row>
</Header.Content>
<Header.ToolBox>
Expand Down
2 changes: 1 addition & 1 deletion client/views/room/contextualBar/UserInfo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const UserInfo = React.memo(function UserInfo({

{bio && <>
<Label>{t('Bio')}</Label>
<Info withTruncatedText={false}><MarkdownText content={bio}/></Info>
<Info withTruncatedText={false}><MarkdownText variant='inline' content={bio}/></Info>
</>}

{phone && <> <Label>{t('Phone')}</Label>
Expand Down
Loading