Chore: Migration to Hooks - Markdown (#4264)
* chore: migrate TextInput from class to functional * changing from themes[theme] to colors * removing markdown theme props from other files * adding a force update and fix a stories * adding testID and tests for markdown * fixing some interfaces * minor tweak Co-authored-by: GleidsonDaniel <gleidson10daniel@hotmail.com>
This commit is contained in:
parent
18c44178d7
commit
4fd0084bc1
|
@ -44,7 +44,14 @@ const Avatar = React.memo(
|
|||
let image;
|
||||
if (emoji) {
|
||||
image = (
|
||||
<Emoji baseUrl={server} getCustomEmoji={getCustomEmoji} isMessageContainsOnlyEmoji literal={emoji} style={avatarStyle} />
|
||||
<Emoji
|
||||
baseUrl={server}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
isMessageContainsOnlyEmoji
|
||||
literal={emoji}
|
||||
style={avatarStyle}
|
||||
testID='avatar'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
let uri = avatar;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
|
||||
import { ICustomEmoji } from '../../definitions/IEmoji';
|
||||
import { ICustomEmoji } from '../../definitions';
|
||||
|
||||
const CustomEmoji = React.memo(
|
||||
({ baseUrl, emoji, style }: ICustomEmoji) => (
|
||||
({ baseUrl, emoji, style, testID }: ICustomEmoji) => (
|
||||
<FastImage
|
||||
style={style}
|
||||
source={{
|
||||
|
@ -12,6 +12,7 @@ const CustomEmoji = React.memo(
|
|||
priority: FastImage.priority.high
|
||||
}}
|
||||
resizeMode={FastImage.resizeMode.contain}
|
||||
testID={testID}
|
||||
/>
|
||||
),
|
||||
(prevProps, nextProps) => {
|
||||
|
|
|
@ -76,7 +76,7 @@ class MessageParser extends UiKitParserMessage<React.ReactElement> {
|
|||
<MarkdownPreview msg={element.text} style={[isContext && { color: themes[theme].auxiliaryText }]} numberOfLines={0} />
|
||||
);
|
||||
}
|
||||
return <Markdown msg={element.text} theme={theme} style={[isContext && { color: themes[theme].auxiliaryText }]} />;
|
||||
return <Markdown msg={element.text} style={[isContext && { color: themes[theme].auxiliaryText }]} />;
|
||||
}
|
||||
|
||||
button(element: IButton, context: BlockContext) {
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import { StyleProp, Text, TextStyle } from 'react-native';
|
||||
|
||||
import { useTheme } from '../../theme';
|
||||
import { themes } from '../../lib/constants';
|
||||
import styles from './styles';
|
||||
import { events, logEvent } from '../../lib/methods/helpers/log';
|
||||
import { IUserMention } from './interfaces';
|
||||
|
@ -14,20 +13,22 @@ interface IAtMention {
|
|||
style?: StyleProp<TextStyle>[];
|
||||
useRealName?: boolean;
|
||||
mentions?: IUserMention[];
|
||||
testID: string;
|
||||
}
|
||||
|
||||
const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, style = [], useRealName }: IAtMention) => {
|
||||
const { theme } = useTheme();
|
||||
const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, style = [], useRealName, testID }: IAtMention) => {
|
||||
const { colors } = useTheme();
|
||||
if (mention === 'all' || mention === 'here') {
|
||||
return (
|
||||
<Text
|
||||
style={[
|
||||
styles.mention,
|
||||
{
|
||||
color: themes[theme].mentionGroupColor
|
||||
color: colors.mentionGroupColor
|
||||
},
|
||||
...style
|
||||
]}>
|
||||
]}
|
||||
testID={`${testID}-mention-all-here`}>
|
||||
{mention}
|
||||
</Text>
|
||||
);
|
||||
|
@ -36,11 +37,11 @@ const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, styl
|
|||
let mentionStyle = {};
|
||||
if (mention === username) {
|
||||
mentionStyle = {
|
||||
color: themes[theme].mentionMeColor
|
||||
color: colors.mentionMeColor
|
||||
};
|
||||
} else {
|
||||
mentionStyle = {
|
||||
color: themes[theme].mentionOtherColor
|
||||
color: colors.mentionOtherColor
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -59,13 +60,15 @@ const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, styl
|
|||
|
||||
if (user) {
|
||||
return (
|
||||
<Text style={[styles.mention, mentionStyle, ...style]} onPress={handlePress}>
|
||||
<Text style={[styles.mention, mentionStyle, ...style]} onPress={handlePress} testID={`${testID}-mention-users`}>
|
||||
{useRealName && user.name ? user.name : user.username}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
return <Text style={[styles.text, { color: themes[theme].bodyText }, ...style]}>{`@${mention}`}</Text>;
|
||||
return (
|
||||
<Text style={[styles.text, { color: colors.bodyText }, ...style]} testID={`${testID}-mention-unknown`}>{`@${mention}`}</Text>
|
||||
);
|
||||
});
|
||||
|
||||
export default AtMention;
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
import React from 'react';
|
||||
import { View } from 'react-native';
|
||||
|
||||
import { TSupportedThemes } from '../../theme';
|
||||
import { themes } from '../../lib/constants';
|
||||
import { useTheme } from '../../theme';
|
||||
import styles from './styles';
|
||||
|
||||
interface IBlockQuote {
|
||||
children: React.ReactElement | null;
|
||||
theme: TSupportedThemes;
|
||||
}
|
||||
|
||||
const BlockQuote = React.memo(({ children, theme }: IBlockQuote) => (
|
||||
<View style={styles.container}>
|
||||
<View style={[styles.quote, { backgroundColor: themes[theme].borderColor }]} />
|
||||
<View style={styles.childContainer}>{children}</View>
|
||||
</View>
|
||||
));
|
||||
const BlockQuote = React.memo(({ children }: IBlockQuote) => {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<View style={styles.container} testID='markdown-block-quote'>
|
||||
<View style={[styles.quote, { backgroundColor: colors.borderColor }]} />
|
||||
<View style={styles.childContainer}>{children}</View>
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
||||
export default BlockQuote;
|
||||
|
|
|
@ -15,10 +15,11 @@ interface IEmoji {
|
|||
style?: object;
|
||||
onEmojiSelected?: Function;
|
||||
tabEmojiStyle?: object;
|
||||
testID: string;
|
||||
}
|
||||
|
||||
const Emoji = React.memo(
|
||||
({ literal, isMessageContainsOnlyEmoji, getCustomEmoji, baseUrl, customEmojis = true, style = {} }: IEmoji) => {
|
||||
({ literal, isMessageContainsOnlyEmoji, getCustomEmoji, baseUrl, testID, customEmojis = true, style = {} }: IEmoji) => {
|
||||
const { colors } = useTheme();
|
||||
const emojiUnicode = shortnameToUnicode(literal);
|
||||
const emoji: any = getCustomEmoji && getCustomEmoji(literal.replace(/:/g, ''));
|
||||
|
@ -28,11 +29,14 @@ const Emoji = React.memo(
|
|||
baseUrl={baseUrl}
|
||||
style={[isMessageContainsOnlyEmoji ? styles.customEmojiBig : styles.customEmoji, style]}
|
||||
emoji={emoji}
|
||||
testID={`${testID}-custom-emoji`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Text style={[{ color: colors.bodyText }, isMessageContainsOnlyEmoji ? styles.textBig : styles.text, style]}>
|
||||
<Text
|
||||
style={[{ color: colors.bodyText }, isMessageContainsOnlyEmoji ? styles.textBig : styles.text, style]}
|
||||
testID={`${testID}-unicode-emoji`}>
|
||||
{emojiUnicode}
|
||||
</Text>
|
||||
);
|
||||
|
|
|
@ -3,7 +3,6 @@ import { StyleProp, Text, TextStyle } from 'react-native';
|
|||
import { useNavigation } from '@react-navigation/native';
|
||||
import { StackNavigationProp } from '@react-navigation/stack';
|
||||
|
||||
import { themes } from '../../lib/constants';
|
||||
import { useTheme } from '../../theme';
|
||||
import { IUserChannel } from './interfaces';
|
||||
import styles from './styles';
|
||||
|
@ -17,10 +16,11 @@ interface IHashtag {
|
|||
navToRoomInfo?: Function;
|
||||
style?: StyleProp<TextStyle>[];
|
||||
channels?: IUserChannel[];
|
||||
testID: string;
|
||||
}
|
||||
|
||||
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IHashtag) => {
|
||||
const { theme } = useTheme();
|
||||
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, testID, style = [] }: IHashtag) => {
|
||||
const { colors } = useTheme();
|
||||
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
||||
const navigation = useNavigation<StackNavigationProp<ChatsStackParamList, 'RoomView'>>();
|
||||
|
||||
|
@ -46,16 +46,21 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IH
|
|||
style={[
|
||||
styles.mention,
|
||||
{
|
||||
color: themes[theme].mentionOtherColor
|
||||
color: colors.mentionOtherColor
|
||||
},
|
||||
...style
|
||||
]}
|
||||
onPress={handlePress}>
|
||||
onPress={handlePress}
|
||||
testID={`${testID}-hashtag-channels`}>
|
||||
{`#${hashtag}`}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
return <Text style={[styles.text, { color: themes[theme].bodyText }, ...style]}>{`#${hashtag}`}</Text>;
|
||||
return (
|
||||
<Text
|
||||
style={[styles.text, { color: colors.bodyText }, ...style]}
|
||||
testID={`${testID}-hashtag-without-channels`}>{`#${hashtag}`}</Text>
|
||||
);
|
||||
});
|
||||
|
||||
export default Hashtag;
|
||||
|
|
|
@ -3,22 +3,23 @@ import { Text } from 'react-native';
|
|||
import Clipboard from '@react-native-clipboard/clipboard';
|
||||
|
||||
import styles from './styles';
|
||||
import { themes } from '../../lib/constants';
|
||||
import { LISTENER } from '../Toast';
|
||||
import EventEmitter from '../../lib/methods/helpers/events';
|
||||
import I18n from '../../i18n';
|
||||
import openLink from '../../lib/methods/helpers/openLink';
|
||||
import { TOnLinkPress } from './interfaces';
|
||||
import { TSupportedThemes } from '../../theme';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
interface ILink {
|
||||
children: React.ReactElement | null;
|
||||
link: string;
|
||||
theme: TSupportedThemes;
|
||||
onLinkPress?: TOnLinkPress;
|
||||
testID: string;
|
||||
}
|
||||
|
||||
const Link = React.memo(({ children, link, theme, onLinkPress }: ILink) => {
|
||||
const Link = React.memo(({ children, link, onLinkPress, testID }: ILink) => {
|
||||
const { colors, theme } = useTheme();
|
||||
|
||||
const handlePress = () => {
|
||||
if (!link) {
|
||||
return;
|
||||
|
@ -37,7 +38,11 @@ const Link = React.memo(({ children, link, theme, onLinkPress }: ILink) => {
|
|||
|
||||
// if you have a [](https://rocket.chat) render https://rocket.chat
|
||||
return (
|
||||
<Text onPress={handlePress} onLongPress={onLongPress} style={{ ...styles.link, color: themes[theme].actionTintColor }}>
|
||||
<Text
|
||||
onPress={handlePress}
|
||||
onLongPress={onLongPress}
|
||||
style={{ ...styles.link, color: colors.actionTintColor }}
|
||||
testID={`${testID}-link`}>
|
||||
{childLength !== 0 ? children : link}
|
||||
</Text>
|
||||
);
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import React from 'react';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
import { TSupportedThemes } from '../../theme';
|
||||
import { themes } from '../../lib/constants';
|
||||
import { useTheme } from '../../theme';
|
||||
|
||||
const style = StyleSheet.create({
|
||||
container: {
|
||||
|
@ -24,11 +23,13 @@ interface IListItem {
|
|||
level: number;
|
||||
ordered: boolean;
|
||||
continue: boolean;
|
||||
theme: TSupportedThemes;
|
||||
index: number;
|
||||
testID: string;
|
||||
}
|
||||
|
||||
const ListItem = React.memo(({ children, level, bulletWidth, continue: _continue, ordered, index, theme }: IListItem) => {
|
||||
const ListItem = React.memo(({ children, level, bulletWidth, continue: _continue, ordered, index, testID }: IListItem) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
let bullet;
|
||||
if (_continue) {
|
||||
bullet = '';
|
||||
|
@ -41,9 +42,9 @@ const ListItem = React.memo(({ children, level, bulletWidth, continue: _continue
|
|||
}
|
||||
|
||||
return (
|
||||
<View style={style.container}>
|
||||
<View style={style.container} testID={`${testID}-list`}>
|
||||
<View style={[{ width: bulletWidth }, style.bullet]}>
|
||||
<Text style={{ color: themes[theme].bodyText }}>{bullet}</Text>
|
||||
<Text style={{ color: colors.bodyText }}>{bullet}</Text>
|
||||
</View>
|
||||
<View style={style.contents}>{children}</View>
|
||||
</View>
|
||||
|
|
|
@ -5,24 +5,25 @@ import { CELL_WIDTH } from './TableCell';
|
|||
import Navigation from '../../lib/navigation/appNavigation';
|
||||
import styles from './styles';
|
||||
import I18n from '../../i18n';
|
||||
import { TSupportedThemes } from '../../theme';
|
||||
import { themes } from '../../lib/constants';
|
||||
import { useTheme } from '../../theme';
|
||||
import { useAppSelector } from '../../lib/hooks';
|
||||
|
||||
interface ITable {
|
||||
children: React.ReactElement | null;
|
||||
numColumns: number;
|
||||
theme: TSupportedThemes;
|
||||
testID: string;
|
||||
}
|
||||
|
||||
const MAX_HEIGHT = 300;
|
||||
|
||||
const Table = React.memo(({ children, numColumns, theme }: ITable) => {
|
||||
const Table = React.memo(({ children, numColumns, testID }: ITable) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
const getTableWidth = () => numColumns * CELL_WIDTH;
|
||||
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
||||
|
||||
const renderRows = (drawExtraBorders = true) => {
|
||||
const tableStyle: ViewStyle[] = [styles.table, { borderColor: themes[theme].borderColor }];
|
||||
const tableStyle: ViewStyle[] = [styles.table, { borderColor: colors.borderColor }];
|
||||
if (drawExtraBorders) {
|
||||
tableStyle.push(styles.tableExtraBorders);
|
||||
}
|
||||
|
@ -47,18 +48,15 @@ const Table = React.memo(({ children, numColumns, theme }: ITable) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={onPress}>
|
||||
<TouchableOpacity onPress={onPress} testID={`${testID}-table`}>
|
||||
<ScrollView
|
||||
contentContainerStyle={{ width: getTableWidth() }}
|
||||
scrollEnabled={false}
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={[
|
||||
styles.containerTable,
|
||||
{ maxWidth: getTableWidth(), maxHeight: MAX_HEIGHT, borderColor: themes[theme].borderColor }
|
||||
]}>
|
||||
style={[styles.containerTable, { maxWidth: getTableWidth(), maxHeight: MAX_HEIGHT, borderColor: colors.borderColor }]}>
|
||||
{renderRows(false)}
|
||||
</ScrollView>
|
||||
<Text style={[styles.textInfo, { color: themes[theme].auxiliaryText }]}>{I18n.t('Full_table')}</Text>
|
||||
<Text style={[styles.textInfo, { color: colors.auxiliaryText }]}>{I18n.t('Full_table')}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import React from 'react';
|
||||
import { Text, View, ViewStyle } from 'react-native';
|
||||
|
||||
import { TSupportedThemes } from '../../theme';
|
||||
import { themes } from '../../lib/constants';
|
||||
import { useTheme } from '../../theme';
|
||||
import styles from './styles';
|
||||
|
||||
interface ITableCell {
|
||||
export interface ITableCell {
|
||||
align: '' | 'left' | 'center' | 'right';
|
||||
children: React.ReactElement | null;
|
||||
isLastCell: boolean;
|
||||
theme: TSupportedThemes;
|
||||
}
|
||||
|
||||
export const CELL_WIDTH = 100;
|
||||
|
||||
const TableCell = React.memo(({ isLastCell, align, children, theme }: ITableCell) => {
|
||||
const cellStyle: ViewStyle[] = [styles.cell, { borderColor: themes[theme].borderColor }];
|
||||
const TableCell = React.memo(({ isLastCell, align, children }: ITableCell) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
const cellStyle: ViewStyle[] = [styles.cell, { borderColor: colors.borderColor }];
|
||||
if (!isLastCell) {
|
||||
cellStyle.push(styles.cellRightBorder);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ const TableCell = React.memo(({ isLastCell, align, children, theme }: ITableCell
|
|||
|
||||
return (
|
||||
<View style={[...cellStyle, { width: CELL_WIDTH }]}>
|
||||
<Text style={[textStyle, { color: themes[theme].bodyText }]}>{children}</Text>
|
||||
<Text style={[textStyle, { color: colors.bodyText }]}>{children}</Text>
|
||||
</View>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import React from 'react';
|
||||
import { View, ViewStyle } from 'react-native';
|
||||
|
||||
import { TSupportedThemes } from '../../theme';
|
||||
import { themes } from '../../lib/constants';
|
||||
import { useTheme } from '../../theme';
|
||||
import styles from './styles';
|
||||
|
||||
interface ITableRow {
|
||||
export interface ITableRow {
|
||||
children: React.ReactElement | null;
|
||||
isLastRow: boolean;
|
||||
theme: TSupportedThemes;
|
||||
}
|
||||
|
||||
const TableRow = React.memo(({ isLastRow, children: _children, theme }: ITableRow) => {
|
||||
const rowStyle: ViewStyle[] = [styles.row, { borderColor: themes[theme].borderColor }];
|
||||
const TableRow = React.memo(({ isLastRow, children: _children }: ITableRow) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
const rowStyle: ViewStyle[] = [styles.row, { borderColor: colors.borderColor }];
|
||||
if (!isLastRow) {
|
||||
rowStyle.push(styles.rowBottomBorder);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { PureComponent } from 'react';
|
||||
import React, { useCallback, useEffect, useReducer, useRef } from 'react';
|
||||
import { Image, StyleProp, Text, TextStyle } from 'react-native';
|
||||
import { Parser } from 'commonmark';
|
||||
import Renderer from 'commonmark-react-renderer';
|
||||
|
@ -12,24 +12,23 @@ import MarkdownHashtag from './Hashtag';
|
|||
import MarkdownBlockQuote from './BlockQuote';
|
||||
import MarkdownEmoji from './Emoji';
|
||||
import MarkdownTable from './Table';
|
||||
import MarkdownTableRow from './TableRow';
|
||||
import MarkdownTableCell from './TableCell';
|
||||
import MarkdownTableRow, { ITableRow } from './TableRow';
|
||||
import MarkdownTableCell, { ITableCell } from './TableCell';
|
||||
import mergeTextNodes from './mergeTextNodes';
|
||||
import styles from './styles';
|
||||
import { isValidURL } from '../../lib/methods/helpers/url';
|
||||
import { isValidURL } from '../../lib/methods/helpers';
|
||||
import NewMarkdown from './new';
|
||||
import { formatText } from './formatText';
|
||||
import { IUserMention, IUserChannel, TOnLinkPress } from './interfaces';
|
||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||
import { TGetCustomEmoji } from '../../definitions';
|
||||
import { formatHyperlink } from './formatHyperlink';
|
||||
import { TSupportedThemes } from '../../theme';
|
||||
import { themes } from '../../lib/constants';
|
||||
import { useTheme } from '../../theme';
|
||||
import { IRoomInfoParam } from '../../views/SearchMessagesView';
|
||||
|
||||
export { default as MarkdownPreview } from './Preview';
|
||||
|
||||
interface IMarkdownProps {
|
||||
export interface IMarkdownProps {
|
||||
msg?: string | null;
|
||||
theme: TSupportedThemes;
|
||||
md?: MarkdownAST;
|
||||
mentions?: IUserMention[];
|
||||
getCustomEmoji?: TGetCustomEmoji;
|
||||
|
@ -41,8 +40,7 @@ interface IMarkdownProps {
|
|||
useRealName?: boolean;
|
||||
channels?: IUserChannel[];
|
||||
enableMessageParser?: boolean;
|
||||
// TODO: Refactor when migrate Room
|
||||
navToRoomInfo?: Function;
|
||||
navToRoomInfo?: (params: IRoomInfoParam) => void;
|
||||
testID?: string;
|
||||
style?: StyleProp<TextStyle>[];
|
||||
onLinkPress?: TOnLinkPress;
|
||||
|
@ -86,270 +84,243 @@ const emojiCount = (str: string) => {
|
|||
};
|
||||
|
||||
const parser = new Parser();
|
||||
export const markdownTestID = 'markdown';
|
||||
|
||||
class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||
private renderer: any;
|
||||
const Markdown = ({
|
||||
msg,
|
||||
md,
|
||||
mentions,
|
||||
getCustomEmoji,
|
||||
baseUrl,
|
||||
username,
|
||||
tmid,
|
||||
numberOfLines,
|
||||
customEmojis,
|
||||
useRealName,
|
||||
channels,
|
||||
enableMessageParser,
|
||||
navToRoomInfo,
|
||||
style,
|
||||
onLinkPress
|
||||
}: IMarkdownProps) => {
|
||||
const { colors } = useTheme();
|
||||
const renderer = useRef<any>();
|
||||
const isMessageContainsOnlyEmoji = useRef(false);
|
||||
const [, forceUpdate] = useReducer(x => x + 1, 0);
|
||||
|
||||
private isMessageContainsOnlyEmoji!: boolean;
|
||||
const isNewMarkdown = useCallback(() => !!enableMessageParser && !!md, [enableMessageParser, md]);
|
||||
const createRenderer = useCallback(
|
||||
() =>
|
||||
new Renderer({
|
||||
renderers: {
|
||||
text: renderText,
|
||||
|
||||
constructor(props: IMarkdownProps) {
|
||||
super(props);
|
||||
if (!this.isNewMarkdown) {
|
||||
this.renderer = this.createRenderer();
|
||||
emph: Renderer.forwardChildren,
|
||||
strong: Renderer.forwardChildren,
|
||||
del: Renderer.forwardChildren,
|
||||
code: renderCodeInline,
|
||||
link: renderLink,
|
||||
image: renderImage,
|
||||
atMention: renderAtMention,
|
||||
emoji: renderEmoji,
|
||||
hashtag: renderHashtag,
|
||||
|
||||
paragraph: renderParagraph,
|
||||
heading: renderHeading,
|
||||
codeBlock: renderCodeBlock,
|
||||
blockQuote: renderBlockQuote,
|
||||
|
||||
list: renderList,
|
||||
item: renderListItem,
|
||||
|
||||
hardBreak: renderBreak,
|
||||
thematicBreak: renderBreak,
|
||||
softBreak: renderBreak,
|
||||
|
||||
htmlBlock: renderText,
|
||||
htmlInline: renderText,
|
||||
|
||||
table: renderTable,
|
||||
table_row: renderTableRow,
|
||||
table_cell: renderTableCell
|
||||
},
|
||||
renderParagraphsInLists: true
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isNewMarkdown() && msg) {
|
||||
renderer.current = createRenderer();
|
||||
forceUpdate();
|
||||
}
|
||||
}, [createRenderer, isNewMarkdown, msg]);
|
||||
|
||||
if (!msg) {
|
||||
return null;
|
||||
}
|
||||
|
||||
createRenderer = () =>
|
||||
new Renderer({
|
||||
renderers: {
|
||||
text: this.renderText,
|
||||
|
||||
emph: Renderer.forwardChildren,
|
||||
strong: Renderer.forwardChildren,
|
||||
del: Renderer.forwardChildren,
|
||||
code: this.renderCodeInline,
|
||||
link: this.renderLink,
|
||||
image: this.renderImage,
|
||||
atMention: this.renderAtMention,
|
||||
emoji: this.renderEmoji,
|
||||
hashtag: this.renderHashtag,
|
||||
|
||||
paragraph: this.renderParagraph,
|
||||
heading: this.renderHeading,
|
||||
codeBlock: this.renderCodeBlock,
|
||||
blockQuote: this.renderBlockQuote,
|
||||
|
||||
list: this.renderList,
|
||||
item: this.renderListItem,
|
||||
|
||||
hardBreak: this.renderBreak,
|
||||
thematicBreak: this.renderBreak,
|
||||
softBreak: this.renderBreak,
|
||||
|
||||
htmlBlock: this.renderText,
|
||||
htmlInline: this.renderText,
|
||||
|
||||
table: this.renderTable,
|
||||
table_row: this.renderTableRow,
|
||||
table_cell: this.renderTableCell
|
||||
},
|
||||
renderParagraphsInLists: true
|
||||
});
|
||||
|
||||
get isNewMarkdown(): boolean {
|
||||
const { md, enableMessageParser } = this.props;
|
||||
return !!enableMessageParser && !!md;
|
||||
}
|
||||
|
||||
renderText = ({ context, literal }: { context: []; literal: string }) => {
|
||||
const { numberOfLines, style = [] } = this.props;
|
||||
const defaultStyle = [this.isMessageContainsOnlyEmoji ? styles.textBig : {}, ...context.map(type => styles[type])];
|
||||
const renderText = ({ context, literal }: { context: []; literal: string }) => {
|
||||
const defaultStyle = [isMessageContainsOnlyEmoji.current ? styles.textBig : {}, ...context.map(type => styles[type])];
|
||||
return (
|
||||
<Text accessibilityLabel={literal} style={[styles.text, defaultStyle, ...style]} numberOfLines={numberOfLines}>
|
||||
<Text accessibilityLabel={literal} style={[styles.text, defaultStyle, ...(style || [])]} numberOfLines={numberOfLines}>
|
||||
{literal}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
renderCodeInline = ({ literal }: TLiteral) => {
|
||||
const { theme, style = [] } = this.props;
|
||||
return (
|
||||
<Text
|
||||
style={[
|
||||
{
|
||||
...styles.codeInline,
|
||||
color: themes[theme].bodyText,
|
||||
backgroundColor: themes[theme].bannerBackground,
|
||||
borderColor: themes[theme].bannerBackground
|
||||
},
|
||||
...style
|
||||
]}>
|
||||
{literal}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
const renderCodeInline = ({ literal }: TLiteral) => (
|
||||
<Text
|
||||
testID={`${markdownTestID}-code-in-line`}
|
||||
style={[
|
||||
{
|
||||
...styles.codeInline,
|
||||
color: colors.bodyText,
|
||||
backgroundColor: colors.bannerBackground,
|
||||
borderColor: colors.bannerBackground
|
||||
},
|
||||
...(style || [])
|
||||
]}>
|
||||
{literal}
|
||||
</Text>
|
||||
);
|
||||
|
||||
renderCodeBlock = ({ literal }: TLiteral) => {
|
||||
const { theme, style = [] } = this.props;
|
||||
return (
|
||||
<Text
|
||||
style={[
|
||||
{
|
||||
...styles.codeBlock,
|
||||
color: themes[theme].bodyText,
|
||||
backgroundColor: themes[theme].bannerBackground,
|
||||
borderColor: themes[theme].bannerBackground
|
||||
},
|
||||
...style
|
||||
]}>
|
||||
{literal}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
const renderCodeBlock = ({ literal }: TLiteral) => (
|
||||
<Text
|
||||
testID={`${markdownTestID}-code-block`}
|
||||
style={[
|
||||
{
|
||||
...styles.codeBlock,
|
||||
color: colors.bodyText,
|
||||
backgroundColor: colors.bannerBackground,
|
||||
borderColor: colors.bannerBackground
|
||||
},
|
||||
...(style || [])
|
||||
]}>
|
||||
{literal}
|
||||
</Text>
|
||||
);
|
||||
|
||||
renderBreak = () => {
|
||||
const { tmid } = this.props;
|
||||
return <Text>{tmid ? ' ' : '\n'}</Text>;
|
||||
};
|
||||
const renderBreak = () => <Text>{tmid ? ' ' : '\n'}</Text>;
|
||||
|
||||
renderParagraph = ({ children }: any) => {
|
||||
const { numberOfLines, style, theme } = this.props;
|
||||
const renderParagraph = ({ children }: { children: React.ReactElement[] }) => {
|
||||
if (!children || children.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Text style={[styles.text, style, { color: themes[theme].bodyText }]} numberOfLines={numberOfLines}>
|
||||
<Text style={[styles.text, style, { color: colors.bodyText }]} numberOfLines={numberOfLines}>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
renderLink = ({ children, href }: any) => {
|
||||
const { theme, onLinkPress } = this.props;
|
||||
return (
|
||||
<MarkdownLink link={href} theme={theme} onLinkPress={onLinkPress}>
|
||||
{children}
|
||||
</MarkdownLink>
|
||||
);
|
||||
};
|
||||
const renderLink = ({ children, href }: { children: React.ReactElement | null; href: string }) => (
|
||||
<MarkdownLink link={href} onLinkPress={onLinkPress} testID={markdownTestID}>
|
||||
{children}
|
||||
</MarkdownLink>
|
||||
);
|
||||
|
||||
renderHashtag = ({ hashtag }: { hashtag: string }) => {
|
||||
const { channels, navToRoomInfo, style } = this.props;
|
||||
return <MarkdownHashtag hashtag={hashtag} channels={channels} navToRoomInfo={navToRoomInfo} style={style} />;
|
||||
};
|
||||
const renderHashtag = ({ hashtag }: { hashtag: string }) => (
|
||||
<MarkdownHashtag hashtag={hashtag} channels={channels} navToRoomInfo={navToRoomInfo} style={style} testID={markdownTestID} />
|
||||
);
|
||||
|
||||
renderAtMention = ({ mentionName }: { mentionName: string }) => {
|
||||
const { username = '', mentions, navToRoomInfo, useRealName, style } = this.props;
|
||||
return (
|
||||
<MarkdownAtMention
|
||||
mentions={mentions}
|
||||
mention={mentionName}
|
||||
useRealName={useRealName}
|
||||
username={username}
|
||||
navToRoomInfo={navToRoomInfo}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
};
|
||||
const renderAtMention = ({ mentionName }: { mentionName: string }) => (
|
||||
<MarkdownAtMention
|
||||
mentions={mentions}
|
||||
mention={mentionName}
|
||||
useRealName={useRealName}
|
||||
username={username}
|
||||
navToRoomInfo={navToRoomInfo}
|
||||
style={style}
|
||||
testID={markdownTestID}
|
||||
/>
|
||||
);
|
||||
|
||||
renderEmoji = ({ literal }: TLiteral) => {
|
||||
const { getCustomEmoji, baseUrl = '', customEmojis, style } = this.props;
|
||||
return (
|
||||
<MarkdownEmoji
|
||||
literal={literal}
|
||||
isMessageContainsOnlyEmoji={this.isMessageContainsOnlyEmoji}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
baseUrl={baseUrl}
|
||||
customEmojis={customEmojis}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
};
|
||||
const renderEmoji = ({ literal }: TLiteral) => (
|
||||
<MarkdownEmoji
|
||||
literal={literal}
|
||||
isMessageContainsOnlyEmoji={isMessageContainsOnlyEmoji.current}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
baseUrl={baseUrl || ''}
|
||||
customEmojis={customEmojis}
|
||||
style={style}
|
||||
testID={markdownTestID}
|
||||
/>
|
||||
);
|
||||
|
||||
renderImage = ({ src }: { src: string }) => {
|
||||
const renderImage = ({ src }: { src: string }) => {
|
||||
if (!isValidURL(src)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Image style={styles.inlineImage} source={{ uri: encodeURI(src) }} />;
|
||||
return <Image style={styles.inlineImage} source={{ uri: encodeURI(src) }} testID={`${markdownTestID}-image`} />;
|
||||
};
|
||||
|
||||
renderHeading = ({ children, level }: any) => {
|
||||
const { numberOfLines, theme } = this.props;
|
||||
const renderHeading = ({ children, level }: { children: React.ReactElement; level: string }) => {
|
||||
// @ts-ignore
|
||||
const textStyle = styles[`heading${level}Text`];
|
||||
return (
|
||||
<Text numberOfLines={numberOfLines} style={[textStyle, { color: themes[theme].bodyText }]}>
|
||||
<Text testID={`${markdownTestID}-header`} numberOfLines={numberOfLines} style={[textStyle, { color: colors.bodyText }]}>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
renderList = ({ children, start, tight, type }: any) => {
|
||||
const { numberOfLines } = this.props;
|
||||
return (
|
||||
<MarkdownList ordered={type !== 'bullet'} start={start} tight={tight} numberOfLines={numberOfLines}>
|
||||
{children}
|
||||
</MarkdownList>
|
||||
);
|
||||
};
|
||||
const renderList = ({ children, start, tight, type }: any) => (
|
||||
<MarkdownList ordered={type !== 'bullet'} start={start} tight={tight} numberOfLines={numberOfLines}>
|
||||
{children}
|
||||
</MarkdownList>
|
||||
);
|
||||
|
||||
renderListItem = ({ children, context, ...otherProps }: any) => {
|
||||
const { theme } = this.props;
|
||||
const renderListItem = ({ children, context, ...otherProps }: any) => {
|
||||
const level = context.filter((type: string) => type === 'list').length;
|
||||
|
||||
return (
|
||||
<MarkdownListItem level={level} theme={theme} {...otherProps}>
|
||||
<MarkdownListItem level={level} {...otherProps}>
|
||||
{children}
|
||||
</MarkdownListItem>
|
||||
);
|
||||
};
|
||||
|
||||
renderBlockQuote = ({ children }: { children: JSX.Element }) => {
|
||||
const { theme } = this.props;
|
||||
return <MarkdownBlockQuote theme={theme}>{children}</MarkdownBlockQuote>;
|
||||
};
|
||||
const renderBlockQuote = ({ children }: { children: React.ReactElement }) => (
|
||||
<MarkdownBlockQuote>{children}</MarkdownBlockQuote>
|
||||
);
|
||||
|
||||
renderTable = ({ children, numColumns }: { children: JSX.Element; numColumns: number }) => {
|
||||
const { theme } = this.props;
|
||||
const renderTable = ({ children, numColumns }: { children: React.ReactElement; numColumns: number }) => (
|
||||
<MarkdownTable numColumns={numColumns} testID={markdownTestID}>
|
||||
{children}
|
||||
</MarkdownTable>
|
||||
);
|
||||
|
||||
const renderTableRow = ({ children, isLastRow }: ITableRow) => (
|
||||
<MarkdownTableRow isLastRow={isLastRow}>{children}</MarkdownTableRow>
|
||||
);
|
||||
|
||||
const renderTableCell = ({ align, children, isLastCell }: ITableCell) => (
|
||||
<MarkdownTableCell align={align} isLastCell={isLastCell}>
|
||||
{children}
|
||||
</MarkdownTableCell>
|
||||
);
|
||||
|
||||
if (isNewMarkdown()) {
|
||||
return (
|
||||
<MarkdownTable numColumns={numColumns} theme={theme}>
|
||||
{children}
|
||||
</MarkdownTable>
|
||||
<NewMarkdown
|
||||
username={username || ''}
|
||||
baseUrl={baseUrl || ''}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
useRealName={useRealName}
|
||||
tokens={md}
|
||||
mentions={mentions}
|
||||
channels={channels}
|
||||
navToRoomInfo={navToRoomInfo}
|
||||
onLinkPress={onLinkPress}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
renderTableRow = (args: any) => {
|
||||
const { theme } = this.props;
|
||||
return <MarkdownTableRow {...args} theme={theme} />;
|
||||
};
|
||||
|
||||
renderTableCell = (args: any) => {
|
||||
const { theme } = this.props;
|
||||
return <MarkdownTableCell {...args} theme={theme} />;
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
msg,
|
||||
md,
|
||||
mentions,
|
||||
channels,
|
||||
navToRoomInfo,
|
||||
useRealName,
|
||||
username = '',
|
||||
getCustomEmoji,
|
||||
baseUrl = '',
|
||||
onLinkPress
|
||||
} = this.props;
|
||||
|
||||
if (!msg) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.isNewMarkdown) {
|
||||
return (
|
||||
<NewMarkdown
|
||||
username={username}
|
||||
baseUrl={baseUrl}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
useRealName={useRealName}
|
||||
tokens={md}
|
||||
mentions={mentions}
|
||||
channels={channels}
|
||||
navToRoomInfo={navToRoomInfo}
|
||||
onLinkPress={onLinkPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let m = formatText(msg);
|
||||
m = formatHyperlink(m);
|
||||
let ast = parser.parse(m);
|
||||
ast = mergeTextNodes(ast);
|
||||
this.isMessageContainsOnlyEmoji = isOnlyEmoji(m) && emojiCount(m) <= 3;
|
||||
return this.renderer.render(ast);
|
||||
}
|
||||
}
|
||||
|
||||
const formattedMessage = formatHyperlink(formatText(msg));
|
||||
const ast = mergeTextNodes(parser.parse(formattedMessage));
|
||||
isMessageContainsOnlyEmoji.current = isOnlyEmoji(formattedMessage) && emojiCount(formattedMessage) <= 3;
|
||||
return renderer?.current?.render(ast) || null;
|
||||
};
|
||||
export default Markdown;
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
import React from 'react';
|
||||
import { render } from '@testing-library/react-native';
|
||||
import { Provider } from 'react-redux';
|
||||
import { combineReducers, createStore } from 'redux';
|
||||
|
||||
import Markdown, { IMarkdownProps, markdownTestID } from './index';
|
||||
import { IEmoji, TGetCustomEmoji } from '../../definitions';
|
||||
|
||||
const getCustomEmoji: TGetCustomEmoji = content =>
|
||||
({
|
||||
marioparty: { name: content, extension: 'gif' },
|
||||
react_rocket: { name: content, extension: 'png' },
|
||||
nyan_rocket: { name: content, extension: 'png' }
|
||||
}[content] as IEmoji);
|
||||
|
||||
const Render = ({ msg, baseUrl, getCustomEmoji, mentions, username }: IMarkdownProps) => {
|
||||
const reducers = combineReducers({
|
||||
app: () => ({ isMasterDetail: false })
|
||||
});
|
||||
const store = createStore(reducers);
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<Markdown msg={msg} baseUrl={baseUrl} getCustomEmoji={getCustomEmoji} mentions={mentions} username={username} />
|
||||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('Markdown', () => {
|
||||
test('should render header', () => {
|
||||
const header1 = render(<Render msg='# Header 1' />);
|
||||
const header2 = render(<Render msg='## Header 2' />);
|
||||
const header3 = render(<Render msg='### Header 3' />);
|
||||
const header4 = render(<Render msg='#### Header 4' />);
|
||||
const header5 = render(<Render msg='##### Header 5' />);
|
||||
const header6 = render(<Render msg='###### Header 6' />);
|
||||
|
||||
expect(header1.findByTestId(`${markdownTestID}-header`)).toBeTruthy();
|
||||
expect(header2.findByTestId(`${markdownTestID}-header`)).toBeTruthy();
|
||||
expect(header3.findByTestId(`${markdownTestID}-header`)).toBeTruthy();
|
||||
expect(header4.findByTestId(`${markdownTestID}-header`)).toBeTruthy();
|
||||
expect(header5.findByTestId(`${markdownTestID}-header`)).toBeTruthy();
|
||||
expect(header6.findByTestId(`${markdownTestID}-header`)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should render code in line', async () => {
|
||||
const { findByTestId } = render(<Render msg='This is `inline code`' />);
|
||||
const component = await findByTestId(`${markdownTestID}-code-in-line`);
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should render code block', async () => {
|
||||
const { findByTestId } = render(
|
||||
<Render
|
||||
msg='
|
||||
```
|
||||
Code block
|
||||
```'
|
||||
/>
|
||||
);
|
||||
const component = await findByTestId(`${markdownTestID}-code-block`);
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should render image', async () => {
|
||||
const { findByTestId } = render(<Render msg='![alt text](https://play.google.com/intl/en_us/badges/images/badge_new.png)' />);
|
||||
const component = await findByTestId(`${markdownTestID}-image`);
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should render link', () => {
|
||||
const markdownLink = render(<Render msg='[Markdown link](https://rocket.chat): `[description](url)`' />);
|
||||
const link = render(<Render msg='<https://rocket.chat|Formatted Link>: `<url|description>`' />);
|
||||
expect(markdownLink.findByTestId(`${markdownTestID}-link`)).toBeTruthy();
|
||||
expect(link.findByTestId(`${markdownTestID}-link`)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should render block quote', async () => {
|
||||
const { findByTestId } = render(
|
||||
<Render
|
||||
msg={`> This is block quote
|
||||
this is a normal line`}
|
||||
/>
|
||||
);
|
||||
const component = await findByTestId(`${markdownTestID}-block-quote`);
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should render list', () => {
|
||||
const markdownList = render(<Render msg={'* Open Source\n* Rocket.Chat\n - nodejs\n - ReactNative'} />);
|
||||
const markdownNumberList = render(<Render msg={'1. Open Source\n2. Rocket.Chat'} />);
|
||||
expect(markdownList.findByTestId(`${markdownTestID}-list`)).toBeTruthy();
|
||||
expect(markdownNumberList.findByTestId(`${markdownTestID}-list`)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should render emojis', () => {
|
||||
const markdownCustomEmoji = render(
|
||||
<Render msg='😃 :+1: :marioparty:' getCustomEmoji={getCustomEmoji} baseUrl='https://open.rocket.chat' />
|
||||
);
|
||||
|
||||
const markdownUnicodeEmoji = render(<Render msg='Unicode: 😃😇👍' />);
|
||||
|
||||
expect(markdownCustomEmoji.findByTestId(`${markdownTestID}-custom-emoji`)).toBeTruthy();
|
||||
expect(markdownUnicodeEmoji.findByTestId(`${markdownTestID}-unicode-emoji`)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should render hashtags', () => {
|
||||
const markdownHashtagChannels = render(<Render msg='#test-channel' channels={[{ _id: '123', name: 'test-channel' }]} />);
|
||||
const markdownHashtagWithoutChannels = render(<Render msg='#unknown' />);
|
||||
expect(markdownHashtagChannels.findByTestId(`${markdownTestID}-hashtag-channels`)).toBeTruthy();
|
||||
expect(markdownHashtagWithoutChannels.findByTestId(`${markdownTestID}-hashtag-without-channels`)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should render mentions', async () => {
|
||||
const markdownMentionsAllAndHere = render(<Render msg='@all @here' username='rocket.cat' />);
|
||||
const markdownMentionsUnknown = render(<Render msg='@unknown' username='rocket.cat' />);
|
||||
const markdownMentionsUsers = render(
|
||||
<Render
|
||||
msg='@rocket.cat '
|
||||
mentions={[{ _id: 'random', name: 'Rocket Cat', username: 'rocket.cat' }]}
|
||||
username='rocket.cat'
|
||||
/>
|
||||
);
|
||||
expect(await markdownMentionsAllAndHere.findAllByTestId(`${markdownTestID}-mention-all-here`)).toBeTruthy();
|
||||
expect(await markdownMentionsUnknown.findByTestId(`${markdownTestID}-mention-unknown`)).toBeTruthy();
|
||||
expect(await markdownMentionsUsers.findByTestId(`${markdownTestID}-mention-users`)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should render table', async () => {
|
||||
const { findByTestId } = render(
|
||||
<Render
|
||||
msg='First Header | Second Header
|
||||
------------ | -------------
|
||||
Content from cell 1 | Content from cell 2
|
||||
Content in the first column | Content in the second column'
|
||||
/>
|
||||
);
|
||||
const component = await findByTestId(`${markdownTestID}-table`);
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -3,7 +3,6 @@ import { Text } from 'react-native';
|
|||
import { Code as CodeProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import styles from '../styles';
|
||||
import { themes } from '../../../lib/constants';
|
||||
import { useTheme } from '../../../theme';
|
||||
import CodeLine from './CodeLine';
|
||||
|
||||
|
@ -12,16 +11,16 @@ interface ICodeProps {
|
|||
}
|
||||
|
||||
const Code = ({ value }: ICodeProps) => {
|
||||
const { theme } = useTheme();
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<Text
|
||||
style={[
|
||||
styles.codeBlock,
|
||||
{
|
||||
color: themes[theme].bodyText,
|
||||
backgroundColor: themes[theme].bannerBackground,
|
||||
borderColor: themes[theme].borderColor
|
||||
color: colors.bodyText,
|
||||
backgroundColor: colors.bannerBackground,
|
||||
borderColor: colors.borderColor
|
||||
}
|
||||
]}>
|
||||
{value.map(block => {
|
||||
|
|
|
@ -3,7 +3,6 @@ import { Text } from 'react-native';
|
|||
import { Emoji as EmojiProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import shortnameToUnicode from '../../../lib/methods/helpers/shortnameToUnicode';
|
||||
import { themes } from '../../../lib/constants';
|
||||
import { useTheme } from '../../../theme';
|
||||
import styles from '../styles';
|
||||
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
|
||||
|
@ -15,7 +14,7 @@ interface IEmojiProps {
|
|||
}
|
||||
|
||||
const Emoji = ({ value, isBigEmoji }: IEmojiProps) => {
|
||||
const { theme } = useTheme();
|
||||
const { colors } = useTheme();
|
||||
const { baseUrl, getCustomEmoji } = useContext(MarkdownContext);
|
||||
const emojiUnicode = shortnameToUnicode(`:${value.value}:`);
|
||||
const emoji = getCustomEmoji?.(value.value);
|
||||
|
@ -23,7 +22,7 @@ const Emoji = ({ value, isBigEmoji }: IEmojiProps) => {
|
|||
if (emoji) {
|
||||
return <CustomEmoji baseUrl={baseUrl} style={[isBigEmoji ? styles.customEmojiBig : styles.customEmoji]} emoji={emoji} />;
|
||||
}
|
||||
return <Text style={[{ color: themes[theme].bodyText }, isBigEmoji ? styles.textBig : styles.text]}>{emojiUnicode}</Text>;
|
||||
return <Text style={[{ color: colors.bodyText }, isBigEmoji ? styles.textBig : styles.text]}>{emojiUnicode}</Text>;
|
||||
};
|
||||
|
||||
export default Emoji;
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import { Text } from 'react-native';
|
||||
import { Heading as HeadingProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import { themes } from '../../../lib/constants';
|
||||
import styles from '../styles';
|
||||
import { useTheme } from '../../../theme';
|
||||
|
||||
|
@ -12,11 +11,11 @@ interface IHeadingProps {
|
|||
}
|
||||
|
||||
const Heading = ({ value, level }: IHeadingProps) => {
|
||||
const { theme } = useTheme();
|
||||
const { colors } = useTheme();
|
||||
const textStyle = styles[`heading${level}`];
|
||||
|
||||
return (
|
||||
<Text style={[textStyle, { color: themes[theme].bodyText }]}>
|
||||
<Text style={[textStyle, { color: colors.bodyText }]}>
|
||||
{value.map(block => {
|
||||
switch (block.type) {
|
||||
case 'PLAIN_TEXT':
|
||||
|
|
|
@ -4,38 +4,30 @@ import { createImageProgress } from 'react-native-image-progress';
|
|||
import * as Progress from 'react-native-progress';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
|
||||
import { TSupportedThemes, useTheme } from '../../../theme';
|
||||
import { themes } from '../../../lib/constants';
|
||||
import { useTheme } from '../../../theme';
|
||||
import styles from '../../message/styles';
|
||||
|
||||
interface IImageProps {
|
||||
value: ImageProps['value'];
|
||||
}
|
||||
|
||||
type TMessageImage = {
|
||||
img: string;
|
||||
theme: TSupportedThemes;
|
||||
};
|
||||
|
||||
const ImageProgress = createImageProgress(FastImage);
|
||||
|
||||
const MessageImage = ({ img, theme }: TMessageImage) => (
|
||||
<ImageProgress
|
||||
style={[styles.inlineImage, { borderColor: themes[theme].borderColor }]}
|
||||
source={{ uri: encodeURI(img) }}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
indicator={Progress.Pie}
|
||||
indicatorProps={{
|
||||
color: themes[theme].actionTintColor
|
||||
}}
|
||||
/>
|
||||
);
|
||||
const MessageImage = ({ img }: { img: string }) => {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<ImageProgress
|
||||
style={[styles.inlineImage, { borderColor: colors.borderColor }]}
|
||||
source={{ uri: encodeURI(img) }}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
indicator={Progress.Pie}
|
||||
indicatorProps={{
|
||||
color: colors.actionTintColor
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Image = ({ value }: IImageProps) => {
|
||||
const { theme } = useTheme();
|
||||
const Image = ({ value }: { value: ImageProps['value'] }) => {
|
||||
const { src } = value;
|
||||
|
||||
return <MessageImage img={src.value} theme={theme} />;
|
||||
return <MessageImage img={src.value} />;
|
||||
};
|
||||
|
||||
export default Image;
|
||||
|
|
|
@ -45,12 +45,15 @@ const Inline = ({ value }: IParagraphProps) => {
|
|||
username={username}
|
||||
navToRoomInfo={navToRoomInfo}
|
||||
mentions={mentions}
|
||||
testID='new-markdown'
|
||||
/>
|
||||
);
|
||||
case 'EMOJI':
|
||||
return <Emoji value={block.value} />;
|
||||
case 'MENTION_CHANNEL':
|
||||
return <Hashtag hashtag={block.value.value} navToRoomInfo={navToRoomInfo} channels={channels} />;
|
||||
return (
|
||||
<Hashtag hashtag={block.value.value} navToRoomInfo={navToRoomInfo} channels={channels} testID='new-markdown' />
|
||||
);
|
||||
case 'INLINE_CODE':
|
||||
return <InlineCode value={block.value} />;
|
||||
default:
|
||||
|
|
|
@ -3,7 +3,6 @@ import { Text } from 'react-native';
|
|||
import { InlineCode as InlineCodeProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import styles from '../styles';
|
||||
import { themes } from '../../../lib/constants';
|
||||
import { useTheme } from '../../../theme';
|
||||
|
||||
interface IInlineCodeProps {
|
||||
|
@ -11,16 +10,16 @@ interface IInlineCodeProps {
|
|||
}
|
||||
|
||||
const InlineCode = ({ value }: IInlineCodeProps) => {
|
||||
const { theme } = useTheme();
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<Text
|
||||
style={[
|
||||
styles.codeInline,
|
||||
{
|
||||
color: themes[theme].bodyText,
|
||||
backgroundColor: themes[theme].bannerBackground,
|
||||
borderColor: themes[theme].borderColor
|
||||
color: colors.bodyText,
|
||||
backgroundColor: colors.bannerBackground,
|
||||
borderColor: colors.borderColor
|
||||
}
|
||||
]}>
|
||||
{(block => {
|
||||
|
|
|
@ -9,7 +9,6 @@ import { LISTENER } from '../../Toast';
|
|||
import { useTheme } from '../../../theme';
|
||||
import openLink from '../../../lib/methods/helpers/openLink';
|
||||
import EventEmitter from '../../../lib/methods/helpers/events';
|
||||
import { themes } from '../../../lib/constants';
|
||||
import Strike from './Strike';
|
||||
import Italic from './Italic';
|
||||
import Bold from './Bold';
|
||||
|
@ -20,7 +19,7 @@ interface ILinkProps {
|
|||
}
|
||||
|
||||
const Link = ({ value }: ILinkProps) => {
|
||||
const { theme } = useTheme();
|
||||
const { theme, colors } = useTheme();
|
||||
const { onLinkPress } = useContext(MarkdownContext);
|
||||
const { src, label } = value;
|
||||
const handlePress = () => {
|
||||
|
@ -39,7 +38,7 @@ const Link = ({ value }: ILinkProps) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Text onPress={handlePress} onLongPress={onLongPress} style={[styles.link, { color: themes[theme].actionTintColor }]}>
|
||||
<Text onPress={handlePress} onLongPress={onLongPress} style={[styles.link, { color: colors.actionTintColor }]}>
|
||||
{(block => {
|
||||
switch (block.type) {
|
||||
case 'PLAIN_TEXT':
|
||||
|
|
|
@ -4,7 +4,6 @@ import { OrderedList as OrderedListProps } from '@rocket.chat/message-parser';
|
|||
|
||||
import Inline from './Inline';
|
||||
import styles from '../styles';
|
||||
import { themes } from '../../../lib/constants';
|
||||
import { useTheme } from '../../../theme';
|
||||
|
||||
interface IOrderedListProps {
|
||||
|
@ -12,12 +11,12 @@ interface IOrderedListProps {
|
|||
}
|
||||
|
||||
const OrderedList = ({ value }: IOrderedListProps) => {
|
||||
const { theme } = useTheme();
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<View>
|
||||
{value.map((item, index) => (
|
||||
<View style={styles.row}>
|
||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>{index + 1}. </Text>
|
||||
<Text style={[styles.text, { color: colors.bodyText }]}>{index + 1}. </Text>
|
||||
<Inline value={item.value} />
|
||||
</View>
|
||||
))}
|
||||
|
|
|
@ -5,16 +5,15 @@ import { Paragraph as ParagraphProps } from '@rocket.chat/message-parser';
|
|||
import Inline from './Inline';
|
||||
import styles from '../styles';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { themes } from '../../../lib/constants';
|
||||
|
||||
interface IParagraphProps {
|
||||
value: ParagraphProps['value'];
|
||||
}
|
||||
|
||||
const Paragraph = ({ value }: IParagraphProps) => {
|
||||
const { theme } = useTheme();
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>
|
||||
<Text style={[styles.text, { color: colors.bodyText }]}>
|
||||
<Inline value={value} />
|
||||
</Text>
|
||||
);
|
||||
|
|
|
@ -4,16 +4,15 @@ import { Plain as PlainProps } from '@rocket.chat/message-parser';
|
|||
|
||||
import styles from '../styles';
|
||||
import { useTheme } from '../../../theme';
|
||||
import { themes } from '../../../lib/constants';
|
||||
|
||||
interface IPlainProps {
|
||||
value: PlainProps['value'];
|
||||
}
|
||||
|
||||
const Plain = ({ value }: IPlainProps) => {
|
||||
const { theme } = useTheme();
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<Text accessibilityLabel={value} style={[styles.plainText, { color: themes[theme].bodyText }]}>
|
||||
<Text accessibilityLabel={value} style={[styles.plainText, { color: colors.bodyText }]}>
|
||||
{value}
|
||||
</Text>
|
||||
);
|
||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||
import { View } from 'react-native';
|
||||
import { Quote as QuoteProps } from '@rocket.chat/message-parser';
|
||||
|
||||
import { themes } from '../../../lib/constants';
|
||||
import { useTheme } from '../../../theme';
|
||||
import styles from '../styles';
|
||||
import Paragraph from './Paragraph';
|
||||
|
@ -12,10 +11,10 @@ interface IQuoteProps {
|
|||
}
|
||||
|
||||
const Quote = ({ value }: IQuoteProps) => {
|
||||
const { theme } = useTheme();
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={[styles.quote, { backgroundColor: themes[theme].borderColor }]} />
|
||||
<View style={[styles.quote, { backgroundColor: colors.borderColor }]} />
|
||||
<View style={styles.childContainer}>
|
||||
{value.map(item => (
|
||||
<Paragraph value={item.value} />
|
||||
|
|
|
@ -4,7 +4,6 @@ import { Tasks as TasksProps } from '@rocket.chat/message-parser';
|
|||
|
||||
import Inline from './Inline';
|
||||
import styles from '../styles';
|
||||
import { themes } from '../../../lib/constants';
|
||||
import { useTheme } from '../../../theme';
|
||||
|
||||
interface ITasksProps {
|
||||
|
@ -12,12 +11,12 @@ interface ITasksProps {
|
|||
}
|
||||
|
||||
const TaskList = ({ value = [] }: ITasksProps) => {
|
||||
const { theme } = useTheme();
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<View>
|
||||
{value.map(item => (
|
||||
<View style={styles.row}>
|
||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>{item.status ? '- [x] ' : '- [ ] '}</Text>
|
||||
<Text style={[styles.text, { color: colors.bodyText }]}>{item.status ? '- [x] ' : '- [ ] '}</Text>
|
||||
<Inline value={item.value} />
|
||||
</View>
|
||||
))}
|
||||
|
|
|
@ -4,7 +4,6 @@ import { View, Text } from 'react-native';
|
|||
|
||||
import Inline from './Inline';
|
||||
import styles from '../styles';
|
||||
import { themes } from '../../../lib/constants';
|
||||
import { useTheme } from '../../../theme';
|
||||
|
||||
interface IUnorderedListProps {
|
||||
|
@ -12,12 +11,12 @@ interface IUnorderedListProps {
|
|||
}
|
||||
|
||||
const UnorderedList = ({ value }: IUnorderedListProps) => {
|
||||
const { theme } = useTheme();
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<View>
|
||||
{value.map(item => (
|
||||
<View style={styles.row}>
|
||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>- </Text>
|
||||
<Text style={[styles.text, { color: colors.bodyText }]}>- </Text>
|
||||
<Inline value={item.value} />
|
||||
</View>
|
||||
))}
|
||||
|
|
|
@ -280,7 +280,6 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
|
|||
baseUrl={baseUrl}
|
||||
username={user.username}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
theme={theme}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
|
|
|
@ -100,7 +100,6 @@ const Fields = React.memo(
|
|||
baseUrl={baseUrl}
|
||||
username={user.username}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
theme={theme}
|
||||
style={[styles.markdownFontSize]}
|
||||
/>
|
||||
</View>
|
||||
|
|
|
@ -63,7 +63,6 @@ const Content = React.memo(
|
|||
navToRoomInfo={props.navToRoomInfo}
|
||||
tmid={props.tmid}
|
||||
useRealName={props.useRealName}
|
||||
theme={theme}
|
||||
onLinkPress={onLinkPress}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -83,7 +83,6 @@ const ImageContainer = React.memo(
|
|||
baseUrl={baseUrl}
|
||||
username={user.username}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
theme={theme}
|
||||
/>
|
||||
<MessageImage imgUri={img} theme={theme} />
|
||||
</View>
|
||||
|
|
|
@ -131,7 +131,6 @@ const Description = React.memo(
|
|||
baseUrl={baseUrl}
|
||||
username={user.username}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
@ -184,13 +183,7 @@ const Fields = React.memo(
|
|||
{attachment.fields.map(field => (
|
||||
<View key={field.title} style={[styles.fieldContainer, { width: field.short ? '50%' : '100%' }]}>
|
||||
<Text style={[styles.fieldTitle, { color: themes[theme].bodyText }]}>{field.title}</Text>
|
||||
<Markdown
|
||||
msg={field?.value || ''}
|
||||
baseUrl={baseUrl}
|
||||
username={user.username}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
theme={theme}
|
||||
/>
|
||||
<Markdown msg={field?.value || ''} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} />
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
@ -271,13 +264,7 @@ const Reply = React.memo(
|
|||
) : null}
|
||||
</View>
|
||||
</Touchable>
|
||||
<Markdown
|
||||
msg={attachment.description}
|
||||
baseUrl={baseUrl}
|
||||
username={user.username}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
theme={theme}
|
||||
/>
|
||||
<Markdown msg={attachment.description} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} />
|
||||
</>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -83,7 +83,6 @@ const Video = React.memo(
|
|||
username={user.username}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
style={[isReply && style]}
|
||||
theme={theme}
|
||||
/>
|
||||
<Touchable
|
||||
disabled={isReply}
|
||||
|
|
|
@ -18,6 +18,7 @@ export interface ICustomEmoji {
|
|||
baseUrl?: string;
|
||||
emoji: IEmoji;
|
||||
style: StyleProp<ImageStyle>;
|
||||
testID?: string;
|
||||
}
|
||||
|
||||
export interface ICustomEmojiModel {
|
||||
|
|
|
@ -84,7 +84,7 @@ const Item = ({ label, content, theme, testID }: IItem) =>
|
|||
<Text accessibilityLabel={label} style={[styles.itemLabel, { color: themes[theme].titleText }]}>
|
||||
{label}
|
||||
</Text>
|
||||
<Markdown style={[styles.itemContent, { color: themes[theme].auxiliaryText }]} msg={content} theme={theme} />
|
||||
<Markdown style={[styles.itemContent, { color: themes[theme].auxiliaryText }]} msg={content} />
|
||||
</View>
|
||||
) : null;
|
||||
|
||||
|
|
|
@ -40,10 +40,10 @@ class E2EHowItWorksView extends React.Component<TE2EHowItWorksViewProps, any> {
|
|||
|
||||
return (
|
||||
<SafeAreaView style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]} testID='e2e-how-it-works-view'>
|
||||
<Markdown msg={I18n.t('E2E_How_It_Works_info1')} style={infoStyle} theme={theme} />
|
||||
<Markdown msg={I18n.t('E2E_How_It_Works_info2')} style={infoStyle} theme={theme} />
|
||||
<Markdown msg={I18n.t('E2E_How_It_Works_info3')} style={infoStyle} theme={theme} />
|
||||
<Markdown msg={I18n.t('E2E_How_It_Works_info4')} style={infoStyle} theme={theme} />
|
||||
<Markdown msg={I18n.t('E2E_How_It_Works_info1')} style={infoStyle} />
|
||||
<Markdown msg={I18n.t('E2E_How_It_Works_info2')} style={infoStyle} />
|
||||
<Markdown msg={I18n.t('E2E_How_It_Works_info3')} style={infoStyle} />
|
||||
<Markdown msg={I18n.t('E2E_How_It_Works_info4')} style={infoStyle} />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ const InviteUsersView = ({ route, navigation }: IInviteUsersViewProps): React.Re
|
|||
const rid = route.params?.rid;
|
||||
const timeDateFormat = useSelector((state: IApplicationState) => state.settings.Message_TimeAndDateFormat as string);
|
||||
const invite = useSelector((state: IApplicationState) => state.inviteLinks.invite);
|
||||
const { colors, theme } = useTheme();
|
||||
const { colors } = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -81,7 +81,7 @@ const InviteUsersView = ({ route, navigation }: IInviteUsersViewProps): React.Re
|
|||
|
||||
const renderExpiration = () => {
|
||||
const expirationMessage = linkExpirationText();
|
||||
return <Markdown msg={expirationMessage} theme={theme} />;
|
||||
return <Markdown msg={expirationMessage} />;
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -24,7 +24,7 @@ const Item = ({ label, content, testID }: IItem) => {
|
|||
<Text accessibilityLabel={label} style={[styles.itemLabel, { color: themes[theme].titleText }]}>
|
||||
{label}
|
||||
</Text>
|
||||
<Markdown style={[styles.itemContent, { color: themes[theme].auxiliaryText }]} msg={content} theme={theme} />
|
||||
<Markdown style={[styles.itemContent, { color: themes[theme].auxiliaryText }]} msg={content} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -45,7 +45,7 @@ const Banner = React.memo(
|
|||
<View style={[styles.modalView, { backgroundColor: themes[theme].bannerBackground }]}>
|
||||
<Text style={[styles.bannerModalTitle, { color: themes[theme].auxiliaryText }]}>{title}</Text>
|
||||
<ScrollView style={styles.modalScrollView}>
|
||||
<Markdown msg={text} theme={theme} />
|
||||
<Markdown msg={text} />
|
||||
</ScrollView>
|
||||
</View>
|
||||
</Modal>
|
||||
|
|
|
@ -6,7 +6,7 @@ import { storiesOf } from '@storybook/react-native';
|
|||
import { longText } from '../../../../storybook/utils';
|
||||
import { ThemeContext } from '../../../theme';
|
||||
import { Message, MessageDecorator, StoryProvider } from '../../../../storybook/stories/Message';
|
||||
import { MessageTypeLoad, themes } from '../../../lib/constants';
|
||||
import { colors, MessageTypeLoad, themes } from '../../../lib/constants';
|
||||
import LoadMore from './index';
|
||||
|
||||
const stories = storiesOf('LoadMore', module);
|
||||
|
@ -24,7 +24,7 @@ stories.add('basic', () => (
|
|||
));
|
||||
|
||||
const ThemeStory = ({ theme }) => (
|
||||
<ThemeContext.Provider value={{ theme }}>
|
||||
<ThemeContext.Provider value={{ theme, colors: colors[theme] }}>
|
||||
<ScrollView style={{ backgroundColor: themes[theme].backgroundColor }}>
|
||||
<LoadMore load={load} type={MessageTypeLoad.PREVIOUS_CHUNK} />
|
||||
<Message msg='Hey!' theme={theme} />
|
||||
|
|
|
@ -334,7 +334,7 @@ class SearchMessagesView extends React.Component<ISearchMessagesViewProps, ISear
|
|||
placeholder={I18n.t('Search_Messages')}
|
||||
testID='search-message-view-input'
|
||||
/>
|
||||
<Markdown msg={I18n.t('You_can_search_using_RegExp_eg')} theme={theme} />
|
||||
<Markdown msg={I18n.t('You_can_search_using_RegExp_eg')} />
|
||||
<View style={[styles.divider, { backgroundColor: themes[theme].separatorColor }]} />
|
||||
</View>
|
||||
{this.renderList()}
|
||||
|
|
|
@ -48,11 +48,11 @@ const stories = storiesOf('Markdown', module).addDecorator(story => <Provider st
|
|||
|
||||
stories.add('Text', () => (
|
||||
<View style={styles.container}>
|
||||
<Markdown msg='This is Rocket.Chat' theme={theme} />
|
||||
<Markdown msg={longText} theme={theme} />
|
||||
<Markdown msg={lineBreakText} theme={theme} />
|
||||
<Markdown msg={sequentialEmptySpacesText} theme={theme} />
|
||||
<Markdown msg='Strong emphasis, aka bold, with **asterisks** or __underscores__' theme={theme} />
|
||||
<Markdown msg='This is Rocket.Chat' />
|
||||
<Markdown msg={longText} />
|
||||
<Markdown msg={lineBreakText} />
|
||||
<Markdown msg={sequentialEmptySpacesText} />
|
||||
<Markdown msg='Strong emphasis, aka bold, with **asterisks** or __underscores__' />
|
||||
</View>
|
||||
));
|
||||
|
||||
|
@ -71,7 +71,6 @@ stories.add('Mentions', () => (
|
|||
<ScrollView style={styles.container}>
|
||||
<Markdown
|
||||
msg='@rocket.cat @name1 @all @here @unknown'
|
||||
theme={theme}
|
||||
mentions={[
|
||||
{ _id: 'random', name: 'Rocket Cat', username: 'rocket.cat' },
|
||||
{ _id: 'random2', name: 'Name', username: 'name1' },
|
||||
|
@ -82,7 +81,6 @@ stories.add('Mentions', () => (
|
|||
/>
|
||||
<Markdown
|
||||
msg='@rocket.cat @name1 @all @here @unknown'
|
||||
theme={theme}
|
||||
mentions={[
|
||||
{ _id: 'random', name: 'Rocket Cat', username: 'rocket.cat' },
|
||||
{ _id: 'random2', name: 'Name', username: 'name1' },
|
||||
|
@ -97,21 +95,16 @@ stories.add('Mentions', () => (
|
|||
|
||||
stories.add('Hashtag', () => (
|
||||
<View style={styles.container}>
|
||||
<Markdown msg='#test-channel #unknown' theme={theme} channels={[{ _id: '123', name: 'test-channel' }]} />
|
||||
<Markdown msg='#test-channel #unknown' channels={[{ _id: '123', name: 'test-channel' }]} />
|
||||
</View>
|
||||
));
|
||||
|
||||
stories.add('Emoji', () => (
|
||||
<View style={styles.container}>
|
||||
<Markdown msg='Unicode: 😃😇👍' theme={theme} />
|
||||
<Markdown msg='Shortnames: :joy::+1:' theme={theme} />
|
||||
<Markdown
|
||||
msg='Custom emojis: :react_rocket: :nyan_rocket: :marioparty:'
|
||||
theme={theme}
|
||||
getCustomEmoji={getCustomEmoji}
|
||||
baseUrl={baseUrl}
|
||||
/>
|
||||
<Markdown msg='😃 :+1: :marioparty:' theme={theme} getCustomEmoji={getCustomEmoji} baseUrl={baseUrl} />
|
||||
<Markdown msg='Unicode: 😃😇👍' />
|
||||
<Markdown msg='Shortnames: :joy::+1:' />
|
||||
<Markdown msg='Custom emojis: :react_rocket: :nyan_rocket: :marioparty:' getCustomEmoji={getCustomEmoji} baseUrl={baseUrl} />
|
||||
<Markdown msg='😃 :+1: :marioparty:' getCustomEmoji={getCustomEmoji} baseUrl={baseUrl} />
|
||||
</View>
|
||||
));
|
||||
|
||||
|
@ -120,52 +113,50 @@ stories.add('Block quote', () => (
|
|||
<Markdown
|
||||
msg={`> This is block quote
|
||||
this is a normal line`}
|
||||
theme={theme}
|
||||
/>
|
||||
</View>
|
||||
));
|
||||
|
||||
stories.add('Links', () => (
|
||||
<View style={styles.container}>
|
||||
<Markdown msg='[Markdown link](https://rocket.chat): `[description](url)`' theme={theme} />
|
||||
<Markdown msg='<https://rocket.chat|Formatted Link>: `<url|description>`' theme={theme} />
|
||||
<Markdown msg='[Markdown link](https://rocket.chat): `[description](url)`' />
|
||||
<Markdown msg='<https://rocket.chat|Formatted Link>: `<url|description>`' />
|
||||
</View>
|
||||
));
|
||||
|
||||
stories.add('Image', () => (
|
||||
<View style={styles.container}>
|
||||
<Markdown msg='![alt text](https://play.google.com/intl/en_us/badges/images/badge_new.png)' theme={theme} />
|
||||
<Markdown msg='![alt text](https://play.google.com/intl/en_us/badges/images/badge_new.png)' />
|
||||
</View>
|
||||
));
|
||||
|
||||
stories.add('Headers', () => (
|
||||
<View style={styles.container}>
|
||||
<Markdown msg='# Header 1' theme={theme} />
|
||||
<Markdown msg='## Header 2' theme={theme} />
|
||||
<Markdown msg='### Header 3' theme={theme} />
|
||||
<Markdown msg='#### Header 4' theme={theme} />
|
||||
<Markdown msg='##### Header 5' theme={theme} />
|
||||
<Markdown msg='###### Header 6' theme={theme} />
|
||||
<Markdown msg='# Header 1' />
|
||||
<Markdown msg='## Header 2' />
|
||||
<Markdown msg='### Header 3' />
|
||||
<Markdown msg='#### Header 4' />
|
||||
<Markdown msg='##### Header 5' />
|
||||
<Markdown msg='###### Header 6' />
|
||||
</View>
|
||||
));
|
||||
|
||||
stories.add('Code', () => (
|
||||
<View style={styles.container}>
|
||||
<Markdown msg='This is `inline code`' theme={theme} />
|
||||
<Markdown msg='This is `inline code`' />
|
||||
<Markdown
|
||||
msg='Inline `code` has `back-ticks around` it.
|
||||
```
|
||||
Code block
|
||||
```'
|
||||
theme={theme}
|
||||
/>
|
||||
</View>
|
||||
));
|
||||
|
||||
stories.add('Lists', () => (
|
||||
<View style={styles.container}>
|
||||
<Markdown msg={'* Open Source\n* Rocket.Chat\n - nodejs\n - ReactNative'} theme={theme} />
|
||||
<Markdown msg={'1. Open Source\n2. Rocket.Chat'} theme={theme} />
|
||||
<Markdown msg={'* Open Source\n* Rocket.Chat\n - nodejs\n - ReactNative'} />
|
||||
<Markdown msg={'1. Open Source\n2. Rocket.Chat'} />
|
||||
</View>
|
||||
));
|
||||
|
||||
|
@ -176,7 +167,6 @@ stories.add('Table', () => (
|
|||
------------ | -------------
|
||||
Content from cell 1 | Content from cell 2
|
||||
Content in the first column | Content in the second column'
|
||||
theme={theme}
|
||||
/>
|
||||
</View>
|
||||
));
|
||||
|
|
|
@ -18,7 +18,7 @@ exports[`Storyshots Avatar Custom style 1`] = `"{\\"type\\":\\"View\\",\\"props\
|
|||
|
||||
exports[`Storyshots Avatar Direct 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":56,\\"height\\":56,\\"borderRadius\\":4},null],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":56,\\"height\\":56,\\"borderRadius\\":4}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/avatar/diego.mello?format=png&size=112\\",\\"headers\\":{\\"User-Agent\\":\\"RC Mobile; ios unknown; vunknown (unknown)\\"},\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}"`;
|
||||
|
||||
exports[`Storyshots Avatar Emoji 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":56,\\"height\\":56,\\"borderRadius\\":4},null],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":30,\\"height\\":30},{\\"width\\":56,\\"height\\":56,\\"borderRadius\\":4}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/emoji-custom/troll.jpg\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"contain\\"},\\"children\\":null}]}]}"`;
|
||||
exports[`Storyshots Avatar Emoji 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":56,\\"height\\":56,\\"borderRadius\\":4},null],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},[{\\"width\\":30,\\"height\\":30},{\\"width\\":56,\\"height\\":56,\\"borderRadius\\":4}]]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"testID\\":\\"avatar-custom-emoji\\",\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://open.rocket.chat/emoji-custom/troll.jpg\\",\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"contain\\"},\\"children\\":null}]}]}"`;
|
||||
|
||||
exports[`Storyshots Avatar Static 1`] = `"{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"width\\":56,\\"height\\":56,\\"borderRadius\\":4},null],\\"testID\\":\\"avatar\\"},\\"children\\":[{\\"type\\":\\"View\\",\\"props\\":{\\"style\\":[{\\"overflow\\":\\"hidden\\"},{\\"width\\":56,\\"height\\":56,\\"borderRadius\\":4}]},\\"children\\":[{\\"type\\":\\"FastImageView\\",\\"props\\":{\\"style\\":{\\"position\\":\\"absolute\\",\\"left\\":0,\\"right\\":0,\\"top\\":0,\\"bottom\\":0},\\"source\\":{\\"uri\\":\\"https://user-images.githubusercontent.com/29778115/89444446-14738480-d728-11ea-9412-75fd978d95fb.jpg\\",\\"headers\\":{\\"User-Agent\\":\\"RC Mobile; ios unknown; vunknown (unknown)\\"},\\"priority\\":\\"high\\"},\\"resizeMode\\":\\"cover\\"},\\"children\\":null}]}]}"`;
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue