diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js
index 56fb4f21b..961b88eee 100644
--- a/app/actions/actionsTypes.js
+++ b/app/actions/actionsTypes.js
@@ -65,8 +65,8 @@ export const MESSAGES = createRequestTypes('MESSAGES', [
'TOGGLE_PIN_REQUEST',
'TOGGLE_PIN_SUCCESS',
'TOGGLE_PIN_FAILURE',
- 'SET_INPUT',
- 'CLEAR_INPUT',
+ 'REPLY_INIT',
+ 'REPLY_CANCEL',
'TOGGLE_REACTION_PICKER',
'REPLY_BROADCAST'
]);
diff --git a/app/actions/messages.js b/app/actions/messages.js
index b7b3ee690..1b8451c7b 100644
--- a/app/actions/messages.js
+++ b/app/actions/messages.js
@@ -137,16 +137,17 @@ export function togglePinFailure(err) {
};
}
-export function setInput(message) {
+export function replyInit(message, mention) {
return {
- type: types.MESSAGES.SET_INPUT,
- message
+ type: types.MESSAGES.REPLY_INIT,
+ message,
+ mention
};
}
-export function clearInput() {
+export function replyCancel() {
return {
- type: types.MESSAGES.CLEAR_INPUT
+ type: types.MESSAGES.REPLY_CANCEL
};
}
diff --git a/app/containers/MessageActions.js b/app/containers/MessageActions.js
index ccb25a79d..7eec64339 100644
--- a/app/containers/MessageActions.js
+++ b/app/containers/MessageActions.js
@@ -10,9 +10,9 @@ import {
editInit,
toggleStarRequest,
togglePinRequest,
- setInput,
actionsHide,
- toggleReactionPicker
+ toggleReactionPicker,
+ replyInit
} from '../actions/messages';
import { showToast } from '../utils/info';
import RocketChat from '../lib/rocketchat';
@@ -34,8 +34,8 @@ import I18n from '../i18n';
editInit: message => dispatch(editInit(message)),
toggleStarRequest: message => dispatch(toggleStarRequest(message)),
togglePinRequest: message => dispatch(togglePinRequest(message)),
- setInput: message => dispatch(setInput(message)),
- toggleReactionPicker: message => dispatch(toggleReactionPicker(message))
+ toggleReactionPicker: message => dispatch(toggleReactionPicker(message)),
+ replyInit: (message, mention) => dispatch(replyInit(message, mention))
})
)
export default class MessageActions extends React.Component {
@@ -43,13 +43,13 @@ export default class MessageActions extends React.Component {
actionsHide: PropTypes.func.isRequired,
room: PropTypes.object.isRequired,
actionMessage: PropTypes.object,
- user: PropTypes.object.isRequired,
+ // user: PropTypes.object.isRequired,
deleteRequest: PropTypes.func.isRequired,
editInit: PropTypes.func.isRequired,
toggleStarRequest: PropTypes.func.isRequired,
togglePinRequest: PropTypes.func.isRequired,
- setInput: PropTypes.func.isRequired,
toggleReactionPicker: PropTypes.func.isRequired,
+ replyInit: PropTypes.func.isRequired,
Message_AllowDeleting: PropTypes.bool,
Message_AllowDeleting_BlockDeleteInMinutes: PropTypes.number,
Message_AllowEditing: PropTypes.bool,
@@ -248,21 +248,12 @@ export default class MessageActions extends React.Component {
this.props.togglePinRequest(this.props.actionMessage);
}
- async handleReply() {
- const permalink = await this.getPermalink(this.props.actionMessage);
- let msg = `[ ](${ permalink }) `;
-
- // if original message wasn't sent by current user and neither from a direct room
- if (this.props.user.username !== this.props.actionMessage.u.username && this.props.room.t !== 'd') {
- msg += `@${ this.props.actionMessage.u.username } `;
- }
- this.props.setInput({ msg });
+ handleReply() {
+ this.props.replyInit(this.props.actionMessage, true);
}
- async handleQuote() {
- const permalink = await this.getPermalink(this.props.actionMessage);
- const msg = `[ ](${ permalink }) `;
- this.props.setInput({ msg });
+ handleQuote() {
+ this.props.replyInit(this.props.actionMessage, false);
}
handleReaction() {
diff --git a/app/containers/MessageBox/ReplyPreview.js b/app/containers/MessageBox/ReplyPreview.js
new file mode 100644
index 000000000..d71816c40
--- /dev/null
+++ b/app/containers/MessageBox/ReplyPreview.js
@@ -0,0 +1,78 @@
+import React, { Component } from 'react';
+import { View, Text, StyleSheet } from 'react-native';
+import PropTypes from 'prop-types';
+import moment from 'moment';
+import { connect } from 'react-redux';
+import Icon from 'react-native-vector-icons/MaterialIcons';
+
+import Markdown from '../message/Markdown';
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ flexDirection: 'row'
+ },
+ messageContainer: {
+ flex: 1,
+ marginHorizontal: 15,
+ backgroundColor: '#F3F4F5',
+ paddingHorizontal: 15,
+ paddingVertical: 10,
+ borderRadius: 2
+ },
+ header: {
+ flexDirection: 'row',
+ alignItems: 'center'
+ },
+ username: {
+ color: '#1D74F5',
+ fontSize: 16,
+ fontWeight: '500'
+ },
+ time: {
+ color: '#9EA2A8',
+ fontSize: 12,
+ lineHeight: 16,
+ marginLeft: 5
+ },
+ content: {
+ color: '#0C0D0F',
+ fontSize: 16,
+ lineHeight: 20
+ },
+ close: {
+ marginRight: 15
+ }
+});
+
+@connect(state => ({
+ Message_TimeFormat: state.settings.Message_TimeFormat
+}))
+export default class ReplyPreview extends Component {
+ static propTypes = {
+ message: PropTypes.object.isRequired,
+ Message_TimeFormat: PropTypes.string.isRequired,
+ close: PropTypes.func.isRequired
+ }
+
+ close = () => {
+ this.props.close();
+ }
+
+ render() {
+ const { message, Message_TimeFormat } = this.props;
+ const time = moment(message.ts).format(Message_TimeFormat);
+ return (
+
+
+
+ {message.u.username}
+ {time}
+
+
+
+
+
+ );
+ }
+}
diff --git a/app/containers/MessageBox/index.js b/app/containers/MessageBox/index.js
index c16cc3f81..63eb3bb12 100644
--- a/app/containers/MessageBox/index.js
+++ b/app/containers/MessageBox/index.js
@@ -9,7 +9,7 @@ import ImagePicker from 'react-native-image-crop-picker';
import { userTyping } from '../../actions/room';
import RocketChat from '../../lib/rocketchat';
-import { editRequest, editCancel, clearInput } from '../../actions/messages';
+import { editRequest, editCancel, replyCancel } from '../../actions/messages';
import styles from './styles';
import MyIcon from '../icons';
import database from '../../lib/realm';
@@ -22,6 +22,7 @@ import UploadModal from './UploadModal';
import './EmojiKeyboard';
import log from '../../utils/log';
import I18n from '../../i18n';
+import ReplyPreview from './ReplyPreview';
const MENTIONS_TRACKING_TYPE_USERS = '@';
const MENTIONS_TRACKING_TYPE_EMOJIS = ':';
@@ -39,27 +40,34 @@ const imagePickerConfig = {
};
@connect(state => ({
- room: state.room,
+ roomType: state.room.t,
message: state.messages.message,
+ replyMessage: state.messages.replyMessage,
+ replying: state.messages.replyMessage && !!state.messages.replyMessage.msg,
editing: state.messages.editing,
- baseUrl: state.settings.Site_Url || state.server ? state.server.server : ''
+ baseUrl: state.settings.Site_Url || state.server ? state.server.server : '',
+ username: state.login.user && state.login.user.username
}), dispatch => ({
editCancel: () => dispatch(editCancel()),
editRequest: message => dispatch(editRequest(message)),
typing: status => dispatch(userTyping(status)),
- clearInput: () => dispatch(clearInput())
+ closeReply: () => dispatch(replyCancel())
}))
export default class MessageBox extends React.PureComponent {
static propTypes = {
- onSubmit: PropTypes.func.isRequired,
rid: PropTypes.string.isRequired,
- editCancel: PropTypes.func.isRequired,
- editRequest: PropTypes.func.isRequired,
baseUrl: PropTypes.string.isRequired,
message: PropTypes.object,
+ replyMessage: PropTypes.object,
+ replying: PropTypes.bool,
editing: PropTypes.bool,
+ username: PropTypes.string,
+ roomType: PropTypes.string,
+ editCancel: PropTypes.func.isRequired,
+ editRequest: PropTypes.func.isRequired,
+ onSubmit: PropTypes.func.isRequired,
typing: PropTypes.func,
- clearInput: PropTypes.func
+ closeReply: PropTypes.func
}
constructor(props) {
@@ -84,6 +92,8 @@ export default class MessageBox extends React.PureComponent {
if (this.props.message !== nextProps.message && nextProps.message.msg) {
this.setState({ text: nextProps.message.msg });
this.component.focus();
+ } else if (this.props.replyMessage !== nextProps.replyMessage && nextProps.replyMessage.msg) {
+ this.component.focus();
} else if (!nextProps.message) {
this.setState({ text: '' });
}
@@ -180,6 +190,14 @@ export default class MessageBox extends React.PureComponent {
return icons;
}
+ getPermalink = async(message) => {
+ try {
+ return await RocketChat.getPermalink(message);
+ } catch (error) {
+ return null;
+ }
+ }
+
toggleFilesActions = () => {
this.setState(prevState => ({ showFilesAction: !prevState.showFilesAction }));
}
@@ -259,7 +277,7 @@ export default class MessageBox extends React.PureComponent {
this.setState({ showEmojiKeyboard: false });
}
- submit(message) {
+ async submit(message) {
this.setState({ text: '' });
this.closeEmoji();
this.stopTrackingMention();
@@ -268,15 +286,32 @@ export default class MessageBox extends React.PureComponent {
return;
}
// if is editing a message
- const { editing } = this.props;
+ const {
+ editing, replying
+ } = this.props;
+
if (editing) {
const { _id, rid } = this.props.message;
this.props.editRequest({ _id, msg: message, rid });
+ } else if (replying) {
+ const {
+ username, replyMessage, roomType, closeReply
+ } = this.props;
+ const permalink = await this.getPermalink(replyMessage);
+ let msg = `[ ](${ permalink }) `;
+
+ // if original message wasn't sent by current user and neither from a direct room
+ if (username !== replyMessage.u.username && roomType !== 'd' && replyMessage.mention) {
+ msg += `@${ replyMessage.u.username } `;
+ }
+
+ msg = `${ msg } ${ message }`;
+ this.props.onSubmit(msg);
+ closeReply();
} else {
// if is submiting a new message
this.props.onSubmit(message);
}
- this.props.clearInput();
}
_getFixedMentions(keyword) {
@@ -520,6 +555,14 @@ export default class MessageBox extends React.PureComponent {
);
};
+ renderReplyPreview = () => {
+ const { replyMessage, replying, closeReply } = this.props;
+ if (!replying) {
+ return null;
+ }
+ return ;
+ };
+
renderFilesActions = () => {
if (!this.state.showFilesAction) {
return null;
@@ -541,6 +584,7 @@ export default class MessageBox extends React.PureComponent {
return (
[
this.renderMentions(),
+ this.renderReplyPreview(),
state.server.server);
- const msg = `[ ](${ server }/direct/${ username }?msg=${ message._id }) `;
- yield put(setInput({ msg }));
+ yield put(replyInit(message, false));
} catch (e) {
log('handleReplyBroadcast', e);
}