[NEW] New Message Parser (#21962)
parent
645b1191ee
commit
6e9d541d5e
@ -0,0 +1,18 @@ |
||||
import { BigEmoji as ASTBigEmoji } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
import Emoji from '../../Emoji'; |
||||
|
||||
type BigEmojiProps = { |
||||
value: ASTBigEmoji['value']; |
||||
}; |
||||
|
||||
const BigEmoji: FC<BigEmojiProps> = ({ value }) => ( |
||||
<> |
||||
{value.map((block, index) => ( |
||||
<Emoji className='big' key={index} emojiHandle={`:${block.value.value}:`} /> |
||||
))} |
||||
</> |
||||
); |
||||
|
||||
export default BigEmoji; |
@ -0,0 +1,63 @@ |
||||
import { BigEmoji as ASTBigEmoji, MarkdownAST as GazzodownAST } from '@rocket.chat/message-parser'; |
||||
import React, { FC, memo } from 'react'; |
||||
|
||||
import BigEmoji from './BigEmoji'; |
||||
import Code from './Code'; |
||||
import Heading from './Heading'; |
||||
import OrderedList from './OrderedList'; |
||||
import Paragraph from './Paragraph'; |
||||
import Quote from './Quote'; |
||||
import TaskList from './TaskList'; |
||||
import UnorderedList from './UnorderedList'; |
||||
import { UserMention } from './definitions/UserMention'; |
||||
|
||||
type BodyProps = { |
||||
tokens: GazzodownAST; |
||||
mentions: UserMention[]; |
||||
}; |
||||
|
||||
const isBigEmoji = (tokens: GazzodownAST): tokens is [ASTBigEmoji] => |
||||
tokens.length === 1 && tokens[0].type === 'BIG_EMOJI'; |
||||
|
||||
const Body: FC<BodyProps> = ({ tokens, mentions }) => { |
||||
if (isBigEmoji(tokens)) { |
||||
return <BigEmoji value={tokens[0].value} />; |
||||
} |
||||
|
||||
return ( |
||||
<> |
||||
{tokens.map((block, index) => { |
||||
if (block.type === 'UNORDERED_LIST') { |
||||
return <UnorderedList value={block.value} key={index} />; |
||||
} |
||||
|
||||
if (block.type === 'QUOTE') { |
||||
return <Quote value={block.value} key={index} />; |
||||
} |
||||
if (block.type === 'TASKS') { |
||||
return <TaskList value={block.value} key={index} />; |
||||
} |
||||
|
||||
if (block.type === 'ORDERED_LIST') { |
||||
return <OrderedList value={block.value} key={index} />; |
||||
} |
||||
|
||||
if (block.type === 'PARAGRAPH') { |
||||
return <Paragraph mentions={mentions} value={block.value} key={index} />; |
||||
} |
||||
|
||||
if (block.type === 'CODE') { |
||||
return <Code value={block.value} key={index} />; |
||||
} |
||||
|
||||
if (block.type === 'HEADING') { |
||||
return <Heading value={block.value} key={index} />; |
||||
} |
||||
|
||||
return null; |
||||
})} |
||||
</> |
||||
); |
||||
}; |
||||
|
||||
export default memo(Body); |
@ -0,0 +1,24 @@ |
||||
import { Bold as ASTBold } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
import Italic from './Italic'; |
||||
import Strike from './Strike'; |
||||
|
||||
const Bold: FC<{ value: ASTBold['value'] }> = ({ value = [] }) => ( |
||||
<strong> |
||||
{value.map((block, index) => { |
||||
switch (block.type) { |
||||
case 'PLAIN_TEXT': |
||||
return block.value; |
||||
case 'STRIKE': |
||||
return <Strike key={index} value={block.value} />; |
||||
case 'ITALIC': |
||||
return <Italic key={index} value={block.value} />; |
||||
default: |
||||
return null; |
||||
} |
||||
})} |
||||
</strong> |
||||
); |
||||
|
||||
export default Bold; |
@ -0,0 +1,19 @@ |
||||
import { Code as ASTCode } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
import CodeLine from './CodeLine'; |
||||
|
||||
const Code: FC<{ value: ASTCode['value'] }> = ({ value = [] }) => ( |
||||
<code className='code-colors hljs'> |
||||
{value.map((block, index) => { |
||||
switch (block.type) { |
||||
case 'CODE_LINE': |
||||
return <CodeLine key={index} value={block.value} />; |
||||
default: |
||||
return null; |
||||
} |
||||
})} |
||||
</code> |
||||
); |
||||
|
||||
export default Code; |
@ -0,0 +1,8 @@ |
||||
import { CodeLine as ASTCodeLine } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
const CodeLine: FC<{ value: ASTCodeLine['value'] }> = ({ value }) => ( |
||||
<div>{value.type === 'PLAIN_TEXT' && value.value}</div> |
||||
); |
||||
|
||||
export default CodeLine; |
@ -0,0 +1,17 @@ |
||||
import { Bold as ASTHeading } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
const Heading: FC<{ value: ASTHeading['value'] }> = ({ value = [] }) => ( |
||||
<h1> |
||||
{value.map((block) => { |
||||
switch (block.type) { |
||||
case 'PLAIN_TEXT': |
||||
return block.value; |
||||
default: |
||||
return null; |
||||
} |
||||
})} |
||||
</h1> |
||||
); |
||||
|
||||
export default Heading; |
@ -0,0 +1,47 @@ |
||||
import { Paragraph as ASTParagraph } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
import Emoji from '../../Emoji'; |
||||
import Bold from './Bold'; |
||||
import InlineCode from './InlineCode'; |
||||
import Italic from './Italic'; |
||||
import Link from './Link'; |
||||
import Mention from './Mention'; |
||||
import Plain from './Plain'; |
||||
import Strike from './Strike'; |
||||
import { UserMention } from './definitions/UserMention'; |
||||
|
||||
const Inline: FC<{ value: ASTParagraph['value']; mentions?: UserMention[] }> = ({ |
||||
value = [], |
||||
mentions = [], |
||||
}) => ( |
||||
<> |
||||
{value.map((block) => { |
||||
switch (block.type) { |
||||
case 'PLAIN_TEXT': |
||||
return block.value; |
||||
case 'BOLD': |
||||
return <Bold value={block.value} />; |
||||
case 'STRIKE': |
||||
return <Strike value={block.value} />; |
||||
case 'ITALIC': |
||||
return <Italic value={block.value} />; |
||||
case 'LINK': |
||||
return <Link value={block.value} />; |
||||
case 'MENTION_USER': |
||||
return <Mention value={block.value} mentions={mentions} />; |
||||
case 'EMOJI': |
||||
return <Emoji emojiHandle={`:${block.value.value}:`} />; |
||||
case 'MENTION_CHANNEL': |
||||
// case 'COLOR':
|
||||
return <Plain value={block.value} />; |
||||
case 'INLINE_CODE': |
||||
return <InlineCode value={block.value} />; |
||||
default: |
||||
return null; |
||||
} |
||||
})} |
||||
</> |
||||
); |
||||
|
||||
export default Inline; |
@ -0,0 +1,17 @@ |
||||
import { InlineCode as ASTInlineCode } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
const InlineCode: FC<{ value: ASTInlineCode['value'] }> = ({ value }) => ( |
||||
<code className='code-colors inline'> |
||||
{((block): string | null => { |
||||
switch (block.type) { |
||||
case 'PLAIN_TEXT': |
||||
return block.value; |
||||
default: |
||||
return null; |
||||
} |
||||
})(value)} |
||||
</code> |
||||
); |
||||
|
||||
export default InlineCode; |
@ -0,0 +1,25 @@ |
||||
import { Italic as ASTItalic } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
import Bold from './Bold'; |
||||
import Strike from './Strike'; |
||||
|
||||
const Italic: FC<{ value: ASTItalic['value'] }> = ({ value = [] }) => ( |
||||
<i> |
||||
{value.map((block, index) => { |
||||
switch (block.type) { |
||||
case 'PLAIN_TEXT': |
||||
return block.value; |
||||
case 'STRIKE': |
||||
return <Strike key={index} value={block.value} />; |
||||
case 'BOLD': |
||||
return <Bold key={index} value={block.value} />; |
||||
|
||||
default: |
||||
return null; |
||||
} |
||||
})} |
||||
</i> |
||||
); |
||||
|
||||
export default Italic; |
@ -0,0 +1,36 @@ |
||||
import { Link as ASTLink } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
import { baseURI } from '../../../lib/baseuri'; |
||||
import Bold from './Bold'; |
||||
import Italic from './Italic'; |
||||
import Strike from './Strike'; |
||||
|
||||
type LinkProps = { |
||||
value: ASTLink['value']; |
||||
}; |
||||
|
||||
const Link: FC<LinkProps> = ({ value }) => { |
||||
const { src, label } = value; |
||||
const target = src.value.indexOf(baseURI) === 0 ? '' : '_blank'; |
||||
return ( |
||||
<a href={src.value} target={target} rel='noopener noreferrer'> |
||||
{((block: ASTLink['value']['label']): JSX.Element | string | null => { |
||||
switch (block.type) { |
||||
case 'PLAIN_TEXT': |
||||
return <>{block.value}</>; |
||||
case 'STRIKE': |
||||
return <Strike value={block.value} />; |
||||
case 'ITALIC': |
||||
return <Italic value={block.value} />; |
||||
case 'BOLD': |
||||
return <Bold value={block.value} />; |
||||
default: |
||||
return null; |
||||
} |
||||
})(label)} |
||||
</a> |
||||
); |
||||
}; |
||||
|
||||
export default Link; |
@ -0,0 +1,34 @@ |
||||
import { UserMention as ASTUserMention } from '@rocket.chat/message-parser'; |
||||
import React, { FC, memo } from 'react'; |
||||
|
||||
import { useUserId } from '../../../contexts/UserContext'; |
||||
import { UserMention } from './definitions/UserMention'; |
||||
|
||||
const Mention: FC<{ value: ASTUserMention['value']; mentions: UserMention[] }> = ({ |
||||
value: { value: mention }, |
||||
mentions, |
||||
}) => { |
||||
const uid = useUserId(); |
||||
const mentioned = mentions.find((mentioned) => mentioned.username === mention); |
||||
const classNames = ['mention-link']; |
||||
if (mention === 'all') { |
||||
classNames.push('mention-link--all'); |
||||
classNames.push('mention-link--group'); |
||||
} else if (mention === 'here') { |
||||
classNames.push('mention-link--here'); |
||||
classNames.push('mention-link--group'); |
||||
} else if (mentioned && mentioned._id === uid) { |
||||
classNames.push('mention-link--me'); |
||||
classNames.push('mention-link--user'); |
||||
} else { |
||||
classNames.push('mention-link--user'); |
||||
} |
||||
return ( |
||||
<> |
||||
{mentioned && <span className={classNames.join(' ')}>{mentioned.name || mention}</span>} |
||||
{!mentioned && `@${mention}`} |
||||
</> |
||||
); |
||||
}; |
||||
|
||||
export default memo(Mention); |
@ -0,0 +1,16 @@ |
||||
import { OrderedList as ASTOrderedList } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
import Inline from './Inline'; |
||||
|
||||
const OrderedList: FC<{ value: ASTOrderedList['value'] }> = ({ value }) => ( |
||||
<ol> |
||||
{value.map((item, index) => ( |
||||
<li key={index}> |
||||
<Inline value={item.value} /> |
||||
</li> |
||||
))} |
||||
</ol> |
||||
); |
||||
|
||||
export default OrderedList; |
@ -0,0 +1,16 @@ |
||||
import { Paragraph as ASTParagraph } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
import Inline from './Inline'; |
||||
import { UserMention } from './definitions/UserMention'; |
||||
|
||||
const Paragraph: FC<{ value: ASTParagraph['value']; mentions: UserMention[] }> = ({ |
||||
value = [], |
||||
mentions, |
||||
}) => ( |
||||
<p> |
||||
<Inline value={value} mentions={mentions} /> |
||||
</p> |
||||
); |
||||
|
||||
export default Paragraph; |
@ -0,0 +1,8 @@ |
||||
import { Plain as ASTPlain } from '@rocket.chat/message-parser'; |
||||
import React, { FC, memo } from 'react'; |
||||
|
||||
const Plain: FC<{ value: ASTPlain }> = ({ value }) => ( |
||||
<>{value.type === 'PLAIN_TEXT' && value.value}</> |
||||
); |
||||
|
||||
export default memo(Plain); |
@ -0,0 +1,15 @@ |
||||
import { Box } from '@rocket.chat/fuselage'; |
||||
import { Quote as ASTQuote } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
import Paragraph from './Paragraph'; |
||||
|
||||
const Quote: FC<{ value: ASTQuote['value'] }> = ({ value }) => ( |
||||
<Box is='blockquote' backgroundColor='neutral-200' pi='x8'> |
||||
{value.map((item, index) => ( |
||||
<Paragraph key={index} value={item.value} mentions={[]} /> |
||||
))} |
||||
</Box> |
||||
); |
||||
|
||||
export default Quote; |
@ -0,0 +1,24 @@ |
||||
import { Strike as ASTStrike } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
import Bold from './Bold'; |
||||
import Italic from './Italic'; |
||||
|
||||
const Strike: FC<{ value: ASTStrike['value'] }> = ({ value = [] }) => ( |
||||
<del> |
||||
{value.map((block, index) => { |
||||
switch (block.type) { |
||||
case 'PLAIN_TEXT': |
||||
return block.value; |
||||
case 'BOLD': |
||||
return <Bold key={index} value={block.value} />; |
||||
case 'ITALIC': |
||||
return <Italic key={index} value={block.value} />; |
||||
default: |
||||
return null; |
||||
} |
||||
})} |
||||
</del> |
||||
); |
||||
|
||||
export default Strike; |
@ -0,0 +1,23 @@ |
||||
import { CheckBox } from '@rocket.chat/fuselage'; |
||||
import { Tasks as ASTTasks } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
import Inline from './Inline'; |
||||
|
||||
const TaksList: FC<{ value: ASTTasks['value'] }> = ({ value }) => ( |
||||
<ul |
||||
style={{ |
||||
listStyle: 'none', |
||||
marginLeft: 0, |
||||
paddingLeft: 0, |
||||
}} |
||||
> |
||||
{value.map((item) => ( |
||||
<li> |
||||
<CheckBox checked={item.status} /> <Inline value={item.value} /> |
||||
</li> |
||||
))} |
||||
</ul> |
||||
); |
||||
|
||||
export default TaksList; |
@ -0,0 +1,16 @@ |
||||
import { UnorderedList as ASTUnorderedList } from '@rocket.chat/message-parser'; |
||||
import React, { FC } from 'react'; |
||||
|
||||
import Inline from './Inline'; |
||||
|
||||
const UnorderedList: FC<{ value: ASTUnorderedList['value'] }> = ({ value }) => ( |
||||
<ul> |
||||
{value.map((item) => ( |
||||
<li> |
||||
<Inline value={item.value} /> |
||||
</li> |
||||
))} |
||||
</ul> |
||||
); |
||||
|
||||
export default UnorderedList; |
@ -0,0 +1,3 @@ |
||||
import { IUser } from '../../../../../definition/IUser'; |
||||
|
||||
export type UserMention = Pick<IUser, '_id' | 'name' | 'username'>; |
@ -0,0 +1 @@ |
||||
export { default } from './Body'; |
@ -0,0 +1,18 @@ |
||||
export const baseURI: string = ((): string => { |
||||
if (document.baseURI) { |
||||
return document.baseURI; |
||||
} |
||||
|
||||
// Should be exactly one tag:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
||||
const base = document.getElementsByTagName('base'); |
||||
|
||||
// Return location from BASE tag.
|
||||
if (base.length > 0) { |
||||
return base[0].href; |
||||
} |
||||
|
||||
// Else use implementation of documentURI:
|
||||
// http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-baseURI
|
||||
return document.URL; |
||||
})(); |
@ -1,12 +0,0 @@ |
||||
/* eslint-disable @typescript-eslint/interface-name-prefix */ |
||||
|
||||
declare module 'meteor/reactive-dict' { |
||||
const ReactiveDict: ReactiveDictStatic; |
||||
interface ReactiveDictStatic { |
||||
new <T>(name: string, initialValue?: T): ReactiveDict<T>; |
||||
} |
||||
interface ReactiveDict<T> { |
||||
get(name: string): T; |
||||
set(name: string, newValue: T): void; |
||||
} |
||||
} |
Loading…
Reference in new issue