diff --git a/app/containers/MessageBox/FilesActions.js b/app/containers/MessageBox/FilesActions.js
deleted file mode 100644
index 1850bacf8..000000000
--- a/app/containers/MessageBox/FilesActions.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import { PureComponent } from 'react';
-import PropTypes from 'prop-types';
-import ActionSheet from 'react-native-action-sheet';
-
-import I18n from '../../i18n';
-
-export default class FilesActions extends PureComponent {
- static propTypes = {
- hideActions: PropTypes.func.isRequired,
- takePhoto: PropTypes.func.isRequired,
- chooseFromLibrary: PropTypes.func.isRequired
- }
-
- constructor(props) {
- super(props);
-
- // Cancel
- this.options = [I18n.t('Cancel')];
- this.CANCEL_INDEX = 0;
-
- // Photo
- this.options.push(I18n.t('Take_a_photo'));
- this.PHOTO_INDEX = 1;
-
- // Library
- this.options.push(I18n.t('Choose_from_library'));
- this.LIBRARY_INDEX = 2;
-
- setTimeout(() => {
- this.showActionSheet();
- });
- }
-
- showActionSheet = () => {
- ActionSheet.showActionSheetWithOptions({
- options: this.options,
- cancelButtonIndex: this.CANCEL_INDEX
- }, (actionIndex) => {
- this.handleActionPress(actionIndex);
- });
- }
-
- handleActionPress = (actionIndex) => {
- const { takePhoto, chooseFromLibrary, hideActions } = this.props;
- switch (actionIndex) {
- case this.PHOTO_INDEX:
- takePhoto();
- break;
- case this.LIBRARY_INDEX:
- chooseFromLibrary();
- break;
- default:
- break;
- }
- hideActions();
- }
-
- render() {
- return (
- null
- );
- }
-}
diff --git a/app/containers/MessageBox/LeftButtons.android.js b/app/containers/MessageBox/LeftButtons.android.js
new file mode 100644
index 000000000..ed21b839f
--- /dev/null
+++ b/app/containers/MessageBox/LeftButtons.android.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { CancelEditingButton, ToggleEmojiButton } from './buttons';
+
+const LeftButtons = React.memo(({
+ showEmojiKeyboard, editing, editCancel, openEmoji, closeEmoji
+}) => {
+ if (editing) {
+ return ;
+ }
+ return (
+
+ );
+});
+
+LeftButtons.propTypes = {
+ showEmojiKeyboard: PropTypes.bool,
+ openEmoji: PropTypes.func.isRequired,
+ closeEmoji: PropTypes.func.isRequired,
+ editing: PropTypes.bool,
+ editCancel: PropTypes.func.isRequired
+};
+
+export default LeftButtons;
diff --git a/app/containers/MessageBox/LeftButtons.ios.js b/app/containers/MessageBox/LeftButtons.ios.js
new file mode 100644
index 000000000..76a58d61b
--- /dev/null
+++ b/app/containers/MessageBox/LeftButtons.ios.js
@@ -0,0 +1,21 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { CancelEditingButton, FileButton } from './buttons';
+
+const LeftButtons = React.memo(({
+ showFileActions, editing, editCancel
+}) => {
+ if (editing) {
+ return ;
+ }
+ return ;
+});
+
+LeftButtons.propTypes = {
+ showFileActions: PropTypes.func.isRequired,
+ editing: PropTypes.bool,
+ editCancel: PropTypes.func.isRequired
+};
+
+export default LeftButtons;
diff --git a/app/containers/MessageBox/RightButtons.android.js b/app/containers/MessageBox/RightButtons.android.js
new file mode 100644
index 000000000..6384fabf3
--- /dev/null
+++ b/app/containers/MessageBox/RightButtons.android.js
@@ -0,0 +1,27 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { SendButton, AudioButton, FileButton } from './buttons';
+
+const RightButtons = React.memo(({
+ showSend, submit, recordAudioMessage, showFileActions
+}) => {
+ if (showSend) {
+ return ;
+ }
+ return (
+
+
+
+
+ );
+});
+
+RightButtons.propTypes = {
+ showSend: PropTypes.bool,
+ submit: PropTypes.func.isRequired,
+ recordAudioMessage: PropTypes.func.isRequired,
+ showFileActions: PropTypes.func.isRequired
+};
+
+export default RightButtons;
diff --git a/app/containers/MessageBox/RightButtons.ios.js b/app/containers/MessageBox/RightButtons.ios.js
new file mode 100644
index 000000000..344e2fea5
--- /dev/null
+++ b/app/containers/MessageBox/RightButtons.ios.js
@@ -0,0 +1,21 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { SendButton, AudioButton } from './buttons';
+
+const RightButtons = React.memo(({
+ showSend, submit, recordAudioMessage
+}) => {
+ if (showSend) {
+ return ;
+ }
+ return ;
+});
+
+RightButtons.propTypes = {
+ showSend: PropTypes.bool,
+ submit: PropTypes.func.isRequired,
+ recordAudioMessage: PropTypes.func.isRequired
+};
+
+export default RightButtons;
diff --git a/app/containers/MessageBox/buttons/AudioButton.js b/app/containers/MessageBox/buttons/AudioButton.js
new file mode 100644
index 000000000..a2beaecf8
--- /dev/null
+++ b/app/containers/MessageBox/buttons/AudioButton.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import BaseButton from './BaseButton';
+
+const AudioButton = React.memo(({ onPress }) => (
+
+));
+
+AudioButton.propTypes = {
+ onPress: PropTypes.func.isRequired
+};
+
+export default AudioButton;
diff --git a/app/containers/MessageBox/buttons/BaseButton.js b/app/containers/MessageBox/buttons/BaseButton.js
new file mode 100644
index 000000000..b8ab18c4f
--- /dev/null
+++ b/app/containers/MessageBox/buttons/BaseButton.js
@@ -0,0 +1,31 @@
+import React from 'react';
+import { BorderlessButton } from 'react-native-gesture-handler';
+import PropTypes from 'prop-types';
+
+import { COLOR_PRIMARY } from '../../../constants/colors';
+import { CustomIcon } from '../../../lib/Icons';
+import styles from '../styles';
+import I18n from '../../../i18n';
+
+const BaseButton = React.memo(({
+ onPress, testID, accessibilityLabel, icon
+}) => (
+
+
+
+));
+
+BaseButton.propTypes = {
+ onPress: PropTypes.func.isRequired,
+ testID: PropTypes.string.isRequired,
+ accessibilityLabel: PropTypes.string.isRequired,
+ icon: PropTypes.string.isRequired
+};
+
+export default BaseButton;
diff --git a/app/containers/MessageBox/buttons/CancelEditingButton.js b/app/containers/MessageBox/buttons/CancelEditingButton.js
new file mode 100644
index 000000000..6cb44d765
--- /dev/null
+++ b/app/containers/MessageBox/buttons/CancelEditingButton.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import BaseButton from './BaseButton';
+
+const CancelEditingButton = React.memo(({ onPress }) => (
+
+));
+
+CancelEditingButton.propTypes = {
+ onPress: PropTypes.func.isRequired
+};
+
+export default CancelEditingButton;
diff --git a/app/containers/MessageBox/buttons/FileButton.js b/app/containers/MessageBox/buttons/FileButton.js
new file mode 100644
index 000000000..45f09815e
--- /dev/null
+++ b/app/containers/MessageBox/buttons/FileButton.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import BaseButton from './BaseButton';
+
+const FileButton = React.memo(({ onPress }) => (
+
+));
+
+FileButton.propTypes = {
+ onPress: PropTypes.func.isRequired
+};
+
+export default FileButton;
diff --git a/app/containers/MessageBox/buttons/SendButton.js b/app/containers/MessageBox/buttons/SendButton.js
new file mode 100644
index 000000000..0c4291833
--- /dev/null
+++ b/app/containers/MessageBox/buttons/SendButton.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import BaseButton from './BaseButton';
+
+const SendButton = React.memo(({ onPress }) => (
+
+));
+
+SendButton.propTypes = {
+ onPress: PropTypes.func.isRequired
+};
+
+export default SendButton;
diff --git a/app/containers/MessageBox/buttons/ToggleEmojiButton.js b/app/containers/MessageBox/buttons/ToggleEmojiButton.js
new file mode 100644
index 000000000..2a96ae502
--- /dev/null
+++ b/app/containers/MessageBox/buttons/ToggleEmojiButton.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import BaseButton from './BaseButton';
+
+const ToggleEmojiButton = React.memo(({ show, open, close }) => {
+ if (show) {
+ return (
+
+ );
+ }
+ return (
+
+ );
+});
+
+ToggleEmojiButton.propTypes = {
+ show: PropTypes.bool,
+ open: PropTypes.func.isRequired,
+ close: PropTypes.func.isRequired
+};
+
+export default ToggleEmojiButton;
diff --git a/app/containers/MessageBox/buttons/index.js b/app/containers/MessageBox/buttons/index.js
new file mode 100644
index 000000000..5046ca50e
--- /dev/null
+++ b/app/containers/MessageBox/buttons/index.js
@@ -0,0 +1,13 @@
+import CancelEditingButton from './CancelEditingButton';
+import ToggleEmojiButton from './ToggleEmojiButton';
+import SendButton from './SendButton';
+import AudioButton from './AudioButton';
+import FileButton from './FileButton';
+
+export {
+ CancelEditingButton,
+ ToggleEmojiButton,
+ SendButton,
+ AudioButton,
+ FileButton
+};
diff --git a/app/containers/MessageBox/index.js b/app/containers/MessageBox/index.js
index d22da3638..ba07f598a 100644
--- a/app/containers/MessageBox/index.js
+++ b/app/containers/MessageBox/index.js
@@ -7,8 +7,8 @@ import { connect } from 'react-redux';
import { emojify } from 'react-emojione';
import { KeyboardAccessoryView } from 'react-native-keyboard-input';
import ImagePicker from 'react-native-image-crop-picker';
-import { BorderlessButton } from 'react-native-gesture-handler';
import equal from 'deep-equal';
+import ActionSheet from 'react-native-action-sheet';
import { userTyping as userTypingAction } from '../../actions/room';
import {
@@ -23,15 +23,15 @@ import Avatar from '../Avatar';
import CustomEmoji from '../EmojiPicker/CustomEmoji';
import { emojis } from '../../emojis';
import Recording from './Recording';
-import FilesActions from './FilesActions';
import UploadModal from './UploadModal';
-import './EmojiKeyboard';
import log from '../../utils/log';
import I18n from '../../i18n';
import ReplyPreview from './ReplyPreview';
-import { CustomIcon } from '../../lib/Icons';
import debounce from '../../utils/debounce';
-import { COLOR_PRIMARY, COLOR_TEXT_DESCRIPTION } from '../../constants/colors';
+import { COLOR_TEXT_DESCRIPTION } from '../../constants/colors';
+import LeftButtons from './LeftButtons';
+import RightButtons from './RightButtons';
+import { isAndroid } from '../../utils/deviceInfo';
const MENTIONS_TRACKING_TYPE_USERS = '@';
const MENTIONS_TRACKING_TYPE_EMOJIS = ':';
@@ -48,6 +48,17 @@ const imagePickerConfig = {
cropperCancelText: I18n.t('Cancel')
};
+const fileOptions = [I18n.t('Cancel')];
+const FILE_CANCEL_INDEX = 0;
+
+// Photo
+fileOptions.push(I18n.t('Take_a_photo'));
+const FILE_PHOTO_INDEX = 1;
+
+// Library
+fileOptions.push(I18n.t('Choose_from_library'));
+const FILE_LIBRARY_INDEX = 2;
+
class MessageBox extends Component {
static propTypes = {
rid: PropTypes.string.isRequired,
@@ -77,7 +88,6 @@ class MessageBox extends Component {
this.state = {
mentions: [],
showEmojiKeyboard: false,
- showFilesAction: false,
showSend: false,
recording: false,
trackingType: '',
@@ -111,6 +121,10 @@ class MessageBox extends Component {
this.setInput(msg);
this.setShowSend(true);
}
+
+ if (isAndroid) {
+ require('./EmojiKeyboard');
+ }
}
componentWillReceiveProps(nextProps) {
@@ -133,7 +147,7 @@ class MessageBox extends Component {
shouldComponentUpdate(nextProps, nextState) {
const {
- showEmojiKeyboard, showFilesAction, showSend, recording, mentions, file
+ showEmojiKeyboard, showSend, recording, mentions, file
} = this.state;
const {
roomType, replying, editing, isFocused
@@ -153,9 +167,6 @@ class MessageBox extends Component {
if (nextState.showEmojiKeyboard !== showEmojiKeyboard) {
return true;
}
- if (nextState.showFilesAction !== showFilesAction) {
- return true;
- }
if (nextState.showSend !== showSend) {
return true;
}
@@ -171,32 +182,25 @@ class MessageBox extends Component {
return false;
}
- onChangeText = (text) => {
+ onChangeText = debounce((text) => {
const isTextEmpty = text.length === 0;
this.setShowSend(!isTextEmpty);
this.handleTyping(!isTextEmpty);
- this.debouncedOnChangeText(text);
- }
-
- // eslint-disable-next-line react/sort-comp
- debouncedOnChangeText = debounce((text) => {
this.setInput(text);
if (this.component) {
- requestAnimationFrame(() => {
- const { start, end } = this.component._lastNativeSelection;
- const cursor = Math.max(start, end);
- const lastNativeText = this.component._lastNativeText;
- const regexp = /(#|@|:)([a-z0-9._-]+)$/im;
- const result = lastNativeText.substr(0, cursor).match(regexp);
- if (!result) {
- return this.stopTrackingMention();
- }
- const [, lastChar, name] = result;
- this.identifyMentionKeyword(name, lastChar);
- });
+ const { start, end } = this.component._lastNativeSelection;
+ const cursor = Math.max(start, end);
+ const lastNativeText = this.component._lastNativeText;
+ const regexp = /(#|@|:)([a-z0-9._-]+)$/im;
+ const result = lastNativeText.substr(0, cursor).match(regexp);
+ if (!result) {
+ return this.stopTrackingMention();
+ }
+ const [, lastChar, name] = result;
+ this.identifyMentionKeyword(name, lastChar);
}
- }, 100);
+ }, 100)
onKeyboardResigned = () => {
this.closeEmoji();
@@ -239,106 +243,6 @@ class MessageBox extends Component {
this.setShowSend(true);
}
- get leftButtons() {
- const { showEmojiKeyboard } = this.state;
- const { editing } = this.props;
-
- if (editing) {
- return (
-
-
-
- );
- }
- return !showEmojiKeyboard
- ? (
-
-
-
- )
- : (
-
-
-
- );
- }
-
- get rightButtons() {
- const { showSend } = this.state;
- const icons = [];
-
- if (showSend) {
- icons.push(
-
-
-
- );
- return icons;
- }
- icons.push(
-
-
-
- );
- icons.push(
-
-
-
- );
- return icons;
- }
-
getPermalink = async(message) => {
try {
return await RocketChat.getPermalink(message);
@@ -495,10 +399,6 @@ class MessageBox extends Component {
this.setShowSend(false);
}
- toggleFilesActions = () => {
- this.setState(prevState => ({ showFilesAction: !prevState.showFilesAction }));
- }
-
sendImageMessage = async(file) => {
const { rid, tmid } = this.props;
@@ -540,6 +440,28 @@ class MessageBox extends Component {
this.setState({ file: { ...file, isVisible: true } });
}
+ showFileActions = () => {
+ ActionSheet.showActionSheetWithOptions({
+ options: fileOptions,
+ cancelButtonIndex: FILE_CANCEL_INDEX
+ }, (actionIndex) => {
+ this.handleFileActionPress(actionIndex);
+ });
+ }
+
+ handleFileActionPress = (actionIndex) => {
+ switch (actionIndex) {
+ case FILE_PHOTO_INDEX:
+ this.takePhoto();
+ break;
+ case FILE_LIBRARY_INDEX:
+ this.chooseFromLibrary();
+ break;
+ default:
+ break;
+ }
+ }
+
editCancel = () => {
const { editCancel } = this.props;
editCancel();
@@ -585,6 +507,7 @@ class MessageBox extends Component {
} = this.props;
const message = this.text;
+ this.clearInput();
this.closeEmoji();
this.stopTrackingMention();
this.handleTyping(false);
@@ -629,7 +552,6 @@ class MessageBox extends Component {
} else {
onSubmit(message);
}
- this.clearInput();
}
updateMentions = (keyword, type) => {
@@ -713,23 +635,27 @@ class MessageBox extends Component {
testID={`mention-item-${ trackingType === MENTIONS_TRACKING_TYPE_EMOJIS ? item.name || item : item.username || item.name }`}
>
{trackingType === MENTIONS_TRACKING_TYPE_EMOJIS
- ? [
- this.renderMentionEmoji(item),
- :{ item.name || item }:
- ]
- : [
- ,
- { item.username || item.name }
- ]
+ ? (
+
+ {this.renderMentionEmoji(item)}
+ :{ item.name || item }:
+
+ )
+ : (
+
+
+ { item.username || item.name }
+
+ )
}
);
@@ -741,7 +667,7 @@ class MessageBox extends Component {
return null;
}
return (
-
+
;
};
- renderFilesActions = () => {
- const { showFilesAction } = this.state;
-
- if (!showFilesAction) {
- return null;
- }
- return (
-
- );
- }
-
renderContent = () => {
- const { recording } = this.state;
+ const { recording, showEmojiKeyboard, showSend } = this.state;
const { editing } = this.props;
if (recording) {
return ();
}
return (
- [
- this.renderMentions(),
+
+ {this.renderMentions()}
{this.renderReplyPreview()}
- {this.leftButtons}
+
this.component = component}
style={styles.textBoxInput}
@@ -810,19 +727,23 @@ class MessageBox extends Component {
placeholderTextColor={COLOR_TEXT_DESCRIPTION}
testID='messagebox-input'
/>
- {this.rightButtons}
+
- ]
+
);
}
render() {
const { showEmojiKeyboard, file } = this.state;
return (
- [
+
,
- this.renderFilesActions(),
+ />
this.setState({ file: {} })}
submit={this.sendImageMessage}
/>
- ]
+
);
}
}