New markdown (#306)

Our current markdown is causing a lot of issues on Android devices, since it wraps everything inside a Text component.
On Android, Text doesn't support View as a child.
This PR adds react-native-markdown-renderer, that uses View as wrapper and may be better.
This commit is contained in:
Diego Mello 2018-05-29 14:09:20 -03:00 committed by Guilherme Gazzo
parent 061c313e3f
commit f61a57bb30
17 changed files with 309 additions and 229 deletions

View File

@ -268,7 +268,6 @@ workflows:
- ios-build: - ios-build:
requires: requires:
- lint-testunit - lint-testunit
- e2e-test
- ios-testflight: - ios-testflight:
requires: requires:
- ios-build - ios-build
@ -285,4 +284,3 @@ workflows:
- android-build: - android-build:
requires: requires:
- lint-testunit - lint-testunit
- e2e-test

View File

@ -160,9 +160,9 @@ exports[`render channel 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
/> />
@ -332,9 +332,9 @@ exports[`render no icon 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
/> />
@ -504,9 +504,9 @@ exports[`render private group 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
/> />
@ -744,9 +744,9 @@ exports[`render unread +999 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
> >
@ -1006,9 +1006,9 @@ exports[`render unread 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
> >
@ -1268,9 +1268,9 @@ exports[`renders correctly 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
/> />

View File

@ -551,9 +551,9 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
/> />
@ -789,9 +789,9 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
/> />
@ -1023,9 +1023,9 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
> >
@ -1284,9 +1284,9 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
> >
@ -1541,9 +1541,9 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
> >
@ -1798,9 +1798,9 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
> >
@ -2055,9 +2055,9 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
> >
@ -2312,9 +2312,9 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
> >
@ -2569,9 +2569,9 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
/> />
@ -2803,9 +2803,9 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
/> />
@ -3037,9 +3037,9 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = `
<View <View
style={ style={
Object { Object {
"alignItems": "flex-end", "alignItems": "center",
"flex": 1,
"flexDirection": "row", "flexDirection": "row",
"justifyContent": "flex-end",
} }
} }
/> />

View File

@ -1,98 +1,13 @@
import React from 'react'; import React from 'react';
import { Text, StyleSheet, ViewPropTypes } from 'react-native'; import { Text, Platform } from 'react-native';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import EasyMarkdown from 'react-native-easy-markdown'; // eslint-disable-line
import SimpleMarkdown from 'simple-markdown';
import { emojify } from 'react-emojione'; import { emojify } from 'react-emojione';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import MarkdownRenderer, { PluginContainer } from 'react-native-markdown-renderer';
import MarkdownFlowdock from 'markdown-it-flowdock';
import styles from './styles'; import styles from './styles';
import CustomEmoji from '../EmojiPicker/CustomEmoji'; import CustomEmoji from '../EmojiPicker/CustomEmoji';
import MarkdownEmojiPlugin from './MarkdownEmojiPlugin';
const BlockCode = ({ node, state }) => (
<Text
key={state.key}
style={styles.codeStyle}
>
{node.content}
</Text>
);
const mentionStyle = { color: '#13679a' };
const defaultRules = {
username: {
order: -1,
match: SimpleMarkdown.inlineRegex(/^@[0-9a-zA-Z-_.]+/),
parse: capture => ({ content: capture[0] }),
react: (node, output, state) => ({
type: 'custom',
key: state.key,
props: {
children: (
<Text
key={state.key}
style={mentionStyle}
onPress={() => alert('Username')}
>
{node.content}
</Text>
)
}
})
},
heading: {
order: -2,
match: SimpleMarkdown.inlineRegex(/^#[0-9a-zA-Z-_.]+/),
parse: capture => ({ content: capture[0] }),
react: (node, output, state) => ({
type: 'custom',
key: state.key,
props: {
children: (
<Text
key={state.key}
style={mentionStyle}
onPress={() => alert('Room')}
>
{node.content}
</Text>
)
}
})
},
fence: {
order: -3,
match: SimpleMarkdown.blockRegex(/^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n *)+\n/),
parse: capture => ({
lang: capture[2] || undefined,
content: capture[3]
}),
react: (node, output, state) => ({
type: 'custom',
key: state.key,
props: {
children: (
<BlockCode key={state.key} node={node} state={state} />
)
}
})
},
blockCode: {
order: -4,
match: SimpleMarkdown.blockRegex(/^(```)\s*([\s\S]*?[^`])\s*\1(?!```)/),
parse: capture => ({ content: capture[2] }),
react: (node, output, state) => ({
type: 'custom',
key: state.key,
props: {
children: (
<BlockCode key={state.key} node={node} state={state} />
)
}
})
}
};
const codeStyle = StyleSheet.flatten(styles.codeStyle);
// Support <http://link|Text> // Support <http://link|Text>
const formatText = text => const formatText = text =>
@ -110,49 +25,64 @@ export default class Markdown extends React.Component {
} }
render() { render() {
const { const {
msg, customEmojis = {}, style, markdownStyle, customRules, renderInline msg, customEmojis, style, rules
} = this.props; } = this.props;
if (!msg) { if (!msg) {
return null; return null;
} }
let m = formatText(msg); let m = formatText(msg);
m = emojify(m, { output: 'unicode' }); m = emojify(m, { output: 'unicode' });
const s = StyleSheet.flatten(style);
return ( return (
<EasyMarkdown <MarkdownRenderer
style={{ marginBottom: 0, ...s }}
markdownStyles={{ code: codeStyle, ...markdownStyle }}
rules={{ rules={{
customEmoji: { ...Platform.OS === 'android' ? {} : {
order: -5, paragraph: (node, children) => (
match: SimpleMarkdown.inlineRegex(/^:([0-9a-zA-Z-_.]+):/), <Text key={node.key} style={styles.paragraph}>
parse: capture => ({ content: capture }), {children}
react: (node, output, state) => { </Text>
const element = { )
type: 'custom', },
key: state.key, mention: node => (
props: { <Text key={node.key} onPress={() => alert(`Username @${ node.content }`)} style={styles.mention}>
children: <Text key={state.key}>{node.content[0]}</Text> @{node.content}
} </Text>
}; ),
const content = node.content[1]; hashtag: node => (
<Text key={node.key} onPress={() => alert(`Room #${ node.content }`)} style={styles.mention}>
#{node.content}
</Text>
),
emoji: (node) => {
if (node.children && node.children.length && node.children[0].children && node.children[0].children.length) {
const { content } = node.children[0].children[0];
const emojiExtension = customEmojis[content]; const emojiExtension = customEmojis[content];
if (emojiExtension) { if (emojiExtension) {
const emoji = { extension: emojiExtension, content }; const emoji = { extension: emojiExtension, content };
element.props.children = ( return <CustomEmoji key={node.key} style={styles.customEmoji} emoji={emoji} />;
<CustomEmoji key={state.key} style={styles.customEmoji} emoji={emoji} />
);
} }
return element; return <Text key={node.key}>:{content}:</Text>;
} }
return null;
}, },
...defaultRules, ...rules
...customRules
}} }}
renderInline={renderInline} style={{
paragraph: styles.paragraph,
codeInline: {
borderWidth: 1,
borderColor: '#CCCCCC',
backgroundColor: '#f5f5f5',
padding: 2,
borderRadius: 4
},
...style
}}
plugins={[
new PluginContainer(MarkdownFlowdock),
new PluginContainer(MarkdownEmojiPlugin)
]}
>{m} >{m}
</EasyMarkdown> </MarkdownRenderer>
); );
} }
} }
@ -160,14 +90,6 @@ export default class Markdown extends React.Component {
Markdown.propTypes = { Markdown.propTypes = {
msg: PropTypes.string, msg: PropTypes.string,
customEmojis: PropTypes.object, customEmojis: PropTypes.object,
// eslint-disable-next-line react/no-typos style: PropTypes.any,
style: ViewPropTypes.style, rules: PropTypes.object
markdownStyle: PropTypes.object,
customRules: PropTypes.object,
renderInline: PropTypes.bool
};
BlockCode.propTypes = {
node: PropTypes.object,
state: PropTypes.object
}; };

View File

@ -0,0 +1,78 @@
export default function(md) {
function tokenize(state, silent) {
let token;
const start = state.pos;
const marker = state.src.charCodeAt(start);
if (silent) {
return false;
}
// :
if (marker !== 58) {
return false;
}
const scanned = state.scanDelims(state.pos, true);
const len = scanned.length;
const ch = String.fromCharCode(marker);
for (let i = 0; i < len; i += 1) {
token = state.push('text', '', 0);
token.content = ch;
state.delimiters.push({
marker,
jump: i,
token: state.tokens.length - 1,
level: state.level,
end: -1,
open: scanned.can_open,
close: scanned.can_close
});
}
state.pos += scanned.length;
return true;
}
function postProcess(state) {
let startDelim;
let endDelim;
let token;
const { delimiters } = state;
const max = delimiters.length;
for (const i = 0; i < max; i++) { // eslint-disable-line
startDelim = delimiters[i];
// :
if (startDelim.marker !== 58) {
continue; // eslint-disable-line
}
if (startDelim.end === -1) {
continue; // eslint-disable-line
}
endDelim = delimiters[startDelim.end];
token = state.tokens[startDelim.token];
token.type = 'emoji_open';
token.tag = 'emoji';
token.nesting = 1;
token.markup = ':';
token.content = '';
token = state.tokens[endDelim.token];
token.type = 'emoji_close';
token.tag = 'emoji';
token.nesting = -1;
token.markup = ':';
token.content = '';
}
}
md.inline.ruler.before('emphasis', 'emoji', tokenize);
md.inline.ruler2.before('emphasis', 'emoji', postProcess);
}

View File

@ -97,5 +97,16 @@ export default StyleSheet.create({
}, },
broadcastButtonText: { broadcastButtonText: {
color: '#1d74f5' color: '#1d74f5'
},
mention: {
color: '#13679a'
},
paragraph: {
marginTop: 0,
marginBottom: 0,
flexWrap: 'wrap',
flexDirection: 'row',
alignItems: 'flex-start',
justifyContent: 'flex-start'
} }
}); });

View File

@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import { View, Text, StyleSheet, ViewPropTypes } from 'react-native'; import { View, Text, StyleSheet, ViewPropTypes } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import SimpleMarkdown from 'simple-markdown'; // import SimpleMarkdown from 'simple-markdown';
import Avatar from '../containers/Avatar'; import Avatar from '../containers/Avatar';
import Status from '../containers/status'; import Status from '../containers/status';
@ -65,10 +65,10 @@ const styles = StyleSheet.create({
}, },
row: { row: {
// width: '100%', // width: '100%',
// flex: 1, flex: 1,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'flex-end', alignItems: 'center'
justifyContent: 'flex-end' // justifyContent: 'flex-end'
}, },
firstRow: { firstRow: {
width: '100%', width: '100%',
@ -98,36 +98,36 @@ const styles = StyleSheet.create({
marginTop: 3 marginTop: 3
} }
}); });
const markdownStyle = { block: { marginBottom: 0, flexWrap: 'wrap', flexDirection: 'row' } }; // const markdownStyle = { block: { marginBottom: 0, flexWrap: 'wrap', flexDirection: 'row' } };
const parseInline = (parse, content, state) => { // const parseInline = (parse, content, state) => {
const isCurrentlyInline = state.inline || false; // const isCurrentlyInline = state.inline || false;
state.inline = true; // state.inline = true;
const result = parse(content, state); // const result = parse(content, state);
state.inline = isCurrentlyInline; // state.inline = isCurrentlyInline;
return result; // return result;
}; // };
const parseCaptureInline = (capture, parse, state) => ({ content: parseInline(parse, capture[1], state) }); // const parseCaptureInline = (capture, parse, state) => ({ content: parseInline(parse, capture[1], state) });
const customRules = { // const customRules = {
strong: { // strong: {
order: -4, // order: -4,
match: SimpleMarkdown.inlineRegex(/^\*\*([\s\S]+?)\*\*(?!\*)/), // match: SimpleMarkdown.inlineRegex(/^\*\*([\s\S]+?)\*\*(?!\*)/),
parse: parseCaptureInline, // parse: parseCaptureInline,
react: (node, output, state) => ({ // react: (node, output, state) => ({
type: 'strong', // type: 'strong',
key: state.key, // key: state.key,
props: { // props: {
children: output(node.content, state) // children: output(node.content, state)
} // }
}) // })
}, // },
text: { // text: {
order: -3, // order: -3,
match: SimpleMarkdown.inlineRegex(/^[\s\S]+?(?=[^0-9A-Za-z\s\u00c0-\uffff]|\n\n| {2,}\n|\w+:\S|$)/), // match: SimpleMarkdown.inlineRegex(/^[\s\S]+?(?=[^0-9A-Za-z\s\u00c0-\uffff]|\n\n| {2,}\n|\w+:\S|$)/),
parse: capture => ({ content: capture[0] }), // parse: capture => ({ content: capture[0] }),
react: node => node.content // react: node => node.content
} // }
}; // };
const renderNumber = (unread, userMentions) => { const renderNumber = (unread, userMentions) => {
if (!unread || unread <= 0) { if (!unread || unread <= 0) {
@ -283,11 +283,23 @@ export default class RoomItem extends React.Component {
<View style={styles.row}> <View style={styles.row}>
<Markdown <Markdown
msg={this.lastMessage} msg={this.lastMessage}
style={styles.lastMessage} style={{
markdownStyle={markdownStyle} view: {
customRules={customRules} flex: 1
renderInline }
numberOfLines={1} }}
rules={{
mention: node => (
<Text key={node.key}>
@{node.content}
</Text>
),
hashtag: node => (
<Text key={node.key}>
#{node.content}
</Text>
)
}}
/> />
{renderNumber(unread, userMentions)} {renderNumber(unread, userMentions)}
</View> </View>

View File

@ -13,7 +13,7 @@ const openMentionedMessagesRoom = function* openMentionedMessagesRoom({ rid, lim
newSub = yield RocketChat.subscribe('mentionedMessages', rid, limit); newSub = yield RocketChat.subscribe('mentionedMessages', rid, limit);
yield put(readyMentionedMessages()); yield put(readyMentionedMessages());
if (sub) { if (sub) {
sub.unsubscribe(); sub.unsubscribe().catch(err => console.warn(err));
} }
sub = newSub; sub = newSub;
} catch (e) { } catch (e) {

View File

@ -13,7 +13,7 @@ const openPinnedMessagesRoom = function* openPinnedMessagesRoom({ rid, limit })
newSub = yield RocketChat.subscribe('pinnedMessages', rid, limit); newSub = yield RocketChat.subscribe('pinnedMessages', rid, limit);
yield put(readyPinnedMessages()); yield put(readyPinnedMessages());
if (sub) { if (sub) {
sub.unsubscribe(); sub.unsubscribe().catch(err => console.warn(err));
} }
sub = newSub; sub = newSub;
} catch (e) { } catch (e) {

View File

@ -13,7 +13,7 @@ const openRoomFiles = function* openRoomFiles({ rid, limit }) {
newSub = yield RocketChat.subscribe('roomFiles', rid, limit); newSub = yield RocketChat.subscribe('roomFiles', rid, limit);
yield put(readyRoomFiles()); yield put(readyRoomFiles());
if (sub) { if (sub) {
sub.unsubscribe(); sub.unsubscribe().catch(err => console.warn(err));
} }
sub = newSub; sub = newSub;
} catch (e) { } catch (e) {

View File

@ -13,7 +13,7 @@ const openSnippetedMessagesRoom = function* openSnippetedMessagesRoom({ rid, lim
newSub = yield RocketChat.subscribe('snippetedMessages', rid, limit); newSub = yield RocketChat.subscribe('snippetedMessages', rid, limit);
yield put(readySnippetedMessages()); yield put(readySnippetedMessages());
if (sub) { if (sub) {
sub.unsubscribe(); sub.unsubscribe().catch(err => console.warn(err));
} }
sub = newSub; sub = newSub;
} catch (e) { } catch (e) {

View File

@ -13,7 +13,7 @@ const openStarredMessagesRoom = function* openStarredMessagesRoom({ rid, limit }
newSub = yield RocketChat.subscribe('starredMessages', rid, limit); newSub = yield RocketChat.subscribe('starredMessages', rid, limit);
yield put(readyStarredMessages()); yield put(readyStarredMessages());
if (sub) { if (sub) {
sub.unsubscribe(); sub.unsubscribe().catch(err => console.warn(err));
} }
sub = newSub; sub = newSub;
} catch (e) { } catch (e) {

View File

@ -67,7 +67,7 @@ describe('Rooms list screen', () => {
await waitFor(element(by.id('rooms-list-view-search'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('rooms-list-view-search'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('rooms-list-view-search'))).toBeVisible(); await expect(element(by.id('rooms-list-view-search'))).toBeVisible();
await element(by.id('rooms-list-view-search')).replaceText('rocket.cat'); await element(by.id('rooms-list-view-search')).replaceText('rocket.cat');
await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible().withTimeout(10000); await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible().withTimeout(60000);
await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible(); await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible();
await element(by.id('rooms-list-view-item-rocket.cat')).tap(); await element(by.id('rooms-list-view-item-rocket.cat')).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(10000); await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(10000);
@ -77,8 +77,9 @@ describe('Rooms list screen', () => {
await element(by.id('header-back')).atIndex(0).tap(); await element(by.id('header-back')).atIndex(0).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
await expect(element(by.id('rooms-list-view'))).toBeVisible(); await expect(element(by.id('rooms-list-view'))).toBeVisible();
await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible().withTimeout(60000); await element(by.id('rooms-list-view-search')).replaceText('');
await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible(); await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toExist().withTimeout(60000);
await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toExist();
}); });
// Usage - Sidebar // Usage - Sidebar

View File

@ -90,7 +90,7 @@ describe('Room info screen', () => {
}); });
it('should have name input', async() => { it('should have name input', async() => {
await expect(element(by.id('room-info-edit-view-name'))).toBeVisible(); await expect(element(by.id('room-info-edit-view-name'))).toExist();
}); });
it('should have description input', async() => { it('should have description input', async() => {

View File

@ -56,6 +56,8 @@ describe('Broadcast room', () => {
await element(by.id('login-view-password')).replaceText(data.alternateUserPassword); await element(by.id('login-view-password')).replaceText(data.alternateUserPassword);
await element(by.id('login-view-submit')).tap(); await element(by.id('login-view-submit')).tap();
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000); await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await device.reloadReactNative(); // remove after fix logout
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await waitFor(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toBeVisible().withTimeout(60000); await waitFor(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toBeVisible().withTimeout(60000);
await expect(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toBeVisible(); await expect(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toBeVisible();
await element(by.id(`rooms-list-view-item-broadcast${ data.random }`)).tap(); await element(by.id(`rooms-list-view-item-broadcast${ data.random }`)).tap();

92
package-lock.json generated
View File

@ -1229,6 +1229,27 @@
} }
} }
}, },
"@types/markdown-it": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-0.0.4.tgz",
"integrity": "sha512-FWR7QB7EqBRq1s9BMk0ccOSOuRLfVEWYpHQYpFPaXtCoqN6dJx2ttdsdQbUxLLnAlKpYeVjveGGhQ3583TTa7g=="
},
"@types/react": {
"version": "16.3.14",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.3.14.tgz",
"integrity": "sha512-wNUGm49fPl7eE2fnYdF0v5vSOrUMdKMQD/4NwtQRnb6mnPwtkhabmuFz37eq90+hhyfz0pWd38jkZHOcaZ6LGw==",
"requires": {
"csstype": "2.5.2"
}
},
"@types/react-native": {
"version": "0.55.15",
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.55.15.tgz",
"integrity": "sha512-AEnb2qacurrUL8A1EQknPKzJUXMtliPNRkd+xa4J/joUbsFen3aynVkYi+OPZ2cyomB+FWz+vv9uKCxURkgChQ==",
"requires": {
"@types/react": "16.3.14"
}
},
"@zamotany/react-proxy": { "@zamotany/react-proxy": {
"version": "3.0.0-alpha.4", "version": "3.0.0-alpha.4",
"resolved": "https://registry.npmjs.org/@zamotany/react-proxy/-/react-proxy-3.0.0-alpha.4.tgz", "resolved": "https://registry.npmjs.org/@zamotany/react-proxy/-/react-proxy-3.0.0-alpha.4.tgz",
@ -10746,6 +10767,14 @@
"type-check": "0.3.2" "type-check": "0.3.2"
} }
}, },
"linkify-it": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz",
"integrity": "sha1-2UpGSPmxwXnWT6lykSaL22zpQ08=",
"requires": {
"uc.micro": "1.0.5"
}
},
"load-json-file": { "load-json-file": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
@ -11037,6 +11066,23 @@
"object-visit": "1.0.1" "object-visit": "1.0.1"
} }
}, },
"markdown-it": {
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.1.tgz",
"integrity": "sha512-CzzqSSNkFRUf9vlWvhK1awpJreMRqdCrBvZ8DIoDWTOkESMIF741UPAhuAmbyWmdiFPA6WARNhnu2M6Nrhwa+A==",
"requires": {
"argparse": "1.0.9",
"entities": "1.1.1",
"linkify-it": "2.0.3",
"mdurl": "1.0.1",
"uc.micro": "1.0.5"
}
},
"markdown-it-flowdock": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/markdown-it-flowdock/-/markdown-it-flowdock-0.3.7.tgz",
"integrity": "sha1-Oj6/V646BKBoTOK2Hbb+lOhZV4E="
},
"markdown-loader": { "markdown-loader": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/markdown-loader/-/markdown-loader-2.0.2.tgz", "resolved": "https://registry.npmjs.org/markdown-loader/-/markdown-loader-2.0.2.tgz",
@ -11076,6 +11122,11 @@
} }
} }
}, },
"mdurl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
"integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
},
"media-typer": { "media-typer": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@ -14710,19 +14761,6 @@
"react-native-drawer-layout": "1.3.2" "react-native-drawer-layout": "1.3.2"
} }
}, },
"react-native-easy-markdown": {
"version": "git+https://github.com/diegolmello/react-native-easy-markdown.git#1cf9d2bd9683576af0bb09d80ffa286c4dc03f82",
"requires": {
"simple-markdown": "0.1.2"
},
"dependencies": {
"simple-markdown": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/simple-markdown/-/simple-markdown-0.1.2.tgz",
"integrity": "sha1-PBUQ/kC9nqBncXuKUzyc82MltBM="
}
}
},
"react-native-fabric": { "react-native-fabric": {
"version": "0.5.1", "version": "0.5.1",
"resolved": "https://registry.npmjs.org/react-native-fabric/-/react-native-fabric-0.5.1.tgz", "resolved": "https://registry.npmjs.org/react-native-fabric/-/react-native-fabric-0.5.1.tgz",
@ -14760,6 +14798,14 @@
} }
} }
}, },
"react-native-fit-image": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/react-native-fit-image/-/react-native-fit-image-1.5.4.tgz",
"integrity": "sha512-wNHlGdDWsUU31qlM5SsvZrMH4eXBZt586FQNXFRFuOiXVqdA++6Xait7aiZ+5vxglgqLf+zzSnoICn0NEvDfrw==",
"requires": {
"prop-types": "15.6.1"
}
},
"react-native-image-picker": { "react-native-image-picker": {
"version": "0.26.10", "version": "0.26.10",
"resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-0.26.10.tgz", "resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-0.26.10.tgz",
@ -14789,6 +14835,16 @@
"react-native-keyboard-tracking-view": { "react-native-keyboard-tracking-view": {
"version": "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git#82be12805eb3aa448c1f09f545c334e4776b3148" "version": "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git#82be12805eb3aa448c1f09f545c334e4776b3148"
}, },
"react-native-markdown-renderer": {
"version": "git+https://github.com/RocketChat/react-native-markdown-renderer.git#cecc6d0a2c940ac7a1e1e98c624d8b9b4d37ab68",
"requires": {
"@types/markdown-it": "0.0.4",
"@types/react-native": "0.55.15",
"markdown-it": "8.4.1",
"prop-types": "15.6.1",
"react-native-fit-image": "1.5.4"
}
},
"react-native-meteor": { "react-native-meteor": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/react-native-meteor/-/react-native-meteor-1.2.0.tgz", "resolved": "https://registry.npmjs.org/react-native-meteor/-/react-native-meteor-1.2.0.tgz",
@ -16320,11 +16376,6 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
}, },
"simple-markdown": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/simple-markdown/-/simple-markdown-0.4.1.tgz",
"integrity": "sha512-EkGc+efa/7qv2s9+G6sBBHPfvC6y+mq9mq0zStf8KDgHBlaRbT0U7mbA2WFUrxhUe/IDfSTilgC+G8ctc88n6A=="
},
"simple-plist": { "simple-plist": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-0.2.1.tgz", "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-0.2.1.tgz",
@ -18119,6 +18170,11 @@
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz",
"integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==" "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g=="
}, },
"uc.micro": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.5.tgz",
"integrity": "sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg=="
},
"uglify-js": { "uglify-js": {
"version": "2.8.29", "version": "2.8.29",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",

View File

@ -34,6 +34,7 @@
"ejson": "^2.1.2", "ejson": "^2.1.2",
"js-base64": "^2.4.5", "js-base64": "^2.4.5",
"lodash": "^4.17.10", "lodash": "^4.17.10",
"markdown-it-flowdock": "^0.3.7",
"moment": "^2.22.1", "moment": "^2.22.1",
"prop-types": "^15.6.1", "prop-types": "^15.6.1",
"react": "^16.3.2", "react": "^16.3.2",
@ -43,7 +44,6 @@
"react-native-action-button": "^2.8.3", "react-native-action-button": "^2.8.3",
"react-native-actionsheet": "^2.4.2", "react-native-actionsheet": "^2.4.2",
"react-native-audio": "^4.1.3", "react-native-audio": "^4.1.3",
"react-native-easy-markdown": "git+https://github.com/diegolmello/react-native-easy-markdown.git",
"react-native-fabric": "^0.5.1", "react-native-fabric": "^0.5.1",
"react-native-fast-image": "^4.0.14", "react-native-fast-image": "^4.0.14",
"react-native-fetch-blob": "^0.10.8", "react-native-fetch-blob": "^0.10.8",
@ -51,6 +51,7 @@
"react-native-keyboard-aware-scroll-view": "^0.5.0", "react-native-keyboard-aware-scroll-view": "^0.5.0",
"react-native-keyboard-input": "git+https://github.com/RocketChat/react-native-keyboard-input.git", "react-native-keyboard-input": "git+https://github.com/RocketChat/react-native-keyboard-input.git",
"react-native-keyboard-tracking-view": "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git", "react-native-keyboard-tracking-view": "git+https://github.com/RocketChat/react-native-keyboard-tracking-view.git",
"react-native-markdown-renderer": "git+https://github.com/RocketChat/react-native-markdown-renderer.git",
"react-native-meteor": "^1.2.0", "react-native-meteor": "^1.2.0",
"react-native-modal": "^6.0.0", "react-native-modal": "^6.0.0",
"react-native-optimized-flatlist": "^1.0.4", "react-native-optimized-flatlist": "^1.0.4",
@ -75,7 +76,6 @@
"redux-immutable-state-invariant": "^2.1.0", "redux-immutable-state-invariant": "^2.1.0",
"redux-saga": "^0.16.0", "redux-saga": "^0.16.0",
"regenerator-runtime": "^0.11.1", "regenerator-runtime": "^0.11.1",
"simple-markdown": "^0.4.1",
"snyk": "^1.80.1", "snyk": "^1.80.1",
"strip-ansi": "^4.0.0" "strip-ansi": "^4.0.0"
}, },