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

feat: Markdown library comparison: react-markdown #396

Closed
wants to merge 3 commits into from
Closed
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"prettier": "^3.3.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-markdown": "^9.0.1",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-visualizer": "^5.12.0",
"ts-pattern": "^5.1.1",
Expand Down
20 changes: 13 additions & 7 deletions src/components/CustomMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import AdminMessage from './AdminMessage';
import BotMessageFeedback from './BotMessageFeedback';
import BotMessageWithBodyInput from './BotMessageWithBodyInput';
import { useChatContext } from './chat/context/ChatProvider';
import CurrentUserMessage from './CurrentUserMessage';
import CustomMessageBody from './CustomMessageBody';
import CustomTypingIndicatorBubble from './CustomTypingIndicatorBubble';
import FileMessage from './FileMessage';
Expand Down Expand Up @@ -67,12 +66,19 @@ export default function CustomMessage(props: Props) {
* typing indicator bubble is displayed below to indicate
* a reply message from bot is expected to arrive.
*/
return (
<div>
<CurrentUserMessage message={message} />
{isWaitingForBotReply && <CustomTypingIndicatorBubble />}
</div>
);
// return (
// <div>
// <CurrentUserMessage message={message} />
// {isWaitingForBotReply && <CustomTypingIndicatorBubble />}
// </div>
// );
const tokens: Token[] = parseTextMessage(message.message, replacementTextList);
const textMessageBody = <ParsedBotMessageBody text={message.message} tokens={tokens} />;
return <BotMessageWithBodyInput
{...props}
bodyComponent={textMessageBody}
createdAt={message.createdAt}
/>
}

if (message.isFileMessage()) {
Expand Down
87 changes: 6 additions & 81 deletions src/components/TokensBody.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,18 @@
import { ReactNode } from 'react';
import Markdown from 'react-markdown'
import styled from 'styled-components';

import BotMessageBottom from './BotMessageBottom';
import SourceContainer, { Source } from './SourceContainer';
import { CodeBlock } from './ui/CodeBlock';
import { useConstantState } from '../context/ConstantContext';
import { asSafeURL, replaceWithRegex, Token, TokenType } from '../utils';

const urlRegex =
/(?:https?:\/\/|www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.(xn--)?[a-z]{2,20}\b([-a-zA-Z0-9@:%_+[\],.~#?&/=]*[-a-zA-Z0-9@:%_+~#?&/=])*/g;
const markdownUrlRegex = /\[(.*?)\]\((.*?)\)/g;
const markdownBoldRegex = /\*\*(.*?)\*\*/g;
import { Token, TokenType } from '../utils';
import './markdown.css';

type TokensBodyProps = {
tokens: Token[];
sources?: Source[];
};

interface RegexTextPattern {
regex: RegExp;
replacer(params: { match: string; groups: string[]; index: number }): string | ReactNode;
}

const BlockContainer = styled.div`
width: 100%;
`;
Expand All @@ -41,30 +32,6 @@ export const TextContainer = styled.div`
white-space: pre-wrap;
`;

const RegexText = ({ children, patterns }: { children: string; patterns: RegexTextPattern[] }) => {
if (patterns.length === 0 || typeof children !== 'string') {
return <>{children}</>;
}

const convertedNodes: Array<string | ReactNode> = [children];
patterns.forEach(({ regex, replacer }) => {
const node = convertedNodes.concat();
let offset = 0;
node.forEach((text, index) => {
if (typeof text === 'string' && text) {
const children = replaceWithRegex(text, regex, replacer);

if (children.length > 1) {
convertedNodes.splice(index + offset, 1, ...children);
offset += children.length - 1;
}
}
});
});

return <TextContainer>{convertedNodes}</TextContainer>;
};

export default function TokensBody({ tokens, sources }: TokensBodyProps) {
const { enableSourceMessage } = useConstantState();

Expand All @@ -74,51 +41,9 @@ export default function TokensBody({ tokens, sources }: TokensBodyProps) {
// Normal text part of the message.
if (token.type === TokenType.string) {
return (
<RegexText
key={'token' + i}
patterns={[
{
regex: markdownBoldRegex,
replacer({ match, groups, index }) {
return <strong key={`${match}-${index}`}>{groups[1]}</strong>;
},
},
{
regex: markdownUrlRegex,
replacer({ match, groups, index }) {
return (
<a
key={`${match}-${index}`}
className="sendbird-word__url"
href={asSafeURL(groups[2])}
target="_blank"
rel="noreferrer"
>
{groups[1]}
</a>
);
},
},
{
regex: urlRegex,
replacer({ match, index }) {
return (
<a
key={`${match}-${index}`}
className="sendbird-word__url"
href={asSafeURL(match)}
target="_blank"
rel="noreferrer"
>
{match}
</a>
);
},
},
]}
>
{token.value}
</RegexText>
<div key={i} style={{ padding: '8px 12px', fontSize: '14px' }}>
<Markdown className='markdown'>{token.value}</Markdown>
</div>
);
}
// Code part of the message.
Expand Down
28 changes: 28 additions & 0 deletions src/components/markdown.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.markdown p {
font-size: 14px;
line-height: 1.43;
margin: 0;
}

.markdown h1 {
margin: 0;
padding: 0;
font-size: 28px;
}

.markdown h2 {
margin: 0;
padding: 0;
font-size: 22px;
}

.markdown h3 {
margin: 0;
padding: 0;
font-size: 18px;
}

.markdown code {
margin: 0;
padding: 0;
}
Loading