[IMPROVEMENT] Message Touchable (#2082)
* [FIX] Avatar touchable * [IMPROVEMENT] onLongPress on all Message Touchables * [IMPROVEMENT] User & baseUrl on MessageContext * [FIX] Context Access * [FIX] BaseURL * Fix User Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
2ea6d34fd1
commit
e46ee13b38
File diff suppressed because it is too large
Load Diff
|
@ -2,12 +2,13 @@ import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import FastImage from 'react-native-fast-image';
|
import FastImage from 'react-native-fast-image';
|
||||||
|
import Touchable from 'react-native-platform-touchable';
|
||||||
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
|
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
|
||||||
import Touch from '../utils/touch';
|
|
||||||
import { avatarURL } from '../utils/avatar';
|
import { avatarURL } from '../utils/avatar';
|
||||||
|
|
||||||
const Avatar = React.memo(({
|
const Avatar = React.memo(({
|
||||||
text, size, baseUrl, borderRadius, style, avatar, type, children, userId, token, onPress, theme
|
text, size, baseUrl, borderRadius, style, avatar, type, children, userId, token, onPress
|
||||||
}) => {
|
}) => {
|
||||||
const avatarStyle = {
|
const avatarStyle = {
|
||||||
width: size,
|
width: size,
|
||||||
|
@ -36,9 +37,9 @@ const Avatar = React.memo(({
|
||||||
|
|
||||||
if (onPress) {
|
if (onPress) {
|
||||||
image = (
|
image = (
|
||||||
<Touch onPress={onPress} theme={theme}>
|
<Touchable onPress={onPress}>
|
||||||
{image}
|
{image}
|
||||||
</Touch>
|
</Touchable>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +62,6 @@ Avatar.propTypes = {
|
||||||
children: PropTypes.object,
|
children: PropTypes.object,
|
||||||
userId: PropTypes.string,
|
userId: PropTypes.string,
|
||||||
token: PropTypes.string,
|
token: PropTypes.string,
|
||||||
theme: PropTypes.string,
|
|
||||||
onPress: PropTypes.func
|
onPress: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import Video from './Video';
|
||||||
import Reply from './Reply';
|
import Reply from './Reply';
|
||||||
|
|
||||||
const Attachments = React.memo(({
|
const Attachments = React.memo(({
|
||||||
attachments, timeFormat, user, baseUrl, showAttachment, getCustomEmoji, theme
|
attachments, timeFormat, showAttachment, getCustomEmoji, theme
|
||||||
}) => {
|
}) => {
|
||||||
if (!attachments || attachments.length === 0) {
|
if (!attachments || attachments.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -16,25 +16,23 @@ const Attachments = React.memo(({
|
||||||
|
|
||||||
return attachments.map((file, index) => {
|
return attachments.map((file, index) => {
|
||||||
if (file.image_url) {
|
if (file.image_url) {
|
||||||
return <Image key={file.image_url} file={file} user={user} baseUrl={baseUrl} showAttachment={showAttachment} getCustomEmoji={getCustomEmoji} theme={theme} />;
|
return <Image key={file.image_url} file={file} showAttachment={showAttachment} getCustomEmoji={getCustomEmoji} theme={theme} />;
|
||||||
}
|
}
|
||||||
if (file.audio_url) {
|
if (file.audio_url) {
|
||||||
return <Audio key={file.audio_url} file={file} user={user} baseUrl={baseUrl} getCustomEmoji={getCustomEmoji} theme={theme} />;
|
return <Audio key={file.audio_url} file={file} getCustomEmoji={getCustomEmoji} theme={theme} />;
|
||||||
}
|
}
|
||||||
if (file.video_url) {
|
if (file.video_url) {
|
||||||
return <Video key={file.video_url} file={file} user={user} baseUrl={baseUrl} showAttachment={showAttachment} getCustomEmoji={getCustomEmoji} theme={theme} />;
|
return <Video key={file.video_url} file={file} showAttachment={showAttachment} getCustomEmoji={getCustomEmoji} theme={theme} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/no-array-index-key
|
// eslint-disable-next-line react/no-array-index-key
|
||||||
return <Reply key={index} index={index} attachment={file} timeFormat={timeFormat} user={user} baseUrl={baseUrl} getCustomEmoji={getCustomEmoji} theme={theme} />;
|
return <Reply key={index} index={index} attachment={file} timeFormat={timeFormat} getCustomEmoji={getCustomEmoji} theme={theme} />;
|
||||||
});
|
});
|
||||||
}, (prevProps, nextProps) => isEqual(prevProps.attachments, nextProps.attachments) && prevProps.theme === nextProps.theme);
|
}, (prevProps, nextProps) => isEqual(prevProps.attachments, nextProps.attachments) && prevProps.theme === nextProps.theme);
|
||||||
|
|
||||||
Attachments.propTypes = {
|
Attachments.propTypes = {
|
||||||
attachments: PropTypes.array,
|
attachments: PropTypes.array,
|
||||||
timeFormat: PropTypes.string,
|
timeFormat: PropTypes.string,
|
||||||
user: PropTypes.object,
|
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
showAttachment: PropTypes.func,
|
showAttachment: PropTypes.func,
|
||||||
getCustomEmoji: PropTypes.func,
|
getCustomEmoji: PropTypes.func,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
|
|
|
@ -7,14 +7,15 @@ import { Audio } from 'expo-av';
|
||||||
import Slider from '@react-native-community/slider';
|
import Slider from '@react-native-community/slider';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import equal from 'deep-equal';
|
import equal from 'deep-equal';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
|
||||||
|
|
||||||
|
import Touchable from './Touchable';
|
||||||
import Markdown from '../markdown';
|
import Markdown 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 { isAndroid, isIOS } from '../../utils/deviceInfo';
|
import { isAndroid, isIOS } from '../../utils/deviceInfo';
|
||||||
import { withSplit } from '../../split';
|
import { withSplit } from '../../split';
|
||||||
|
import MessageContext from './Context';
|
||||||
import ActivityIndicator from '../ActivityIndicator';
|
import ActivityIndicator from '../ActivityIndicator';
|
||||||
|
|
||||||
const mode = {
|
const mode = {
|
||||||
|
@ -91,10 +92,10 @@ Button.propTypes = {
|
||||||
Button.displayName = 'MessageAudioButton';
|
Button.displayName = 'MessageAudioButton';
|
||||||
|
|
||||||
class MessageAudio extends React.Component {
|
class MessageAudio extends React.Component {
|
||||||
|
static contextType = MessageContext;
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
file: PropTypes.object.isRequired,
|
file: PropTypes.object.isRequired,
|
||||||
baseUrl: PropTypes.string.isRequired,
|
|
||||||
user: PropTypes.object.isRequired,
|
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
split: PropTypes.bool,
|
split: PropTypes.bool,
|
||||||
getCustomEmoji: PropTypes.func
|
getCustomEmoji: PropTypes.func
|
||||||
|
@ -102,13 +103,11 @@ class MessageAudio extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
const { baseUrl, file, user } = props;
|
|
||||||
this.state = {
|
this.state = {
|
||||||
loading: false,
|
loading: false,
|
||||||
currentTime: 0,
|
currentTime: 0,
|
||||||
duration: 0,
|
duration: 0,
|
||||||
paused: true,
|
paused: true
|
||||||
uri: `${ baseUrl }${ file.audio_url }?rc_uid=${ user.id }&rc_token=${ user.token }`
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.sound = new Audio.Sound();
|
this.sound = new Audio.Sound();
|
||||||
|
@ -116,12 +115,13 @@ class MessageAudio extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
const { uri } = this.state;
|
const { file } = this.props;
|
||||||
|
const { baseUrl, user } = this.context;
|
||||||
|
|
||||||
this.setState({ loading: true });
|
this.setState({ loading: true });
|
||||||
try {
|
try {
|
||||||
await Audio.setAudioModeAsync(mode);
|
await Audio.setAudioModeAsync(mode);
|
||||||
await this.sound.loadAsync({ uri });
|
await this.sound.loadAsync({ uri: `${ baseUrl }${ file.audio_url }?rc_uid=${ user.id }&rc_token=${ user.token }` });
|
||||||
} catch {
|
} catch {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ class MessageAudio extends React.Component {
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
const {
|
const {
|
||||||
currentTime, duration, paused, uri, loading
|
currentTime, duration, paused, loading
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const { file, split, theme } = this.props;
|
const { file, split, theme } = this.props;
|
||||||
if (nextProps.theme !== theme) {
|
if (nextProps.theme !== theme) {
|
||||||
|
@ -145,9 +145,6 @@ class MessageAudio extends React.Component {
|
||||||
if (nextState.paused !== paused) {
|
if (nextState.paused !== paused) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (nextState.uri !== uri) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!equal(nextProps.file, file)) {
|
if (!equal(nextProps.file, file)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -237,9 +234,10 @@ class MessageAudio extends React.Component {
|
||||||
loading, paused, currentTime, duration
|
loading, paused, currentTime, duration
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const {
|
const {
|
||||||
user, baseUrl, file, getCustomEmoji, split, theme
|
file, getCustomEmoji, split, theme
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { description } = file;
|
const { description } = file;
|
||||||
|
const { baseUrl, user } = this.context;
|
||||||
|
|
||||||
if (!baseUrl) {
|
if (!baseUrl) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { View, Text } from 'react-native';
|
import { View, Text } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import Touchable from './Touchable';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { BUTTON_HIT_SLOP } from './utils';
|
import { BUTTON_HIT_SLOP } from './utils';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
import MessageContext from './Context';
|
||||||
|
|
||||||
const Broadcast = React.memo(({
|
const Broadcast = React.memo(({
|
||||||
author, user, broadcast, replyBroadcast, theme
|
author, broadcast, theme
|
||||||
}) => {
|
}) => {
|
||||||
|
const { user, replyBroadcast } = useContext(MessageContext);
|
||||||
const isOwn = author._id === user.id;
|
const isOwn = author._id === user.id;
|
||||||
if (broadcast && !isOwn) {
|
if (broadcast && !isOwn) {
|
||||||
return (
|
return (
|
||||||
|
@ -36,10 +38,8 @@ const Broadcast = React.memo(({
|
||||||
|
|
||||||
Broadcast.propTypes = {
|
Broadcast.propTypes = {
|
||||||
author: PropTypes.object,
|
author: PropTypes.object,
|
||||||
user: PropTypes.object,
|
|
||||||
broadcast: PropTypes.bool,
|
broadcast: PropTypes.bool,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string
|
||||||
replyBroadcast: PropTypes.func
|
|
||||||
};
|
};
|
||||||
Broadcast.displayName = 'MessageBroadcast';
|
Broadcast.displayName = 'MessageBroadcast';
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, Text } from 'react-native';
|
import { View, Text } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import Touchable from './Touchable';
|
||||||
import { formatLastMessage, BUTTON_HIT_SLOP } from './utils';
|
import { formatLastMessage, BUTTON_HIT_SLOP } from './utils';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { Text, View } from 'react-native';
|
import { Text, View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import equal from 'deep-equal';
|
import equal from 'deep-equal';
|
||||||
|
@ -8,6 +8,7 @@ import styles from './styles';
|
||||||
import Markdown from '../markdown';
|
import Markdown from '../markdown';
|
||||||
import { getInfoMessage } from './utils';
|
import { getInfoMessage } from './utils';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
import MessageContext from './Context';
|
||||||
|
|
||||||
const Content = React.memo((props) => {
|
const Content = React.memo((props) => {
|
||||||
if (props.isInfo) {
|
if (props.isInfo) {
|
||||||
|
@ -26,12 +27,13 @@ const Content = React.memo((props) => {
|
||||||
if (props.tmid && !props.msg) {
|
if (props.tmid && !props.msg) {
|
||||||
content = <Text style={[styles.text, { color: themes[props.theme].bodyText }]}>{I18n.t('Sent_an_attachment')}</Text>;
|
content = <Text style={[styles.text, { color: themes[props.theme].bodyText }]}>{I18n.t('Sent_an_attachment')}</Text>;
|
||||||
} else {
|
} else {
|
||||||
|
const { baseUrl, user } = useContext(MessageContext);
|
||||||
content = (
|
content = (
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={props.msg}
|
msg={props.msg}
|
||||||
baseUrl={props.baseUrl}
|
baseUrl={baseUrl}
|
||||||
getCustomEmoji={props.getCustomEmoji}
|
getCustomEmoji={props.getCustomEmoji}
|
||||||
username={props.user.username}
|
username={user.username}
|
||||||
isEdited={props.isEdited}
|
isEdited={props.isEdited}
|
||||||
numberOfLines={(props.tmid && !props.isThreadRoom) ? 1 : 0}
|
numberOfLines={(props.tmid && !props.isThreadRoom) ? 1 : 0}
|
||||||
preview={props.tmid && !props.isThreadRoom}
|
preview={props.tmid && !props.isThreadRoom}
|
||||||
|
@ -77,8 +79,6 @@ Content.propTypes = {
|
||||||
msg: PropTypes.string,
|
msg: PropTypes.string,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
isEdited: PropTypes.bool,
|
isEdited: PropTypes.bool,
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
user: PropTypes.object,
|
|
||||||
getCustomEmoji: PropTypes.func,
|
getCustomEmoji: PropTypes.func,
|
||||||
channels: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
channels: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
||||||
mentions: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
mentions: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const MessageContext = React.createContext();
|
||||||
|
export default MessageContext;
|
|
@ -1,20 +1,22 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { View, Text } from 'react-native';
|
import { View, Text } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import Touchable from './Touchable';
|
||||||
import { formatLastMessage, formatMessageCount, BUTTON_HIT_SLOP } from './utils';
|
import { formatLastMessage, formatMessageCount, BUTTON_HIT_SLOP } from './utils';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import { DISCUSSION } from './constants';
|
import { DISCUSSION } from './constants';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
import MessageContext from './Context';
|
||||||
|
|
||||||
const Discussion = React.memo(({
|
const Discussion = React.memo(({
|
||||||
msg, dcount, dlm, onDiscussionPress, theme
|
msg, dcount, dlm, theme
|
||||||
}) => {
|
}) => {
|
||||||
const time = formatLastMessage(dlm);
|
const time = formatLastMessage(dlm);
|
||||||
const buttonText = formatMessageCount(dcount, DISCUSSION);
|
const buttonText = formatMessageCount(dcount, DISCUSSION);
|
||||||
|
const { onDiscussionPress } = useContext(MessageContext);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Text style={[styles.startedDiscussion, { color: themes[theme].auxiliaryText }]}>{I18n.t('Started_discussion')}</Text>
|
<Text style={[styles.startedDiscussion, { color: themes[theme].auxiliaryText }]}>{I18n.t('Started_discussion')}</Text>
|
||||||
|
@ -55,8 +57,7 @@ Discussion.propTypes = {
|
||||||
msg: PropTypes.string,
|
msg: PropTypes.string,
|
||||||
dcount: PropTypes.number,
|
dcount: PropTypes.number,
|
||||||
dlm: PropTypes.string,
|
dlm: PropTypes.string,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string
|
||||||
onDiscussionPress: PropTypes.func
|
|
||||||
};
|
};
|
||||||
Discussion.displayName = 'MessageDiscussion';
|
Discussion.displayName = 'MessageDiscussion';
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { Text } from 'react-native';
|
import { Text } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
import shortnameToUnicode from '../../utils/shortnameToUnicode';
|
||||||
import CustomEmoji from '../EmojiPicker/CustomEmoji';
|
import CustomEmoji from '../EmojiPicker/CustomEmoji';
|
||||||
|
import MessageContext from './Context';
|
||||||
|
|
||||||
const Emoji = React.memo(({
|
const Emoji = React.memo(({
|
||||||
content, standardEmojiStyle, customEmojiStyle, baseUrl, getCustomEmoji
|
content, standardEmojiStyle, customEmojiStyle, getCustomEmoji
|
||||||
}) => {
|
}) => {
|
||||||
|
const { baseUrl } = useContext(MessageContext);
|
||||||
const parsedContent = content.replace(/^:|:$/g, '');
|
const parsedContent = content.replace(/^:|:$/g, '');
|
||||||
const emoji = getCustomEmoji(parsedContent);
|
const emoji = getCustomEmoji(parsedContent);
|
||||||
if (emoji) {
|
if (emoji) {
|
||||||
|
@ -20,7 +22,6 @@ Emoji.propTypes = {
|
||||||
content: PropTypes.string,
|
content: PropTypes.string,
|
||||||
standardEmojiStyle: PropTypes.object,
|
standardEmojiStyle: PropTypes.object,
|
||||||
customEmojiStyle: PropTypes.object,
|
customEmojiStyle: PropTypes.object,
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
getCustomEmoji: PropTypes.func
|
getCustomEmoji: PropTypes.func
|
||||||
};
|
};
|
||||||
Emoji.displayName = 'MessageEmoji';
|
Emoji.displayName = 'MessageEmoji';
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import FastImage from 'react-native-fast-image';
|
import FastImage from 'react-native-fast-image';
|
||||||
import equal from 'deep-equal';
|
import equal from 'deep-equal';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
|
||||||
import { createImageProgress } from 'react-native-image-progress';
|
import { createImageProgress } from 'react-native-image-progress';
|
||||||
import * as Progress from 'react-native-progress';
|
import * as Progress from 'react-native-progress';
|
||||||
|
|
||||||
|
import Touchable from './Touchable';
|
||||||
import Markdown from '../markdown';
|
import Markdown from '../markdown';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { formatAttachmentUrl } from '../../lib/utils';
|
import { formatAttachmentUrl } from '../../lib/utils';
|
||||||
import { withSplit } from '../../split';
|
import { withSplit } from '../../split';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
|
import MessageContext from './Context';
|
||||||
|
|
||||||
const ImageProgress = createImageProgress(FastImage);
|
const ImageProgress = createImageProgress(FastImage);
|
||||||
|
|
||||||
|
@ -41,8 +42,9 @@ export const MessageImage = React.memo(({ img, theme }) => (
|
||||||
));
|
));
|
||||||
|
|
||||||
const ImageContainer = React.memo(({
|
const ImageContainer = React.memo(({
|
||||||
file, imageUrl, baseUrl, user, showAttachment, getCustomEmoji, split, theme
|
file, imageUrl, showAttachment, getCustomEmoji, split, theme
|
||||||
}) => {
|
}) => {
|
||||||
|
const { baseUrl, user } = useContext(MessageContext);
|
||||||
const img = imageUrl || formatAttachmentUrl(file.image_url, user.id, user.token, baseUrl);
|
const img = imageUrl || formatAttachmentUrl(file.image_url, user.id, user.token, baseUrl);
|
||||||
if (!img) {
|
if (!img) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -71,8 +73,6 @@ const ImageContainer = React.memo(({
|
||||||
ImageContainer.propTypes = {
|
ImageContainer.propTypes = {
|
||||||
file: PropTypes.object,
|
file: PropTypes.object,
|
||||||
imageUrl: PropTypes.string,
|
imageUrl: PropTypes.string,
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
user: PropTypes.object,
|
|
||||||
showAttachment: PropTypes.func,
|
showAttachment: PropTypes.func,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
getCustomEmoji: PropTypes.func,
|
getCustomEmoji: PropTypes.func,
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
|
||||||
|
import MessageContext from './Context';
|
||||||
|
|
||||||
import User from './User';
|
import User from './User';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import RepliedThread from './RepliedThread';
|
import RepliedThread from './RepliedThread';
|
||||||
|
@ -111,10 +113,11 @@ const MessageTouchable = React.memo((props) => {
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
const { onPress, onLongPress } = useContext(MessageContext);
|
||||||
return (
|
return (
|
||||||
<Touchable
|
<Touchable
|
||||||
onLongPress={props.onLongPress}
|
onLongPress={onLongPress}
|
||||||
onPress={props.onPress}
|
onPress={onPress}
|
||||||
disabled={props.isInfo || props.archived || props.isTemp}
|
disabled={props.isInfo || props.archived || props.isTemp}
|
||||||
>
|
>
|
||||||
<View>
|
<View>
|
||||||
|
@ -129,9 +132,7 @@ MessageTouchable.propTypes = {
|
||||||
hasError: PropTypes.bool,
|
hasError: PropTypes.bool,
|
||||||
isInfo: PropTypes.bool,
|
isInfo: PropTypes.bool,
|
||||||
isTemp: PropTypes.bool,
|
isTemp: PropTypes.bool,
|
||||||
archived: PropTypes.bool,
|
archived: PropTypes.bool
|
||||||
onLongPress: PropTypes.func,
|
|
||||||
onPress: PropTypes.func
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Message.propTypes = {
|
Message.propTypes = {
|
||||||
|
@ -143,7 +144,6 @@ Message.propTypes = {
|
||||||
hasError: PropTypes.bool,
|
hasError: PropTypes.bool,
|
||||||
style: PropTypes.any,
|
style: PropTypes.any,
|
||||||
onLongPress: PropTypes.func,
|
onLongPress: PropTypes.func,
|
||||||
onPress: PropTypes.func,
|
|
||||||
isReadReceiptEnabled: PropTypes.bool,
|
isReadReceiptEnabled: PropTypes.bool,
|
||||||
unread: PropTypes.bool,
|
unread: PropTypes.bool,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
|
|
|
@ -1,34 +1,31 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { TouchableOpacity } from 'react-native';
|
|
||||||
|
|
||||||
import Avatar from '../Avatar';
|
import Avatar from '../Avatar';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
|
import MessageContext from './Context';
|
||||||
|
|
||||||
const MessageAvatar = React.memo(({
|
const MessageAvatar = React.memo(({
|
||||||
isHeader, avatar, author, baseUrl, user, small, navToRoomInfo
|
isHeader, avatar, author, small, navToRoomInfo
|
||||||
}) => {
|
}) => {
|
||||||
|
const { baseUrl, user } = useContext(MessageContext);
|
||||||
if (isHeader && author) {
|
if (isHeader && author) {
|
||||||
const navParam = {
|
const navParam = {
|
||||||
t: 'd',
|
t: 'd',
|
||||||
rid: author._id
|
rid: author._id
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<Avatar
|
||||||
onPress={() => navToRoomInfo(navParam)}
|
style={small ? styles.avatarSmall : styles.avatar}
|
||||||
disabled={author._id === user.id}
|
text={avatar ? '' : author.username}
|
||||||
>
|
size={small ? 20 : 36}
|
||||||
<Avatar
|
borderRadius={small ? 2 : 4}
|
||||||
style={small ? styles.avatarSmall : styles.avatar}
|
onPress={author._id === user.id ? undefined : () => navToRoomInfo(navParam)}
|
||||||
text={avatar ? '' : author.username}
|
avatar={avatar}
|
||||||
size={small ? 20 : 36}
|
baseUrl={baseUrl}
|
||||||
borderRadius={small ? 2 : 4}
|
userId={user.id}
|
||||||
avatar={avatar}
|
token={user.token}
|
||||||
baseUrl={baseUrl}
|
/>
|
||||||
userId={user.id}
|
|
||||||
token={user.token}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -38,8 +35,6 @@ MessageAvatar.propTypes = {
|
||||||
isHeader: PropTypes.bool,
|
isHeader: PropTypes.bool,
|
||||||
avatar: PropTypes.string,
|
avatar: PropTypes.string,
|
||||||
author: PropTypes.obj,
|
author: PropTypes.obj,
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
user: PropTypes.obj,
|
|
||||||
small: PropTypes.bool,
|
small: PropTypes.bool,
|
||||||
navToRoomInfo: PropTypes.func
|
navToRoomInfo: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import Touchable from './Touchable';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { BUTTON_HIT_SLOP } from './utils';
|
import { BUTTON_HIT_SLOP } from './utils';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
import MessageContext from './Context';
|
||||||
|
|
||||||
const MessageError = React.memo(({ hasError, onErrorPress, theme }) => {
|
const MessageError = React.memo(({ hasError, theme }) => {
|
||||||
if (!hasError) {
|
if (!hasError) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const { onErrorPress } = useContext(MessageContext);
|
||||||
return (
|
return (
|
||||||
<Touchable onPress={onErrorPress} style={styles.errorButton} hitSlop={BUTTON_HIT_SLOP}>
|
<Touchable onPress={onErrorPress} style={styles.errorButton} hitSlop={BUTTON_HIT_SLOP}>
|
||||||
<CustomIcon name='warning' color={themes[theme].dangerColor} size={18} />
|
<CustomIcon name='warning' color={themes[theme].dangerColor} size={18} />
|
||||||
|
@ -20,7 +22,6 @@ const MessageError = React.memo(({ hasError, onErrorPress, theme }) => {
|
||||||
|
|
||||||
MessageError.propTypes = {
|
MessageError.propTypes = {
|
||||||
hasError: PropTypes.bool,
|
hasError: PropTypes.bool,
|
||||||
onErrorPress: PropTypes.func,
|
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
};
|
};
|
||||||
MessageError.displayName = 'MessageError';
|
MessageError.displayName = 'MessageError';
|
||||||
|
|
|
@ -1,33 +1,40 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { View, Text } from 'react-native';
|
import { View, Text } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import Touchable from './Touchable';
|
||||||
import { CustomIcon } from '../../lib/Icons';
|
import { CustomIcon } from '../../lib/Icons';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import Emoji from './Emoji';
|
import Emoji from './Emoji';
|
||||||
import { BUTTON_HIT_SLOP } from './utils';
|
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';
|
||||||
|
|
||||||
const AddReaction = React.memo(({ reactionInit, theme }) => (
|
const AddReaction = React.memo(({ theme }) => {
|
||||||
<Touchable
|
const { reactionInit } = useContext(MessageContext);
|
||||||
onPress={reactionInit}
|
return (
|
||||||
key='message-add-reaction'
|
<Touchable
|
||||||
testID='message-add-reaction'
|
onPress={reactionInit}
|
||||||
style={[styles.reactionButton, { backgroundColor: themes[theme].backgroundColor }]}
|
key='message-add-reaction'
|
||||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
testID='message-add-reaction'
|
||||||
hitSlop={BUTTON_HIT_SLOP}
|
style={[styles.reactionButton, { backgroundColor: themes[theme].backgroundColor }]}
|
||||||
>
|
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||||
<View style={[styles.reactionContainer, { borderColor: themes[theme].borderColor }]}>
|
hitSlop={BUTTON_HIT_SLOP}
|
||||||
<CustomIcon name='add-reaction' size={21} color={themes[theme].tintColor} />
|
>
|
||||||
</View>
|
<View style={[styles.reactionContainer, { borderColor: themes[theme].borderColor }]}>
|
||||||
</Touchable>
|
<CustomIcon name='add-reaction' size={21} color={themes[theme].tintColor} />
|
||||||
));
|
</View>
|
||||||
|
</Touchable>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const Reaction = React.memo(({
|
const Reaction = React.memo(({
|
||||||
reaction, user, onReactionLongPress, onReactionPress, baseUrl, getCustomEmoji, theme
|
reaction, getCustomEmoji, theme
|
||||||
}) => {
|
}) => {
|
||||||
|
const {
|
||||||
|
onReactionPress, onReactionLongPress, baseUrl, user
|
||||||
|
} = useContext(MessageContext);
|
||||||
const reacted = reaction.usernames.findIndex(item => item === user.username) !== -1;
|
const reacted = reaction.usernames.findIndex(item => item === user.username) !== -1;
|
||||||
return (
|
return (
|
||||||
<Touchable
|
<Touchable
|
||||||
|
@ -54,7 +61,7 @@ const Reaction = React.memo(({
|
||||||
});
|
});
|
||||||
|
|
||||||
const Reactions = React.memo(({
|
const Reactions = React.memo(({
|
||||||
reactions, user, baseUrl, onReactionPress, reactionInit, onReactionLongPress, getCustomEmoji, theme
|
reactions, getCustomEmoji, theme
|
||||||
}) => {
|
}) => {
|
||||||
if (!Array.isArray(reactions) || reactions.length === 0) {
|
if (!Array.isArray(reactions) || reactions.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -65,25 +72,17 @@ const Reactions = React.memo(({
|
||||||
<Reaction
|
<Reaction
|
||||||
key={reaction.emoji}
|
key={reaction.emoji}
|
||||||
reaction={reaction}
|
reaction={reaction}
|
||||||
user={user}
|
|
||||||
baseUrl={baseUrl}
|
|
||||||
onReactionLongPress={onReactionLongPress}
|
|
||||||
onReactionPress={onReactionPress}
|
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<AddReaction reactionInit={reactionInit} theme={theme} />
|
<AddReaction theme={theme} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Reaction.propTypes = {
|
Reaction.propTypes = {
|
||||||
reaction: PropTypes.object,
|
reaction: PropTypes.object,
|
||||||
user: PropTypes.object,
|
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
onReactionPress: PropTypes.func,
|
|
||||||
onReactionLongPress: PropTypes.func,
|
|
||||||
getCustomEmoji: PropTypes.func,
|
getCustomEmoji: PropTypes.func,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
};
|
};
|
||||||
|
@ -91,18 +90,12 @@ Reaction.displayName = 'MessageReaction';
|
||||||
|
|
||||||
Reactions.propTypes = {
|
Reactions.propTypes = {
|
||||||
reactions: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
reactions: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
||||||
user: PropTypes.object,
|
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
onReactionPress: PropTypes.func,
|
|
||||||
reactionInit: PropTypes.func,
|
|
||||||
onReactionLongPress: PropTypes.func,
|
|
||||||
getCustomEmoji: PropTypes.func,
|
getCustomEmoji: PropTypes.func,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
};
|
};
|
||||||
Reactions.displayName = 'MessageReactions';
|
Reactions.displayName = 'MessageReactions';
|
||||||
|
|
||||||
AddReaction.propTypes = {
|
AddReaction.propTypes = {
|
||||||
reactionInit: PropTypes.func,
|
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
};
|
};
|
||||||
AddReaction.displayName = 'MessageAddReaction';
|
AddReaction.displayName = 'MessageAddReaction';
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { View, Text, StyleSheet } from 'react-native';
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
|
||||||
import isEqual from 'deep-equal';
|
import isEqual from 'deep-equal';
|
||||||
|
|
||||||
|
import Touchable from './Touchable';
|
||||||
import Markdown from '../markdown';
|
import Markdown from '../markdown';
|
||||||
import openLink from '../../utils/openLink';
|
import openLink from '../../utils/openLink';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import { withSplit } from '../../split';
|
import { withSplit } from '../../split';
|
||||||
|
import MessageContext from './Context';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
button: {
|
button: {
|
||||||
|
@ -79,12 +80,13 @@ const Title = React.memo(({ attachment, timeFormat, theme }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const Description = React.memo(({
|
const Description = React.memo(({
|
||||||
attachment, baseUrl, user, getCustomEmoji, theme
|
attachment, getCustomEmoji, theme
|
||||||
}) => {
|
}) => {
|
||||||
const text = attachment.text || attachment.title;
|
const text = attachment.text || attachment.title;
|
||||||
if (!text) {
|
if (!text) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const { baseUrl, user } = useContext(MessageContext);
|
||||||
return (
|
return (
|
||||||
<Markdown
|
<Markdown
|
||||||
msg={text}
|
msg={text}
|
||||||
|
@ -124,11 +126,12 @@ const Fields = React.memo(({ attachment, theme }) => {
|
||||||
}, (prevProps, nextProps) => isEqual(prevProps.attachment.fields, nextProps.attachment.fields) && prevProps.theme === nextProps.theme);
|
}, (prevProps, nextProps) => isEqual(prevProps.attachment.fields, nextProps.attachment.fields) && prevProps.theme === nextProps.theme);
|
||||||
|
|
||||||
const Reply = React.memo(({
|
const Reply = React.memo(({
|
||||||
attachment, timeFormat, baseUrl, user, index, getCustomEmoji, split, theme
|
attachment, timeFormat, index, getCustomEmoji, split, theme
|
||||||
}) => {
|
}) => {
|
||||||
if (!attachment) {
|
if (!attachment) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const { baseUrl, user } = useContext(MessageContext);
|
||||||
|
|
||||||
const onPress = () => {
|
const onPress = () => {
|
||||||
let url = attachment.title_link || attachment.author_link;
|
let url = attachment.title_link || attachment.author_link;
|
||||||
|
@ -160,8 +163,6 @@ const Reply = React.memo(({
|
||||||
<Description
|
<Description
|
||||||
attachment={attachment}
|
attachment={attachment}
|
||||||
timeFormat={timeFormat}
|
timeFormat={timeFormat}
|
||||||
baseUrl={baseUrl}
|
|
||||||
user={user}
|
|
||||||
getCustomEmoji={getCustomEmoji}
|
getCustomEmoji={getCustomEmoji}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
|
@ -174,8 +175,6 @@ const Reply = React.memo(({
|
||||||
Reply.propTypes = {
|
Reply.propTypes = {
|
||||||
attachment: PropTypes.object,
|
attachment: PropTypes.object,
|
||||||
timeFormat: PropTypes.string,
|
timeFormat: PropTypes.string,
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
user: PropTypes.object,
|
|
||||||
index: PropTypes.number,
|
index: PropTypes.number,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
getCustomEmoji: PropTypes.func,
|
getCustomEmoji: PropTypes.func,
|
||||||
|
@ -192,8 +191,6 @@ Title.displayName = 'MessageReplyTitle';
|
||||||
|
|
||||||
Description.propTypes = {
|
Description.propTypes = {
|
||||||
attachment: PropTypes.object,
|
attachment: PropTypes.object,
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
user: PropTypes.object,
|
|
||||||
getCustomEmoji: PropTypes.func,
|
getCustomEmoji: PropTypes.func,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import React, { useContext } from 'react';
|
||||||
|
import Touchable from 'react-native-platform-touchable';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import MessageContext from './Context';
|
||||||
|
|
||||||
|
const RCTouchable = React.memo(({ children, ...props }) => {
|
||||||
|
const { onLongPress } = useContext(MessageContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Touchable
|
||||||
|
onLongPress={onLongPress}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Touchable>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
RCTouchable.propTypes = {
|
||||||
|
children: PropTypes.node
|
||||||
|
};
|
||||||
|
RCTouchable.Ripple = (...args) => Touchable.Ripple(...args);
|
||||||
|
RCTouchable.SelectableBackgroundBorderless = () => Touchable.SelectableBackgroundBorderless();
|
||||||
|
|
||||||
|
export default RCTouchable;
|
|
@ -1,12 +1,12 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import {
|
import {
|
||||||
View, Text, StyleSheet, Clipboard
|
View, Text, StyleSheet, Clipboard
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import FastImage from 'react-native-fast-image';
|
import FastImage from 'react-native-fast-image';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
|
||||||
import isEqual from 'lodash/isEqual';
|
import isEqual from 'lodash/isEqual';
|
||||||
|
|
||||||
|
import Touchable from './Touchable';
|
||||||
import openLink from '../../utils/openLink';
|
import openLink from '../../utils/openLink';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
|
@ -15,6 +15,7 @@ import { withSplit } from '../../split';
|
||||||
import { LISTENER } from '../Toast';
|
import { LISTENER } from '../Toast';
|
||||||
import EventEmitter from '../../utils/events';
|
import EventEmitter from '../../utils/events';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
|
import MessageContext from './Context';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
button: {
|
button: {
|
||||||
|
@ -52,10 +53,11 @@ const styles = StyleSheet.create({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const UrlImage = React.memo(({ image, user, baseUrl }) => {
|
const UrlImage = React.memo(({ image }) => {
|
||||||
if (!image) {
|
if (!image) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const { baseUrl, user } = useContext(MessageContext);
|
||||||
image = image.includes('http') ? image : `${ baseUrl }/${ image }?rc_uid=${ user.id }&rc_token=${ user.token }`;
|
image = image.includes('http') ? image : `${ baseUrl }/${ image }?rc_uid=${ user.id }&rc_token=${ user.token }`;
|
||||||
return <FastImage source={{ uri: image }} style={styles.image} resizeMode={FastImage.resizeMode.cover} />;
|
return <FastImage source={{ uri: image }} style={styles.image} resizeMode={FastImage.resizeMode.cover} />;
|
||||||
}, (prevProps, nextProps) => prevProps.image === nextProps.image);
|
}, (prevProps, nextProps) => prevProps.image === nextProps.image);
|
||||||
|
@ -79,7 +81,7 @@ const UrlContent = React.memo(({ title, description, theme }) => (
|
||||||
});
|
});
|
||||||
|
|
||||||
const Url = React.memo(({
|
const Url = React.memo(({
|
||||||
url, index, user, baseUrl, split, theme
|
url, index, split, theme
|
||||||
}) => {
|
}) => {
|
||||||
if (!url) {
|
if (!url) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -109,7 +111,7 @@ const Url = React.memo(({
|
||||||
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
background={Touchable.Ripple(themes[theme].bannerBackground)}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
<UrlImage image={url.image} user={user} baseUrl={baseUrl} />
|
<UrlImage image={url.image} />
|
||||||
<UrlContent title={url.title} description={url.description} theme={theme} />
|
<UrlContent title={url.title} description={url.description} theme={theme} />
|
||||||
</>
|
</>
|
||||||
</Touchable>
|
</Touchable>
|
||||||
|
@ -117,21 +119,19 @@ const Url = React.memo(({
|
||||||
}, (oldProps, newProps) => isEqual(oldProps.url, newProps.url) && oldProps.split === newProps.split && oldProps.theme === newProps.theme);
|
}, (oldProps, newProps) => isEqual(oldProps.url, newProps.url) && oldProps.split === newProps.split && oldProps.theme === newProps.theme);
|
||||||
|
|
||||||
const Urls = React.memo(({
|
const Urls = React.memo(({
|
||||||
urls, user, baseUrl, split, theme
|
urls, split, theme
|
||||||
}) => {
|
}) => {
|
||||||
if (!urls || urls.length === 0) {
|
if (!urls || urls.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return urls.map((url, index) => (
|
return urls.map((url, index) => (
|
||||||
<Url url={url} key={url.url} index={index} user={user} baseUrl={baseUrl} split={split} theme={theme} />
|
<Url url={url} key={url.url} index={index} split={split} theme={theme} />
|
||||||
));
|
));
|
||||||
}, (oldProps, newProps) => isEqual(oldProps.urls, newProps.urls) && oldProps.split === newProps.split && oldProps.theme === newProps.theme);
|
}, (oldProps, newProps) => isEqual(oldProps.urls, newProps.urls) && oldProps.split === newProps.split && oldProps.theme === newProps.theme);
|
||||||
|
|
||||||
UrlImage.propTypes = {
|
UrlImage.propTypes = {
|
||||||
image: PropTypes.string,
|
image: PropTypes.string
|
||||||
user: PropTypes.object,
|
|
||||||
baseUrl: PropTypes.string
|
|
||||||
};
|
};
|
||||||
UrlImage.displayName = 'MessageUrlImage';
|
UrlImage.displayName = 'MessageUrlImage';
|
||||||
|
|
||||||
|
@ -145,8 +145,6 @@ UrlContent.displayName = 'MessageUrlContent';
|
||||||
Url.propTypes = {
|
Url.propTypes = {
|
||||||
url: PropTypes.object.isRequired,
|
url: PropTypes.object.isRequired,
|
||||||
index: PropTypes.number,
|
index: PropTypes.number,
|
||||||
user: PropTypes.object,
|
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
split: PropTypes.bool
|
split: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
@ -154,8 +152,6 @@ Url.displayName = 'MessageUrl';
|
||||||
|
|
||||||
Urls.propTypes = {
|
Urls.propTypes = {
|
||||||
urls: PropTypes.array,
|
urls: PropTypes.array,
|
||||||
user: PropTypes.object,
|
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
split: PropTypes.bool
|
split: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {
|
import {
|
||||||
View, Text, StyleSheet, TouchableOpacity
|
View, Text, StyleSheet, TouchableOpacity
|
||||||
|
@ -11,6 +11,7 @@ import { withTheme } from '../../theme';
|
||||||
import MessageError from './MessageError';
|
import MessageError from './MessageError';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
import messageStyles from './styles';
|
import messageStyles from './styles';
|
||||||
|
import MessageContext from './Context';
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -35,13 +36,14 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
const User = React.memo(({
|
const User = React.memo(({
|
||||||
isHeader, useRealName, author, alias, ts, timeFormat, hasError, theme, navToRoomInfo, user, ...props
|
isHeader, useRealName, author, alias, ts, timeFormat, hasError, theme, navToRoomInfo, ...props
|
||||||
}) => {
|
}) => {
|
||||||
if (isHeader || hasError) {
|
if (isHeader || hasError) {
|
||||||
const navParam = {
|
const navParam = {
|
||||||
t: 'd',
|
t: 'd',
|
||||||
rid: author._id
|
rid: author._id
|
||||||
};
|
};
|
||||||
|
const { user } = useContext(MessageContext);
|
||||||
const username = (useRealName && author.name) || author.username;
|
const username = (useRealName && author.name) || author.username;
|
||||||
const aliasUsername = alias ? (<Text style={[styles.alias, { color: themes[theme].auxiliaryText }]}> @{username}</Text>) : null;
|
const aliasUsername = alias ? (<Text style={[styles.alias, { color: themes[theme].auxiliaryText }]}> @{username}</Text>) : null;
|
||||||
const time = moment(ts).format(timeFormat);
|
const time = moment(ts).format(timeFormat);
|
||||||
|
@ -49,15 +51,14 @@ const User = React.memo(({
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
style={styles.titleContainer}
|
||||||
onPress={() => navToRoomInfo(navParam)}
|
onPress={() => navToRoomInfo(navParam)}
|
||||||
disabled={author._id === user.id}
|
disabled={author._id === user.id}
|
||||||
>
|
>
|
||||||
<View style={styles.titleContainer}>
|
<Text style={[styles.username, { color: themes[theme].titleText }]} numberOfLines={1}>
|
||||||
<Text style={[styles.username, { color: themes[theme].titleText }]} numberOfLines={1}>
|
{alias || username}
|
||||||
{alias || username}
|
{aliasUsername}
|
||||||
{aliasUsername}
|
</Text>
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Text style={[messageStyles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
<Text style={[messageStyles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
||||||
{ hasError && <MessageError hasError={hasError} theme={theme} {...props} /> }
|
{ hasError && <MessageError hasError={hasError} theme={theme} {...props} /> }
|
||||||
|
@ -76,7 +77,6 @@ User.propTypes = {
|
||||||
ts: PropTypes.instanceOf(Date),
|
ts: PropTypes.instanceOf(Date),
|
||||||
timeFormat: PropTypes.string,
|
timeFormat: PropTypes.string,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
user: PropTypes.obj,
|
|
||||||
navToRoomInfo: PropTypes.func
|
navToRoomInfo: PropTypes.func
|
||||||
};
|
};
|
||||||
User.displayName = 'MessageUser';
|
User.displayName = 'MessageUser';
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { StyleSheet } from 'react-native';
|
import { StyleSheet } from 'react-native';
|
||||||
import Touchable from 'react-native-platform-touchable';
|
|
||||||
import isEqual from 'deep-equal';
|
import isEqual from 'deep-equal';
|
||||||
|
|
||||||
|
import Touchable from './Touchable';
|
||||||
import Markdown from '../markdown';
|
import Markdown from '../markdown';
|
||||||
import openLink from '../../utils/openLink';
|
import openLink from '../../utils/openLink';
|
||||||
import { isIOS, isTablet } from '../../utils/deviceInfo';
|
import { isIOS, isTablet } from '../../utils/deviceInfo';
|
||||||
|
@ -11,6 +11,7 @@ import { CustomIcon } from '../../lib/Icons';
|
||||||
import { formatAttachmentUrl } from '../../lib/utils';
|
import { formatAttachmentUrl } from '../../lib/utils';
|
||||||
import { themes } from '../../constants/colors';
|
import { themes } from '../../constants/colors';
|
||||||
import sharedStyles from '../../views/Styles';
|
import sharedStyles from '../../views/Styles';
|
||||||
|
import MessageContext from './Context';
|
||||||
|
|
||||||
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 => SUPPORTED_TYPES.indexOf(type) !== -1;
|
const isTypeSupported = type => SUPPORTED_TYPES.indexOf(type) !== -1;
|
||||||
|
@ -27,12 +28,12 @@ const styles = StyleSheet.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
const Video = React.memo(({
|
const Video = React.memo(({
|
||||||
file, baseUrl, user, showAttachment, getCustomEmoji, theme
|
file, showAttachment, getCustomEmoji, theme
|
||||||
}) => {
|
}) => {
|
||||||
|
const { baseUrl, user } = useContext(MessageContext);
|
||||||
if (!baseUrl) {
|
if (!baseUrl) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPress = () => {
|
const onPress = () => {
|
||||||
if (isTypeSupported(file.video_type)) {
|
if (isTypeSupported(file.video_type)) {
|
||||||
return showAttachment(file);
|
return showAttachment(file);
|
||||||
|
@ -61,8 +62,6 @@ const Video = React.memo(({
|
||||||
|
|
||||||
Video.propTypes = {
|
Video.propTypes = {
|
||||||
file: PropTypes.object,
|
file: PropTypes.object,
|
||||||
baseUrl: PropTypes.string,
|
|
||||||
user: PropTypes.object,
|
|
||||||
showAttachment: PropTypes.func,
|
showAttachment: PropTypes.func,
|
||||||
getCustomEmoji: PropTypes.func,
|
getCustomEmoji: PropTypes.func,
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string
|
||||||
|
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||||
import { KeyboardUtils } from 'react-native-keyboard-input';
|
import { KeyboardUtils } from 'react-native-keyboard-input';
|
||||||
|
|
||||||
import Message from './Message';
|
import Message from './Message';
|
||||||
|
import MessageContext from './Context';
|
||||||
import debounce from '../../utils/debounce';
|
import debounce from '../../utils/debounce';
|
||||||
import { SYSTEM_MESSAGES, getMessageTranslation } from './utils';
|
import { SYSTEM_MESSAGES, getMessageTranslation } from './utils';
|
||||||
import messagesStatus from '../../constants/messagesStatus';
|
import messagesStatus from '../../constants/messagesStatus';
|
||||||
|
@ -240,63 +241,68 @@ class MessageContainer extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Message
|
<MessageContext.Provider
|
||||||
id={id}
|
value={{
|
||||||
msg={message}
|
user,
|
||||||
rid={rid}
|
baseUrl,
|
||||||
author={u}
|
onPress: this.onPress,
|
||||||
ts={ts}
|
onLongPress: this.onLongPress,
|
||||||
type={t}
|
reactionInit: this.reactionInit,
|
||||||
attachments={attachments}
|
onErrorPress: this.onErrorPress,
|
||||||
blocks={blocks}
|
replyBroadcast: this.replyBroadcast,
|
||||||
urls={urls}
|
onReactionPress: this.onReactionPress,
|
||||||
reactions={reactions}
|
onDiscussionPress: this.onDiscussionPress,
|
||||||
alias={alias}
|
onReactionLongPress: this.onReactionLongPress
|
||||||
avatar={avatar}
|
}}
|
||||||
user={user}
|
>
|
||||||
timeFormat={timeFormat}
|
<Message
|
||||||
customThreadTimeFormat={customThreadTimeFormat}
|
id={id}
|
||||||
style={style}
|
msg={message}
|
||||||
archived={archived}
|
rid={rid}
|
||||||
broadcast={broadcast}
|
author={u}
|
||||||
baseUrl={baseUrl}
|
ts={ts}
|
||||||
useRealName={useRealName}
|
type={t}
|
||||||
isReadReceiptEnabled={isReadReceiptEnabled}
|
attachments={attachments}
|
||||||
unread={unread}
|
blocks={blocks}
|
||||||
role={role}
|
urls={urls}
|
||||||
drid={drid}
|
reactions={reactions}
|
||||||
dcount={dcount}
|
alias={alias}
|
||||||
dlm={dlm}
|
avatar={avatar}
|
||||||
tmid={tmid}
|
timeFormat={timeFormat}
|
||||||
tcount={tcount}
|
customThreadTimeFormat={customThreadTimeFormat}
|
||||||
tlm={tlm}
|
style={style}
|
||||||
tmsg={tmsg}
|
archived={archived}
|
||||||
fetchThreadName={fetchThreadName}
|
broadcast={broadcast}
|
||||||
mentions={mentions}
|
useRealName={useRealName}
|
||||||
channels={channels}
|
isReadReceiptEnabled={isReadReceiptEnabled}
|
||||||
isEdited={editedBy && !!editedBy.username}
|
unread={unread}
|
||||||
isHeader={this.isHeader}
|
role={role}
|
||||||
isThreadReply={this.isThreadReply}
|
drid={drid}
|
||||||
isThreadSequential={this.isThreadSequential}
|
dcount={dcount}
|
||||||
isThreadRoom={isThreadRoom}
|
dlm={dlm}
|
||||||
isInfo={this.isInfo}
|
tmid={tmid}
|
||||||
isTemp={this.isTemp}
|
tcount={tcount}
|
||||||
hasError={this.hasError}
|
tlm={tlm}
|
||||||
onErrorPress={this.onErrorPress}
|
tmsg={tmsg}
|
||||||
onPress={this.onPress}
|
fetchThreadName={fetchThreadName}
|
||||||
onLongPress={this.onLongPress}
|
mentions={mentions}
|
||||||
onReactionLongPress={this.onReactionLongPress}
|
channels={channels}
|
||||||
onReactionPress={this.onReactionPress}
|
isEdited={editedBy && !!editedBy.username}
|
||||||
replyBroadcast={this.replyBroadcast}
|
isHeader={this.isHeader}
|
||||||
reactionInit={this.reactionInit}
|
isThreadReply={this.isThreadReply}
|
||||||
onDiscussionPress={this.onDiscussionPress}
|
isThreadSequential={this.isThreadSequential}
|
||||||
showAttachment={showAttachment}
|
isThreadRoom={isThreadRoom}
|
||||||
getCustomEmoji={getCustomEmoji}
|
isInfo={this.isInfo}
|
||||||
navToRoomInfo={navToRoomInfo}
|
isTemp={this.isTemp}
|
||||||
callJitsi={callJitsi}
|
hasError={this.hasError}
|
||||||
blockAction={blockAction}
|
showAttachment={showAttachment}
|
||||||
theme={theme}
|
getCustomEmoji={getCustomEmoji}
|
||||||
/>
|
navToRoomInfo={navToRoomInfo}
|
||||||
|
callJitsi={callJitsi}
|
||||||
|
blockAction={blockAction}
|
||||||
|
theme={theme}
|
||||||
|
/>
|
||||||
|
</MessageContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,6 @@ const RoomHeaderLeft = ({
|
||||||
style={styles.avatar}
|
style={styles.avatar}
|
||||||
userId={userId}
|
userId={userId}
|
||||||
token={token}
|
token={token}
|
||||||
theme={theme}
|
|
||||||
onPress={goRoomActionsView}
|
onPress={goRoomActionsView}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,6 +8,7 @@ import messagesStatus from '../../app/constants/messagesStatus';
|
||||||
import MessageSeparator from '../../app/views/RoomView/Separator';
|
import MessageSeparator from '../../app/views/RoomView/Separator';
|
||||||
|
|
||||||
import { themes } from '../../app/constants/colors';
|
import { themes } from '../../app/constants/colors';
|
||||||
|
import MessageContext from '../../app/containers/message/Context';
|
||||||
|
|
||||||
let _theme = 'light';
|
let _theme = 'light';
|
||||||
|
|
||||||
|
@ -41,17 +42,32 @@ const getCustomEmoji = (content) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Message = props => (
|
const Message = props => (
|
||||||
<MessageComponent
|
<MessageContext.Provider
|
||||||
baseUrl={baseUrl}
|
value={{
|
||||||
user={user}
|
user,
|
||||||
author={author}
|
baseUrl,
|
||||||
ts={date}
|
onPress: () => {},
|
||||||
timeFormat='LT'
|
onLongPress: () => {},
|
||||||
isHeader
|
reactionInit: () => {},
|
||||||
getCustomEmoji={getCustomEmoji}
|
onErrorPress: () => {},
|
||||||
theme={_theme}
|
replyBroadcast: () => {},
|
||||||
{...props}
|
onReactionPress: () => {},
|
||||||
/>
|
onDiscussionPress: () => {},
|
||||||
|
onReactionLongPress: () => {}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MessageComponent
|
||||||
|
baseUrl={baseUrl}
|
||||||
|
user={user}
|
||||||
|
author={author}
|
||||||
|
ts={date}
|
||||||
|
timeFormat='LT'
|
||||||
|
isHeader
|
||||||
|
getCustomEmoji={getCustomEmoji}
|
||||||
|
theme={_theme}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</MessageContext.Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
// eslint-disable-next-line react/prop-types
|
// eslint-disable-next-line react/prop-types
|
||||||
|
|
Loading…
Reference in New Issue