Chore: Migrate Markdown to Typescript (#3558)
* Chore: Migrate Markdown to TS * Chore: Migrate Markdown to TS * minor tweak * added preview where markdown was preview and fixed params within markdown * removed ts-ignore * fix lint * removed numbersofline={0} and default value to numberOfLines=1 * change how to import markdown preview and remove numberOfLines * using useTheme inside markdownPreview and remove theme from components * minor tweak on interfaces * isNewMarkdown return as boolean * minor tweaks * minor tweaks * removed unused component * fixed markdown stories * updated snapshot because removed numberOfLines={0} from message/content * create IEmoji.ts in definitions and refactor all places where getCustomEmoji was called * onLinkPress typed * todo: refactor navtoroominfo * formatText.test.ts * markdown stories to typescript too * minor tweak * IMessage definition * refactor: update new types and interfaces for use ISubscription * refactor: update threadItem for use new MarkdownPreview * refactor: rollback wrong file commited * formatHyperlink * fix lint * updated item story shot * refactor and refactor some types * Remove non-null assertion * Minor change on useRealName * tweak Co-authored-by: AlexAlexandre <alexalexandrejr@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
9ec598407d
commit
5ac4700d51
|
@ -44,7 +44,7 @@ const Avatar = React.memo(
|
||||||
if (emoji) {
|
if (emoji) {
|
||||||
image = (
|
image = (
|
||||||
<Emoji
|
<Emoji
|
||||||
theme={theme}
|
theme={theme!}
|
||||||
baseUrl={server}
|
baseUrl={server}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
isMessageContainsOnlyEmoji
|
isMessageContainsOnlyEmoji
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
export interface IAvatar {
|
export interface IAvatar {
|
||||||
server?: string;
|
server?: string;
|
||||||
style?: any;
|
style?: any;
|
||||||
|
@ -14,7 +16,7 @@ export interface IAvatar {
|
||||||
};
|
};
|
||||||
theme?: string;
|
theme?: string;
|
||||||
onPress?: () => void;
|
onPress?: () => void;
|
||||||
getCustomEmoji?: () => any;
|
getCustomEmoji?: TGetCustomEmoji;
|
||||||
avatarETag?: string;
|
avatarETag?: string;
|
||||||
isStatic?: boolean | string;
|
isStatic?: boolean | string;
|
||||||
rid?: string;
|
rid?: string;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FastImage from '@rocket.chat/react-native-fast-image';
|
import FastImage from '@rocket.chat/react-native-fast-image';
|
||||||
|
|
||||||
import { ICustomEmoji } from './interfaces';
|
import { ICustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
const CustomEmoji = React.memo(
|
const CustomEmoji = React.memo(
|
||||||
({ baseUrl, emoji, style }: ICustomEmoji) => (
|
({ baseUrl, emoji, style }: ICustomEmoji) => (
|
||||||
|
|
|
@ -5,7 +5,7 @@ import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import CustomEmoji from './CustomEmoji';
|
import CustomEmoji from './CustomEmoji';
|
||||||
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
import scrollPersistTaps from '../../utils/scrollPersistTaps';
|
||||||
import { IEmoji, IEmojiCategory } from './interfaces';
|
import { IEmoji, IEmojiCategory } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
const EMOJI_SIZE = 50;
|
const EMOJI_SIZE = 50;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import { IEmoji } from './interfaces';
|
import { IEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
const scrollProps = {
|
const scrollProps = {
|
||||||
keyboardShouldPersistTaps: 'always',
|
keyboardShouldPersistTaps: 'always',
|
||||||
|
|
|
@ -10,8 +10,8 @@ import database from '../../lib/database';
|
||||||
import { Button } from '../ActionSheet';
|
import { Button } from '../ActionSheet';
|
||||||
import { useDimensions } from '../../dimensions';
|
import { useDimensions } from '../../dimensions';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
|
import { IEmoji } from '../../definitions/IEmoji';
|
||||||
import { TFrequentlyUsedEmojiModel } from '../../definitions/IFrequentlyUsedEmoji';
|
import { TFrequentlyUsedEmojiModel } from '../../definitions/IFrequentlyUsedEmoji';
|
||||||
import { IEmoji } from '../EmojiPicker/interfaces';
|
|
||||||
|
|
||||||
interface IHeader {
|
interface IHeader {
|
||||||
handleReaction: Function;
|
handleReaction: Function;
|
||||||
|
|
|
@ -6,7 +6,7 @@ import shortnameToUnicode from '../../../utils/shortnameToUnicode';
|
||||||
import styles from '../styles';
|
import styles from '../styles';
|
||||||
import MessageboxContext from '../Context';
|
import MessageboxContext from '../Context';
|
||||||
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
|
import CustomEmoji from '../../EmojiPicker/CustomEmoji';
|
||||||
import { IEmoji } from '../../EmojiPicker/interfaces';
|
import { IEmoji } from '../../../definitions/IEmoji';
|
||||||
|
|
||||||
interface IMessageBoxMentionEmoji {
|
interface IMessageBoxMentionEmoji {
|
||||||
item: IEmoji;
|
item: IEmoji;
|
||||||
|
|
|
@ -8,7 +8,7 @@ import FixedMentionItem from './FixedMentionItem';
|
||||||
import MentionEmoji from './MentionEmoji';
|
import MentionEmoji from './MentionEmoji';
|
||||||
import { MENTIONS_TRACKING_TYPE_EMOJIS, MENTIONS_TRACKING_TYPE_COMMANDS, MENTIONS_TRACKING_TYPE_CANNED } from '../constants';
|
import { MENTIONS_TRACKING_TYPE_EMOJIS, MENTIONS_TRACKING_TYPE_COMMANDS, MENTIONS_TRACKING_TYPE_CANNED } from '../constants';
|
||||||
import { themes } from '../../../constants/colors';
|
import { themes } from '../../../constants/colors';
|
||||||
import { IEmoji } from '../../EmojiPicker/interfaces';
|
import { IEmoji } from '../../../definitions/IEmoji';
|
||||||
|
|
||||||
interface IMessageBoxMentionItem {
|
interface IMessageBoxMentionItem {
|
||||||
item: {
|
item: {
|
||||||
|
|
|
@ -3,10 +3,11 @@ import { StyleSheet, Text, View } from 'react-native';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import Markdown from '../markdown';
|
import { MarkdownPreview } from '../markdown';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
import { IMessage } from '../../definitions/IMessage';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -42,11 +43,7 @@ const styles = StyleSheet.create({
|
||||||
|
|
||||||
interface IMessageBoxReplyPreview {
|
interface IMessageBoxReplyPreview {
|
||||||
replying: boolean;
|
replying: boolean;
|
||||||
message: {
|
message: IMessage;
|
||||||
ts: Date;
|
|
||||||
msg: string;
|
|
||||||
u: any;
|
|
||||||
};
|
|
||||||
Message_TimeFormat: string;
|
Message_TimeFormat: string;
|
||||||
close(): void;
|
close(): void;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
|
@ -57,17 +54,7 @@ interface IMessageBoxReplyPreview {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReplyPreview = React.memo(
|
const ReplyPreview = React.memo(
|
||||||
({
|
({ message, Message_TimeFormat, replying, close, theme, useRealName }: IMessageBoxReplyPreview) => {
|
||||||
message,
|
|
||||||
Message_TimeFormat,
|
|
||||||
baseUrl,
|
|
||||||
username,
|
|
||||||
replying,
|
|
||||||
getCustomEmoji,
|
|
||||||
close,
|
|
||||||
theme,
|
|
||||||
useRealName
|
|
||||||
}: IMessageBoxReplyPreview) => {
|
|
||||||
if (!replying) {
|
if (!replying) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -82,16 +69,7 @@ const ReplyPreview = React.memo(
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
<Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
||||||
</View>
|
</View>
|
||||||
{/* @ts-ignore*/}
|
<MarkdownPreview msg={message.msg} />
|
||||||
<Markdown
|
|
||||||
msg={message.msg}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
username={username}
|
|
||||||
getCustomEmoji={getCustomEmoji}
|
|
||||||
numberOfLines={1}
|
|
||||||
preview
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
<CustomIcon name='close' color={themes[theme].auxiliaryText} size={20} style={styles.close} onPress={close} />
|
<CustomIcon name='close' color={themes[theme].auxiliaryText} size={20} style={styles.close} onPress={close} />
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -47,6 +47,7 @@ import Navigation from '../../lib/Navigation';
|
||||||
import { withActionSheet } from '../ActionSheet';
|
import { withActionSheet } from '../ActionSheet';
|
||||||
import { sanitizeLikeString } from '../../lib/database/utils';
|
import { sanitizeLikeString } from '../../lib/database/utils';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
|
import { IMessage } from '../../definitions/IMessage';
|
||||||
import { forceJpgExtension } from './forceJpgExtension';
|
import { forceJpgExtension } from './forceJpgExtension';
|
||||||
|
|
||||||
if (isAndroid) {
|
if (isAndroid) {
|
||||||
|
@ -74,12 +75,7 @@ const videoPickerConfig = {
|
||||||
interface IMessageBoxProps {
|
interface IMessageBoxProps {
|
||||||
rid: string;
|
rid: string;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
message: {
|
message: IMessage;
|
||||||
u: {
|
|
||||||
username: string;
|
|
||||||
};
|
|
||||||
id: any;
|
|
||||||
};
|
|
||||||
replying: boolean;
|
replying: boolean;
|
||||||
editing: boolean;
|
editing: boolean;
|
||||||
threadsEnabled: boolean;
|
threadsEnabled: boolean;
|
||||||
|
@ -1072,7 +1068,6 @@ class MessageBox extends Component<IMessageBoxProps, IMessageBoxState> {
|
||||||
|
|
||||||
const replyPreview = !recording ? (
|
const replyPreview = !recording ? (
|
||||||
<ReplyPreview
|
<ReplyPreview
|
||||||
// @ts-ignore
|
|
||||||
message={message}
|
message={message}
|
||||||
close={replyCancel}
|
close={replyCancel}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { CustomIcon } from '../lib/Icons';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
|
import { TGetCustomEmoji } from '../definitions/IEmoji';
|
||||||
import SafeAreaView from './SafeAreaView';
|
import SafeAreaView from './SafeAreaView';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -66,7 +67,7 @@ interface IItem {
|
||||||
};
|
};
|
||||||
user?: { username: any };
|
user?: { username: any };
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
getCustomEmoji?: Function;
|
getCustomEmoji?: TGetCustomEmoji;
|
||||||
theme?: string;
|
theme?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import Markdown from '../markdown';
|
import { MarkdownPreview } from '../markdown';
|
||||||
import RoomTypeIcon from '../RoomTypeIcon';
|
import RoomTypeIcon from '../RoomTypeIcon';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
|
|
||||||
|
@ -101,16 +101,7 @@ const SubTitle = React.memo(({ usersTyping, subtitle, renderFunc, theme, scale }
|
||||||
|
|
||||||
// subtitle
|
// subtitle
|
||||||
if (subtitle) {
|
if (subtitle) {
|
||||||
return (
|
return <MarkdownPreview msg={subtitle} style={[styles.subtitle, { fontSize, color: themes[theme].auxiliaryText }]} />;
|
||||||
// @ts-ignore
|
|
||||||
<Markdown
|
|
||||||
preview
|
|
||||||
msg={subtitle}
|
|
||||||
style={[styles.subtitle, { fontSize, color: themes[theme].auxiliaryText }]}
|
|
||||||
numberOfLines={1}
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -126,10 +117,7 @@ const HeaderTitle = React.memo(({ title, tmid, prid, scale, theme, testID }: TRo
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <MarkdownPreview msg={title} style={[styles.title, titleStyle]} testID={testID} />;
|
||||||
// @ts-ignore
|
|
||||||
<Markdown preview msg={title} style={[styles.title, titleStyle]} numberOfLines={1} theme={theme} testID={testID} />
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const Header = React.memo(
|
const Header = React.memo(
|
||||||
|
|
|
@ -3,7 +3,7 @@ import React, { useContext } from 'react';
|
||||||
import { StyleSheet, Text } from 'react-native';
|
import { StyleSheet, Text } from 'react-native';
|
||||||
import { BLOCK_CONTEXT, UiKitParserMessage, UiKitParserModal, uiKitMessage, uiKitModal } from '@rocket.chat/ui-kit';
|
import { BLOCK_CONTEXT, UiKitParserMessage, UiKitParserModal, uiKitMessage, uiKitModal } from '@rocket.chat/ui-kit';
|
||||||
|
|
||||||
import Markdown from '../markdown';
|
import Markdown, { MarkdownPreview } from '../markdown';
|
||||||
import Button from '../Button';
|
import Button from '../Button';
|
||||||
import TextInput from '../TextInput';
|
import TextInput from '../TextInput';
|
||||||
import { textParser, useBlockContext } from './utils';
|
import { textParser, useBlockContext } from './utils';
|
||||||
|
@ -49,10 +49,10 @@ class MessageParser extends UiKitParserMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
const isContext = context === BLOCK_CONTEXT.CONTEXT;
|
const isContext = context === BLOCK_CONTEXT.CONTEXT;
|
||||||
return (
|
if (isContext) {
|
||||||
// @ts-ignore
|
return <MarkdownPreview msg={text} style={[isContext && { color: themes[theme].auxiliaryText }]} numberOfLines={0} />;
|
||||||
<Markdown msg={text} theme={theme} style={[isContext && { color: themes[theme].auxiliaryText }]} preview={isContext} />
|
}
|
||||||
);
|
return <Markdown msg={text} theme={theme} style={[isContext && { color: themes[theme].auxiliaryText }]} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
button(element: any, context: any) {
|
button(element: any, context: any) {
|
||||||
|
|
|
@ -8,10 +8,10 @@ import { events, logEvent } from '../../utils/log';
|
||||||
|
|
||||||
interface IAtMention {
|
interface IAtMention {
|
||||||
mention: string;
|
mention: string;
|
||||||
username: string;
|
username?: string;
|
||||||
navToRoomInfo: Function;
|
navToRoomInfo?: Function;
|
||||||
style?: any;
|
style?: any;
|
||||||
useRealName: boolean;
|
useRealName?: boolean;
|
||||||
mentions: any;
|
mentions: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,9 @@ const AtMention = React.memo(({ mention, mentions, username, navToRoomInfo, styl
|
||||||
t: 'd',
|
t: 'd',
|
||||||
rid: user && user._id
|
rid: user && user._id
|
||||||
};
|
};
|
||||||
|
if (navToRoomInfo) {
|
||||||
navToRoomInfo(navParam);
|
navToRoomInfo(navParam);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
|
|
|
@ -12,8 +12,8 @@ interface IEmoji {
|
||||||
getCustomEmoji?: Function;
|
getCustomEmoji?: Function;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
customEmojis?: any;
|
customEmojis?: any;
|
||||||
style: object;
|
style?: object;
|
||||||
theme?: string;
|
theme: string;
|
||||||
onEmojiSelected?: Function;
|
onEmojiSelected?: Function;
|
||||||
tabEmojiStyle?: object;
|
tabEmojiStyle?: object;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ const Emoji = React.memo(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Text style={[{ color: themes[theme!].bodyText }, isMessageContainsOnlyEmoji ? styles.textBig : styles.text, style]}>
|
<Text style={[{ color: themes[theme].bodyText }, isMessageContainsOnlyEmoji ? styles.textBig : styles.text, style]}>
|
||||||
{emojiUnicode}
|
{emojiUnicode}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, TextStyle } from 'react-native';
|
import { Text, TextStyle, StyleProp } from 'react-native';
|
||||||
|
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
|
import { IUserChannel } from './interfaces';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
|
||||||
interface IHashtag {
|
interface IHashtag {
|
||||||
hashtag: string;
|
hashtag: string;
|
||||||
navToRoomInfo: Function;
|
navToRoomInfo?: Function;
|
||||||
style?: TextStyle[];
|
style?: StyleProp<TextStyle>[];
|
||||||
channels: {
|
channels?: IUserChannel[];
|
||||||
[index: number]: string | number;
|
|
||||||
name: string;
|
|
||||||
_id: number;
|
|
||||||
}[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IHashtag) => {
|
const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IHashtag) => {
|
||||||
|
@ -21,11 +18,13 @@ const Hashtag = React.memo(({ hashtag, channels, navToRoomInfo, style = [] }: IH
|
||||||
|
|
||||||
const handlePress = () => {
|
const handlePress = () => {
|
||||||
const index = channels?.findIndex(channel => channel.name === hashtag);
|
const index = channels?.findIndex(channel => channel.name === hashtag);
|
||||||
|
if (index && navToRoomInfo) {
|
||||||
const navParam = {
|
const navParam = {
|
||||||
t: 'c',
|
t: 'c',
|
||||||
rid: channels[index]._id
|
rid: channels?.[index]._id
|
||||||
};
|
};
|
||||||
navToRoomInfo(navParam);
|
navToRoomInfo(navParam);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (channels && channels.length && channels.findIndex(channel => channel.name === hashtag) !== -1) {
|
if (channels && channels.length && channels.findIndex(channel => channel.name === hashtag) !== -1) {
|
||||||
|
|
|
@ -7,12 +7,13 @@ import { LISTENER } from '../Toast';
|
||||||
import EventEmitter from '../../utils/events';
|
import EventEmitter from '../../utils/events';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import openLink from '../../utils/openLink';
|
import openLink from '../../utils/openLink';
|
||||||
|
import { TOnLinkPress } from './interfaces';
|
||||||
|
|
||||||
interface ILink {
|
interface ILink {
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
link: string;
|
link: string;
|
||||||
theme: string;
|
theme: string;
|
||||||
onLinkPress: Function;
|
onLinkPress?: TOnLinkPress;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Link = React.memo(({ children, link, theme, onLinkPress }: ILink) => {
|
const Link = React.memo(({ children, link, theme, onLinkPress }: ILink) => {
|
||||||
|
|
|
@ -5,7 +5,7 @@ interface IList {
|
||||||
ordered: boolean;
|
ordered: boolean;
|
||||||
start: number;
|
start: number;
|
||||||
tight: boolean;
|
tight: boolean;
|
||||||
numberOfLines: number;
|
numberOfLines?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const List = React.memo(({ children, ordered, tight, start = 1, numberOfLines = 0 }: IList) => {
|
const List = React.memo(({ children, ordered, tight, start = 1, numberOfLines = 0 }: IList) => {
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleProp, Text, TextStyle } from 'react-native';
|
||||||
|
import removeMarkdown from 'remove-markdown';
|
||||||
|
|
||||||
|
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||||
|
import { themes } from '../../constants/colors';
|
||||||
|
import { formatText } from './formatText';
|
||||||
|
import { useTheme } from '../../theme';
|
||||||
|
import styles from './styles';
|
||||||
|
import { formatHyperlink } from './formatHyperlink';
|
||||||
|
|
||||||
|
interface IMarkdownPreview {
|
||||||
|
msg?: string;
|
||||||
|
numberOfLines?: number;
|
||||||
|
testID?: string;
|
||||||
|
style?: StyleProp<TextStyle>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const MarkdownPreview = ({ msg, numberOfLines = 1, testID, style = [] }: IMarkdownPreview): React.ReactElement | null => {
|
||||||
|
if (!msg) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
|
let m = formatText(msg);
|
||||||
|
m = formatHyperlink(m);
|
||||||
|
m = shortnameToUnicode(m);
|
||||||
|
// Removes sequential empty spaces
|
||||||
|
m = m.replace(/\s+/g, ' ');
|
||||||
|
m = removeMarkdown(m);
|
||||||
|
m = m.replace(/\n+/g, ' ');
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
accessibilityLabel={m}
|
||||||
|
style={[styles.text, { color: themes[theme].bodyText }, ...style]}
|
||||||
|
numberOfLines={numberOfLines}
|
||||||
|
testID={testID}>
|
||||||
|
{m}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MarkdownPreview;
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { formatHyperlink } from './formatHyperlink';
|
||||||
|
|
||||||
|
describe('FormatText', () => {
|
||||||
|
test('empty to be empty', () => {
|
||||||
|
expect(formatHyperlink('')).toBe('');
|
||||||
|
});
|
||||||
|
test('A123 to be A123', () => {
|
||||||
|
expect(formatHyperlink('A123')).toBe('A123');
|
||||||
|
});
|
||||||
|
test('Format <http://link|Text> to be <http://link|Text>', () => {
|
||||||
|
expect(formatHyperlink('<http://link|Text>')).toBe('<http://link|Text>');
|
||||||
|
});
|
||||||
|
test('Format "[ ](https://open.rocket.chat/) Test" to be Test', () => {
|
||||||
|
expect(formatHyperlink('[ ](https://open.rocket.chat/) Test')).toBe('Test');
|
||||||
|
});
|
||||||
|
test('Format "[Open](https://open.rocket.chat/) Test" to be Test', () => {
|
||||||
|
expect(formatHyperlink('[Open](https://open.rocket.chat/) Test')).toBe('[Open](https://open.rocket.chat/) Test');
|
||||||
|
});
|
||||||
|
test('render test (arabic)', () => {
|
||||||
|
expect(formatHyperlink('[ ](https://open.rocket.chat/) اختبا')).toBe('اختبا');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('render test (russian)', () => {
|
||||||
|
expect(formatHyperlink('[ ](https://open.rocket.chat/) тест123')).toBe('тест123');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Ex: '[ ](https://open.rocket.chat/group/test?msg=abcdef) Test'
|
||||||
|
// Return: 'Test'
|
||||||
|
export const formatHyperlink = (text: string): string => text.replace(/^\[([\s]*)\]\(([^)]*)\)\s/, '').trim();
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { formatText } from './formatText';
|
||||||
|
|
||||||
|
describe('FormatText', () => {
|
||||||
|
test('empty to be empty', () => {
|
||||||
|
expect(formatText('')).toBe('');
|
||||||
|
});
|
||||||
|
test('A123 to be A123', () => {
|
||||||
|
expect(formatText('A123')).toBe('A123');
|
||||||
|
});
|
||||||
|
test('Format <http://link|Text> to be [Text](http://link)', () => {
|
||||||
|
expect(formatText('<http://link|Text>')).toBe('[Text](http://link)');
|
||||||
|
});
|
||||||
|
test('render test (arabic)', () => {
|
||||||
|
expect(formatText('اختبا <http://link|ر123>')).toBe('اختبا [ر123](http://link)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('render test (russian)', () => {
|
||||||
|
expect(formatText('<http://link|тест123>')).toBe('[тест123](http://link)');
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Support <http://link|Text>
|
||||||
|
export const formatText = (text: string): string =>
|
||||||
|
text.replace(
|
||||||
|
new RegExp('(?:<|<)((?:https|http):\\/\\/[^\\|]+)\\|(.+?)(?=>|>)(?:>|>)', 'gm'),
|
||||||
|
(match, url, title) => `[${title}](${url})`
|
||||||
|
);
|
|
@ -1,12 +1,9 @@
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { Image, Text } from 'react-native';
|
import { Image, StyleProp, Text, TextStyle } from 'react-native';
|
||||||
import { Node, Parser } from 'commonmark';
|
import { Node, Parser } from 'commonmark';
|
||||||
import Renderer from 'commonmark-react-renderer';
|
import Renderer from 'commonmark-react-renderer';
|
||||||
import removeMarkdown from 'remove-markdown';
|
|
||||||
import { MarkdownAST } from '@rocket.chat/message-parser';
|
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
import { UserMention } from '../message/interfaces';
|
|
||||||
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import MarkdownLink from './Link';
|
import MarkdownLink from './Link';
|
||||||
|
@ -23,43 +20,39 @@ import mergeTextNodes from './mergeTextNodes';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { isValidURL } from '../../utils/url';
|
import { isValidURL } from '../../utils/url';
|
||||||
import NewMarkdown from './new';
|
import NewMarkdown from './new';
|
||||||
|
import { formatText } from './formatText';
|
||||||
|
import { IUserMention, IUserChannel, TOnLinkPress } from './interfaces';
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
import { formatHyperlink } from './formatHyperlink';
|
||||||
|
|
||||||
|
export { default as MarkdownPreview } from './Preview';
|
||||||
|
|
||||||
interface IMarkdownProps {
|
interface IMarkdownProps {
|
||||||
msg?: string;
|
msg?: string;
|
||||||
md: MarkdownAST;
|
|
||||||
mentions: UserMention[];
|
|
||||||
getCustomEmoji: Function;
|
|
||||||
baseUrl: string;
|
|
||||||
username: string;
|
|
||||||
tmid: string;
|
|
||||||
isEdited: boolean;
|
|
||||||
numberOfLines: number;
|
|
||||||
customEmojis: boolean;
|
|
||||||
useRealName: boolean;
|
|
||||||
channels: {
|
|
||||||
name: string;
|
|
||||||
_id: number;
|
|
||||||
}[];
|
|
||||||
enableMessageParser: boolean;
|
|
||||||
navToRoomInfo: Function;
|
|
||||||
preview: boolean;
|
|
||||||
theme: string;
|
theme: string;
|
||||||
testID: string;
|
md?: MarkdownAST;
|
||||||
style: any;
|
mentions?: IUserMention[];
|
||||||
onLinkPress: Function;
|
getCustomEmoji?: TGetCustomEmoji;
|
||||||
|
baseUrl?: string;
|
||||||
|
username?: string;
|
||||||
|
tmid?: string;
|
||||||
|
isEdited?: boolean;
|
||||||
|
numberOfLines?: number;
|
||||||
|
customEmojis?: boolean;
|
||||||
|
useRealName?: boolean;
|
||||||
|
channels?: IUserChannel[];
|
||||||
|
enableMessageParser?: boolean;
|
||||||
|
// TODO: Refactor when migrate Room
|
||||||
|
navToRoomInfo?: Function;
|
||||||
|
testID?: string;
|
||||||
|
style?: StyleProp<TextStyle>[];
|
||||||
|
onLinkPress?: TOnLinkPress;
|
||||||
}
|
}
|
||||||
|
|
||||||
type TLiteral = {
|
type TLiteral = {
|
||||||
literal: string;
|
literal: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Support <http://link|Text>
|
|
||||||
const formatText = (text: string) =>
|
|
||||||
text.replace(
|
|
||||||
new RegExp('(?:<|<)((?:https|http):\\/\\/[^\\|]+)\\|(.+?)(?=>|>)(?:>|>)', 'gm'),
|
|
||||||
(match, url, title) => `[${title}](${url})`
|
|
||||||
);
|
|
||||||
|
|
||||||
const emojiRanges = [
|
const emojiRanges = [
|
||||||
'\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]', // unicode emoji from https://www.regextester.com/106421
|
'\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]', // unicode emoji from https://www.regextester.com/106421
|
||||||
':.{1,40}:', // custom emoji
|
':.{1,40}:', // custom emoji
|
||||||
|
@ -148,7 +141,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
|
|
||||||
get isNewMarkdown(): boolean {
|
get isNewMarkdown(): boolean {
|
||||||
const { md, enableMessageParser } = this.props;
|
const { md, enableMessageParser } = this.props;
|
||||||
return enableMessageParser && !!md;
|
return !!enableMessageParser && !!md;
|
||||||
}
|
}
|
||||||
|
|
||||||
editedMessage = (ast: any) => {
|
editedMessage = (ast: any) => {
|
||||||
|
@ -244,7 +237,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
};
|
};
|
||||||
|
|
||||||
renderAtMention = ({ mentionName }: { mentionName: string }) => {
|
renderAtMention = ({ mentionName }: { mentionName: string }) => {
|
||||||
const { username, mentions, navToRoomInfo, useRealName, style } = this.props;
|
const { username = '', mentions, navToRoomInfo, useRealName, style } = this.props;
|
||||||
return (
|
return (
|
||||||
<MarkdownAtMention
|
<MarkdownAtMention
|
||||||
mentions={mentions}
|
mentions={mentions}
|
||||||
|
@ -258,7 +251,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
};
|
};
|
||||||
|
|
||||||
renderEmoji = ({ literal }: TLiteral) => {
|
renderEmoji = ({ literal }: TLiteral) => {
|
||||||
const { getCustomEmoji, baseUrl, customEmojis, style, theme } = this.props;
|
const { getCustomEmoji, baseUrl = '', customEmojis, style, theme } = this.props;
|
||||||
return (
|
return (
|
||||||
<MarkdownEmoji
|
<MarkdownEmoji
|
||||||
literal={literal}
|
literal={literal}
|
||||||
|
@ -343,18 +336,13 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
const {
|
const {
|
||||||
msg,
|
msg,
|
||||||
md,
|
md,
|
||||||
numberOfLines,
|
|
||||||
preview = false,
|
|
||||||
theme,
|
|
||||||
style = [],
|
|
||||||
testID,
|
|
||||||
mentions,
|
mentions,
|
||||||
channels,
|
channels,
|
||||||
navToRoomInfo,
|
navToRoomInfo,
|
||||||
useRealName,
|
useRealName,
|
||||||
username,
|
username = '',
|
||||||
getCustomEmoji,
|
getCustomEmoji,
|
||||||
baseUrl,
|
baseUrl = '',
|
||||||
onLinkPress
|
onLinkPress
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
@ -362,7 +350,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isNewMarkdown && !preview) {
|
if (this.isNewMarkdown) {
|
||||||
return (
|
return (
|
||||||
<NewMarkdown
|
<NewMarkdown
|
||||||
username={username}
|
username={username}
|
||||||
|
@ -379,28 +367,7 @@ class Markdown extends PureComponent<IMarkdownProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let m = formatText(msg);
|
let m = formatText(msg);
|
||||||
|
m = formatHyperlink(m);
|
||||||
// Ex: '[ ](https://open.rocket.chat/group/test?msg=abcdef) Test'
|
|
||||||
// Return: 'Test'
|
|
||||||
m = m.replace(/^\[([\s]*)\]\(([^)]*)\)\s/, '').trim();
|
|
||||||
|
|
||||||
if (preview) {
|
|
||||||
m = shortnameToUnicode(m);
|
|
||||||
// Removes sequential empty spaces
|
|
||||||
m = m.replace(/\s+/g, ' ');
|
|
||||||
m = removeMarkdown(m);
|
|
||||||
m = m.replace(/\n+/g, ' ');
|
|
||||||
return (
|
|
||||||
<Text
|
|
||||||
accessibilityLabel={m}
|
|
||||||
style={[styles.text, { color: themes[theme].bodyText }, ...style]}
|
|
||||||
numberOfLines={numberOfLines}
|
|
||||||
testID={testID}>
|
|
||||||
{m}
|
|
||||||
</Text>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ast = parser.parse(m);
|
let ast = parser.parse(m);
|
||||||
ast = mergeTextNodes(ast);
|
ast = mergeTextNodes(ast);
|
||||||
this.isMessageContainsOnlyEmoji = isOnlyEmoji(m) && emojiCount(m) <= 3;
|
this.isMessageContainsOnlyEmoji = isOnlyEmoji(m) && emojiCount(m) <= 3;
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
export interface IUserMention {
|
||||||
|
_id: string;
|
||||||
|
username: string;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUserChannel {
|
||||||
|
name: string;
|
||||||
|
_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TOnLinkPress = (link: string) => void;
|
|
@ -1,17 +1,14 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { UserMention } from '../../message/interfaces';
|
import { IUserMention, IUserChannel } from '../interfaces';
|
||||||
|
|
||||||
interface IMarkdownContext {
|
interface IMarkdownContext {
|
||||||
mentions: UserMention[];
|
mentions?: IUserMention[];
|
||||||
channels: {
|
channels?: IUserChannel[];
|
||||||
name: string;
|
useRealName?: boolean;
|
||||||
_id: number;
|
username?: string;
|
||||||
}[];
|
baseUrl?: string;
|
||||||
useRealName: boolean;
|
navToRoomInfo?: Function;
|
||||||
username: string;
|
|
||||||
baseUrl: string;
|
|
||||||
navToRoomInfo: Function;
|
|
||||||
getCustomEmoji?: Function;
|
getCustomEmoji?: Function;
|
||||||
onLinkPress?: Function;
|
onLinkPress?: Function;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MarkdownAST } from '@rocket.chat/message-parser';
|
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||||
|
import isEmpty from 'lodash/isEmpty';
|
||||||
|
|
||||||
import Quote from './Quote';
|
import Quote from './Quote';
|
||||||
import Paragraph from './Paragraph';
|
import Paragraph from './Paragraph';
|
||||||
|
@ -8,21 +9,18 @@ import Code from './Code';
|
||||||
import BigEmoji from './BigEmoji';
|
import BigEmoji from './BigEmoji';
|
||||||
import OrderedList from './OrderedList';
|
import OrderedList from './OrderedList';
|
||||||
import UnorderedList from './UnorderedList';
|
import UnorderedList from './UnorderedList';
|
||||||
import { UserMention } from '../../message/interfaces';
|
import { IUserMention, IUserChannel, TOnLinkPress } from '../interfaces';
|
||||||
import TaskList from './TaskList';
|
import TaskList from './TaskList';
|
||||||
import MarkdownContext from './MarkdownContext';
|
import MarkdownContext from './MarkdownContext';
|
||||||
|
|
||||||
interface IBodyProps {
|
interface IBodyProps {
|
||||||
tokens: MarkdownAST;
|
tokens?: MarkdownAST;
|
||||||
mentions: UserMention[];
|
mentions?: IUserMention[];
|
||||||
channels: {
|
channels?: IUserChannel[];
|
||||||
name: string;
|
|
||||||
_id: number;
|
|
||||||
}[];
|
|
||||||
getCustomEmoji?: Function;
|
getCustomEmoji?: Function;
|
||||||
onLinkPress?: Function;
|
onLinkPress?: TOnLinkPress;
|
||||||
navToRoomInfo: Function;
|
navToRoomInfo?: Function;
|
||||||
useRealName: boolean;
|
useRealName?: boolean;
|
||||||
username: string;
|
username: string;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
}
|
}
|
||||||
|
@ -37,7 +35,12 @@ const Body = ({
|
||||||
getCustomEmoji,
|
getCustomEmoji,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
onLinkPress
|
onLinkPress
|
||||||
}: IBodyProps): JSX.Element => (
|
}: IBodyProps): React.ReactElement | null => {
|
||||||
|
if (isEmpty(tokens)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
<MarkdownContext.Provider
|
<MarkdownContext.Provider
|
||||||
value={{
|
value={{
|
||||||
mentions,
|
mentions,
|
||||||
|
@ -49,7 +52,7 @@ const Body = ({
|
||||||
baseUrl,
|
baseUrl,
|
||||||
onLinkPress
|
onLinkPress
|
||||||
}}>
|
}}>
|
||||||
{tokens.map(block => {
|
{tokens?.map(block => {
|
||||||
switch (block.type) {
|
switch (block.type) {
|
||||||
case 'BIG_EMOJI':
|
case 'BIG_EMOJI':
|
||||||
return <BigEmoji value={block.value} />;
|
return <BigEmoji value={block.value} />;
|
||||||
|
@ -73,5 +76,6 @@ const Body = ({
|
||||||
})}
|
})}
|
||||||
</MarkdownContext.Provider>
|
</MarkdownContext.Provider>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Body;
|
export default Body;
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { isAndroid, isIOS } from '../../utils/deviceInfo';
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
import ActivityIndicator from '../ActivityIndicator';
|
import ActivityIndicator from '../ActivityIndicator';
|
||||||
import { withDimensions } from '../../dimensions';
|
import { withDimensions } from '../../dimensions';
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
interface IButton {
|
interface IButton {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
@ -29,7 +30,7 @@ interface IMessageAudioProps {
|
||||||
description: string;
|
description: string;
|
||||||
};
|
};
|
||||||
theme: string;
|
theme: string;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
scale?: number;
|
scale?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,7 +281,6 @@ class MessageAudio extends React.Component<IMessageAudioProps, IMessageAudioStat
|
||||||
/>
|
/>
|
||||||
<Text style={[styles.duration, { color: themes[theme].auxiliaryText }]}>{this.duration}</Text>
|
<Text style={[styles.duration, { color: themes[theme].auxiliaryText }]}>{this.duration}</Text>
|
||||||
</View>
|
</View>
|
||||||
{/* @ts-ignore*/}
|
|
||||||
<Markdown msg={description} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />
|
<Markdown msg={description} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { dequal } from 'dequal';
|
||||||
|
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import Markdown from '../markdown';
|
import Markdown, { MarkdownPreview } from '../markdown';
|
||||||
import User from './User';
|
import User from './User';
|
||||||
import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME, getInfoMessage } from './utils';
|
import { SYSTEM_MESSAGE_TYPES_WITH_AUTHOR_NAME, getInfoMessage } from './utils';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
@ -49,10 +49,11 @@ const Content = React.memo(
|
||||||
{I18n.t('Encrypted_message')}
|
{I18n.t('Encrypted_message')}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
} else if (isPreview) {
|
||||||
|
content = <MarkdownPreview msg={props.msg} />;
|
||||||
} else {
|
} else {
|
||||||
const { baseUrl, user, onLinkPress } = useContext(MessageContext);
|
const { baseUrl, user, onLinkPress } = useContext(MessageContext);
|
||||||
content = (
|
content = (
|
||||||
// @ts-ignore
|
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={props.msg}
|
msg={props.msg}
|
||||||
md={props.md}
|
md={props.md}
|
||||||
|
@ -61,8 +62,6 @@ const Content = React.memo(
|
||||||
enableMessageParser={user.enableMessageParserEarlyAdoption}
|
enableMessageParser={user.enableMessageParserEarlyAdoption}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
isEdited={props.isEdited}
|
isEdited={props.isEdited}
|
||||||
numberOfLines={isPreview ? 1 : 0}
|
|
||||||
preview={isPreview}
|
|
||||||
channels={props.channels}
|
channels={props.channels}
|
||||||
mentions={props.mentions}
|
mentions={props.mentions}
|
||||||
navToRoomInfo={props.navToRoomInfo}
|
navToRoomInfo={props.navToRoomInfo}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import styles from './styles';
|
||||||
import { formatAttachmentUrl } from '../../lib/utils';
|
import { formatAttachmentUrl } from '../../lib/utils';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
type TMessageButton = {
|
type TMessageButton = {
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
|
@ -28,7 +29,7 @@ interface IMessageImage {
|
||||||
imageUrl?: string;
|
imageUrl?: string;
|
||||||
showAttachment: Function;
|
showAttachment: Function;
|
||||||
theme: string;
|
theme: string;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ImageProgress = createImageProgress(FastImage);
|
const ImageProgress = createImageProgress(FastImage);
|
||||||
|
@ -66,7 +67,6 @@ const ImageContainer = React.memo(
|
||||||
<Button theme={theme} onPress={onPress}>
|
<Button theme={theme} onPress={onPress}>
|
||||||
<View>
|
<View>
|
||||||
<MessageImage img={img} theme={theme} />
|
<MessageImage img={img} theme={theme} />
|
||||||
{/* @ts-ignore */}
|
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={file.description}
|
msg={file.description}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { BUTTON_HIT_SLOP } from './utils';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import MessageContext from './Context';
|
import MessageContext from './Context';
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
interface IMessageAddReaction {
|
interface IMessageAddReaction {
|
||||||
theme: string;
|
theme: string;
|
||||||
|
@ -19,13 +20,13 @@ interface IMessageReaction {
|
||||||
usernames: [];
|
usernames: [];
|
||||||
emoji: object;
|
emoji: object;
|
||||||
};
|
};
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessageReactions {
|
interface IMessageReactions {
|
||||||
reactions?: object[];
|
reactions?: object[];
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { CustomIcon } from '../../lib/Icons';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import Markdown from '../markdown';
|
import { MarkdownPreview } from '../markdown';
|
||||||
import { IMessageRepliedThread } from './interfaces';
|
import { IMessageRepliedThread } from './interfaces';
|
||||||
|
|
||||||
const RepliedThread = memo(({ tmid, tmsg, isHeader, fetchThreadName, id, isEncrypted, theme }: IMessageRepliedThread) => {
|
const RepliedThread = memo(({ tmid, tmsg, isHeader, fetchThreadName, id, isEncrypted, theme }: IMessageRepliedThread) => {
|
||||||
|
@ -32,14 +32,7 @@ const RepliedThread = memo(({ tmid, tmsg, isHeader, fetchThreadName, id, isEncry
|
||||||
return (
|
return (
|
||||||
<View style={styles.repliedThread} testID={`message-thread-replied-on-${msg}`}>
|
<View style={styles.repliedThread} testID={`message-thread-replied-on-${msg}`}>
|
||||||
<CustomIcon name='threads' size={20} style={styles.repliedThreadIcon} color={themes[theme].tintColor} />
|
<CustomIcon name='threads' size={20} style={styles.repliedThreadIcon} color={themes[theme].tintColor} />
|
||||||
{/* @ts-ignore*/}
|
<MarkdownPreview msg={msg} style={[styles.repliedThreadName, { color: themes[theme].tintColor }]} />
|
||||||
<Markdown
|
|
||||||
msg={msg}
|
|
||||||
theme={theme}
|
|
||||||
style={[styles.repliedThreadName, { color: themes[theme].tintColor }]}
|
|
||||||
preview
|
|
||||||
numberOfLines={1}
|
|
||||||
/>
|
|
||||||
<View style={styles.repliedThreadDisclosure}>
|
<View style={styles.repliedThreadDisclosure}>
|
||||||
<CustomIcon name='chevron-right' color={themes[theme].auxiliaryText} size={20} />
|
<CustomIcon name='chevron-right' color={themes[theme].auxiliaryText} size={20} />
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -14,6 +14,7 @@ import MessageContext from './Context';
|
||||||
import { fileDownloadAndPreview } from '../../utils/fileDownload';
|
import { fileDownloadAndPreview } from '../../utils/fileDownload';
|
||||||
import { formatAttachmentUrl } from '../../lib/utils';
|
import { formatAttachmentUrl } from '../../lib/utils';
|
||||||
import { IAttachment } from '../../definitions/IAttachment';
|
import { IAttachment } from '../../definitions/IAttachment';
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
import RCActivityIndicator from '../ActivityIndicator';
|
import RCActivityIndicator from '../ActivityIndicator';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
@ -99,14 +100,14 @@ interface IMessageTitle {
|
||||||
|
|
||||||
interface IMessageDescription {
|
interface IMessageDescription {
|
||||||
attachment: IAttachment;
|
attachment: IAttachment;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessageFields {
|
interface IMessageFields {
|
||||||
attachment: IAttachment;
|
attachment: IAttachment;
|
||||||
theme: string;
|
theme: string;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessageReply {
|
interface IMessageReply {
|
||||||
|
@ -114,7 +115,7 @@ interface IMessageReply {
|
||||||
timeFormat: string;
|
timeFormat: string;
|
||||||
index: number;
|
index: number;
|
||||||
theme: string;
|
theme: string;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Title = React.memo(({ attachment, timeFormat, theme }: IMessageTitle) => {
|
const Title = React.memo(({ attachment, timeFormat, theme }: IMessageTitle) => {
|
||||||
|
@ -137,10 +138,7 @@ const Description = React.memo(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const { baseUrl, user } = useContext(MessageContext);
|
const { baseUrl, user } = useContext(MessageContext);
|
||||||
return (
|
return <Markdown msg={text} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />;
|
||||||
// @ts-ignore
|
|
||||||
<Markdown msg={text} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} theme={theme} />
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
(prevProps, nextProps) => {
|
(prevProps, nextProps) => {
|
||||||
if (prevProps.attachment.text !== nextProps.attachment.text) {
|
if (prevProps.attachment.text !== nextProps.attachment.text) {
|
||||||
|
@ -180,7 +178,6 @@ 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>
|
||||||
{/* @ts-ignore*/}
|
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={field?.value || ''}
|
msg={field?.value || ''}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
|
@ -266,9 +263,8 @@ const Reply = React.memo(
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
</Touchable>
|
</Touchable>
|
||||||
{/* @ts-ignore*/}
|
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={attachment.description!}
|
msg={attachment.description}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
username={user.username}
|
username={user.username}
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { LISTENER } from '../Toast';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { IAttachment } from '../../definitions/IAttachment';
|
import { IAttachment } from '../../definitions/IAttachment';
|
||||||
import RCActivityIndicator from '../ActivityIndicator';
|
import RCActivityIndicator from '../ActivityIndicator';
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])];
|
const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])];
|
||||||
const isTypeSupported = (type: any) => SUPPORTED_TYPES.indexOf(type) !== -1;
|
const isTypeSupported = (type: any) => SUPPORTED_TYPES.indexOf(type) !== -1;
|
||||||
|
@ -33,7 +34,7 @@ const styles = StyleSheet.create({
|
||||||
interface IMessageVideo {
|
interface IMessageVideo {
|
||||||
file: IAttachment;
|
file: IAttachment;
|
||||||
showAttachment: Function;
|
showAttachment: Function;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +83,6 @@ const Video = React.memo(
|
||||||
<CustomIcon name='play-filled' size={54} color={themes[theme].buttonText} />
|
<CustomIcon name='play-filled' size={54} color={themes[theme].buttonText} />
|
||||||
)}
|
)}
|
||||||
</Touchable>
|
</Touchable>
|
||||||
{/* @ts-ignore*/}
|
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={file.description}
|
msg={file.description}
|
||||||
baseUrl={baseUrl}
|
baseUrl={baseUrl}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/encryption/constants';
|
||||||
import messagesStatus from '../../constants/messagesStatus';
|
import messagesStatus from '../../constants/messagesStatus';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import openLink from '../../utils/openLink';
|
import openLink from '../../utils/openLink';
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
interface IMessageContainerProps {
|
interface IMessageContainerProps {
|
||||||
item: any;
|
item: any;
|
||||||
|
@ -42,7 +43,7 @@ interface IMessageContainerProps {
|
||||||
status?: number;
|
status?: number;
|
||||||
isIgnored?: boolean;
|
isIgnored?: boolean;
|
||||||
highlighted?: boolean;
|
highlighted?: boolean;
|
||||||
getCustomEmoji(name: string): void;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
onLongPress?: Function;
|
onLongPress?: Function;
|
||||||
onReactionPress?: Function;
|
onReactionPress?: Function;
|
||||||
onEncryptedPress?: Function;
|
onEncryptedPress?: Function;
|
||||||
|
@ -67,7 +68,7 @@ interface IMessageContainerProps {
|
||||||
|
|
||||||
class MessageContainer extends React.Component<IMessageContainerProps> {
|
class MessageContainer extends React.Component<IMessageContainerProps> {
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
getCustomEmoji: () => {},
|
getCustomEmoji: () => null,
|
||||||
onLongPress: () => {},
|
onLongPress: () => {},
|
||||||
onReactionPress: () => {},
|
onReactionPress: () => {},
|
||||||
onEncryptedPress: () => {},
|
onEncryptedPress: () => {},
|
||||||
|
@ -303,7 +304,7 @@ class MessageContainer extends React.Component<IMessageContainerProps> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onLinkPress = (link: any) => {
|
onLinkPress = (link: string): void => {
|
||||||
const { item, theme, jumpToMessage } = this.props;
|
const { item, theme, jumpToMessage } = this.props;
|
||||||
const isMessageLink = item?.attachments?.findIndex((att: any) => att?.message_link === link) !== -1;
|
const isMessageLink = item?.attachments?.findIndex((att: any) => att?.message_link === link) !== -1;
|
||||||
if (isMessageLink) {
|
if (isMessageLink) {
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
import { MarkdownAST } from '@rocket.chat/message-parser';
|
import { MarkdownAST } from '@rocket.chat/message-parser';
|
||||||
|
|
||||||
|
import { IUserChannel, IUserMention } from '../markdown/interfaces';
|
||||||
|
import { TGetCustomEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
export type TMessageType = 'discussion-created' | 'jitsi_call_started';
|
export type TMessageType = 'discussion-created' | 'jitsi_call_started';
|
||||||
|
|
||||||
export interface IMessageAttachments {
|
export interface IMessageAttachments {
|
||||||
attachments: any;
|
attachments: any;
|
||||||
timeFormat: string;
|
timeFormat: string;
|
||||||
showAttachment: Function;
|
showAttachment: Function;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +31,7 @@ export interface IMessageAvatar {
|
||||||
};
|
};
|
||||||
small?: boolean;
|
small?: boolean;
|
||||||
navToRoomInfo: Function;
|
navToRoomInfo: Function;
|
||||||
getCustomEmoji(): void;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
theme: string;
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,8 +62,6 @@ export interface IUser {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserMention = Pick<IUser, 'id' | 'username' | 'name'>;
|
|
||||||
|
|
||||||
export interface IMessageContent {
|
export interface IMessageContent {
|
||||||
_id: string;
|
_id: string;
|
||||||
isTemp: boolean;
|
isTemp: boolean;
|
||||||
|
@ -72,12 +73,9 @@ export interface IMessageContent {
|
||||||
theme: string;
|
theme: string;
|
||||||
isEdited: boolean;
|
isEdited: boolean;
|
||||||
isEncrypted: boolean;
|
isEncrypted: boolean;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
channels: {
|
channels: IUserChannel[];
|
||||||
name: string;
|
mentions: IUserMention[];
|
||||||
_id: number;
|
|
||||||
}[];
|
|
||||||
mentions: UserMention[];
|
|
||||||
navToRoomInfo: Function;
|
navToRoomInfo: Function;
|
||||||
useRealName: boolean;
|
useRealName: boolean;
|
||||||
isIgnored: boolean;
|
isIgnored: boolean;
|
||||||
|
@ -96,7 +94,7 @@ export interface IMessageEmoji {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
standardEmojiStyle: object;
|
standardEmojiStyle: object;
|
||||||
customEmojiStyle: object;
|
customEmojiStyle: object;
|
||||||
getCustomEmoji: Function;
|
getCustomEmoji: TGetCustomEmoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMessageThread {
|
export interface IMessageThread {
|
||||||
|
|
|
@ -76,7 +76,7 @@ type TInfoMessage = {
|
||||||
msg: string;
|
msg: string;
|
||||||
author: { username: string };
|
author: { username: string };
|
||||||
};
|
};
|
||||||
export const getInfoMessage = ({ type, role, msg, author }: TInfoMessage) => {
|
export const getInfoMessage = ({ type, role, msg, author }: TInfoMessage): string => {
|
||||||
const { username } = author;
|
const { username } = author;
|
||||||
if (type === 'rm') {
|
if (type === 'rm') {
|
||||||
return I18n.t('Message_removed');
|
return I18n.t('Message_removed');
|
||||||
|
|
|
@ -6,7 +6,7 @@ export interface IEmoji {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICustomEmoji {
|
export interface ICustomEmoji {
|
||||||
baseUrl: string;
|
baseUrl?: string;
|
||||||
emoji: IEmoji;
|
emoji: IEmoji;
|
||||||
style: any;
|
style: any;
|
||||||
}
|
}
|
||||||
|
@ -20,3 +20,5 @@ export interface IEmojiCategory {
|
||||||
style: any;
|
style: any;
|
||||||
tabLabel: string;
|
tabLabel: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TGetCustomEmoji = (name: string) => IEmoji | null;
|
|
@ -63,6 +63,7 @@ export interface IMessage {
|
||||||
_id: string;
|
_id: string;
|
||||||
rid: string;
|
rid: string;
|
||||||
msg?: string;
|
msg?: string;
|
||||||
|
id?: string;
|
||||||
t?: MessageType;
|
t?: MessageType;
|
||||||
ts: string | Date;
|
ts: string | Date;
|
||||||
u: IUserMessage;
|
u: IUserMessage;
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { dequal } from 'dequal';
|
||||||
|
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import Markdown from '../../containers/markdown';
|
import { MarkdownPreview } from '../../containers/markdown';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/encryption/constants';
|
import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/encryption/constants';
|
||||||
|
|
||||||
|
@ -65,8 +65,7 @@ const arePropsEqual = (oldProps: any, newProps: any) => dequal(oldProps, newProp
|
||||||
|
|
||||||
const LastMessage = React.memo(
|
const LastMessage = React.memo(
|
||||||
({ lastMessage, type, showLastMessage, username, alert, useRealName, theme }: ILastMessage) => (
|
({ lastMessage, type, showLastMessage, username, alert, useRealName, theme }: ILastMessage) => (
|
||||||
// @ts-ignore
|
<MarkdownPreview
|
||||||
<Markdown
|
|
||||||
msg={formatMsg({
|
msg={formatMsg({
|
||||||
lastMessage,
|
lastMessage,
|
||||||
type,
|
type,
|
||||||
|
@ -75,11 +74,7 @@ const LastMessage = React.memo(
|
||||||
useRealName
|
useRealName
|
||||||
})}
|
})}
|
||||||
style={[styles.markdownText, { color: alert ? themes[theme].bodyText : themes[theme].auxiliaryText }]}
|
style={[styles.markdownText, { color: alert ? themes[theme].bodyText : themes[theme].auxiliaryText }]}
|
||||||
customEmojis={false}
|
|
||||||
useRealName={useRealName}
|
|
||||||
numberOfLines={2}
|
numberOfLines={2}
|
||||||
preview
|
|
||||||
theme={theme}
|
|
||||||
testID='room-item-last-message'
|
testID='room-item-last-message'
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { useTheme } from '../../theme';
|
||||||
import Avatar from '../../containers/Avatar';
|
import Avatar from '../../containers/Avatar';
|
||||||
import sharedStyles from '../Styles';
|
import sharedStyles from '../Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import Markdown from '../../containers/markdown';
|
import { MarkdownPreview } from '../../containers/markdown';
|
||||||
import { formatDateThreads, makeThreadName } from '../../utils/room';
|
import { formatDateThreads, makeThreadName } from '../../utils/room';
|
||||||
import DiscussionDetails from './DiscussionDetails';
|
import DiscussionDetails from './DiscussionDetails';
|
||||||
import { TThreadModel } from '../../definitions/IThread';
|
import { TThreadModel } from '../../definitions/IThread';
|
||||||
|
@ -49,14 +49,13 @@ const styles = StyleSheet.create({
|
||||||
|
|
||||||
interface IItem {
|
interface IItem {
|
||||||
item: TThreadModel;
|
item: TThreadModel;
|
||||||
baseUrl: string;
|
|
||||||
onPress: {
|
onPress: {
|
||||||
(...args: any[]): void;
|
(...args: any[]): void;
|
||||||
stop(): void;
|
stop(): void;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const Item = ({ item, baseUrl, onPress }: IItem): JSX.Element => {
|
const Item = ({ item, onPress }: IItem): JSX.Element => {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const username = item?.u?.username;
|
const username = item?.u?.username;
|
||||||
let messageTime = '';
|
let messageTime = '';
|
||||||
|
@ -82,18 +81,7 @@ const Item = ({ item, baseUrl, onPress }: IItem): JSX.Element => {
|
||||||
{messageTime ? <Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{messageTime}</Text> : null}
|
{messageTime ? <Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{messageTime}</Text> : null}
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.messageContainer}>
|
<View style={styles.messageContainer}>
|
||||||
{username ? (
|
{username ? <MarkdownPreview msg={makeThreadName(item)} numberOfLines={2} style={[styles.markdown]} /> : null}
|
||||||
/* @ts-ignore */
|
|
||||||
<Markdown
|
|
||||||
msg={makeThreadName(item)}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
username={username}
|
|
||||||
theme={theme}
|
|
||||||
numberOfLines={2}
|
|
||||||
style={[styles.markdown]}
|
|
||||||
preview
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</View>
|
</View>
|
||||||
{messageDate ? <DiscussionDetails item={item} date={messageDate} /> : null}
|
{messageDate ? <DiscussionDetails item={item} date={messageDate} /> : null}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -46,16 +46,11 @@ class E2EHowItWorksView extends React.Component<IE2EHowItWorksViewProps, any> {
|
||||||
|
|
||||||
const infoStyle = [styles.info, { color: themes[theme].bodyText }];
|
const infoStyle = [styles.info, { color: themes[theme].bodyText }];
|
||||||
|
|
||||||
// TODO: Refactor when migrate Markdown
|
|
||||||
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'>
|
||||||
{/* @ts-ignore */}
|
|
||||||
<Markdown msg={I18n.t('E2E_How_It_Works_info1')} style={infoStyle} theme={theme} />
|
<Markdown msg={I18n.t('E2E_How_It_Works_info1')} style={infoStyle} theme={theme} />
|
||||||
{/* @ts-ignore */}
|
|
||||||
<Markdown msg={I18n.t('E2E_How_It_Works_info2')} style={infoStyle} theme={theme} />
|
<Markdown msg={I18n.t('E2E_How_It_Works_info2')} style={infoStyle} theme={theme} />
|
||||||
{/* @ts-ignore */}
|
|
||||||
<Markdown msg={I18n.t('E2E_How_It_Works_info3')} style={infoStyle} theme={theme} />
|
<Markdown msg={I18n.t('E2E_How_It_Works_info3')} style={infoStyle} theme={theme} />
|
||||||
{/* @ts-ignore */}
|
|
||||||
<Markdown msg={I18n.t('E2E_How_It_Works_info4')} style={infoStyle} theme={theme} />
|
<Markdown msg={I18n.t('E2E_How_It_Works_info4')} style={infoStyle} theme={theme} />
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
|
|
|
@ -93,8 +93,7 @@ class InviteUsersView extends React.Component<IInviteUsersViewProps, any> {
|
||||||
renderExpiration = () => {
|
renderExpiration = () => {
|
||||||
const { theme } = this.props;
|
const { theme } = this.props;
|
||||||
const expirationMessage = this.linkExpirationText();
|
const expirationMessage = this.linkExpirationText();
|
||||||
// @ts-ignore
|
return <Markdown msg={expirationMessage} theme={theme} />;
|
||||||
return <Markdown msg={expirationMessage} username='' baseUrl='' theme={theme} />;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -21,6 +21,7 @@ import getThreadName from '../../lib/methods/getThreadName';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { ChatsStackParamList } from '../../stacks/types';
|
import { ChatsStackParamList } from '../../stacks/types';
|
||||||
import { ISubscription, SubscriptionType } from '../../definitions/ISubscription';
|
import { ISubscription, SubscriptionType } from '../../definitions/ISubscription';
|
||||||
|
import { IEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
interface IMessagesViewProps {
|
interface IMessagesViewProps {
|
||||||
user: {
|
user: {
|
||||||
|
@ -34,7 +35,7 @@ interface IMessagesViewProps {
|
||||||
StackNavigationProp<MasterDetailInsideStackParamList>
|
StackNavigationProp<MasterDetailInsideStackParamList>
|
||||||
>;
|
>;
|
||||||
route: RouteProp<ChatsStackParamList, 'MessagesView'>;
|
route: RouteProp<ChatsStackParamList, 'MessagesView'>;
|
||||||
customEmojis: { [key: string]: string };
|
customEmojis: { [key: string]: IEmoji };
|
||||||
theme: string;
|
theme: string;
|
||||||
showActionSheet: Function;
|
showActionSheet: Function;
|
||||||
useRealName: boolean;
|
useRealName: boolean;
|
||||||
|
|
|
@ -21,7 +21,7 @@ import StatusBar from '../../containers/StatusBar';
|
||||||
import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors';
|
import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import * as HeaderButton from '../../containers/HeaderButton';
|
import * as HeaderButton from '../../containers/HeaderButton';
|
||||||
import Markdown from '../../containers/markdown';
|
import { MarkdownPreview } from '../../containers/markdown';
|
||||||
import { showConfirmationAlert, showErrorAlert } from '../../utils/info';
|
import { showConfirmationAlert, showErrorAlert } from '../../utils/info';
|
||||||
import SafeAreaView from '../../containers/SafeAreaView';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import { E2E_ROOM_TYPES } from '../../lib/encryption/constants';
|
import { E2E_ROOM_TYPES } from '../../lib/encryption/constants';
|
||||||
|
@ -723,20 +723,14 @@ class RoomActionsView extends React.Component {
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
<Markdown
|
<MarkdownPreview
|
||||||
preview
|
|
||||||
msg={t === 'd' ? `@${name}` : topic}
|
msg={t === 'd' ? `@${name}` : topic}
|
||||||
style={[styles.roomDescription, { color: themes[theme].auxiliaryText }]}
|
style={[styles.roomDescription, { color: themes[theme].auxiliaryText }]}
|
||||||
numberOfLines={1}
|
|
||||||
theme={theme}
|
|
||||||
/>
|
/>
|
||||||
{room.t === 'd' && (
|
{room.t === 'd' && (
|
||||||
<Markdown
|
<MarkdownPreview
|
||||||
msg={member.statusText}
|
msg={member.statusText}
|
||||||
style={[styles.roomDescription, { color: themes[theme].auxiliaryText }]}
|
style={[styles.roomDescription, { color: themes[theme].auxiliaryText }]}
|
||||||
preview
|
|
||||||
theme={theme}
|
|
||||||
numberOfLines={1}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -18,7 +18,7 @@ import StatusBar from '../../containers/StatusBar';
|
||||||
import log, { events, logEvent } from '../../utils/log';
|
import log, { events, logEvent } from '../../utils/log';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withTheme } from '../../theme';
|
import { withTheme } from '../../theme';
|
||||||
import Markdown from '../../containers/markdown';
|
import { MarkdownPreview } from '../../containers/markdown';
|
||||||
import { LISTENER } from '../../containers/Toast';
|
import { LISTENER } from '../../containers/Toast';
|
||||||
import EventEmitter from '../../utils/events';
|
import EventEmitter from '../../utils/events';
|
||||||
import SafeAreaView from '../../containers/SafeAreaView';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
@ -42,12 +42,7 @@ const getRoomTitle = (room, type, name, username, statusText, theme) =>
|
||||||
)}
|
)}
|
||||||
{!!statusText && (
|
{!!statusText && (
|
||||||
<View testID='room-info-view-custom-status'>
|
<View testID='room-info-view-custom-status'>
|
||||||
<Markdown
|
<MarkdownPreview msg={statusText} style={[styles.roomUsername, { color: themes[theme].auxiliaryText }]} />
|
||||||
msg={statusText}
|
|
||||||
style={[styles.roomUsername, { color: themes[theme].auxiliaryText }]}
|
|
||||||
preview
|
|
||||||
theme={theme}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
|
||||||
import { BorderlessButton, ScrollView } from 'react-native-gesture-handler';
|
import { BorderlessButton, ScrollView } from 'react-native-gesture-handler';
|
||||||
import Modal from 'react-native-modal';
|
import Modal from 'react-native-modal';
|
||||||
|
|
||||||
import Markdown from '../../containers/markdown';
|
import Markdown, { MarkdownPreview } from '../../containers/markdown';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
@ -22,7 +22,7 @@ const Banner = React.memo(
|
||||||
style={[styles.bannerContainer, { backgroundColor: themes[theme].bannerBackground }]}
|
style={[styles.bannerContainer, { backgroundColor: themes[theme].bannerBackground }]}
|
||||||
testID='room-view-banner'
|
testID='room-view-banner'
|
||||||
onPress={toggleModal}>
|
onPress={toggleModal}>
|
||||||
<Markdown msg={text} theme={theme} numberOfLines={1} style={[styles.bannerText]} preview />
|
<MarkdownPreview msg={text} style={[styles.bannerText]} />
|
||||||
<BorderlessButton onPress={closeBanner}>
|
<BorderlessButton onPress={closeBanner}>
|
||||||
<CustomIcon color={themes[theme].auxiliaryText} name='close' size={20} />
|
<CustomIcon color={themes[theme].auxiliaryText} name='close' size={20} />
|
||||||
</BorderlessButton>
|
</BorderlessButton>
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -32,6 +32,7 @@ import { isIOS } from '../../utils/deviceInfo';
|
||||||
import { compareServerVersion } from '../../lib/utils';
|
import { compareServerVersion } from '../../lib/utils';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { InsideStackParamList, ChatsStackParamList } from '../../stacks/types';
|
import { InsideStackParamList, ChatsStackParamList } from '../../stacks/types';
|
||||||
|
import { IEmoji } from '../../definitions/IEmoji';
|
||||||
|
|
||||||
const QUERY_SIZE = 50;
|
const QUERY_SIZE = 50;
|
||||||
|
|
||||||
|
@ -66,10 +67,7 @@ interface ISearchMessagesViewProps extends INavigationOption {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
serverVersion: string;
|
serverVersion: string;
|
||||||
customEmojis: {
|
customEmojis: {
|
||||||
[key: string]: {
|
[key: string]: IEmoji;
|
||||||
name: string;
|
|
||||||
extension: string;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
theme: string;
|
theme: string;
|
||||||
useRealName: boolean;
|
useRealName: boolean;
|
||||||
|
@ -312,8 +310,7 @@ class SearchMessagesView extends React.Component<ISearchMessagesViewProps, ISear
|
||||||
testID='search-message-view-input'
|
testID='search-message-view-input'
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
{/* @ts-ignore */}
|
<Markdown msg={I18n.t('You_can_search_using_RegExp_eg')} theme={theme} />
|
||||||
<Markdown msg={I18n.t('You_can_search_using_RegExp_eg')} username='' baseUrl='' theme={theme} />
|
|
||||||
<View style={[styles.divider, { backgroundColor: themes[theme].separatorColor }]} />
|
<View style={[styles.divider, { backgroundColor: themes[theme].separatorColor }]} />
|
||||||
</View>
|
</View>
|
||||||
{this.renderList()}
|
{this.renderList()}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { useTheme } from '../../theme';
|
||||||
import Avatar from '../../containers/Avatar';
|
import Avatar from '../../containers/Avatar';
|
||||||
import sharedStyles from '../Styles';
|
import sharedStyles from '../Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import Markdown from '../../containers/markdown';
|
import { MarkdownPreview } from '../../containers/markdown';
|
||||||
import { formatDateThreads, makeThreadName } from '../../utils/room';
|
import { formatDateThreads, makeThreadName } from '../../utils/room';
|
||||||
import ThreadDetails from '../../containers/ThreadDetails';
|
import ThreadDetails from '../../containers/ThreadDetails';
|
||||||
import { TThreadModel } from '../../definitions/IThread';
|
import { TThreadModel } from '../../definitions/IThread';
|
||||||
|
@ -58,7 +58,6 @@ const styles = StyleSheet.create({
|
||||||
|
|
||||||
interface IItem {
|
interface IItem {
|
||||||
item: TThreadModel;
|
item: TThreadModel;
|
||||||
baseUrl: string;
|
|
||||||
useRealName: boolean;
|
useRealName: boolean;
|
||||||
user: any;
|
user: any;
|
||||||
badgeColor?: string;
|
badgeColor?: string;
|
||||||
|
@ -66,7 +65,7 @@ interface IItem {
|
||||||
toggleFollowThread: (isFollowing: boolean, id: string) => void;
|
toggleFollowThread: (isFollowing: boolean, id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Item = ({ item, baseUrl, useRealName, user, badgeColor, onPress, toggleFollowThread }: IItem) => {
|
const Item = ({ item, useRealName, user, badgeColor, onPress, toggleFollowThread }: IItem) => {
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const username = (useRealName && item?.u?.name) || item?.u?.username;
|
const username = (useRealName && item?.u?.name) || item?.u?.username;
|
||||||
let time;
|
let time;
|
||||||
|
@ -89,18 +88,7 @@ const Item = ({ item, baseUrl, useRealName, user, badgeColor, onPress, toggleFol
|
||||||
<Text style={[styles.time, { color: themes[theme!].auxiliaryText }]}>{time}</Text>
|
<Text style={[styles.time, { color: themes[theme!].auxiliaryText }]}>{time}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.messageContainer}>
|
<View style={styles.messageContainer}>
|
||||||
{makeThreadName(item) && username ? (
|
<MarkdownPreview msg={makeThreadName(item)} numberOfLines={2} style={[styles.markdown]} />
|
||||||
/* @ts-ignore */
|
|
||||||
<Markdown
|
|
||||||
msg={makeThreadName(item)}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
username={username}
|
|
||||||
theme={theme}
|
|
||||||
numberOfLines={2}
|
|
||||||
style={[styles.markdown]}
|
|
||||||
preview
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{badgeColor ? <View style={[styles.badge, { backgroundColor: badgeColor }]} /> : null}
|
{badgeColor ? <View style={[styles.badge, { backgroundColor: badgeColor }]} /> : null}
|
||||||
</View>
|
</View>
|
||||||
<ThreadDetails item={item} user={user} toggleFollowThread={toggleFollowThread} style={styles.threadDetails} />
|
<ThreadDetails item={item} user={user} toggleFollowThread={toggleFollowThread} style={styles.threadDetails} />
|
||||||
|
|
|
@ -462,7 +462,7 @@ class ThreadMessagesView extends React.Component<IThreadMessagesViewProps, IThre
|
||||||
};
|
};
|
||||||
|
|
||||||
renderItem = ({ item }: { item: TThreadModel }) => {
|
renderItem = ({ item }: { item: TThreadModel }) => {
|
||||||
const { user, navigation, baseUrl, useRealName } = this.props;
|
const { user, navigation, useRealName } = this.props;
|
||||||
const badgeColor = this.getBadgeColor(item);
|
const badgeColor = this.getBadgeColor(item);
|
||||||
return (
|
return (
|
||||||
<Item
|
<Item
|
||||||
|
@ -470,7 +470,6 @@ class ThreadMessagesView extends React.Component<IThreadMessagesViewProps, IThre
|
||||||
item,
|
item,
|
||||||
user,
|
user,
|
||||||
navigation,
|
navigation,
|
||||||
baseUrl,
|
|
||||||
useRealName,
|
useRealName,
|
||||||
badgeColor
|
badgeColor
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
/* eslint-disable import/no-extraneous-dependencies */
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ScrollView, StyleSheet, View } from 'react-native';
|
import { ScrollView, StyleSheet, View } from 'react-native';
|
||||||
import { storiesOf } from '@storybook/react-native';
|
import { storiesOf } from '@storybook/react-native';
|
||||||
|
|
||||||
import Markdown from '../../app/containers/markdown';
|
import Markdown, { MarkdownPreview } from '../../app/containers/markdown';
|
||||||
import { themes } from '../../app/constants/colors';
|
import { themes } from '../../app/constants/colors';
|
||||||
|
import { TGetCustomEmoji, IEmoji } from '../../app/definitions/IEmoji';
|
||||||
|
|
||||||
const theme = 'light';
|
const theme = 'light';
|
||||||
|
|
||||||
|
@ -33,12 +33,12 @@ d
|
||||||
e`;
|
e`;
|
||||||
const sequentialEmptySpacesText = 'a b c';
|
const sequentialEmptySpacesText = 'a b c';
|
||||||
|
|
||||||
const getCustomEmoji = content => {
|
const getCustomEmoji: TGetCustomEmoji = content => {
|
||||||
const customEmoji = {
|
const customEmoji = {
|
||||||
marioparty: { name: content, extension: 'gif' },
|
marioparty: { name: content, extension: 'gif' },
|
||||||
react_rocket: { name: content, extension: 'png' },
|
react_rocket: { name: content, extension: 'png' },
|
||||||
nyan_rocket: { name: content, extension: 'png' }
|
nyan_rocket: { name: content, extension: 'png' }
|
||||||
}[content];
|
}[content] as IEmoji;
|
||||||
return customEmoji;
|
return customEmoji;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -62,42 +62,12 @@ stories.add('Edited', () => (
|
||||||
|
|
||||||
stories.add('Preview', () => (
|
stories.add('Preview', () => (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Markdown msg={longText} theme={theme} numberOfLines={1} preview />
|
<MarkdownPreview msg={longText} />
|
||||||
<Markdown msg={lineBreakText} theme={theme} numberOfLines={1} preview />
|
<MarkdownPreview msg={lineBreakText} />
|
||||||
<Markdown msg={sequentialEmptySpacesText} theme={theme} numberOfLines={1} preview />
|
<MarkdownPreview msg={sequentialEmptySpacesText} />
|
||||||
<Markdown
|
<MarkdownPreview msg='@rocket.cat @name1 @all @here @unknown #general #unknown' />
|
||||||
msg='@rocket.cat @name1 @all @here @unknown #general #unknown'
|
<MarkdownPreview msg='Testing: 😃 :+1: :marioparty:' />
|
||||||
theme={theme}
|
<MarkdownPreview msg='Fallback from new md to old' />
|
||||||
numberOfLines={1}
|
|
||||||
preview
|
|
||||||
mentions={[
|
|
||||||
{ _id: 'random', name: 'Rocket Cat', username: 'rocket.cat' },
|
|
||||||
{ _id: 'random2', name: 'Name', username: 'name1' },
|
|
||||||
{ _id: 'here', username: 'here' },
|
|
||||||
{ _id: 'all', username: 'all' }
|
|
||||||
]}
|
|
||||||
channels={[{ _id: '123', name: 'test-channel' }]}
|
|
||||||
username='rocket.cat'
|
|
||||||
/>
|
|
||||||
<Markdown msg='Testing: 😃 :+1: :marioparty:' getCustomEmoji={getCustomEmoji} theme={theme} numberOfLines={1} preview />
|
|
||||||
<Markdown
|
|
||||||
msg='Fallback from new md to old'
|
|
||||||
getCustomEmoji={getCustomEmoji}
|
|
||||||
theme={theme}
|
|
||||||
numberOfLines={1}
|
|
||||||
preview
|
|
||||||
md={[
|
|
||||||
{
|
|
||||||
type: 'PARAGRAPH',
|
|
||||||
value: [
|
|
||||||
{
|
|
||||||
type: 'PLAIN_TEXT',
|
|
||||||
value: 'This is Rocket.Chat'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
));
|
));
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue