diff --git a/package.json b/package.json
index 3868bafb9..d4980db37 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"typescript": "^5.1.3"
},
"dependencies": {
- "dompurify": "^3.1.6"
+ "dompurify": "^3.1.6",
+ "marked": "latest"
}
}
diff --git a/packages/react/src/store/messageStore.js b/packages/react/src/store/messageStore.js
index c359a0c8e..58368594d 100644
--- a/packages/react/src/store/messageStore.js
+++ b/packages/react/src/store/messageStore.js
@@ -10,6 +10,7 @@ const useMessageStore = create((set, get) => ({
editMessage: {},
messagesOffset: 0,
quoteMessage: [],
+ previewMessage: [],
deleteMessageRoles: {},
deleteOwnMessageRoles: {},
forceDeleteMessageRoles: {},
@@ -100,6 +101,14 @@ const useMessageStore = create((set, get) => ({
})),
clearQuoteMessages: () => set({ quoteMessage: [] }),
+ addPreviewMessage: (previewMessage) =>
+ set((state) => ({
+ previewMessage: [...state.previewMessage, previewMessage],
+ })),
+ removePreviewMessage: (previewMessage) =>
+ set((state) => ({
+ previewMessage: state.previewMessage.filter((i) => i !== previewMessage),
+ })),
setMessageToReport: (messageId) =>
set(() => ({ messageToReport: messageId })),
toggleShowReportMessage: () => {
diff --git a/packages/react/src/views/ChatInput/ChatInput.js b/packages/react/src/views/ChatInput/ChatInput.js
index ca748520a..7d3aa0c9c 100644
--- a/packages/react/src/views/ChatInput/ChatInput.js
+++ b/packages/react/src/views/ChatInput/ChatInput.js
@@ -34,6 +34,7 @@ import useShowCommands from '../../hooks/useShowCommands';
import useSearchMentionUser from '../../hooks/useSearchMentionUser';
import formatSelection from '../../lib/formatSelection';
import { parseEmoji } from '../../lib/emoji';
+import PreviewMessage from '../PreviewMessage/PreviewMessage';
const ChatInput = ({ scrollToBottom }) => {
const { styleOverrides, classNames } = useComponentOverrides('ChatInput');
@@ -97,6 +98,7 @@ const ChatInput = ({ scrollToBottom }) => {
editMessage,
setEditMessage,
quoteMessage,
+ previewMessage,
isRecordingMessage,
upsertMessage,
replaceMessage,
@@ -106,6 +108,7 @@ const ChatInput = ({ scrollToBottom }) => {
editMessage: state.editMessage,
setEditMessage: state.setEditMessage,
quoteMessage: state.quoteMessage,
+ previewMessage: state.previewMessage,
isRecordingMessage: state.isRecordingMessage,
upsertMessage: state.upsertMessage,
replaceMessage: state.replaceMessage,
@@ -519,6 +522,13 @@ const ChatInput = ({ scrollToBottom }) => {
))}
+
+ {previewMessage &&
+ previewMessage.length > 0 &&
+ previewMessage.map((message, index) => (
+
+ ))}
+
{editMessage.msg || editMessage.attachments || isChannelReadOnly ? (
state.isRecordingMessage
);
+ const addPreviewMessage = useMessageStore((state) => state.addPreviewMessage);
+ const previewMessage = useMessageStore((state) => state.previewMessage);
+ const removePreviewMessage = useMessageStore(
+ (state) => state.removePreviewMessage
+ );
+
const [isEmojiOpen, setEmojiOpen] = useState(false);
const [isInsertLinkOpen, setInsertLinkOpen] = useState(false);
const [isPopoverOpen, setPopoverOpen] = useState(false);
@@ -144,7 +158,7 @@ const ChatInputFormattingToolbar = ({
}}
>
- file
+ File
) : (
@@ -173,7 +187,7 @@ const ChatInputFormattingToolbar = ({
}}
>
- link
+ Link
) : (
@@ -190,6 +204,41 @@ const ChatInputFormattingToolbar = ({
),
+ preview:
+ isPopoverOpen && popOverItems.includes('preview') ? (
+ {
+ if (isRecordingMessage || !messageRef.current?.value) return;
+ if (previewMessage) {
+ removePreviewMessage(previewMessage[0]);
+ }
+ addPreviewMessage(messageRef.current.value);
+ }}
+ >
+
+ Preview
+
+ ) : (
+
+ {
+ if (isRecordingMessage || !messageRef.current?.value) return;
+ if (previewMessage) {
+ removePreviewMessage(previewMessage[0]);
+ }
+ addPreviewMessage(messageRef.current.value);
+ }}
+ >
+
+
+
+ ),
formatter: formatters
.map((name) => formatter.find((item) => item.name === name))
.map((item) =>
diff --git a/packages/react/src/views/PreviewMessage/PreviewMessage.js b/packages/react/src/views/PreviewMessage/PreviewMessage.js
new file mode 100644
index 000000000..293e0b83a
--- /dev/null
+++ b/packages/react/src/views/PreviewMessage/PreviewMessage.js
@@ -0,0 +1,48 @@
+import React from 'react';
+import {
+ useComponentOverrides,
+ useTheme,
+ Box,
+ ActionButton,
+ Icon,
+} from '@embeddedchat/ui-elements';
+import { marked } from 'marked';
+import Dompurify from 'dompurify';
+import getPreviewMessageStyles from './PreviewMessage.styles';
+import { useMessageStore } from '../../store';
+
+const PreviewMessage = ({ className = '', style = {}, message }) => {
+ const { theme } = useTheme();
+ const styles = getPreviewMessageStyles(theme);
+ const { classNames, styleOverrides } = useComponentOverrides('QuoteMessage');
+ const removePreviewMessage = useMessageStore(
+ (state) => state.removePreviewMessage
+ );
+
+ const formatMessage = () => {
+ const markedText = marked.parse(message);
+ const sanitizedText = Dompurify.sanitize(markedText);
+ return ;
+ };
+
+ return (
+
+
+ removePreviewMessage(message)}
+ size="small"
+ >
+
+
+
+ {formatMessage()}
+
+ );
+};
+
+export default PreviewMessage;
diff --git a/packages/react/src/views/PreviewMessage/PreviewMessage.styles.js b/packages/react/src/views/PreviewMessage/PreviewMessage.styles.js
new file mode 100644
index 000000000..4df4cabf0
--- /dev/null
+++ b/packages/react/src/views/PreviewMessage/PreviewMessage.styles.js
@@ -0,0 +1,43 @@
+import { css } from '@emotion/react';
+
+const getPreviewMessageStyles = (theme) => {
+ const styles = {
+ messageContainer: css`
+ margin: 0.2rem 1.9rem;
+ position: relative;
+ font-size: 0.85rem;
+ background-color: ${theme.colors.background};
+ color: ${theme.colors.foreground};
+ padding: 0.5rem;
+ z-index: 1200;
+ border: 1px solid ${theme.colors.border};
+ border-radius: ${theme.radius};
+ max-width: 100%;
+ box-sizing: border-box;
+ `,
+
+ avatarContainer: css`
+ padding: 0.25rem;
+ display: flex;
+ gap: 0.5rem;
+ `,
+
+ message: css`
+ padding: 0.25rem;
+ overflow-wrap: break-word;
+ word-break: break-word;
+ white-space: normal;
+ width: 100%;
+ `,
+
+ actionBtn: css`
+ position: absolute;
+ top: 0.75rem;
+ right: 0.75rem;
+ `,
+ };
+
+ return styles;
+};
+
+export default getPreviewMessageStyles;
diff --git a/yarn.lock b/yarn.lock
index d3dec11be..2b3d72944 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -15208,6 +15208,7 @@ __metadata:
esbuild: ^0.17.19
husky: ^9.0.11
lerna: ^6.6.2
+ marked: latest
typescript: ^5.1.3
languageName: unknown
linkType: soft
@@ -22445,6 +22446,15 @@ __metadata:
languageName: node
linkType: hard
+"marked@npm:latest":
+ version: 15.0.6
+ resolution: "marked@npm:15.0.6"
+ bin:
+ marked: bin/marked.js
+ checksum: 5218363ac4f6cd1893318ad8b1efacdc8a416f87da28cbcffb419d97602168935249351a3fbe2c59221e7e9955862c6a038ec00f19bf4a6d7e8e0e2e9643d154
+ languageName: node
+ linkType: hard
+
"material-colors@npm:^1.2.1":
version: 1.2.6
resolution: "material-colors@npm:1.2.6"