diff --git a/app/containers/UIKit/MessageBlock.tsx b/app/containers/UIKit/MessageBlock.tsx
index 3039d580539..6ca408e8015 100644
--- a/app/containers/UIKit/MessageBlock.tsx
+++ b/app/containers/UIKit/MessageBlock.tsx
@@ -10,7 +10,13 @@ export const messageBlockWithContext = (context: any) => (props: any) =>
);
-const MessageBlock = ({ blocks }: any) => UiKitMessage(blocks);
+const MessageBlock = ({ blocks }: any) => {
+ if (!blocks) return null;
+ const renderedBlocks = UiKitMessage(blocks);
+ return Array.isArray(renderedBlocks)
+ ? renderedBlocks.map(element => {element})
+ : renderedBlocks;
+};
export const ModalBlockWithContext = (props: any) => (
diff --git a/app/containers/markdown/components/Inline.tsx b/app/containers/markdown/components/Inline.tsx
index 4811bcabdcc..c6f36100994 100644
--- a/app/containers/markdown/components/Inline.tsx
+++ b/app/containers/markdown/components/Inline.tsx
@@ -1,6 +1,6 @@
import React, { useContext } from 'react';
import { Text } from 'react-native';
-import { type Paragraph as ParagraphProps } from '@rocket.chat/message-parser';
+import { type Inlines as InlinesType, type Paragraph as ParagraphProps } from '@rocket.chat/message-parser';
import styles from '../styles';
import { AtMention, Hashtag } from './mentions';
@@ -18,11 +18,14 @@ interface IParagraphProps {
forceTrim?: boolean;
}
+type TInlineWithID = InlinesType & { _id: string };
+
const Inline = ({ value, forceTrim }: IParagraphProps): React.ReactElement | null => {
const { useRealName, username, navToRoomInfo, mentions, channels } = useContext(MarkdownContext);
return (
- {value.map((block, index) => {
+ {value.map((b, index) => {
+ const block = b as TInlineWithID;
// We are forcing trim when is a `[ ](https://https://open.rocket.chat/) plain_text`
// to clean the empty spaces
if (forceTrim) {
@@ -41,20 +44,21 @@ const Inline = ({ value, forceTrim }: IParagraphProps): React.ReactElement | nul
switch (block.type) {
case 'IMAGE':
- return ;
+ return ;
case 'PLAIN_TEXT':
- return ;
+ return ;
case 'BOLD':
- return ;
+ return ;
case 'STRIKE':
- return ;
+ return ;
case 'ITALIC':
- return ;
+ return ;
case 'LINK':
- return ;
+ return ;
case 'MENTION_USER':
return (
);
case 'EMOJI':
- return ;
+ return ;
case 'MENTION_CHANNEL':
- return ;
+ return ;
case 'INLINE_CODE':
- return ;
+ return ;
case 'INLINE_KATEX':
// return ;
- return {block.value};
+ return {block.value};
case 'TIMESTAMP':
- return ;
+ return ;
default:
return null;
}
diff --git a/app/containers/markdown/components/Quote.tsx b/app/containers/markdown/components/Quote.tsx
index e8efbb86957..642dc65ae0e 100644
--- a/app/containers/markdown/components/Quote.tsx
+++ b/app/containers/markdown/components/Quote.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { View } from 'react-native';
-import { type Quote as QuoteProps } from '@rocket.chat/message-parser';
+import { type Paragraph as ParagraphType, type Quote as QuoteProps } from '@rocket.chat/message-parser';
import { themes } from '../../../lib/constants/colors';
import { useTheme } from '../../../theme';
@@ -11,15 +11,18 @@ interface IQuoteProps {
value: QuoteProps['value'];
}
+type TParagraphWithID = ParagraphType & { _id: string };
+
const Quote = ({ value }: IQuoteProps) => {
const { theme } = useTheme();
return (
- {value.map(item => (
-
- ))}
+ {value.map(i => {
+ const item = i as TParagraphWithID;
+ return ;
+ })}
);
diff --git a/app/containers/markdown/components/code/Code.tsx b/app/containers/markdown/components/code/Code.tsx
index aaf524f067d..4b000967e44 100644
--- a/app/containers/markdown/components/code/Code.tsx
+++ b/app/containers/markdown/components/code/Code.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { View } from 'react-native';
-import { type Code as CodeProps } from '@rocket.chat/message-parser';
+import { type CodeLine as CodeLineType, type Code as CodeProps } from '@rocket.chat/message-parser';
import styles from '../../styles';
import { useTheme } from '../../../../theme';
@@ -10,6 +10,8 @@ interface ICodeProps {
value: CodeProps['value'];
}
+type TCodeLineWithID = CodeLineType & { _id: string };
+
const Code = ({ value }: ICodeProps): React.ReactElement => {
const { colors } = useTheme();
@@ -22,10 +24,11 @@ const Code = ({ value }: ICodeProps): React.ReactElement => {
borderColor: colors.strokeLight
}
]}>
- {value.map(block => {
+ {value.map(b => {
+ const block = b as TCodeLineWithID;
switch (block.type) {
case 'CODE_LINE':
- return ;
+ return ;
default:
return null;
}
diff --git a/app/containers/markdown/components/emoji/BigEmoji.tsx b/app/containers/markdown/components/emoji/BigEmoji.tsx
index 817e8b92346..f97cfd05c61 100644
--- a/app/containers/markdown/components/emoji/BigEmoji.tsx
+++ b/app/containers/markdown/components/emoji/BigEmoji.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { StyleSheet, View } from 'react-native';
-import { type BigEmoji as BigEmojiProps } from '@rocket.chat/message-parser';
+import { type Emoji as EmojiType, type BigEmoji as BigEmojiProps } from '@rocket.chat/message-parser';
import Emoji from './Emoji';
@@ -8,6 +8,8 @@ interface IBigEmojiProps {
value: BigEmojiProps['value'];
}
+type TEmojiWithId = EmojiType & { _id: string };
+
const styles = StyleSheet.create({
container: {
flexDirection: 'row'
@@ -16,9 +18,10 @@ const styles = StyleSheet.create({
const BigEmoji = ({ value }: IBigEmojiProps) => (
- {value.map(block => (
-
- ))}
+ {value.map(b => {
+ const block = b as TEmojiWithId;
+ return ;
+ })}
);
diff --git a/app/containers/markdown/components/inline/Bold.tsx b/app/containers/markdown/components/inline/Bold.tsx
index 543296290c4..9dbbf305914 100644
--- a/app/containers/markdown/components/inline/Bold.tsx
+++ b/app/containers/markdown/components/inline/Bold.tsx
@@ -16,25 +16,27 @@ const styles = StyleSheet.create({
}
});
+type TBoldWithID = T & { _id: string };
+
const Bold = ({ value }: IBoldProps) => (
- {value.map(block => {
+ {value.map(b => {
+ const block = b as TBoldWithID;
switch (block.type) {
case 'LINK':
- return ;
+ return ;
case 'PLAIN_TEXT':
- return ;
+ return ;
case 'STRIKE':
- return ;
+ return ;
case 'ITALIC':
- return ;
+ return ;
case 'MENTION_CHANNEL':
- return ;
+ return ;
default:
return null;
}
})}
);
-
export default Bold;
diff --git a/app/containers/markdown/components/inline/Italic.tsx b/app/containers/markdown/components/inline/Italic.tsx
index 0817465c7dd..d71fdcad9e5 100644
--- a/app/containers/markdown/components/inline/Italic.tsx
+++ b/app/containers/markdown/components/inline/Italic.tsx
@@ -15,20 +15,23 @@ const styles = StyleSheet.create({
}
});
+type TItalicWithID = T & { _id: string };
+
const Italic = ({ value }: IItalicProps) => (
- {value.map(block => {
+ {value.map(b => {
+ const block = b as TItalicWithID;
switch (block.type) {
case 'LINK':
- return ;
+ return ;
case 'PLAIN_TEXT':
- return ;
+ return ;
case 'STRIKE':
- return ;
+ return ;
case 'BOLD':
- return ;
+ return ;
case 'MENTION_CHANNEL':
- return ;
+ return ;
default:
return null;
}
diff --git a/app/containers/markdown/components/inline/Strike.tsx b/app/containers/markdown/components/inline/Strike.tsx
index 552b2fd3a72..e2ea2cf94fd 100644
--- a/app/containers/markdown/components/inline/Strike.tsx
+++ b/app/containers/markdown/components/inline/Strike.tsx
@@ -15,20 +15,23 @@ const styles = StyleSheet.create({
}
});
+type TStrikeWithID = T & { _id: string };
+
const Strike = ({ value }: IStrikeProps) => (
- {value.map(block => {
+ {value.map(b => {
+ const block = b as TStrikeWithID;
switch (block.type) {
case 'LINK':
- return ;
+ return ;
case 'PLAIN_TEXT':
- return ;
+ return ;
case 'BOLD':
- return ;
+ return ;
case 'ITALIC':
- return ;
+ return ;
case 'MENTION_CHANNEL':
- return ;
+ return ;
default:
return null;
}
diff --git a/app/containers/markdown/components/list/TaskList.tsx b/app/containers/markdown/components/list/TaskList.tsx
index 059d02cb2d6..8b6e5805801 100644
--- a/app/containers/markdown/components/list/TaskList.tsx
+++ b/app/containers/markdown/components/list/TaskList.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { Text, View } from 'react-native';
-import { type Tasks as TasksProps } from '@rocket.chat/message-parser';
+import { type Task as TaskType, type Tasks as TasksProps } from '@rocket.chat/message-parser';
import Inline from '../Inline';
import styles from '../../styles';
@@ -11,24 +11,29 @@ interface ITasksProps {
value: TasksProps['value'];
}
+type TTaskWithID = TaskType & { _id: string };
+
const TaskList = ({ value = [] }: ITasksProps) => {
const { colors } = useTheme();
return (
- {value.map(item => (
-
-
-
-
-
-
-
-
- ))}
+ {value.map(i => {
+ const item = i as TTaskWithID;
+ return (
+
+
+
+
+
+
+
+
+ );
+ })}
);
};
diff --git a/app/containers/markdown/components/list/UnorderedList.tsx b/app/containers/markdown/components/list/UnorderedList.tsx
index 71e11f351f7..d7ca10114f5 100644
--- a/app/containers/markdown/components/list/UnorderedList.tsx
+++ b/app/containers/markdown/components/list/UnorderedList.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { type UnorderedList as UnorderedListProps } from '@rocket.chat/message-parser';
+import { type ListItem, type UnorderedList as UnorderedListProps } from '@rocket.chat/message-parser';
import { View, Text } from 'react-native';
import Inline from '../Inline';
@@ -11,18 +11,23 @@ interface IUnorderedListProps {
value: UnorderedListProps['value'];
}
+type TListItemWithID = ListItem & { _id: string };
+
const UnorderedList = ({ value }: IUnorderedListProps) => {
const { theme } = useTheme();
return (
- {value.map(item => (
-
- {'\u2022 '}
-
-
-
-
- ))}
+ {value.map(i => {
+ const item = i as TListItemWithID;
+ return (
+
+ {'\u2022 '}
+
+
+
+
+ );
+ })}
);
};
diff --git a/app/containers/markdown/index.tsx b/app/containers/markdown/index.tsx
index c641b3f4cf8..9aca7bcc049 100644
--- a/app/containers/markdown/index.tsx
+++ b/app/containers/markdown/index.tsx
@@ -35,6 +35,28 @@ interface IMarkdownProps {
textStyle?: StyleProp;
}
+const generateId = (size = 16) => {
+ const rand = Math.random().toString(36).slice(2);
+ const time = Date.now().toString(36);
+ return (rand + time).slice(-size); // use from end
+};
+
+const assignIds = (block: any) => {
+ if (!block._id) {
+ const id = generateId();
+ block._id = id;
+ }
+
+ // add to nested
+ if (Array.isArray(block.value)) {
+ block.value = block.value.map((child: any) => assignIds(child));
+ }
+
+ if (Array.isArray(block.blocks)) {
+ block.blocks = block.blocks.map((child: any) => assignIds(child));
+ }
+ return block;
+};
const Markdown: React.FC = ({
msg,
md,
@@ -58,6 +80,8 @@ const Markdown: React.FC = ({
return null;
}
+ tokens = tokens.map(assignIds);
+
if (isEmpty(tokens)) return null;
return (
@@ -75,29 +99,29 @@ const Markdown: React.FC = ({
{tokens?.map(block => {
switch (block.type) {
case 'BIG_EMOJI':
- return ;
+ return ;
case 'UNORDERED_LIST':
- return ;
+ return ;
case 'ORDERED_LIST':
- return ;
+ return ;
case 'TASKS':
- return ;
+ return ;
case 'QUOTE':
- return
;
+ return
;
case 'PARAGRAPH':
- return ;
+ return ;
case 'CODE':
- return ;
+ return ;
case 'HEADING':
- return ;
+ return ;
case 'LINE_BREAK':
- return ;
+ return ;
// This prop exists, but not even on the web it is treated, so...
// https://github.com/RocketChat/Rocket.Chat/blob/develop/packages/gazzodown/src/Markup.tsx
// case 'LIST_ITEM':
// return ;
case 'KATEX':
- return ;
+ return ;
default:
return null;
}