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;
|
let image;
|
||||||
if (emoji) {
|
if (emoji) {
|
||||||
image = (
|
image = (
|
||||||
<Emoji baseUrl={server} getCustomEmoji={getCustomEmoji} isMessageContainsOnlyEmoji literal={emoji} style={avatarStyle} />
|
<Emoji
|
||||||
|
baseUrl={server}
|
||||||
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
isMessageContainsOnlyEmoji
|
||||||
|
literal={emoji}
|
||||||
|
style={avatarStyle}
|
||||||
|
testID='avatar'
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let uri = avatar;
|
let uri = avatar;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FastImage from 'react-native-fast-image';
|
import FastImage from 'react-native-fast-image';
|
||||||
|
|
||||||
import { ICustomEmoji } from '../../definitions/IEmoji';
|
import { ICustomEmoji } from '../../definitions';
|
||||||
|
|
||||||
const CustomEmoji = React.memo(
|
const CustomEmoji = React.memo(
|
||||||
({ baseUrl, emoji, style }: ICustomEmoji) => (
|
({ baseUrl, emoji, style, testID }: ICustomEmoji) => (
|
||||||
<FastImage
|
<FastImage
|
||||||
style={style}
|
style={style}
|
||||||
source={{
|
source={{
|
||||||
|
@ -12,6 +12,7 @@ const CustomEmoji = React.memo(
|
||||||
priority: FastImage.priority.high
|
priority: FastImage.priority.high
|
||||||
}}
|
}}
|
||||||
resizeMode={FastImage.resizeMode.contain}
|
resizeMode={FastImage.resizeMode.contain}
|
||||||
|
testID={testID}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
(prevProps, nextProps) => {
|
(prevProps, nextProps) => {
|
||||||
|
|
|
@ -76,7 +76,7 @@ class MessageParser extends UiKitParserMessage<React.ReactElement> {
|
||||||
<MarkdownPreview msg={element.text} style={[isContext && { color: themes[theme].auxiliaryText }]} numberOfLines={0} />
|
<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) {
|
button(element: IButton, context: BlockContext) {
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||||
import { StyleProp, Text, TextStyle } from 'react-native';
|
import { StyleProp, Text, TextStyle } from 'react-native';
|
||||||
|
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { themes } from '../../lib/constants';
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { events, logEvent } from '../../lib/methods/helpers/log';
|
import { events, logEvent } from '../../lib/methods/helpers/log';
|
||||||
import { IUserMention } from './interfaces';
|
import { IUserMention } from './interfaces';
|
||||||
|
@ -14,20 +13,22 @@ interface IAtMention {
|
||||||
style?: StyleProp<TextStyle>[];
|
style?: StyleProp<TextStyle>[];
|
||||||
useRealName?: boolean;
|
useRealName?: boolean;
|
||||||
mentions?: IUserMention[];
|
mentions?: IUserMention[];
|
||||||
|
testID: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, style = [], useRealName }: IAtMention) => {
|
const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, style = [], useRealName, testID }: IAtMention) => {
|
||||||
const { theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
if (mention === 'all' || mention === 'here') {
|
if (mention === 'all' || mention === 'here') {
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.mention,
|
styles.mention,
|
||||||
{
|
{
|
||||||
color: themes[theme].mentionGroupColor
|
color: colors.mentionGroupColor
|
||||||
},
|
},
|
||||||
...style
|
...style
|
||||||
]}>
|
]}
|
||||||
|
testID={`${testID}-mention-all-here`}>
|
||||||
{mention}
|
{mention}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
@ -36,11 +37,11 @@ const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, styl
|
||||||
let mentionStyle = {};
|
let mentionStyle = {};
|
||||||
if (mention === username) {
|
if (mention === username) {
|
||||||
mentionStyle = {
|
mentionStyle = {
|
||||||
color: themes[theme].mentionMeColor
|
color: colors.mentionMeColor
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
mentionStyle = {
|
mentionStyle = {
|
||||||
color: themes[theme].mentionOtherColor
|
color: colors.mentionOtherColor
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,13 +60,15 @@ const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, styl
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
return (
|
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}
|
{useRealName && user.name ? user.name : user.username}
|
||||||
</Text>
|
</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;
|
export default AtMention;
|
||||||
|
|
|
@ -1,20 +1,21 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
|
|
||||||
import { TSupportedThemes } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { themes } from '../../lib/constants';
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
interface IBlockQuote {
|
interface IBlockQuote {
|
||||||
children: React.ReactElement | null;
|
children: React.ReactElement | null;
|
||||||
theme: TSupportedThemes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const BlockQuote = React.memo(({ children, theme }: IBlockQuote) => (
|
const BlockQuote = React.memo(({ children }: IBlockQuote) => {
|
||||||
<View style={styles.container}>
|
const { colors } = useTheme();
|
||||||
<View style={[styles.quote, { backgroundColor: themes[theme].borderColor }]} />
|
return (
|
||||||
|
<View style={styles.container} testID='markdown-block-quote'>
|
||||||
|
<View style={[styles.quote, { backgroundColor: colors.borderColor }]} />
|
||||||
<View style={styles.childContainer}>{children}</View>
|
<View style={styles.childContainer}>{children}</View>
|
||||||
</View>
|
</View>
|
||||||
));
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default BlockQuote;
|
export default BlockQuote;
|
||||||
|
|
|
@ -15,10 +15,11 @@ interface IEmoji {
|
||||||
style?: object;
|
style?: object;
|
||||||
onEmojiSelected?: Function;
|
onEmojiSelected?: Function;
|
||||||
tabEmojiStyle?: object;
|
tabEmojiStyle?: object;
|
||||||
|
testID: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Emoji = React.memo(
|
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 { colors } = useTheme();
|
||||||
const emojiUnicode = shortnameToUnicode(literal);
|
const emojiUnicode = shortnameToUnicode(literal);
|
||||||
const emoji: any = getCustomEmoji && getCustomEmoji(literal.replace(/:/g, ''));
|
const emoji: any = getCustomEmoji && getCustomEmoji(literal.replace(/:/g, ''));
|
||||||
|
@ -28,11 +29,14 @@ const Emoji = React.memo(
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
style={[isMessageContainsOnlyEmoji ? styles.customEmojiBig : styles.customEmoji, style]}
|
style={[isMessageContainsOnlyEmoji ? styles.customEmojiBig : styles.customEmoji, style]}
|
||||||
emoji={emoji}
|
emoji={emoji}
|
||||||
|
testID={`${testID}-custom-emoji`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
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}
|
{emojiUnicode}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { StyleProp, Text, TextStyle } from 'react-native';
|
||||||
import { useNavigation } from '@react-navigation/native';
|
import { useNavigation } from '@react-navigation/native';
|
||||||
import { StackNavigationProp } from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
|
||||||
import { themes } from '../../lib/constants';
|
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { IUserChannel } from './interfaces';
|
import { IUserChannel } from './interfaces';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
@ -17,10 +16,11 @@ interface IHashtag {
|
||||||
navToRoomInfo?: Function;
|
navToRoomInfo?: Function;
|
||||||
style?: StyleProp<TextStyle>[];
|
style?: StyleProp<TextStyle>[];
|
||||||
channels?: IUserChannel[];
|
channels?: IUserChannel[];
|
||||||
|
testID: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IHashtag) => {
|
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, testID, style = [] }: IHashtag) => {
|
||||||
const { theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
||||||
const navigation = useNavigation<StackNavigationProp<ChatsStackParamList, 'RoomView'>>();
|
const navigation = useNavigation<StackNavigationProp<ChatsStackParamList, 'RoomView'>>();
|
||||||
|
|
||||||
|
@ -46,16 +46,21 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IH
|
||||||
style={[
|
style={[
|
||||||
styles.mention,
|
styles.mention,
|
||||||
{
|
{
|
||||||
color: themes[theme].mentionOtherColor
|
color: colors.mentionOtherColor
|
||||||
},
|
},
|
||||||
...style
|
...style
|
||||||
]}
|
]}
|
||||||
onPress={handlePress}>
|
onPress={handlePress}
|
||||||
|
testID={`${testID}-hashtag-channels`}>
|
||||||
{`#${hashtag}`}
|
{`#${hashtag}`}
|
||||||
</Text>
|
</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;
|
export default Hashtag;
|
||||||
|
|
|
@ -3,22 +3,23 @@ import { Text } from 'react-native';
|
||||||
import Clipboard from '@react-native-clipboard/clipboard';
|
import Clipboard from '@react-native-clipboard/clipboard';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../lib/constants';
|
|
||||||
import { LISTENER } from '../Toast';
|
import { LISTENER } from '../Toast';
|
||||||
import EventEmitter from '../../lib/methods/helpers/events';
|
import EventEmitter from '../../lib/methods/helpers/events';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import openLink from '../../lib/methods/helpers/openLink';
|
import openLink from '../../lib/methods/helpers/openLink';
|
||||||
import { TOnLinkPress } from './interfaces';
|
import { TOnLinkPress } from './interfaces';
|
||||||
import { TSupportedThemes } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
|
|
||||||
interface ILink {
|
interface ILink {
|
||||||
children: React.ReactElement | null;
|
children: React.ReactElement | null;
|
||||||
link: string;
|
link: string;
|
||||||
theme: TSupportedThemes;
|
|
||||||
onLinkPress?: TOnLinkPress;
|
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 = () => {
|
const handlePress = () => {
|
||||||
if (!link) {
|
if (!link) {
|
||||||
return;
|
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
|
// if you have a [](https://rocket.chat) render https://rocket.chat
|
||||||
return (
|
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}
|
{childLength !== 0 ? children : link}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { StyleSheet, Text, View } from 'react-native';
|
import { StyleSheet, Text, View } from 'react-native';
|
||||||
|
|
||||||
import { TSupportedThemes } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { themes } from '../../lib/constants';
|
|
||||||
|
|
||||||
const style = StyleSheet.create({
|
const style = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -24,11 +23,13 @@ interface IListItem {
|
||||||
level: number;
|
level: number;
|
||||||
ordered: boolean;
|
ordered: boolean;
|
||||||
continue: boolean;
|
continue: boolean;
|
||||||
theme: TSupportedThemes;
|
|
||||||
index: number;
|
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;
|
let bullet;
|
||||||
if (_continue) {
|
if (_continue) {
|
||||||
bullet = '';
|
bullet = '';
|
||||||
|
@ -41,9 +42,9 @@ const ListItem = React.memo(({ children, level, bulletWidth, continue: _continue
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={style.container}>
|
<View style={style.container} testID={`${testID}-list`}>
|
||||||
<View style={[{ width: bulletWidth }, style.bullet]}>
|
<View style={[{ width: bulletWidth }, style.bullet]}>
|
||||||
<Text style={{ color: themes[theme].bodyText }}>{bullet}</Text>
|
<Text style={{ color: colors.bodyText }}>{bullet}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={style.contents}>{children}</View>
|
<View style={style.contents}>{children}</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -5,24 +5,25 @@ import { CELL_WIDTH } from './TableCell';
|
||||||
import Navigation from '../../lib/navigation/appNavigation';
|
import Navigation from '../../lib/navigation/appNavigation';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { TSupportedThemes } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { themes } from '../../lib/constants';
|
|
||||||
import { useAppSelector } from '../../lib/hooks';
|
import { useAppSelector } from '../../lib/hooks';
|
||||||
|
|
||||||
interface ITable {
|
interface ITable {
|
||||||
children: React.ReactElement | null;
|
children: React.ReactElement | null;
|
||||||
numColumns: number;
|
numColumns: number;
|
||||||
theme: TSupportedThemes;
|
testID: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_HEIGHT = 300;
|
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 getTableWidth = () => numColumns * CELL_WIDTH;
|
||||||
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
const isMasterDetail = useAppSelector(state => state.app.isMasterDetail);
|
||||||
|
|
||||||
const renderRows = (drawExtraBorders = true) => {
|
const renderRows = (drawExtraBorders = true) => {
|
||||||
const tableStyle: ViewStyle[] = [styles.table, { borderColor: themes[theme].borderColor }];
|
const tableStyle: ViewStyle[] = [styles.table, { borderColor: colors.borderColor }];
|
||||||
if (drawExtraBorders) {
|
if (drawExtraBorders) {
|
||||||
tableStyle.push(styles.tableExtraBorders);
|
tableStyle.push(styles.tableExtraBorders);
|
||||||
}
|
}
|
||||||
|
@ -47,18 +48,15 @@ const Table = React.memo(({ children, numColumns, theme }: ITable) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity onPress={onPress}>
|
<TouchableOpacity onPress={onPress} testID={`${testID}-table`}>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
contentContainerStyle={{ width: getTableWidth() }}
|
contentContainerStyle={{ width: getTableWidth() }}
|
||||||
scrollEnabled={false}
|
scrollEnabled={false}
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
style={[
|
style={[styles.containerTable, { maxWidth: getTableWidth(), maxHeight: MAX_HEIGHT, borderColor: colors.borderColor }]}>
|
||||||
styles.containerTable,
|
|
||||||
{ maxWidth: getTableWidth(), maxHeight: MAX_HEIGHT, borderColor: themes[theme].borderColor }
|
|
||||||
]}>
|
|
||||||
{renderRows(false)}
|
{renderRows(false)}
|
||||||
</ScrollView>
|
</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>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, View, ViewStyle } from 'react-native';
|
import { Text, View, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
import { TSupportedThemes } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { themes } from '../../lib/constants';
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
interface ITableCell {
|
export interface ITableCell {
|
||||||
align: '' | 'left' | 'center' | 'right';
|
align: '' | 'left' | 'center' | 'right';
|
||||||
children: React.ReactElement | null;
|
children: React.ReactElement | null;
|
||||||
isLastCell: boolean;
|
isLastCell: boolean;
|
||||||
theme: TSupportedThemes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CELL_WIDTH = 100;
|
export const CELL_WIDTH = 100;
|
||||||
|
|
||||||
const TableCell = React.memo(({ isLastCell, align, children, theme }: ITableCell) => {
|
const TableCell = React.memo(({ isLastCell, align, children }: ITableCell) => {
|
||||||
const cellStyle: ViewStyle[] = [styles.cell, { borderColor: themes[theme].borderColor }];
|
const { colors } = useTheme();
|
||||||
|
|
||||||
|
const cellStyle: ViewStyle[] = [styles.cell, { borderColor: colors.borderColor }];
|
||||||
if (!isLastCell) {
|
if (!isLastCell) {
|
||||||
cellStyle.push(styles.cellRightBorder);
|
cellStyle.push(styles.cellRightBorder);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ const TableCell = React.memo(({ isLastCell, align, children, theme }: ITableCell
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[...cellStyle, { width: CELL_WIDTH }]}>
|
<View style={[...cellStyle, { width: CELL_WIDTH }]}>
|
||||||
<Text style={[textStyle, { color: themes[theme].bodyText }]}>{children}</Text>
|
<Text style={[textStyle, { color: colors.bodyText }]}>{children}</Text>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, ViewStyle } from 'react-native';
|
import { View, ViewStyle } from 'react-native';
|
||||||
|
|
||||||
import { TSupportedThemes } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { themes } from '../../lib/constants';
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
interface ITableRow {
|
export interface ITableRow {
|
||||||
children: React.ReactElement | null;
|
children: React.ReactElement | null;
|
||||||
isLastRow: boolean;
|
isLastRow: boolean;
|
||||||
theme: TSupportedThemes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const TableRow = React.memo(({ isLastRow, children: _children, theme }: ITableRow) => {
|
const TableRow = React.memo(({ isLastRow, children: _children }: ITableRow) => {
|
||||||
const rowStyle: ViewStyle[] = [styles.row, { borderColor: themes[theme].borderColor }];
|
const { colors } = useTheme();
|
||||||
|
|
||||||
|
const rowStyle: ViewStyle[] = [styles.row, { borderColor: colors.borderColor }];
|
||||||
if (!isLastRow) {
|
if (!isLastRow) {
|
||||||
rowStyle.push(styles.rowBottomBorder);
|
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 { Image, StyleProp, Text, TextStyle } from 'react-native';
|
||||||
import { Parser } from 'commonmark';
|
import { Parser } from 'commonmark';
|
||||||
import Renderer from 'commonmark-react-renderer';
|
import Renderer from 'commonmark-react-renderer';
|
||||||
|
@ -12,24 +12,23 @@ import MarkdownHashtag from './Hashtag';
|
||||||
import MarkdownBlockQuote from './BlockQuote';
|
import MarkdownBlockQuote from './BlockQuote';
|
||||||
import MarkdownEmoji from './Emoji';
|
import MarkdownEmoji from './Emoji';
|
||||||
import MarkdownTable from './Table';
|
import MarkdownTable from './Table';
|
||||||
import MarkdownTableRow from './TableRow';
|
import MarkdownTableRow, { ITableRow } from './TableRow';
|
||||||
import MarkdownTableCell from './TableCell';
|
import MarkdownTableCell, { ITableCell } from './TableCell';
|
||||||
import mergeTextNodes from './mergeTextNodes';
|
import mergeTextNodes from './mergeTextNodes';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { isValidURL } from '../../lib/methods/helpers/url';
|
import { isValidURL } from '../../lib/methods/helpers';
|
||||||
import NewMarkdown from './new';
|
import NewMarkdown from './new';
|
||||||
import { formatText } from './formatText';
|
import { formatText } from './formatText';
|
||||||
import { IUserMention, IUserChannel, TOnLinkPress } from './interfaces';
|
import { IUserMention, IUserChannel, TOnLinkPress } from './interfaces';
|
||||||
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
import { TGetCustomEmoji } from '../../definitions';
|
||||||
import { formatHyperlink } from './formatHyperlink';
|
import { formatHyperlink } from './formatHyperlink';
|
||||||
import { TSupportedThemes } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import { themes } from '../../lib/constants';
|
import { IRoomInfoParam } from '../../views/SearchMessagesView';
|
||||||
|
|
||||||
export { default as MarkdownPreview } from './Preview';
|
export { default as MarkdownPreview } from './Preview';
|
||||||
|
|
||||||
interface IMarkdownProps {
|
export interface IMarkdownProps {
|
||||||
msg?: string | null;
|
msg?: string | null;
|
||||||
theme: TSupportedThemes;
|
|
||||||
md?: MarkdownAST;
|
md?: MarkdownAST;
|
||||||
mentions?: IUserMention[];
|
mentions?: IUserMention[];
|
||||||
getCustomEmoji?: TGetCustomEmoji;
|
getCustomEmoji?: TGetCustomEmoji;
|
||||||
|
@ -41,8 +40,7 @@ interface IMarkdownProps {
|
||||||
useRealName?: boolean;
|
useRealName?: boolean;
|
||||||
channels?: IUserChannel[];
|
channels?: IUserChannel[];
|
||||||
enableMessageParser?: boolean;
|
enableMessageParser?: boolean;
|
||||||
// TODO: Refactor when migrate Room
|
navToRoomInfo?: (params: IRoomInfoParam) => void;
|
||||||
navToRoomInfo?: Function;
|
|
||||||
testID?: string;
|
testID?: string;
|
||||||
style?: StyleProp<TextStyle>[];
|
style?: StyleProp<TextStyle>[];
|
||||||
onLinkPress?: TOnLinkPress;
|
onLinkPress?: TOnLinkPress;
|
||||||
|
@ -86,141 +84,147 @@ const emojiCount = (str: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const parser = new Parser();
|
const parser = new Parser();
|
||||||
|
export const markdownTestID = 'markdown';
|
||||||
|
|
||||||
class Markdown extends PureComponent<IMarkdownProps, any> {
|
const Markdown = ({
|
||||||
private renderer: any;
|
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(
|
||||||
constructor(props: IMarkdownProps) {
|
() =>
|
||||||
super(props);
|
|
||||||
if (!this.isNewMarkdown) {
|
|
||||||
this.renderer = this.createRenderer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createRenderer = () =>
|
|
||||||
new Renderer({
|
new Renderer({
|
||||||
renderers: {
|
renderers: {
|
||||||
text: this.renderText,
|
text: renderText,
|
||||||
|
|
||||||
emph: Renderer.forwardChildren,
|
emph: Renderer.forwardChildren,
|
||||||
strong: Renderer.forwardChildren,
|
strong: Renderer.forwardChildren,
|
||||||
del: Renderer.forwardChildren,
|
del: Renderer.forwardChildren,
|
||||||
code: this.renderCodeInline,
|
code: renderCodeInline,
|
||||||
link: this.renderLink,
|
link: renderLink,
|
||||||
image: this.renderImage,
|
image: renderImage,
|
||||||
atMention: this.renderAtMention,
|
atMention: renderAtMention,
|
||||||
emoji: this.renderEmoji,
|
emoji: renderEmoji,
|
||||||
hashtag: this.renderHashtag,
|
hashtag: renderHashtag,
|
||||||
|
|
||||||
paragraph: this.renderParagraph,
|
paragraph: renderParagraph,
|
||||||
heading: this.renderHeading,
|
heading: renderHeading,
|
||||||
codeBlock: this.renderCodeBlock,
|
codeBlock: renderCodeBlock,
|
||||||
blockQuote: this.renderBlockQuote,
|
blockQuote: renderBlockQuote,
|
||||||
|
|
||||||
list: this.renderList,
|
list: renderList,
|
||||||
item: this.renderListItem,
|
item: renderListItem,
|
||||||
|
|
||||||
hardBreak: this.renderBreak,
|
hardBreak: renderBreak,
|
||||||
thematicBreak: this.renderBreak,
|
thematicBreak: renderBreak,
|
||||||
softBreak: this.renderBreak,
|
softBreak: renderBreak,
|
||||||
|
|
||||||
htmlBlock: this.renderText,
|
htmlBlock: renderText,
|
||||||
htmlInline: this.renderText,
|
htmlInline: renderText,
|
||||||
|
|
||||||
table: this.renderTable,
|
table: renderTable,
|
||||||
table_row: this.renderTableRow,
|
table_row: renderTableRow,
|
||||||
table_cell: this.renderTableCell
|
table_cell: renderTableCell
|
||||||
},
|
},
|
||||||
renderParagraphsInLists: true
|
renderParagraphsInLists: true
|
||||||
});
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
get isNewMarkdown(): boolean {
|
useEffect(() => {
|
||||||
const { md, enableMessageParser } = this.props;
|
if (!isNewMarkdown() && msg) {
|
||||||
return !!enableMessageParser && !!md;
|
renderer.current = createRenderer();
|
||||||
|
forceUpdate();
|
||||||
|
}
|
||||||
|
}, [createRenderer, isNewMarkdown, msg]);
|
||||||
|
|
||||||
|
if (!msg) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderText = ({ context, literal }: { context: []; literal: string }) => {
|
const renderText = ({ context, literal }: { context: []; literal: string }) => {
|
||||||
const { numberOfLines, style = [] } = this.props;
|
const defaultStyle = [isMessageContainsOnlyEmoji.current ? styles.textBig : {}, ...context.map(type => styles[type])];
|
||||||
const defaultStyle = [this.isMessageContainsOnlyEmoji ? styles.textBig : {}, ...context.map(type => styles[type])];
|
|
||||||
return (
|
return (
|
||||||
<Text accessibilityLabel={literal} style={[styles.text, defaultStyle, ...style]} numberOfLines={numberOfLines}>
|
<Text accessibilityLabel={literal} style={[styles.text, defaultStyle, ...(style || [])]} numberOfLines={numberOfLines}>
|
||||||
{literal}
|
{literal}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderCodeInline = ({ literal }: TLiteral) => {
|
const renderCodeInline = ({ literal }: TLiteral) => (
|
||||||
const { theme, style = [] } = this.props;
|
|
||||||
return (
|
|
||||||
<Text
|
<Text
|
||||||
|
testID={`${markdownTestID}-code-in-line`}
|
||||||
style={[
|
style={[
|
||||||
{
|
{
|
||||||
...styles.codeInline,
|
...styles.codeInline,
|
||||||
color: themes[theme].bodyText,
|
color: colors.bodyText,
|
||||||
backgroundColor: themes[theme].bannerBackground,
|
backgroundColor: colors.bannerBackground,
|
||||||
borderColor: themes[theme].bannerBackground
|
borderColor: colors.bannerBackground
|
||||||
},
|
},
|
||||||
...style
|
...(style || [])
|
||||||
]}>
|
]}>
|
||||||
{literal}
|
{literal}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
renderCodeBlock = ({ literal }: TLiteral) => {
|
const renderCodeBlock = ({ literal }: TLiteral) => (
|
||||||
const { theme, style = [] } = this.props;
|
|
||||||
return (
|
|
||||||
<Text
|
<Text
|
||||||
|
testID={`${markdownTestID}-code-block`}
|
||||||
style={[
|
style={[
|
||||||
{
|
{
|
||||||
...styles.codeBlock,
|
...styles.codeBlock,
|
||||||
color: themes[theme].bodyText,
|
color: colors.bodyText,
|
||||||
backgroundColor: themes[theme].bannerBackground,
|
backgroundColor: colors.bannerBackground,
|
||||||
borderColor: themes[theme].bannerBackground
|
borderColor: colors.bannerBackground
|
||||||
},
|
},
|
||||||
...style
|
...(style || [])
|
||||||
]}>
|
]}>
|
||||||
{literal}
|
{literal}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
renderBreak = () => {
|
const renderBreak = () => <Text>{tmid ? ' ' : '\n'}</Text>;
|
||||||
const { tmid } = this.props;
|
|
||||||
return <Text>{tmid ? ' ' : '\n'}</Text>;
|
|
||||||
};
|
|
||||||
|
|
||||||
renderParagraph = ({ children }: any) => {
|
const renderParagraph = ({ children }: { children: React.ReactElement[] }) => {
|
||||||
const { numberOfLines, style, theme } = this.props;
|
|
||||||
if (!children || children.length === 0) {
|
if (!children || children.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Text style={[styles.text, style, { color: themes[theme].bodyText }]} numberOfLines={numberOfLines}>
|
<Text style={[styles.text, style, { color: colors.bodyText }]} numberOfLines={numberOfLines}>
|
||||||
{children}
|
{children}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderLink = ({ children, href }: any) => {
|
const renderLink = ({ children, href }: { children: React.ReactElement | null; href: string }) => (
|
||||||
const { theme, onLinkPress } = this.props;
|
<MarkdownLink link={href} onLinkPress={onLinkPress} testID={markdownTestID}>
|
||||||
return (
|
|
||||||
<MarkdownLink link={href} theme={theme} onLinkPress={onLinkPress}>
|
|
||||||
{children}
|
{children}
|
||||||
</MarkdownLink>
|
</MarkdownLink>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
renderHashtag = ({ hashtag }: { hashtag: string }) => {
|
const renderHashtag = ({ hashtag }: { hashtag: string }) => (
|
||||||
const { channels, navToRoomInfo, style } = this.props;
|
<MarkdownHashtag hashtag={hashtag} channels={channels} navToRoomInfo={navToRoomInfo} style={style} testID={markdownTestID} />
|
||||||
return <MarkdownHashtag hashtag={hashtag} channels={channels} navToRoomInfo={navToRoomInfo} style={style} />;
|
);
|
||||||
};
|
|
||||||
|
|
||||||
renderAtMention = ({ mentionName }: { mentionName: string }) => {
|
const renderAtMention = ({ mentionName }: { mentionName: string }) => (
|
||||||
const { username = '', mentions, navToRoomInfo, useRealName, style } = this.props;
|
|
||||||
return (
|
|
||||||
<MarkdownAtMention
|
<MarkdownAtMention
|
||||||
mentions={mentions}
|
mentions={mentions}
|
||||||
mention={mentionName}
|
mention={mentionName}
|
||||||
|
@ -228,110 +232,81 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
username={username}
|
username={username}
|
||||||
navToRoomInfo={navToRoomInfo}
|
navToRoomInfo={navToRoomInfo}
|
||||||
style={style}
|
style={style}
|
||||||
|
testID={markdownTestID}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
renderEmoji = ({ literal }: TLiteral) => {
|
const renderEmoji = ({ literal }: TLiteral) => (
|
||||||
const { getCustomEmoji, baseUrl = '', customEmojis, style } = this.props;
|
|
||||||
return (
|
|
||||||
<MarkdownEmoji
|
<MarkdownEmoji
|
||||||
literal={literal}
|
literal={literal}
|
||||||
isMessageContainsOnlyEmoji={this.isMessageContainsOnlyEmoji}
|
isMessageContainsOnlyEmoji={isMessageContainsOnlyEmoji.current}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl || ''}
|
||||||
customEmojis={customEmojis}
|
customEmojis={customEmojis}
|
||||||
style={style}
|
style={style}
|
||||||
|
testID={markdownTestID}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
renderImage = ({ src }: { src: string }) => {
|
const renderImage = ({ src }: { src: string }) => {
|
||||||
if (!isValidURL(src)) {
|
if (!isValidURL(src)) {
|
||||||
return null;
|
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 renderHeading = ({ children, level }: { children: React.ReactElement; level: string }) => {
|
||||||
const { numberOfLines, theme } = this.props;
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const textStyle = styles[`heading${level}Text`];
|
const textStyle = styles[`heading${level}Text`];
|
||||||
return (
|
return (
|
||||||
<Text numberOfLines={numberOfLines} style={[textStyle, { color: themes[theme].bodyText }]}>
|
<Text testID={`${markdownTestID}-header`} numberOfLines={numberOfLines} style={[textStyle, { color: colors.bodyText }]}>
|
||||||
{children}
|
{children}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderList = ({ children, start, tight, type }: any) => {
|
const renderList = ({ children, start, tight, type }: any) => (
|
||||||
const { numberOfLines } = this.props;
|
|
||||||
return (
|
|
||||||
<MarkdownList ordered={type !== 'bullet'} start={start} tight={tight} numberOfLines={numberOfLines}>
|
<MarkdownList ordered={type !== 'bullet'} start={start} tight={tight} numberOfLines={numberOfLines}>
|
||||||
{children}
|
{children}
|
||||||
</MarkdownList>
|
</MarkdownList>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
renderListItem = ({ children, context, ...otherProps }: any) => {
|
const renderListItem = ({ children, context, ...otherProps }: any) => {
|
||||||
const { theme } = this.props;
|
|
||||||
const level = context.filter((type: string) => type === 'list').length;
|
const level = context.filter((type: string) => type === 'list').length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MarkdownListItem level={level} theme={theme} {...otherProps}>
|
<MarkdownListItem level={level} {...otherProps}>
|
||||||
{children}
|
{children}
|
||||||
</MarkdownListItem>
|
</MarkdownListItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderBlockQuote = ({ children }: { children: JSX.Element }) => {
|
const renderBlockQuote = ({ children }: { children: React.ReactElement }) => (
|
||||||
const { theme } = this.props;
|
<MarkdownBlockQuote>{children}</MarkdownBlockQuote>
|
||||||
return <MarkdownBlockQuote theme={theme}>{children}</MarkdownBlockQuote>;
|
);
|
||||||
};
|
|
||||||
|
|
||||||
renderTable = ({ children, numColumns }: { children: JSX.Element; numColumns: number }) => {
|
const renderTable = ({ children, numColumns }: { children: React.ReactElement; numColumns: number }) => (
|
||||||
const { theme } = this.props;
|
<MarkdownTable numColumns={numColumns} testID={markdownTestID}>
|
||||||
return (
|
|
||||||
<MarkdownTable numColumns={numColumns} theme={theme}>
|
|
||||||
{children}
|
{children}
|
||||||
</MarkdownTable>
|
</MarkdownTable>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
renderTableRow = (args: any) => {
|
const renderTableRow = ({ children, isLastRow }: ITableRow) => (
|
||||||
const { theme } = this.props;
|
<MarkdownTableRow isLastRow={isLastRow}>{children}</MarkdownTableRow>
|
||||||
return <MarkdownTableRow {...args} theme={theme} />;
|
);
|
||||||
};
|
|
||||||
|
|
||||||
renderTableCell = (args: any) => {
|
const renderTableCell = ({ align, children, isLastCell }: ITableCell) => (
|
||||||
const { theme } = this.props;
|
<MarkdownTableCell align={align} isLastCell={isLastCell}>
|
||||||
return <MarkdownTableCell {...args} theme={theme} />;
|
{children}
|
||||||
};
|
</MarkdownTableCell>
|
||||||
|
);
|
||||||
|
|
||||||
render() {
|
if (isNewMarkdown()) {
|
||||||
const {
|
|
||||||
msg,
|
|
||||||
md,
|
|
||||||
mentions,
|
|
||||||
channels,
|
|
||||||
navToRoomInfo,
|
|
||||||
useRealName,
|
|
||||||
username = '',
|
|
||||||
getCustomEmoji,
|
|
||||||
baseUrl = '',
|
|
||||||
onLinkPress
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!msg) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isNewMarkdown) {
|
|
||||||
return (
|
return (
|
||||||
<NewMarkdown
|
<NewMarkdown
|
||||||
username={username}
|
username={username || ''}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl || ''}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
useRealName={useRealName}
|
useRealName={useRealName}
|
||||||
tokens={md}
|
tokens={md}
|
||||||
|
@ -343,13 +318,9 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let m = formatText(msg);
|
const formattedMessage = formatHyperlink(formatText(msg));
|
||||||
m = formatHyperlink(m);
|
const ast = mergeTextNodes(parser.parse(formattedMessage));
|
||||||
let ast = parser.parse(m);
|
isMessageContainsOnlyEmoji.current = isOnlyEmoji(formattedMessage) && emojiCount(formattedMessage) <= 3;
|
||||||
ast = mergeTextNodes(ast);
|
return renderer?.current?.render(ast) || null;
|
||||||
this.isMessageContainsOnlyEmoji = isOnlyEmoji(m) && emojiCount(m) <= 3;
|
};
|
||||||
return this.renderer.render(ast);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Markdown;
|
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 { Code as CodeProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import { themes } from '../../../lib/constants';
|
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
import CodeLine from './CodeLine';
|
import CodeLine from './CodeLine';
|
||||||
|
|
||||||
|
@ -12,16 +11,16 @@ interface ICodeProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Code = ({ value }: ICodeProps) => {
|
const Code = ({ value }: ICodeProps) => {
|
||||||
const { theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.codeBlock,
|
styles.codeBlock,
|
||||||
{
|
{
|
||||||
color: themes[theme].bodyText,
|
color: colors.bodyText,
|
||||||
backgroundColor: themes[theme].bannerBackground,
|
backgroundColor: colors.bannerBackground,
|
||||||
borderColor: themes[theme].borderColor
|
borderColor: colors.borderColor
|
||||||
}
|
}
|
||||||
]}>
|
]}>
|
||||||
{value.map(block => {
|
{value.map(block => {
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { Text } from 'react-native';
|
||||||
import { Emoji as EmojiProps } from '@rocket.chat/message-parser';
|
import { Emoji as EmojiProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
import shortnameToUnicode from '../../../lib/methods/helpers/shortnameToUnicode';
|
import shortnameToUnicode from '../../../lib/methods/helpers/shortnameToUnicode';
|
||||||
import { themes } from '../../../lib/constants';
|
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
|
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
|
||||||
|
@ -15,7 +14,7 @@ interface IEmojiProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Emoji = ({ value, isBigEmoji }: IEmojiProps) => {
|
const Emoji = ({ value, isBigEmoji }: IEmojiProps) => {
|
||||||
const { theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
const { baseUrl, getCustomEmoji } = useContext(MarkdownContext);
|
const { baseUrl, getCustomEmoji } = useContext(MarkdownContext);
|
||||||
const emojiUnicode = shortnameToUnicode(`:${value.value}:`);
|
const emojiUnicode = shortnameToUnicode(`:${value.value}:`);
|
||||||
const emoji = getCustomEmoji?.(value.value);
|
const emoji = getCustomEmoji?.(value.value);
|
||||||
|
@ -23,7 +22,7 @@ const Emoji = ({ value, isBigEmoji }: IEmojiProps) => {
|
||||||
if (emoji) {
|
if (emoji) {
|
||||||
return <CustomEmoji baseUrl={baseUrl} style={[isBigEmoji ? styles.customEmojiBig : styles.customEmoji]} emoji={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;
|
export default Emoji;
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||||
import { Text } from 'react-native';
|
import { Text } from 'react-native';
|
||||||
import { Heading as HeadingProps } from '@rocket.chat/message-parser';
|
import { Heading as HeadingProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
import { themes } from '../../../lib/constants';
|
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
|
@ -12,11 +11,11 @@ interface IHeadingProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Heading = ({ value, level }: IHeadingProps) => {
|
const Heading = ({ value, level }: IHeadingProps) => {
|
||||||
const { theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
const textStyle = styles[`heading${level}`];
|
const textStyle = styles[`heading${level}`];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text style={[textStyle, { color: themes[theme].bodyText }]}>
|
<Text style={[textStyle, { color: colors.bodyText }]}>
|
||||||
{value.map(block => {
|
{value.map(block => {
|
||||||
switch (block.type) {
|
switch (block.type) {
|
||||||
case 'PLAIN_TEXT':
|
case 'PLAIN_TEXT':
|
||||||
|
|
|
@ -4,38 +4,30 @@ import { createImageProgress } from 'react-native-image-progress';
|
||||||
import * as Progress from 'react-native-progress';
|
import * as Progress from 'react-native-progress';
|
||||||
import FastImage from 'react-native-fast-image';
|
import FastImage from 'react-native-fast-image';
|
||||||
|
|
||||||
import { TSupportedThemes, useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
import { themes } from '../../../lib/constants';
|
|
||||||
import styles from '../../message/styles';
|
import styles from '../../message/styles';
|
||||||
|
|
||||||
interface IImageProps {
|
|
||||||
value: ImageProps['value'];
|
|
||||||
}
|
|
||||||
|
|
||||||
type TMessageImage = {
|
|
||||||
img: string;
|
|
||||||
theme: TSupportedThemes;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ImageProgress = createImageProgress(FastImage);
|
const ImageProgress = createImageProgress(FastImage);
|
||||||
|
|
||||||
const MessageImage = ({ img, theme }: TMessageImage) => (
|
const MessageImage = ({ img }: { img: string }) => {
|
||||||
|
const { colors } = useTheme();
|
||||||
|
return (
|
||||||
<ImageProgress
|
<ImageProgress
|
||||||
style={[styles.inlineImage, { borderColor: themes[theme].borderColor }]}
|
style={[styles.inlineImage, { borderColor: colors.borderColor }]}
|
||||||
source={{ uri: encodeURI(img) }}
|
source={{ uri: encodeURI(img) }}
|
||||||
resizeMode={FastImage.resizeMode.cover}
|
resizeMode={FastImage.resizeMode.cover}
|
||||||
indicator={Progress.Pie}
|
indicator={Progress.Pie}
|
||||||
indicatorProps={{
|
indicatorProps={{
|
||||||
color: themes[theme].actionTintColor
|
color: colors.actionTintColor
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const Image = ({ value }: IImageProps) => {
|
const Image = ({ value }: { value: ImageProps['value'] }) => {
|
||||||
const { theme } = useTheme();
|
|
||||||
const { src } = value;
|
const { src } = value;
|
||||||
|
|
||||||
return <MessageImage img={src.value} theme={theme} />;
|
return <MessageImage img={src.value} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Image;
|
export default Image;
|
||||||
|
|
|
@ -45,12 +45,15 @@ const Inline = ({ value }: IParagraphProps) => {
|
||||||
username={username}
|
username={username}
|
||||||
navToRoomInfo={navToRoomInfo}
|
navToRoomInfo={navToRoomInfo}
|
||||||
mentions={mentions}
|
mentions={mentions}
|
||||||
|
testID='new-markdown'
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'EMOJI':
|
case 'EMOJI':
|
||||||
return <Emoji value={block.value} />;
|
return <Emoji value={block.value} />;
|
||||||
case 'MENTION_CHANNEL':
|
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':
|
case 'INLINE_CODE':
|
||||||
return <InlineCode value={block.value} />;
|
return <InlineCode value={block.value} />;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { Text } from 'react-native';
|
||||||
import { InlineCode as InlineCodeProps } from '@rocket.chat/message-parser';
|
import { InlineCode as InlineCodeProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import { themes } from '../../../lib/constants';
|
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
interface IInlineCodeProps {
|
interface IInlineCodeProps {
|
||||||
|
@ -11,16 +10,16 @@ interface IInlineCodeProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const InlineCode = ({ value }: IInlineCodeProps) => {
|
const InlineCode = ({ value }: IInlineCodeProps) => {
|
||||||
const { theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.codeInline,
|
styles.codeInline,
|
||||||
{
|
{
|
||||||
color: themes[theme].bodyText,
|
color: colors.bodyText,
|
||||||
backgroundColor: themes[theme].bannerBackground,
|
backgroundColor: colors.bannerBackground,
|
||||||
borderColor: themes[theme].borderColor
|
borderColor: colors.borderColor
|
||||||
}
|
}
|
||||||
]}>
|
]}>
|
||||||
{(block => {
|
{(block => {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { LISTENER } from '../../Toast';
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
import openLink from '../../../lib/methods/helpers/openLink';
|
import openLink from '../../../lib/methods/helpers/openLink';
|
||||||
import EventEmitter from '../../../lib/methods/helpers/events';
|
import EventEmitter from '../../../lib/methods/helpers/events';
|
||||||
import { themes } from '../../../lib/constants';
|
|
||||||
import Strike from './Strike';
|
import Strike from './Strike';
|
||||||
import Italic from './Italic';
|
import Italic from './Italic';
|
||||||
import Bold from './Bold';
|
import Bold from './Bold';
|
||||||
|
@ -20,7 +19,7 @@ interface ILinkProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Link = ({ value }: ILinkProps) => {
|
const Link = ({ value }: ILinkProps) => {
|
||||||
const { theme } = useTheme();
|
const { theme, colors } = useTheme();
|
||||||
const { onLinkPress } = useContext(MarkdownContext);
|
const { onLinkPress } = useContext(MarkdownContext);
|
||||||
const { src, label } = value;
|
const { src, label } = value;
|
||||||
const handlePress = () => {
|
const handlePress = () => {
|
||||||
|
@ -39,7 +38,7 @@ const Link = ({ value }: ILinkProps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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 => {
|
{(block => {
|
||||||
switch (block.type) {
|
switch (block.type) {
|
||||||
case 'PLAIN_TEXT':
|
case 'PLAIN_TEXT':
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { OrderedList as OrderedListProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
import Inline from './Inline';
|
import Inline from './Inline';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import { themes } from '../../../lib/constants';
|
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
interface IOrderedListProps {
|
interface IOrderedListProps {
|
||||||
|
@ -12,12 +11,12 @@ interface IOrderedListProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrderedList = ({ value }: IOrderedListProps) => {
|
const OrderedList = ({ value }: IOrderedListProps) => {
|
||||||
const { theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
{value.map((item, index) => (
|
{value.map((item, index) => (
|
||||||
<View style={styles.row}>
|
<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} />
|
<Inline value={item.value} />
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -5,16 +5,15 @@ import { Paragraph as ParagraphProps } from '@rocket.chat/message-parser';
|
||||||
import Inline from './Inline';
|
import Inline from './Inline';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
import { themes } from '../../../lib/constants';
|
|
||||||
|
|
||||||
interface IParagraphProps {
|
interface IParagraphProps {
|
||||||
value: ParagraphProps['value'];
|
value: ParagraphProps['value'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Paragraph = ({ value }: IParagraphProps) => {
|
const Paragraph = ({ value }: IParagraphProps) => {
|
||||||
const { theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
return (
|
return (
|
||||||
<Text style={[styles.text, { color: themes[theme].bodyText }]}>
|
<Text style={[styles.text, { color: colors.bodyText }]}>
|
||||||
<Inline value={value} />
|
<Inline value={value} />
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,16 +4,15 @@ import { Plain as PlainProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
import { themes } from '../../../lib/constants';
|
|
||||||
|
|
||||||
interface IPlainProps {
|
interface IPlainProps {
|
||||||
value: PlainProps['value'];
|
value: PlainProps['value'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Plain = ({ value }: IPlainProps) => {
|
const Plain = ({ value }: IPlainProps) => {
|
||||||
const { theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
return (
|
return (
|
||||||
<Text accessibilityLabel={value} style={[styles.plainText, { color: themes[theme].bodyText }]}>
|
<Text accessibilityLabel={value} style={[styles.plainText, { color: colors.bodyText }]}>
|
||||||
{value}
|
{value}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import { Quote as QuoteProps } from '@rocket.chat/message-parser';
|
import { Quote as QuoteProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
import { themes } from '../../../lib/constants';
|
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import Paragraph from './Paragraph';
|
import Paragraph from './Paragraph';
|
||||||
|
@ -12,10 +11,10 @@ interface IQuoteProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Quote = ({ value }: IQuoteProps) => {
|
const Quote = ({ value }: IQuoteProps) => {
|
||||||
const { theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<View style={[styles.quote, { backgroundColor: themes[theme].borderColor }]} />
|
<View style={[styles.quote, { backgroundColor: colors.borderColor }]} />
|
||||||
<View style={styles.childContainer}>
|
<View style={styles.childContainer}>
|
||||||
{value.map(item => (
|
{value.map(item => (
|
||||||
<Paragraph value={item.value} />
|
<Paragraph value={item.value} />
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { Tasks as TasksProps } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
import Inline from './Inline';
|
import Inline from './Inline';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import { themes } from '../../../lib/constants';
|
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
interface ITasksProps {
|
interface ITasksProps {
|
||||||
|
@ -12,12 +11,12 @@ interface ITasksProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const TaskList = ({ value = [] }: ITasksProps) => {
|
const TaskList = ({ value = [] }: ITasksProps) => {
|
||||||
const { theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
{value.map(item => (
|
{value.map(item => (
|
||||||
<View style={styles.row}>
|
<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} />
|
<Inline value={item.value} />
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { View, Text } from 'react-native';
|
||||||
|
|
||||||
import Inline from './Inline';
|
import Inline from './Inline';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import { themes } from '../../../lib/constants';
|
|
||||||
import { useTheme } from '../../../theme';
|
import { useTheme } from '../../../theme';
|
||||||
|
|
||||||
interface IUnorderedListProps {
|
interface IUnorderedListProps {
|
||||||
|
@ -12,12 +11,12 @@ interface IUnorderedListProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const UnorderedList = ({ value }: IUnorderedListProps) => {
|
const UnorderedList = ({ value }: IUnorderedListProps) => {
|
||||||
const { theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
{value.map(item => (
|
{value.map(item => (
|
||||||
<View style={styles.row}>
|
<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} />
|
<Inline value={item.value} />
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -280,7 +280,6 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
|
|
|
@ -100,7 +100,6 @@ const Fields = React.memo(
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
theme={theme}
|
|
||||||
style={[styles.markdownFontSize]}
|
style={[styles.markdownFontSize]}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -63,7 +63,6 @@ const Content = React.memo(
|
||||||
navToRoomInfo={props.navToRoomInfo}
|
navToRoomInfo={props.navToRoomInfo}
|
||||||
tmid={props.tmid}
|
tmid={props.tmid}
|
||||||
useRealName={props.useRealName}
|
useRealName={props.useRealName}
|
||||||
theme={theme}
|
|
||||||
onLinkPress={onLinkPress}
|
onLinkPress={onLinkPress}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -83,7 +83,6 @@ const ImageContainer = React.memo(
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
<MessageImage imgUri={img} theme={theme} />
|
<MessageImage imgUri={img} theme={theme} />
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -131,7 +131,6 @@ const Description = React.memo(
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -184,13 +183,7 @@ const Fields = React.memo(
|
||||||
{attachment.fields.map(field => (
|
{attachment.fields.map(field => (
|
||||||
<View key={field.title} style={[styles.fieldContainer, { width: field.short ? '50%' : '100%' }]}>
|
<View key={field.title} style={[styles.fieldContainer, { width: field.short ? '50%' : '100%' }]}>
|
||||||
<Text style={[styles.fieldTitle, { color: themes[theme].bodyText }]}>{field.title}</Text>
|
<Text style={[styles.fieldTitle, { color: themes[theme].bodyText }]}>{field.title}</Text>
|
||||||
<Markdown
|
<Markdown msg={field?.value || ''} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} />
|
||||||
msg={field?.value || ''}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
username={user.username}
|
|
||||||
getCustomEmoji={getCustomEmoji}
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
@ -271,13 +264,7 @@ const Reply = React.memo(
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
</Touchable>
|
</Touchable>
|
||||||
<Markdown
|
<Markdown msg={attachment.description} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} />
|
||||||
msg={attachment.description}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
username={user.username}
|
|
||||||
getCustomEmoji={getCustomEmoji}
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -83,7 +83,6 @@ const Video = React.memo(
|
||||||
username={user.username}
|
username={user.username}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
style={[isReply && style]}
|
style={[isReply && style]}
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
<Touchable
|
<Touchable
|
||||||
disabled={isReply}
|
disabled={isReply}
|
||||||
|
|
|
@ -18,6 +18,7 @@ export interface ICustomEmoji {
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
emoji: IEmoji;
|
emoji: IEmoji;
|
||||||
style: StyleProp<ImageStyle>;
|
style: StyleProp<ImageStyle>;
|
||||||
|
testID?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICustomEmojiModel {
|
export interface ICustomEmojiModel {
|
||||||
|
|
|
@ -84,7 +84,7 @@ const Item = ({ label, content, theme, testID }: IItem) =>
|
||||||
<Text accessibilityLabel={label} style={[styles.itemLabel, { color: themes[theme].titleText }]}>
|
<Text accessibilityLabel={label} style={[styles.itemLabel, { color: themes[theme].titleText }]}>
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
<Markdown style={[styles.itemContent, { color: themes[theme].auxiliaryText }]} msg={content} theme={theme} />
|
<Markdown style={[styles.itemContent, { color: themes[theme].auxiliaryText }]} msg={content} />
|
||||||
</View>
|
</View>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
|
|
@ -40,10 +40,10 @@ class E2EHowItWorksView extends React.Component<TE2EHowItWorksViewProps, any> {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]} testID='e2e-how-it-works-view'>
|
<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_info1')} style={infoStyle} />
|
||||||
<Markdown msg={I18n.t('E2E_How_It_Works_info2')} style={infoStyle} theme={theme} />
|
<Markdown msg={I18n.t('E2E_How_It_Works_info2')} style={infoStyle} />
|
||||||
<Markdown msg={I18n.t('E2E_How_It_Works_info3')} style={infoStyle} theme={theme} />
|
<Markdown msg={I18n.t('E2E_How_It_Works_info3')} style={infoStyle} />
|
||||||
<Markdown msg={I18n.t('E2E_How_It_Works_info4')} style={infoStyle} theme={theme} />
|
<Markdown msg={I18n.t('E2E_How_It_Works_info4')} style={infoStyle} />
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ const InviteUsersView = ({ route, navigation }: IInviteUsersViewProps): React.Re
|
||||||
const rid = route.params?.rid;
|
const rid = route.params?.rid;
|
||||||
const timeDateFormat = useSelector((state: IApplicationState) => state.settings.Message_TimeAndDateFormat as string);
|
const timeDateFormat = useSelector((state: IApplicationState) => state.settings.Message_TimeAndDateFormat as string);
|
||||||
const invite = useSelector((state: IApplicationState) => state.inviteLinks.invite);
|
const invite = useSelector((state: IApplicationState) => state.inviteLinks.invite);
|
||||||
const { colors, theme } = useTheme();
|
const { colors } = useTheme();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -81,7 +81,7 @@ const InviteUsersView = ({ route, navigation }: IInviteUsersViewProps): React.Re
|
||||||
|
|
||||||
const renderExpiration = () => {
|
const renderExpiration = () => {
|
||||||
const expirationMessage = linkExpirationText();
|
const expirationMessage = linkExpirationText();
|
||||||
return <Markdown msg={expirationMessage} theme={theme} />;
|
return <Markdown msg={expirationMessage} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -24,7 +24,7 @@ const Item = ({ label, content, testID }: IItem) => {
|
||||||
<Text accessibilityLabel={label} style={[styles.itemLabel, { color: themes[theme].titleText }]}>
|
<Text accessibilityLabel={label} style={[styles.itemLabel, { color: themes[theme].titleText }]}>
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
<Markdown style={[styles.itemContent, { color: themes[theme].auxiliaryText }]} msg={content} theme={theme} />
|
<Markdown style={[styles.itemContent, { color: themes[theme].auxiliaryText }]} msg={content} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -45,7 +45,7 @@ const Banner = React.memo(
|
||||||
<View style={[styles.modalView, { backgroundColor: themes[theme].bannerBackground }]}>
|
<View style={[styles.modalView, { backgroundColor: themes[theme].bannerBackground }]}>
|
||||||
<Text style={[styles.bannerModalTitle, { color: themes[theme].auxiliaryText }]}>{title}</Text>
|
<Text style={[styles.bannerModalTitle, { color: themes[theme].auxiliaryText }]}>{title}</Text>
|
||||||
<ScrollView style={styles.modalScrollView}>
|
<ScrollView style={styles.modalScrollView}>
|
||||||
<Markdown msg={text} theme={theme} />
|
<Markdown msg={text} />
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</View>
|
</View>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { storiesOf } from '@storybook/react-native';
|
||||||
import { longText } from '../../../../storybook/utils';
|
import { longText } from '../../../../storybook/utils';
|
||||||
import { ThemeContext } from '../../../theme';
|
import { ThemeContext } from '../../../theme';
|
||||||
import { Message, MessageDecorator, StoryProvider } from '../../../../storybook/stories/Message';
|
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';
|
import LoadMore from './index';
|
||||||
|
|
||||||
const stories = storiesOf('LoadMore', module);
|
const stories = storiesOf('LoadMore', module);
|
||||||
|
@ -24,7 +24,7 @@ stories.add('basic', () => (
|
||||||
));
|
));
|
||||||
|
|
||||||
const ThemeStory = ({ theme }) => (
|
const ThemeStory = ({ theme }) => (
|
||||||
<ThemeContext.Provider value={{ theme }}>
|
<ThemeContext.Provider value={{ theme, colors: colors[theme] }}>
|
||||||
<ScrollView style={{ backgroundColor: themes[theme].backgroundColor }}>
|
<ScrollView style={{ backgroundColor: themes[theme].backgroundColor }}>
|
||||||
<LoadMore load={load} type={MessageTypeLoad.PREVIOUS_CHUNK} />
|
<LoadMore load={load} type={MessageTypeLoad.PREVIOUS_CHUNK} />
|
||||||
<Message msg='Hey!' theme={theme} />
|
<Message msg='Hey!' theme={theme} />
|
||||||
|
|
|
@ -334,7 +334,7 @@ class SearchMessagesView extends React.Component<ISearchMessagesViewProps, ISear
|
||||||
placeholder={I18n.t('Search_Messages')}
|
placeholder={I18n.t('Search_Messages')}
|
||||||
testID='search-message-view-input'
|
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 style={[styles.divider, { backgroundColor: themes[theme].separatorColor }]} />
|
||||||
</View>
|
</View>
|
||||||
{this.renderList()}
|
{this.renderList()}
|
||||||
|
|
|
@ -48,11 +48,11 @@ const stories = storiesOf('Markdown', module).addDecorator(story => <Provider st
|
||||||
|
|
||||||
stories.add('Text', () => (
|
stories.add('Text', () => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Markdown msg='This is Rocket.Chat' theme={theme} />
|
<Markdown msg='This is Rocket.Chat' />
|
||||||
<Markdown msg={longText} theme={theme} />
|
<Markdown msg={longText} />
|
||||||
<Markdown msg={lineBreakText} theme={theme} />
|
<Markdown msg={lineBreakText} />
|
||||||
<Markdown msg={sequentialEmptySpacesText} theme={theme} />
|
<Markdown msg={sequentialEmptySpacesText} />
|
||||||
<Markdown msg='Strong emphasis, aka bold, with **asterisks** or __underscores__' theme={theme} />
|
<Markdown msg='Strong emphasis, aka bold, with **asterisks** or __underscores__' />
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -71,7 +71,6 @@ stories.add('Mentions', () => (
|
||||||
<ScrollView style={styles.container}>
|
<ScrollView style={styles.container}>
|
||||||
<Markdown
|
<Markdown
|
||||||
msg='@rocket.cat @name1 @all @here @unknown'
|
msg='@rocket.cat @name1 @all @here @unknown'
|
||||||
theme={theme}
|
|
||||||
mentions={[
|
mentions={[
|
||||||
{ _id: 'random', name: 'Rocket Cat', username: 'rocket.cat' },
|
{ _id: 'random', name: 'Rocket Cat', username: 'rocket.cat' },
|
||||||
{ _id: 'random2', name: 'Name', username: 'name1' },
|
{ _id: 'random2', name: 'Name', username: 'name1' },
|
||||||
|
@ -82,7 +81,6 @@ stories.add('Mentions', () => (
|
||||||
/>
|
/>
|
||||||
<Markdown
|
<Markdown
|
||||||
msg='@rocket.cat @name1 @all @here @unknown'
|
msg='@rocket.cat @name1 @all @here @unknown'
|
||||||
theme={theme}
|
|
||||||
mentions={[
|
mentions={[
|
||||||
{ _id: 'random', name: 'Rocket Cat', username: 'rocket.cat' },
|
{ _id: 'random', name: 'Rocket Cat', username: 'rocket.cat' },
|
||||||
{ _id: 'random2', name: 'Name', username: 'name1' },
|
{ _id: 'random2', name: 'Name', username: 'name1' },
|
||||||
|
@ -97,21 +95,16 @@ stories.add('Mentions', () => (
|
||||||
|
|
||||||
stories.add('Hashtag', () => (
|
stories.add('Hashtag', () => (
|
||||||
<View style={styles.container}>
|
<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>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
stories.add('Emoji', () => (
|
stories.add('Emoji', () => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Markdown msg='Unicode: 😃😇👍' theme={theme} />
|
<Markdown msg='Unicode: 😃😇👍' />
|
||||||
<Markdown msg='Shortnames: :joy::+1:' theme={theme} />
|
<Markdown msg='Shortnames: :joy::+1:' />
|
||||||
<Markdown
|
<Markdown msg='Custom emojis: :react_rocket: :nyan_rocket: :marioparty:' getCustomEmoji={getCustomEmoji} baseUrl={baseUrl} />
|
||||||
msg='Custom emojis: :react_rocket: :nyan_rocket: :marioparty:'
|
<Markdown msg='😃 :+1: :marioparty:' getCustomEmoji={getCustomEmoji} baseUrl={baseUrl} />
|
||||||
theme={theme}
|
|
||||||
getCustomEmoji={getCustomEmoji}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
/>
|
|
||||||
<Markdown msg='😃 :+1: :marioparty:' theme={theme} getCustomEmoji={getCustomEmoji} baseUrl={baseUrl} />
|
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -120,52 +113,50 @@ stories.add('Block quote', () => (
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={`> This is block quote
|
msg={`> This is block quote
|
||||||
this is a normal line`}
|
this is a normal line`}
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
stories.add('Links', () => (
|
stories.add('Links', () => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Markdown msg='[Markdown link](https://rocket.chat): `[description](url)`' theme={theme} />
|
<Markdown msg='[Markdown link](https://rocket.chat): `[description](url)`' />
|
||||||
<Markdown msg='<https://rocket.chat|Formatted Link>: `<url|description>`' theme={theme} />
|
<Markdown msg='<https://rocket.chat|Formatted Link>: `<url|description>`' />
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
stories.add('Image', () => (
|
stories.add('Image', () => (
|
||||||
<View style={styles.container}>
|
<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>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
stories.add('Headers', () => (
|
stories.add('Headers', () => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Markdown msg='# Header 1' theme={theme} />
|
<Markdown msg='# Header 1' />
|
||||||
<Markdown msg='## Header 2' theme={theme} />
|
<Markdown msg='## Header 2' />
|
||||||
<Markdown msg='### Header 3' theme={theme} />
|
<Markdown msg='### Header 3' />
|
||||||
<Markdown msg='#### Header 4' theme={theme} />
|
<Markdown msg='#### Header 4' />
|
||||||
<Markdown msg='##### Header 5' theme={theme} />
|
<Markdown msg='##### Header 5' />
|
||||||
<Markdown msg='###### Header 6' theme={theme} />
|
<Markdown msg='###### Header 6' />
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
stories.add('Code', () => (
|
stories.add('Code', () => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Markdown msg='This is `inline code`' theme={theme} />
|
<Markdown msg='This is `inline code`' />
|
||||||
<Markdown
|
<Markdown
|
||||||
msg='Inline `code` has `back-ticks around` it.
|
msg='Inline `code` has `back-ticks around` it.
|
||||||
```
|
```
|
||||||
Code block
|
Code block
|
||||||
```'
|
```'
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
stories.add('Lists', () => (
|
stories.add('Lists', () => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Markdown msg={'* Open Source\n* Rocket.Chat\n - nodejs\n - ReactNative'} theme={theme} />
|
<Markdown msg={'* Open Source\n* Rocket.Chat\n - nodejs\n - ReactNative'} />
|
||||||
<Markdown msg={'1. Open Source\n2. Rocket.Chat'} theme={theme} />
|
<Markdown msg={'1. Open Source\n2. Rocket.Chat'} />
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -176,7 +167,6 @@ stories.add('Table', () => (
|
||||||
------------ | -------------
|
------------ | -------------
|
||||||
Content from cell 1 | Content from cell 2
|
Content from cell 1 | Content from cell 2
|
||||||
Content in the first column | Content in the second column'
|
Content in the first column | Content in the second column'
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</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 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}]}]}"`;
|
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