Compare commits
30 Commits
develop
...
update.mes
Author | SHA1 | Date |
---|---|---|
Gerzon Z | 9ddbef603c | |
Gerzon Z | 4418a5eacf | |
Gerzon Z | 417ea353a9 | |
Gerzon Z | 47eeadc07f | |
Gerzon Z | 4895fd2e2e | |
Gerzon Z | ddfdba438b | |
Gerzon Z | e27ffd7487 | |
Gerzon Z | 5edbfe65b6 | |
Gerzon Z | 20d2db9aa3 | |
Gerzon Z | 7a88d8df62 | |
Diego Mello | 3c7ad9a427 | |
Gerzon Z | 73f4e815c0 | |
Gerzon Z | 81c0d322c3 | |
Gerzon Z | 06b3da0b7a | |
Gerzon Z | d4c3322c32 | |
Gerzon Z | d7c0b4c76f | |
Gerzon Z | 8f26301428 | |
Gerzon Z | 68e2fb179d | |
Gerzon Z | c2dd2dbc48 | |
Gerzon Z | 71aed0cde8 | |
Gerzon Z | 1e7bb87c9d | |
Gerzon Z | ff9b0fec9c | |
Gerzon Z | 559f804073 | |
Gerzon Z | 332a4ebf71 | |
Gerzon Z | 56ee8d45cd | |
Gerzon Z | be431179e7 | |
Gerzon Z | c95c2089fb | |
Gerzon Z | f779a0ac31 | |
Gerzon Z | 7ec40a1110 | |
Gerzon Z | 1cd297c9ad |
|
@ -203,6 +203,9 @@ export default {
|
|||
Jitsi_Enable_Channels: {
|
||||
type: 'valuesAsBoolean'
|
||||
},
|
||||
Accounts_Default_User_Preferences_enableMessageParserEarlyAdoption: {
|
||||
type: 'valueAsBoolean'
|
||||
},
|
||||
Canned_Responses_Enable: {
|
||||
type: 'valueAsBoolean'
|
||||
}
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
import React from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import { StyleProp, Text, TextStyle } from 'react-native';
|
||||
|
||||
import { themes } from '../../constants/colors';
|
||||
import { useTheme } from '../../theme';
|
||||
import styles from './styles';
|
||||
|
||||
interface IHashtag {
|
||||
hashtag: string;
|
||||
navToRoomInfo: Function;
|
||||
style: [];
|
||||
theme: string;
|
||||
style: StyleProp<TextStyle>;
|
||||
channels: {
|
||||
name: string;
|
||||
_id: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [], theme }: IHashtag) => {
|
||||
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IHashtag) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
const handlePress = () => {
|
||||
const index = channels.findIndex(channel => channel.name === hashtag);
|
||||
const navParam = {
|
||||
|
|
|
@ -3,6 +3,8 @@ import { Image, Text } from 'react-native';
|
|||
import { Node, Parser } from 'commonmark';
|
||||
import Renderer from 'commonmark-react-renderer';
|
||||
import removeMarkdown from 'remove-markdown';
|
||||
import { connect } from 'react-redux';
|
||||
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||
|
||||
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||
import I18n from '../../i18n';
|
||||
|
@ -20,9 +22,21 @@ import MarkdownTableCell from './TableCell';
|
|||
import mergeTextNodes from './mergeTextNodes';
|
||||
import styles from './styles';
|
||||
import { isValidURL } from '../../utils/url';
|
||||
import { getUserSelector } from '../../selectors/login';
|
||||
import NewMarkdown from './new';
|
||||
|
||||
interface IUser {
|
||||
_id: string;
|
||||
username: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
type UserMention = Pick<IUser, '_id' | 'username' | 'name'>;
|
||||
|
||||
interface IMarkdownProps {
|
||||
msg: string;
|
||||
md: MarkdownAST;
|
||||
mentions: UserMention[];
|
||||
getCustomEmoji: Function;
|
||||
baseUrl: string;
|
||||
username: string;
|
||||
|
@ -35,7 +49,9 @@ interface IMarkdownProps {
|
|||
name: string;
|
||||
_id: number;
|
||||
}[];
|
||||
mentions: object[];
|
||||
user: {
|
||||
enableMessageParserEarlyAdoption: boolean;
|
||||
};
|
||||
navToRoomInfo: Function;
|
||||
preview: boolean;
|
||||
theme: string;
|
||||
|
@ -227,8 +243,8 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
|||
};
|
||||
|
||||
renderHashtag = ({ hashtag }: { hashtag: string }) => {
|
||||
const { channels, navToRoomInfo, style, theme } = this.props;
|
||||
return <MarkdownHashtag hashtag={hashtag} channels={channels} navToRoomInfo={navToRoomInfo} theme={theme} style={style} />;
|
||||
const { channels, navToRoomInfo, style } = this.props;
|
||||
return <MarkdownHashtag hashtag={hashtag} channels={channels} navToRoomInfo={navToRoomInfo} style={style} />;
|
||||
};
|
||||
|
||||
renderAtMention = ({ mentionName }: { mentionName: string }) => {
|
||||
|
@ -329,12 +345,28 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { msg, numberOfLines, preview = false, theme, style = [], testID } = this.props;
|
||||
const {
|
||||
msg,
|
||||
md,
|
||||
numberOfLines,
|
||||
preview = false,
|
||||
theme,
|
||||
style = [],
|
||||
testID,
|
||||
user,
|
||||
mentions,
|
||||
channels,
|
||||
navToRoomInfo
|
||||
} = this.props;
|
||||
|
||||
if (!msg) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (user.enableMessageParserEarlyAdoption && md) {
|
||||
return <NewMarkdown tokens={md} style={style} mentions={mentions} channels={channels} navToRoomInfo={navToRoomInfo} />;
|
||||
}
|
||||
|
||||
let m = formatText(msg);
|
||||
|
||||
// Ex: '[ ](https://open.rocket.chat/group/test?msg=abcdef) Test'
|
||||
|
@ -366,4 +398,8 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
|||
}
|
||||
}
|
||||
|
||||
export default Markdown;
|
||||
const mapStateToProps = (state: any) => ({
|
||||
user: getUserSelector(state)
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(Markdown);
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { BigEmoji as BigEmojiProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import Emoji from './Emoji';
|
||||
|
||||
interface IBigEmojiProps {
|
||||
value: BigEmojiProps['value'];
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: 'row'
|
||||
}
|
||||
});
|
||||
|
||||
const BigEmoji: React.FC<IBigEmojiProps> = ({ value }) => (
|
||||
<View style={styles.container}>
|
||||
{value.map(block => (
|
||||
<Emoji value={block.value} isBigEmoji />
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
|
||||
export default BigEmoji;
|
|
@ -0,0 +1,37 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet, Text } from 'react-native';
|
||||
import { Bold as BoldProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import sharedStyles from '../../../views/Styles';
|
||||
import Strike from './Strike';
|
||||
import Italic from './Italic';
|
||||
import Plain from './Plain';
|
||||
|
||||
interface IBoldProps {
|
||||
value: BoldProps['value'];
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
text: {
|
||||
...sharedStyles.textBold
|
||||
}
|
||||
});
|
||||
|
||||
const Bold: React.FC<IBoldProps> = ({ value }) => (
|
||||
<Text style={styles.text}>
|
||||
{value.map(block => {
|
||||
switch (block.type) {
|
||||
case 'PLAIN_TEXT':
|
||||
return <Plain value={block.value} />;
|
||||
case 'STRIKE':
|
||||
return <Strike value={block.value} />;
|
||||
case 'ITALIC':
|
||||
return <Italic value={block.value} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</Text>
|
||||
);
|
||||
|
||||
export default Bold;
|
|
@ -0,0 +1,41 @@
|
|||
import React from 'react';
|
||||
import { StyleProp, Text, TextStyle } from 'react-native';
|
||||
import { Code as CodeProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import styles from '../styles';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import { useTheme } from '../../../theme';
|
||||
import CodeLine from './CodeLine';
|
||||
|
||||
interface ICodeProps {
|
||||
value: CodeProps['value'];
|
||||
style: StyleProp<TextStyle>[];
|
||||
}
|
||||
|
||||
const Code: React.FC<ICodeProps> = ({ value, style }) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
return (
|
||||
<Text
|
||||
style={[
|
||||
{
|
||||
...styles.codeBlock,
|
||||
color: themes[theme].bodyText,
|
||||
backgroundColor: themes[theme].bannerBackground,
|
||||
borderColor: themes[theme].borderColor
|
||||
},
|
||||
...style
|
||||
]}>
|
||||
{value.map(block => {
|
||||
switch (block.type) {
|
||||
case 'CODE_LINE':
|
||||
return <CodeLine value={block.value} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
export default Code;
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import { CodeLine as CodeLineProps } from '@rocket.chat/message-parser';
|
||||
|
||||
interface ICodeLineProps {
|
||||
value: CodeLineProps['value'];
|
||||
}
|
||||
|
||||
const CodeLine: React.FC<ICodeLineProps> = ({ value }) => {
|
||||
if (value.type !== 'PLAIN_TEXT') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Text>{value.value}</Text>;
|
||||
};
|
||||
|
||||
export default CodeLine;
|
|
@ -0,0 +1,24 @@
|
|||
import React from 'react';
|
||||
import { StyleProp, Text, TextStyle } from 'react-native';
|
||||
import { Emoji as EmojiProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import shortnameToUnicode from '../../../utils/shortnameToUnicode';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import { useTheme } from '../../../theme';
|
||||
import styles from '../styles';
|
||||
|
||||
interface IEmojiProps {
|
||||
value: EmojiProps['value'];
|
||||
style?: StyleProp<TextStyle>;
|
||||
isBigEmoji?: boolean;
|
||||
}
|
||||
|
||||
const Emoji: React.FC<IEmojiProps> = ({ value, style, isBigEmoji }) => {
|
||||
const { theme } = useTheme();
|
||||
const emojiUnicode = shortnameToUnicode(`:${value.value}:`);
|
||||
return (
|
||||
<Text style={[{ color: themes[theme].bodyText }, isBigEmoji ? styles.textBig : styles.text, style]}>{emojiUnicode}</Text>
|
||||
);
|
||||
};
|
||||
|
||||
export default Emoji;
|
|
@ -0,0 +1,32 @@
|
|||
import React from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import { Heading as HeadingProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import { themes } from '../../../constants/colors';
|
||||
import styles from '../styles';
|
||||
import { useTheme } from '../../../theme';
|
||||
|
||||
interface IHeadingProps {
|
||||
value: HeadingProps['value'];
|
||||
level: HeadingProps['level'];
|
||||
}
|
||||
|
||||
const Heading: React.FC<IHeadingProps> = ({ value, level }) => {
|
||||
const { theme } = useTheme();
|
||||
const textStyle = styles[`heading${level}`];
|
||||
|
||||
return (
|
||||
<Text style={[textStyle, { color: themes[theme].bodyText }]}>
|
||||
{value.map(block => {
|
||||
switch (block.type) {
|
||||
case 'PLAIN_TEXT':
|
||||
return block.value;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
export default Heading;
|
|
@ -0,0 +1,56 @@
|
|||
import React from 'react';
|
||||
import { StyleProp, ViewStyle } from 'react-native';
|
||||
import { Paragraph as ParagraphProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import Hashtag from '../Hashtag';
|
||||
import Link from './Link';
|
||||
import Plain from './Plain';
|
||||
import Bold from './Bold';
|
||||
import Strike from './Strike';
|
||||
import Italic from './Italic';
|
||||
import Emoji from './Emoji';
|
||||
import Mention from './Mention';
|
||||
import InlineCode from './InlineCode';
|
||||
import { UserMention } from '../../message/interfaces';
|
||||
|
||||
interface IParagraphProps {
|
||||
value: ParagraphProps['value'];
|
||||
mentions?: UserMention[];
|
||||
channels?: {
|
||||
name: string;
|
||||
_id: number;
|
||||
}[];
|
||||
navToRoomInfo?: Function;
|
||||
style?: StyleProp<ViewStyle>[];
|
||||
}
|
||||
|
||||
const Inline: React.FC<IParagraphProps> = ({ value, mentions, channels, navToRoomInfo, style }) => (
|
||||
<>
|
||||
{value.map(block => {
|
||||
switch (block.type) {
|
||||
case 'PLAIN_TEXT':
|
||||
return <Plain value={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} navToRoomInfo={navToRoomInfo} mentions={mentions} style={style} />;
|
||||
case 'EMOJI':
|
||||
return <Emoji value={block.value} />;
|
||||
case 'MENTION_CHANNEL':
|
||||
return <Hashtag hashtag={block.value.value} navToRoomInfo={navToRoomInfo} channels={channels} style={style} />;
|
||||
case 'INLINE_CODE':
|
||||
return <InlineCode value={block.value} style={style} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
||||
export default Inline;
|
|
@ -0,0 +1,41 @@
|
|||
import React from 'react';
|
||||
import { StyleProp, Text, TextStyle } from 'react-native';
|
||||
import { InlineCode as InlineCodeProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import styles from '../styles';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import { useTheme } from '../../../theme';
|
||||
import Plain from './Plain';
|
||||
|
||||
interface IInlineCodeProps {
|
||||
value: InlineCodeProps['value'];
|
||||
style: StyleProp<TextStyle>[];
|
||||
}
|
||||
|
||||
const InlineCode: React.FC<IInlineCodeProps> = ({ value, style }) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
return (
|
||||
<Text
|
||||
style={[
|
||||
{
|
||||
...styles.codeInline,
|
||||
color: themes[theme].bodyText,
|
||||
backgroundColor: themes[theme].bannerBackground,
|
||||
borderColor: themes[theme].borderColor
|
||||
},
|
||||
...style
|
||||
]}>
|
||||
{(block => {
|
||||
switch (block.type) {
|
||||
case 'PLAIN_TEXT':
|
||||
return <Plain value={block.value} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})(value)}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
export default InlineCode;
|
|
@ -0,0 +1,36 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet, Text } from 'react-native';
|
||||
import { Italic as ItalicProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import Strike from './Strike';
|
||||
import Bold from './Bold';
|
||||
import Plain from './Plain';
|
||||
|
||||
interface IItalicProps {
|
||||
value: ItalicProps['value'];
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
text: {
|
||||
fontStyle: 'italic'
|
||||
}
|
||||
});
|
||||
|
||||
const Italic: React.FC<IItalicProps> = ({ value }) => (
|
||||
<Text style={styles.text}>
|
||||
{value.map(block => {
|
||||
switch (block.type) {
|
||||
case 'PLAIN_TEXT':
|
||||
return <Plain value={block.value} />;
|
||||
case 'STRIKE':
|
||||
return <Strike value={block.value} />;
|
||||
case 'BOLD':
|
||||
return <Bold value={block.value} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</Text>
|
||||
);
|
||||
|
||||
export default Italic;
|
|
@ -0,0 +1,55 @@
|
|||
import React from 'react';
|
||||
import { Text, Clipboard } from 'react-native';
|
||||
import { Link as LinkProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import styles from '../styles';
|
||||
import I18n from '../../../i18n';
|
||||
import { LISTENER } from '../../Toast';
|
||||
import { useTheme } from '../../../theme';
|
||||
import openLink from '../../../utils/openLink';
|
||||
import EventEmitter from '../../../utils/events';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import Strike from './Strike';
|
||||
import Italic from './Italic';
|
||||
import Bold from './Bold';
|
||||
|
||||
interface ILinkProps {
|
||||
value: LinkProps['value'];
|
||||
}
|
||||
|
||||
const Link: React.FC<ILinkProps> = ({ value }) => {
|
||||
const { theme } = useTheme();
|
||||
const { src, label } = value;
|
||||
const handlePress = () => {
|
||||
if (!src.value) {
|
||||
return;
|
||||
}
|
||||
openLink(src.value, theme);
|
||||
};
|
||||
|
||||
const onLongPress = () => {
|
||||
Clipboard.setString(src.value);
|
||||
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
|
||||
};
|
||||
|
||||
return (
|
||||
<Text onPress={handlePress} onLongPress={onLongPress} style={{ ...styles.link, color: themes[theme].actionTintColor }}>
|
||||
{(block => {
|
||||
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)}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
export default Link;
|
|
@ -0,0 +1,59 @@
|
|||
import React from 'react';
|
||||
import { StyleProp, Text, TextStyle, ViewStyle } from 'react-native';
|
||||
import { UserMention as UserMentionProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import styles from '../styles';
|
||||
import { events, logEvent } from '../../../utils/log';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import { UserMention } from '../../message/interfaces';
|
||||
|
||||
interface IMentionProps {
|
||||
value: UserMentionProps['value'];
|
||||
mentions: UserMention[];
|
||||
navToRoomInfo: Function;
|
||||
style: StyleProp<ViewStyle>[];
|
||||
}
|
||||
|
||||
const Mention: React.FC<IMentionProps> = ({ value: { value: mention }, mentions, navToRoomInfo, style }) => {
|
||||
const { theme } = useTheme();
|
||||
let mentionStyle: StyleProp<TextStyle>;
|
||||
const notMentionedStyle = [styles.text, { color: themes[theme].bodyText }, ...style];
|
||||
const mentioned = mentions.find(mentioned => mentioned.username === mention);
|
||||
|
||||
if (mention === 'all' || mention === 'here') {
|
||||
mentionStyle = [
|
||||
{
|
||||
color: themes[theme].mentionGroupColor
|
||||
},
|
||||
...style
|
||||
];
|
||||
} else if (mentioned) {
|
||||
mentionStyle = {
|
||||
color: themes[theme].mentionMeColor
|
||||
};
|
||||
} else {
|
||||
mentionStyle = {
|
||||
color: themes[theme].mentionOtherColor
|
||||
};
|
||||
}
|
||||
|
||||
const handlePress = () => {
|
||||
logEvent(events.ROOM_MENTION_GO_USER_INFO);
|
||||
const navParam = {
|
||||
t: 'd',
|
||||
rid: mentioned && mentioned._id
|
||||
};
|
||||
navToRoomInfo(navParam);
|
||||
};
|
||||
|
||||
return (
|
||||
<Text
|
||||
style={[styles.mention, (mention || mentioned) && mentionStyle, !(mention || mentioned) && notMentionedStyle, ...style]}
|
||||
onPress={handlePress}>
|
||||
{mentioned ? mentioned.name || mention : `@{${mention}}`}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
export default Mention;
|
|
@ -0,0 +1,28 @@
|
|||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { OrderedList as OrderedListProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import Inline from './Inline';
|
||||
|
||||
interface IOrderedListProps {
|
||||
value: OrderedListProps['value'];
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: 'row'
|
||||
}
|
||||
});
|
||||
|
||||
const OrderedList: React.FC<IOrderedListProps> = React.memo(({ value }) => (
|
||||
<>
|
||||
{value.map((item, index) => (
|
||||
<View style={styles.container}>
|
||||
<Text>{index + 1}. </Text>
|
||||
<Inline value={item.value} />
|
||||
</View>
|
||||
))}
|
||||
</>
|
||||
));
|
||||
|
||||
export default OrderedList;
|
|
@ -0,0 +1,31 @@
|
|||
import React from 'react';
|
||||
import { StyleProp, Text, ViewStyle } from 'react-native';
|
||||
import { Paragraph as ParagraphProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import { UserMention } from '../../message/interfaces';
|
||||
import Inline from './Inline';
|
||||
import styles from '../styles';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { themes } from '../../../constants/colors';
|
||||
|
||||
interface IParagraphProps {
|
||||
value: ParagraphProps['value'];
|
||||
mentions?: UserMention[];
|
||||
channels?: {
|
||||
name: string;
|
||||
_id: number;
|
||||
}[];
|
||||
navToRoomInfo?: Function;
|
||||
style?: StyleProp<ViewStyle>[];
|
||||
}
|
||||
|
||||
const Paragraph: React.FC<IParagraphProps> = ({ value, mentions, channels, navToRoomInfo, style }) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<Text style={[styles.text, style, { color: themes[theme].bodyText }]}>
|
||||
<Inline value={value} mentions={mentions} channels={channels} navToRoomInfo={navToRoomInfo} style={style} />
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
export default Paragraph;
|
|
@ -0,0 +1,11 @@
|
|||
import React from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import { Plain as PlainProps } from '@rocket.chat/message-parser';
|
||||
|
||||
interface IPlainProps {
|
||||
value: PlainProps['value'];
|
||||
}
|
||||
|
||||
const Plain: React.FC<IPlainProps> = ({ value }) => <Text accessibilityLabel={value}>{value}</Text>;
|
||||
|
||||
export default Plain;
|
|
@ -0,0 +1,28 @@
|
|||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { Quote as QuoteProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import { themes } from '../../../constants/colors';
|
||||
import { useTheme } from '../../../theme';
|
||||
import styles from '../styles';
|
||||
import Paragraph from './Paragraph';
|
||||
|
||||
interface IQuoteProps {
|
||||
value: QuoteProps['value'];
|
||||
}
|
||||
|
||||
const Quote: React.FC<IQuoteProps> = ({ value }) => {
|
||||
const { theme } = useTheme();
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={[styles.quote, { backgroundColor: themes[theme].borderColor }]} />
|
||||
<View style={styles.childContainer}>
|
||||
{value.map(item => (
|
||||
<Paragraph value={item.value} mentions={[]} />
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default Quote;
|
|
@ -0,0 +1,36 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet, Text } from 'react-native';
|
||||
import { Strike as StrikeProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import Bold from './Bold';
|
||||
import Italic from './Italic';
|
||||
import Plain from './Plain';
|
||||
|
||||
interface IStrikeProps {
|
||||
value: StrikeProps['value'];
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
text: {
|
||||
textDecorationLine: 'line-through'
|
||||
}
|
||||
});
|
||||
|
||||
const Strike: React.FC<IStrikeProps> = ({ value }) => (
|
||||
<Text style={styles.text}>
|
||||
{value.map(block => {
|
||||
switch (block.type) {
|
||||
case 'PLAIN_TEXT':
|
||||
return <Plain value={block.value} />;
|
||||
case 'BOLD':
|
||||
return <Bold value={block.value} />;
|
||||
case 'ITALIC':
|
||||
return <Italic value={block.value} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</Text>
|
||||
);
|
||||
|
||||
export default Strike;
|
|
@ -0,0 +1,26 @@
|
|||
import React from 'react';
|
||||
import { Text } from 'react-native';
|
||||
import { Tasks as TasksProps } from '@rocket.chat/message-parser';
|
||||
import { Checkbox } from 'react-native-ui-lib';
|
||||
|
||||
import Inline from './Inline';
|
||||
|
||||
interface ITasksProps {
|
||||
value: TasksProps['value'];
|
||||
}
|
||||
|
||||
const TaskList: React.FC<ITasksProps> = ({ value }) => (
|
||||
<Text
|
||||
style={{
|
||||
marginLeft: 0,
|
||||
paddingLeft: 0
|
||||
}}>
|
||||
{value.map(item => (
|
||||
<>
|
||||
<Checkbox checked={item.status} /> <Inline value={item.value} />
|
||||
</>
|
||||
))}
|
||||
</Text>
|
||||
);
|
||||
|
||||
export default TaskList;
|
|
@ -0,0 +1,18 @@
|
|||
import React from 'react';
|
||||
import { UnorderedList as UnorderedListProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import Inline from './Inline';
|
||||
|
||||
interface IUnorderedListProps {
|
||||
value: UnorderedListProps['value'];
|
||||
}
|
||||
|
||||
const UnorderedList: React.FC<IUnorderedListProps> = ({ value }) => (
|
||||
<>
|
||||
{value.map(item => (
|
||||
<Inline value={item.value} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
export default UnorderedList;
|
|
@ -0,0 +1,67 @@
|
|||
import React from 'react';
|
||||
import { StyleProp, ViewStyle } from 'react-native';
|
||||
import { MarkdownAST, BigEmoji as BigEmojiProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import Quote from './Quote';
|
||||
import Paragraph from './Paragraph';
|
||||
import Heading from './Heading';
|
||||
import Code from './Code';
|
||||
import BigEmoji from './BigEmoji';
|
||||
import OrderedList from './OrderedList';
|
||||
import UnorderedList from './UnorderedList';
|
||||
import { UserMention } from '../../message/interfaces';
|
||||
import TaskList from './TaskList';
|
||||
|
||||
interface IBodyProps {
|
||||
tokens: MarkdownAST;
|
||||
mentions: UserMention[];
|
||||
channels: {
|
||||
name: string;
|
||||
_id: number;
|
||||
}[];
|
||||
navToRoomInfo: Function;
|
||||
style: StyleProp<ViewStyle>[];
|
||||
}
|
||||
|
||||
const isBigEmoji = (tokens: MarkdownAST): tokens is [BigEmojiProps] => tokens.length === 1 && tokens[0].type === 'BIG_EMOJI';
|
||||
|
||||
const Body: React.FC<IBodyProps> = ({ tokens, mentions, channels, navToRoomInfo, style }) => {
|
||||
if (isBigEmoji(tokens)) {
|
||||
return <BigEmoji value={tokens[0].value} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{tokens.map(block => {
|
||||
switch (block.type) {
|
||||
case 'UNORDERED_LIST':
|
||||
return <UnorderedList value={block.value} />;
|
||||
case 'ORDERED_LIST':
|
||||
return <OrderedList value={block.value} />;
|
||||
case 'TASKS':
|
||||
return <TaskList value={block.value} />;
|
||||
case 'QUOTE':
|
||||
return <Quote value={block.value} />;
|
||||
case 'PARAGRAPH':
|
||||
return (
|
||||
<Paragraph
|
||||
value={block.value}
|
||||
navToRoomInfo={navToRoomInfo}
|
||||
channels={channels}
|
||||
mentions={mentions}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
case 'CODE':
|
||||
return <Code value={block.value} style={style} />;
|
||||
case 'HEADING':
|
||||
return <Heading value={block.value} level={block.level} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Body;
|
|
@ -73,7 +73,9 @@ export default StyleSheet.create<any>({
|
|||
...sharedStyles.textRegular,
|
||||
...codeFontFamily,
|
||||
borderWidth: 1,
|
||||
borderRadius: 4
|
||||
borderRadius: 4,
|
||||
paddingLeft: 2,
|
||||
paddingTop: 2
|
||||
},
|
||||
codeBlock: {
|
||||
...sharedStyles.textRegular,
|
||||
|
|
|
@ -51,6 +51,7 @@ const Content = React.memo(
|
|||
// @ts-ignore
|
||||
<Markdown
|
||||
msg={props.msg}
|
||||
md={props.md}
|
||||
baseUrl={baseUrl}
|
||||
getCustomEmoji={props.getCustomEmoji}
|
||||
username={user.username}
|
||||
|
@ -103,6 +104,9 @@ const Content = React.memo(
|
|||
if (prevProps.isIgnored !== nextProps.isIgnored) {
|
||||
return false;
|
||||
}
|
||||
if (!dequal(prevProps.md, nextProps.md)) {
|
||||
return false;
|
||||
}
|
||||
if (!dequal(prevProps.mentions, nextProps.mentions)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -357,7 +357,8 @@ class MessageContainer extends React.Component<IMessageContainerProps, any> {
|
|||
unread,
|
||||
blocks,
|
||||
autoTranslate: autoTranslateMessage,
|
||||
replies
|
||||
replies,
|
||||
md
|
||||
} = item;
|
||||
|
||||
let message = msg;
|
||||
|
@ -391,6 +392,7 @@ class MessageContainer extends React.Component<IMessageContainerProps, any> {
|
|||
<Message
|
||||
id={id}
|
||||
msg={message}
|
||||
md={md}
|
||||
rid={rid}
|
||||
author={u}
|
||||
ts={ts}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||
|
||||
export interface IMessageAttachments {
|
||||
attachments: any;
|
||||
timeFormat: string;
|
||||
|
@ -48,12 +50,21 @@ export interface IMessageCallButton {
|
|||
callJitsi: Function;
|
||||
}
|
||||
|
||||
export interface IUser {
|
||||
_id: string;
|
||||
username: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type UserMention = Pick<IUser, '_id' | 'username' | 'name'>;
|
||||
|
||||
export interface IMessageContent {
|
||||
isTemp: boolean;
|
||||
isInfo: boolean;
|
||||
tmid: string;
|
||||
isThreadRoom: boolean;
|
||||
msg: string;
|
||||
md: MarkdownAST;
|
||||
theme: string;
|
||||
isEdited: boolean;
|
||||
isEncrypted: boolean;
|
||||
|
@ -62,7 +73,7 @@ export interface IMessageContent {
|
|||
name: string;
|
||||
_id: number;
|
||||
}[];
|
||||
mentions: object[];
|
||||
mentions: UserMention[];
|
||||
navToRoomInfo: Function;
|
||||
useRealName: boolean;
|
||||
isIgnored: boolean;
|
||||
|
|
|
@ -773,6 +773,7 @@
|
|||
"Select_Team_Channels_To_Delete": "Select the Team’s Channels you would like to delete, the ones you do not select will be moved to the Workspace. \n\nNotice that public Channels will be public and visible to everyone.",
|
||||
"You_are_converting_the_team": "You are converting this Team to a Channel",
|
||||
"creating_discussion": "creating discussion",
|
||||
"Enable_Message_Parser": "Enable Message Parser",
|
||||
"Canned_Responses": "Canned Responses",
|
||||
"No_match_found": "No match found.",
|
||||
"Check_canned_responses": "Check on canned responses.",
|
||||
|
|
|
@ -81,4 +81,6 @@ export default class Message extends Model {
|
|||
@field('e2e') e2e;
|
||||
|
||||
@field('tshow') tshow;
|
||||
|
||||
@json('md', sanitizer) md;
|
||||
}
|
||||
|
|
|
@ -190,6 +190,15 @@ export default schemaMigrations({
|
|||
]
|
||||
})
|
||||
]
|
||||
},
|
||||
{
|
||||
toVersion: 14,
|
||||
steps: [
|
||||
addColumns({
|
||||
table: 'messages',
|
||||
columns: [{ name: 'md', type: 'string', isOptional: true }]
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
|
@ -25,4 +25,6 @@ export default class User extends Model {
|
|||
@field('show_message_in_main_thread') showMessageInMainThread;
|
||||
|
||||
@field('is_from_webview') isFromWebView;
|
||||
|
||||
@field('enable_message_parser_early_adoption') enableMessageParserEarlyAdoption;
|
||||
}
|
||||
|
|
|
@ -94,6 +94,15 @@ export default schemaMigrations({
|
|||
columns: [{ name: 'is_from_webview', type: 'boolean', isOptional: true }]
|
||||
})
|
||||
]
|
||||
},
|
||||
{
|
||||
toVersion: 12,
|
||||
steps: [
|
||||
addColumns({
|
||||
table: 'users',
|
||||
columns: [{ name: 'enable_message_parser_early_adoption', type: 'boolean', isOptional: true }]
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { appSchema, tableSchema } from '@nozbe/watermelondb';
|
||||
|
||||
export default appSchema({
|
||||
version: 13,
|
||||
version: 14,
|
||||
tables: [
|
||||
tableSchema({
|
||||
name: 'subscriptions',
|
||||
|
@ -115,7 +115,8 @@ export default appSchema({
|
|||
{ name: 'tmsg', type: 'string', isOptional: true },
|
||||
{ name: 'blocks', type: 'string', isOptional: true },
|
||||
{ name: 'e2e', type: 'string', isOptional: true },
|
||||
{ name: 'tshow', type: 'boolean', isOptional: true }
|
||||
{ name: 'tshow', type: 'boolean', isOptional: true },
|
||||
{ name: 'md', type: 'string', isOptional: true }
|
||||
]
|
||||
}),
|
||||
tableSchema({
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { appSchema, tableSchema } from '@nozbe/watermelondb';
|
||||
|
||||
export default appSchema({
|
||||
version: 11,
|
||||
version: 12,
|
||||
tables: [
|
||||
tableSchema({
|
||||
name: 'users',
|
||||
|
@ -16,7 +16,8 @@ export default appSchema({
|
|||
{ name: 'login_email_password', type: 'boolean', isOptional: true },
|
||||
{ name: 'show_message_in_main_thread', type: 'boolean', isOptional: true },
|
||||
{ name: 'avatar_etag', type: 'string', isOptional: true },
|
||||
{ name: 'is_from_webview', type: 'boolean', isOptional: true }
|
||||
{ name: 'is_from_webview', type: 'boolean', isOptional: true },
|
||||
{ name: 'enable_message_parser_early_adoption', type: 'boolean', isOptional: true }
|
||||
]
|
||||
}),
|
||||
tableSchema({
|
||||
|
|
|
@ -19,6 +19,10 @@ const changeMessageStatus = async (id, tmid, status, message) => {
|
|||
if (message) {
|
||||
m.mentions = message.mentions;
|
||||
m.channels = message.channels;
|
||||
|
||||
if (message.md) {
|
||||
m.md = message.md;
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
@ -31,6 +35,10 @@ const changeMessageStatus = async (id, tmid, status, message) => {
|
|||
if (message) {
|
||||
tm.mentions = message.mentions;
|
||||
tm.channels = message.channels;
|
||||
|
||||
if (message.md) {
|
||||
tm.md = message.md;
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
|
|
@ -622,7 +622,8 @@ const RocketChat = {
|
|||
roles: result.me.roles,
|
||||
avatarETag: result.me.avatarETag,
|
||||
isFromWebView,
|
||||
showMessageInMainThread: result.me.settings?.preferences?.showMessageInMainThread ?? true
|
||||
showMessageInMainThread: result.me.settings?.preferences?.showMessageInMainThread ?? true,
|
||||
enableMessageParserEarlyAdoption: result.me.settings?.preferences?.enableMessageParserEarlyAdoption ?? true
|
||||
};
|
||||
return user;
|
||||
},
|
||||
|
|
|
@ -1,46 +1,80 @@
|
|||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Switch } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import I18n from '../../i18n';
|
||||
import { events, logEvent } from '../../utils/log';
|
||||
import log, { logEvent, events } from '../../utils/log';
|
||||
import SafeAreaView from '../../containers/SafeAreaView';
|
||||
import StatusBar from '../../containers/StatusBar';
|
||||
import * as List from '../../containers/List';
|
||||
import { SWITCH_TRACK_COLOR } from '../../constants/colors';
|
||||
import { getUserSelector } from '../../selectors/login';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
|
||||
class UserPreferencesView extends React.Component {
|
||||
static navigationOptions = () => ({
|
||||
title: I18n.t('Preferences')
|
||||
});
|
||||
const UserPreferencesView = ({ navigation }) => {
|
||||
const user = useSelector(state => getUserSelector(state));
|
||||
const isMessageParserServerEnabled = useSelector(
|
||||
state => state.settings.Accounts_Default_User_Preferences_enableMessageParserEarlyAdoption
|
||||
);
|
||||
const [enableParser, setEnableParser] = useState(user.enableMessageParserEarlyAdoption);
|
||||
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object
|
||||
};
|
||||
useEffect(() => {
|
||||
navigation.setOptions({
|
||||
title: I18n.t('Preferences')
|
||||
});
|
||||
}, []);
|
||||
|
||||
navigateToScreen = (screen, params) => {
|
||||
const navigateToScreen = (screen, params) => {
|
||||
logEvent(events[`SE_GO_${screen.replace('View', '').toUpperCase()}`]);
|
||||
const { navigation } = this.props;
|
||||
navigation.navigate(screen, params);
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SafeAreaView testID='preferences-view'>
|
||||
<StatusBar />
|
||||
<List.Container>
|
||||
const toggleMessageParser = async value => {
|
||||
try {
|
||||
await RocketChat.saveUserPreferences({ id: user.id, enableMessageParserEarlyAdoption: value });
|
||||
setEnableParser(value);
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
const renderMessageParserSwitch = () => (
|
||||
<Switch value={enableParser} trackColor={SWITCH_TRACK_COLOR} onValueChange={toggleMessageParser} />
|
||||
);
|
||||
|
||||
return (
|
||||
<SafeAreaView testID='preferences-view'>
|
||||
<StatusBar />
|
||||
<List.Container>
|
||||
<List.Section>
|
||||
<List.Separator />
|
||||
<List.Item
|
||||
title='Notifications'
|
||||
onPress={() => navigateToScreen('UserNotificationPrefView')}
|
||||
showActionIndicator
|
||||
testID='preferences-view-notifications'
|
||||
/>
|
||||
<List.Separator />
|
||||
</List.Section>
|
||||
{isMessageParserServerEnabled && (
|
||||
<List.Section>
|
||||
<List.Separator />
|
||||
<List.Item
|
||||
title='Notifications'
|
||||
onPress={() => this.navigateToScreen('UserNotificationPrefView')}
|
||||
showActionIndicator
|
||||
testID='preferences-view-notifications'
|
||||
title='Enable_Message_Parser'
|
||||
testID='preferences-view-enable-message-parser'
|
||||
right={() => renderMessageParserSwitch()}
|
||||
/>
|
||||
<List.Separator />
|
||||
</List.Section>
|
||||
</List.Container>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
)}
|
||||
</List.Container>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
UserPreferencesView.propTypes = {
|
||||
navigation: PropTypes.object
|
||||
};
|
||||
|
||||
export default UserPreferencesView;
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
"@react-navigation/drawer": "5.12.5",
|
||||
"@react-navigation/native": "5.9.4",
|
||||
"@react-navigation/stack": "5.14.5",
|
||||
"@rocket.chat/message-parser": "^0.29.0",
|
||||
"@rocket.chat/react-native-fast-image": "^8.2.0",
|
||||
"@rocket.chat/sdk": "RocketChat/Rocket.Chat.js.SDK#mobile",
|
||||
"@rocket.chat/ui-kit": "0.13.0",
|
||||
|
|
|
@ -0,0 +1,582 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import React from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { storiesOf } from '@storybook/react-native';
|
||||
|
||||
import MessageBody from '../../app/containers/markdown/MessageBody';
|
||||
import { themes } from '../../app/constants/colors';
|
||||
|
||||
const stories = storiesOf('MessageBody', module);
|
||||
|
||||
const theme = 'light';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
marginHorizontal: 15,
|
||||
backgroundColor: themes[theme].backgroundColor,
|
||||
marginVertical: 50
|
||||
},
|
||||
separator: {
|
||||
marginHorizontal: 10,
|
||||
marginVertical: 10
|
||||
}
|
||||
});
|
||||
|
||||
const simpleTextMsg = [{
|
||||
type: 'PARAGRAPH',
|
||||
value: [{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'This is Rocket.Chat'
|
||||
}]
|
||||
}];
|
||||
|
||||
const longTextMsg = [{
|
||||
type: 'PARAGRAPH',
|
||||
value: [{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
|
||||
}]
|
||||
}];
|
||||
|
||||
const lineBreakMsg = [
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'a'
|
||||
},
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'b'
|
||||
},
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'c'
|
||||
},
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'd'
|
||||
},
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: ''
|
||||
},
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'e'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const sequentialEmptySpacesMsg = [
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'a b c'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const boldOrUnderscoreMsg = [
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'Strong emphasis, aka bold, with '
|
||||
},
|
||||
{
|
||||
type: 'BOLD',
|
||||
value: [{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'asterisks'
|
||||
}]
|
||||
},
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: ' or '
|
||||
},
|
||||
{
|
||||
type: 'ITALIC',
|
||||
value: [{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'underscore'
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
stories.add('Text', () => (
|
||||
<View style={styles.container}>
|
||||
<MessageBody tokens={simpleTextMsg} />
|
||||
<MessageBody tokens={longTextMsg} />
|
||||
<MessageBody tokens={lineBreakMsg} />
|
||||
<MessageBody tokens={sequentialEmptySpacesMsg} />
|
||||
<MessageBody tokens={boldOrUnderscoreMsg} />
|
||||
</View>
|
||||
));
|
||||
|
||||
const allMentionTokens = [
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'MENTION_USER',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'rocket.cat'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const multipleMentionTokens = [
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'MENTION_USER',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'name'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: ' '
|
||||
},
|
||||
{
|
||||
type: 'MENTION_USER',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'rocket.cat'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: ' '
|
||||
},
|
||||
{
|
||||
type: 'MENTION_USER',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'here'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: ' '
|
||||
},
|
||||
{
|
||||
type: 'MENTION_USER',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'all'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const allMentions = [
|
||||
{
|
||||
_id: 'rocket.cat',
|
||||
username: 'rocket.cat'
|
||||
}
|
||||
];
|
||||
|
||||
const multipleMentions = [
|
||||
{
|
||||
_id: 'name',
|
||||
username: 'name'
|
||||
},
|
||||
{
|
||||
_id: 'rocket.cat',
|
||||
username: 'rocket.cat'
|
||||
},
|
||||
{
|
||||
_id: 'here',
|
||||
username: 'here'
|
||||
},
|
||||
{
|
||||
_id: 'all',
|
||||
username: 'all'
|
||||
}
|
||||
];
|
||||
|
||||
stories.add('Mentions', () => (
|
||||
<View style={styles.container}>
|
||||
<MessageBody tokens={allMentionTokens} mentions={allMentions} navToRoomInfo={() => {}} style={[]} />
|
||||
<MessageBody tokens={multipleMentionTokens} mentions={multipleMentions} navToRoomInfo={() => {}} style={[]} />
|
||||
</View>
|
||||
));
|
||||
|
||||
const channelTokens = [
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [{
|
||||
type: 'MENTION_CHANNEL',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'text_channel'
|
||||
}
|
||||
}]
|
||||
}
|
||||
];
|
||||
|
||||
const channelMention = [
|
||||
{
|
||||
_id: 'text_channel',
|
||||
name: 'text_channel'
|
||||
}
|
||||
];
|
||||
|
||||
stories.add('Hashtag', () => (
|
||||
<View style={styles.container}>
|
||||
<MessageBody tokens={channelTokens} channels={channelMention} navToRoomInfo={() => {}} />
|
||||
</View>
|
||||
));
|
||||
|
||||
const bigEmojiTokens = [{
|
||||
type: 'BIG_EMOJI',
|
||||
value: [
|
||||
{
|
||||
type: 'EMOJI',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'green_heart'
|
||||
}
|
||||
}
|
||||
]
|
||||
}];
|
||||
|
||||
const multipleBigEmojiTokens = [{
|
||||
type: 'BIG_EMOJI',
|
||||
value: [
|
||||
{
|
||||
type: 'EMOJI',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'green_heart'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'EMOJI',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'joy'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'EMOJI',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'grin'
|
||||
}
|
||||
}
|
||||
]
|
||||
}];
|
||||
|
||||
const emojiTokens = [{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'EMOJI',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'rocket'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'EMOJI',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'facepalm'
|
||||
}
|
||||
}
|
||||
]
|
||||
}];
|
||||
|
||||
stories.add('Emoji', () => (
|
||||
<View style={styles.container}>
|
||||
<MessageBody tokens={bigEmojiTokens} />
|
||||
<MessageBody tokens={multipleBigEmojiTokens} />
|
||||
<MessageBody tokens={emojiTokens} />
|
||||
</View>
|
||||
));
|
||||
|
||||
const blockQuoteTokens = [{
|
||||
type: 'QUOTE',
|
||||
value: [{
|
||||
type: 'PARAGRAPH',
|
||||
value: [{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'Rocket.Chat to the moon'
|
||||
}]
|
||||
}]
|
||||
|
||||
}];
|
||||
|
||||
stories.add('Block quote', () => (
|
||||
<View style={styles.container}>
|
||||
<MessageBody tokens={blockQuoteTokens} />
|
||||
</View>
|
||||
));
|
||||
|
||||
const rocketChatLink = [
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'LINK',
|
||||
value: {
|
||||
src: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'https://rocket.chat'
|
||||
},
|
||||
label: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'https://rocket.chat'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const markdownLink = [
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'LINK',
|
||||
value: {
|
||||
src: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'https://rocket.chat'
|
||||
},
|
||||
label: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'Markdown link'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
stories.add('Links', () => (
|
||||
<View style={styles.container}>
|
||||
<MessageBody tokens={rocketChatLink} />
|
||||
<MessageBody tokens={markdownLink} />
|
||||
</View>
|
||||
));
|
||||
|
||||
stories.add('Headers', () => (
|
||||
<View style={styles.container}>
|
||||
<MessageBody
|
||||
tokens={
|
||||
[
|
||||
{
|
||||
type: 'HEADING',
|
||||
value: [{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: '# Header 1'
|
||||
}],
|
||||
level: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
/>
|
||||
<MessageBody
|
||||
tokens={
|
||||
[
|
||||
{
|
||||
type: 'HEADING',
|
||||
value: [{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: '## Header 2'
|
||||
}],
|
||||
level: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
/>
|
||||
<MessageBody
|
||||
tokens={
|
||||
[
|
||||
{
|
||||
type: 'HEADING',
|
||||
value: [{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: '### Header 3'
|
||||
}],
|
||||
level: 3
|
||||
}
|
||||
]
|
||||
}
|
||||
/>
|
||||
<MessageBody
|
||||
tokens={
|
||||
[
|
||||
{
|
||||
type: 'HEADING',
|
||||
value: [{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: '#### Header 4'
|
||||
}],
|
||||
level: 4
|
||||
}
|
||||
]
|
||||
}
|
||||
/>
|
||||
<MessageBody
|
||||
tokens={
|
||||
[
|
||||
{
|
||||
type: 'HEADING',
|
||||
value: [{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: '##### Header 5'
|
||||
}],
|
||||
level: 5
|
||||
}
|
||||
]
|
||||
}
|
||||
/>
|
||||
<MessageBody
|
||||
tokens={
|
||||
[
|
||||
{
|
||||
type: 'HEADING',
|
||||
value: [{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: '###### Header 6'
|
||||
}],
|
||||
level: 6
|
||||
}
|
||||
]
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
));
|
||||
|
||||
const inlineCodeToken = [
|
||||
{
|
||||
type: 'PARAGRAPH',
|
||||
value: [
|
||||
{
|
||||
type: 'INLINE_CODE',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'inline code'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const multilineCodeToken = [
|
||||
{
|
||||
type: 'CODE',
|
||||
language: 'none',
|
||||
value: [
|
||||
{
|
||||
type: 'CODE_LINE',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'Multi line '
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'CODE_LINE',
|
||||
value: {
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'Code'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
stories.add('Code', () => (
|
||||
<View style={styles.container}>
|
||||
<MessageBody tokens={inlineCodeToken} style={[]} />
|
||||
<MessageBody tokens={multilineCodeToken} style={[]} />
|
||||
</View>
|
||||
));
|
||||
|
||||
const unorederedListToken = [
|
||||
{
|
||||
type: 'UNORDERED_LIST',
|
||||
value: [
|
||||
{
|
||||
type: 'LIST_ITEM',
|
||||
value: [
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'Open Source'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'LIST_ITEM',
|
||||
value: [
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'Rocket.Chat'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const orderedListToken = [
|
||||
{
|
||||
type: 'ORDERED_LIST',
|
||||
value: [
|
||||
{
|
||||
type: 'LIST_ITEM',
|
||||
value: [
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'Open Source'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'LIST_ITEM',
|
||||
value: [
|
||||
{
|
||||
type: 'PLAIN_TEXT',
|
||||
value: 'Rocket.Chat'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
stories.add('Lists', () => (
|
||||
<View style={styles.container}>
|
||||
<MessageBody tokens={unorederedListToken} />
|
||||
<MessageBody tokens={orderedListToken} />
|
||||
</View>
|
||||
));
|
|
@ -12,6 +12,7 @@ import './HeaderButtons';
|
|||
import './UnreadBadge';
|
||||
import '../../app/views/ThreadMessagesView/Item.stories.js';
|
||||
import './Avatar';
|
||||
import './MessageBody';
|
||||
import '../../app/containers/BackgroundContainer/index.stories.js';
|
||||
import '../../app/containers/RoomHeader/RoomHeader.stories.js';
|
||||
import '../../app/views/RoomView/LoadMore/LoadMore.stories';
|
||||
|
|
|
@ -3203,6 +3203,11 @@
|
|||
dependencies:
|
||||
eslint-plugin-import "^2.17.2"
|
||||
|
||||
"@rocket.chat/message-parser@^0.29.0":
|
||||
version "0.29.0"
|
||||
resolved "https://registry.yarnpkg.com/@rocket.chat/message-parser/-/message-parser-0.29.0.tgz#9577f354756653e6271b9fe2290fadafcb5d4293"
|
||||
integrity sha512-2qEPTNSHeWRCFfxCETqz9Asju6bf5dR98+AQUYwNus5911WW4tuehvrfvoqLYrlYqs4w5Qj6q9m2THCXqN27Gw==
|
||||
|
||||
"@rocket.chat/react-native-fast-image@^8.2.0":
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@rocket.chat/react-native-fast-image/-/react-native-fast-image-8.2.0.tgz#4f48858f95f40afcb10b39cee9b1239c150d6c51"
|
||||
|
|
Loading…
Reference in New Issue