From 5f844f5a85cec9991bda40a59fe0282b66c6ad65 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 21 Nov 2017 12:55:50 -0200 Subject: [PATCH] Messages actions (#90) - delete - edit - pin - star - reply - copy - quote --- app/actions/actionsTypes.js | 21 ++- app/actions/messages.js | 113 +++++++++++++ app/containers/Message.js | 74 --------- app/containers/MessageBox.js | 44 ++++-- app/containers/message/User.js | 32 +++- app/containers/message/index.js | 272 ++++++++++++++++++++++++++++++++ app/lib/realm.js | 24 ++- app/lib/rocketchat.js | 54 +++++++ app/reducers/messages.js | 33 +++- app/sagas/messages.js | 80 +++++++++- app/views/RoomView.js | 2 +- package-lock.json | 49 +----- package.json | 1 + 13 files changed, 660 insertions(+), 139 deletions(-) delete mode 100644 app/containers/Message.js create mode 100644 app/containers/message/index.js diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index d0a621d6c..29a0dd0be 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -28,7 +28,26 @@ export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD', [ ]); export const ROOMS = createRequestTypes('ROOMS'); export const APP = createRequestTypes('APP', ['READY', 'INIT']); -export const MESSAGES = createRequestTypes('MESSAGES'); +export const MESSAGES = createRequestTypes('MESSAGES', [ + ...defaultTypes, + 'DELETE_REQUEST', + 'DELETE_SUCCESS', + 'DELETE_FAILURE', + 'EDIT_INIT', + 'EDIT_REQUEST', + 'EDIT_SUCCESS', + 'EDIT_FAILURE', + 'STAR_REQUEST', + 'STAR_SUCCESS', + 'STAR_FAILURE', + 'PERMALINK_REQUEST', + 'PERMALINK_SUCCESS', + 'PERMALINK_FAILURE', + 'TOGGLE_PIN_REQUEST', + 'TOGGLE_PIN_SUCCESS', + 'TOGGLE_PIN_FAILURE', + 'SET_INPUT' +]); export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL', [ ...defaultTypes, 'REQUEST_USERS', diff --git a/app/actions/messages.js b/app/actions/messages.js index 2f92816ec..5f01815ef 100644 --- a/app/actions/messages.js +++ b/app/actions/messages.js @@ -19,3 +19,116 @@ export function messagesFailure(err) { err }; } + +export function deleteRequest(message) { + return { + type: types.MESSAGES.DELETE_REQUEST, + message + }; +} + +export function deleteSuccess() { + return { + type: types.MESSAGES.DELETE_SUCCESS + }; +} + +export function deleteFailure() { + return { + type: types.MESSAGES.DELETE_FAILURE + }; +} + + +export function editInit(message) { + return { + type: types.MESSAGES.EDIT_INIT, + message + }; +} + +export function editRequest(message) { + return { + type: types.MESSAGES.EDIT_REQUEST, + message + }; +} + +export function editSuccess() { + return { + type: types.MESSAGES.EDIT_SUCCESS + }; +} + +export function editFailure() { + return { + type: types.MESSAGES.EDIT_FAILURE + }; +} + +export function starRequest(message) { + return { + type: types.MESSAGES.STAR_REQUEST, + message + }; +} + +export function starSuccess() { + return { + type: types.MESSAGES.STAR_SUCCESS + }; +} + +export function starFailure() { + return { + type: types.MESSAGES.STAR_FAILURE + }; +} + +export function permalinkRequest(message) { + return { + type: types.MESSAGES.PERMALINK_REQUEST, + message + }; +} + +export function permalinkSuccess(permalink) { + return { + type: types.MESSAGES.PERMALINK_SUCCESS, + permalink + }; +} + +export function permalinkFailure(err) { + return { + type: types.MESSAGES.PERMALINK_FAILURE, + err + }; +} + +export function togglePinRequest(message) { + return { + type: types.MESSAGES.TOGGLE_PIN_REQUEST, + message + }; +} + +export function togglePinSuccess() { + return { + type: types.MESSAGES.TOGGLE_PIN_SUCCESS + }; +} + +export function togglePinFailure(err) { + return { + type: types.MESSAGES.TOGGLE_PIN_FAILURE, + err + }; +} + +export function setInput(message) { + return { + type: types.MESSAGES.SET_INPUT, + message + }; +} diff --git a/app/containers/Message.js b/app/containers/Message.js deleted file mode 100644 index da39c5f2d..000000000 --- a/app/containers/Message.js +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { View, StyleSheet } from 'react-native'; -import { emojify } from 'react-emojione'; -import Markdown from 'react-native-easy-markdown'; - -import Card from './message/Card'; -import Avatar from './Avatar'; -import User from './message/User'; - -const styles = StyleSheet.create({ - content: { - flexGrow: 1, - flexShrink: 1 - }, - message: { - padding: 12, - paddingTop: 6, - paddingBottom: 6, - flexDirection: 'row', - transform: [{ scaleY: -1 }] - } -}); - -export default class Message extends React.PureComponent { - static propTypes = { - item: PropTypes.object.isRequired, - baseUrl: PropTypes.string.isRequired, - Message_TimeFormat: PropTypes.string.isRequired - } - - attachments() { - return this.props.item.attachments.length ? ( - - ) : null; - } - - render() { - const { item } = this.props; - - const extraStyle = {}; - if (item.temp) { - extraStyle.opacity = 0.3; - } - - const msg = emojify(item.msg, { output: 'unicode' }); - const username = item.alias || item.u.username; - - return ( - - - - - {this.attachments()} - - {msg} - - - - ); - } -} diff --git a/app/containers/MessageBox.js b/app/containers/MessageBox.js index 7fa265aa6..eb9f5656d 100644 --- a/app/containers/MessageBox.js +++ b/app/containers/MessageBox.js @@ -3,7 +3,9 @@ import PropTypes from 'prop-types'; import { View, TextInput, StyleSheet, SafeAreaView } from 'react-native'; import Icon from 'react-native-vector-icons/MaterialIcons'; import ImagePicker from 'react-native-image-picker'; +import { connect } from 'react-redux'; import RocketChat from '../lib/rocketchat'; +import { editRequest } from '../actions/messages'; const styles = StyleSheet.create({ textBox: { @@ -19,7 +21,6 @@ const styles = StyleSheet.create({ textBoxInput: { height: 40, alignSelf: 'stretch', - backgroundColor: '#fff', flexGrow: 1 }, fileButton: { @@ -29,24 +30,49 @@ const styles = StyleSheet.create({ paddingTop: 10, paddingBottom: 10, fontSize: 20 + }, + editing: { + backgroundColor: '#fff5df' } }); -export default class MessageBox extends React.PureComponent { +@connect(state => ({ + message: state.messages.message, + editing: state.messages.editing +}), dispatch => ({ + editRequest: message => dispatch(editRequest(message)) +})) +export default class MessageBox extends React.Component { static propTypes = { onSubmit: PropTypes.func.isRequired, - rid: PropTypes.string.isRequired + rid: PropTypes.string.isRequired, + editRequest: PropTypes.func.isRequired, + message: PropTypes.object, + editing: PropTypes.bool + } + + componentWillReceiveProps(nextProps) { + if (this.props.message !== nextProps.message) { + this.component.setNativeProps({ text: nextProps.message.msg }); + this.component.focus(); + } } submit(message) { - const text = message; - if (text.trim() === '') { + const { editing } = this.props; + if (message.trim() === '') { return; } - if (this.component) { - this.component.setNativeProps({ text: '' }); + + // if is editing a message + if (editing) { + const { _id, rid } = this.props.message; + this.props.editRequest({ _id, msg: message, rid }); + } else { + // if is submiting a new message + this.props.onSubmit(message); } - this.props.onSubmit(text); + this.component.setNativeProps({ text: '' }); } addFile = () => { @@ -78,7 +104,7 @@ export default class MessageBox extends React.PureComponent { render() { return ( - + + + + + ); } render() { @@ -48,7 +74,9 @@ export default class Message extends React.PureComponent { {username} - {aliasUsername}{time} + {aliasUsername} + {time} + {this.renderEdited(item)} ); } diff --git a/app/containers/message/index.js b/app/containers/message/index.js new file mode 100644 index 000000000..2835a6d41 --- /dev/null +++ b/app/containers/message/index.js @@ -0,0 +1,272 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { View, StyleSheet, TouchableOpacity, Text, Alert, Clipboard } from 'react-native'; +import { emojify } from 'react-emojione'; +import Markdown from 'react-native-easy-markdown'; // eslint-disable-line +import ActionSheet from 'react-native-actionsheet'; +import { connect } from 'react-redux'; + +import Card from './Card'; +import User from './User'; +import Avatar from '../Avatar'; +import { + deleteRequest, + editInit, + starRequest, + permalinkRequest, + togglePinRequest, + setInput +} from '../../actions/messages'; +import RocketChat from '../../lib/rocketchat'; + +const title = 'Message actions'; +const options = ['Cancel', 'Reply', 'Edit', 'Permalink', 'Copy', 'Quote', 'Star Message', 'Pin Message', 'Delete']; +const CANCEL_INDEX = 0; +const DESTRUCTIVE_INDEX = 8; + +const styles = StyleSheet.create({ + content: { + flexGrow: 1, + flexShrink: 1 + }, + message: { + padding: 12, + paddingTop: 6, + paddingBottom: 6, + flexDirection: 'row', + transform: [{ scaleY: -1 }] + }, + textInfo: { + fontStyle: 'italic', + color: '#a0a0a0' + }, + editing: { + backgroundColor: '#fff5df' + } +}); + +@connect(state => ({ + message: state.messages.message, + permalink: state.messages.permalink, + user: state.login.user +}), dispatch => ({ + deleteRequest: message => dispatch(deleteRequest(message)), + editInit: message => dispatch(editInit(message)), + starRequest: message => dispatch(starRequest(message)), + permalinkRequest: message => dispatch(permalinkRequest(message)), + togglePinRequest: message => dispatch(togglePinRequest(message)), + setInput: message => dispatch(setInput(message)) +})) +export default class Message extends React.Component { + static propTypes = { + item: PropTypes.object.isRequired, + baseUrl: PropTypes.string.isRequired, + Message_TimeFormat: PropTypes.string.isRequired, + deleteRequest: PropTypes.func.isRequired, + editInit: PropTypes.func.isRequired, + starRequest: PropTypes.func.isRequired, + permalinkRequest: PropTypes.func.isRequired, + togglePinRequest: PropTypes.func.isRequired, + setInput: PropTypes.func.isRequired, + user: PropTypes.object.isRequired, + message: PropTypes.object, + permalink: PropTypes.string + } + + constructor(props) { + super(props); + this.state = { + copyPermalink: false, + reply: false, + quote: false + }; + this.handleActionPress = this.handleActionPress.bind(this); + this.showActions = this.showActions.bind(this); + } + + async componentWillReceiveProps(nextProps) { + if (this.props.permalink !== nextProps.permalink) { + // copy permalink + if (this.state.copyPermalink) { + this.setState({ copyPermalink: false }); + await Clipboard.setString(nextProps.permalink); + Alert.alert('Permalink copied to clipboard!'); + + // quote + } else if (this.state.quote) { + this.setState({ quote: false }); + const msg = `[ ](${ nextProps.permalink }) `; + this.props.setInput({ msg }); + + // reply + } else if (this.state.reply) { + this.setState({ reply: false }); + let msg = `[ ](${ nextProps.permalink }) `; + const room = await RocketChat.getRoom(this.props.item.rid); + + // if original message wasn't sent by current user and neither from a direct room + if (this.props.user.username !== this.props.item.u.username && room.t !== 'd') { + msg += `@${ this.props.item.u.username } `; + } + this.props.setInput({ msg }); + } + } + } + + isDeleted() { + return !this.props.item.msg; + } + + attachments() { + return this.props.item.attachments.length ? ( + + ) : null; + } + + showActions = () => { + this.ActionSheet.show(); + } + + handleDelete() { + Alert.alert( + 'Are you sure?', + 'You will not be able to recover this message!', + [ + { + text: 'Cancel', + style: 'cancel' + }, + { + text: 'Yes, delete it!', + style: 'destructive', + onPress: () => this.props.deleteRequest(this.props.item) + } + ], + { cancelable: false } + ); + } + + handleEdit() { + const { _id, msg, rid } = this.props.item; + this.props.editInit({ _id, msg, rid }); + } + + handleCopy = async() => { + await Clipboard.setString(this.props.item.msg); + Alert.alert('Copied to clipboard!'); + } + + handleStar() { + this.props.starRequest(this.props.item); + } + + handlePermalink() { + this.setState({ copyPermalink: true }); + this.props.permalinkRequest(this.props.item); + } + + handleTogglePin() { + this.props.togglePinRequest(this.props.item); + } + + handleReply() { + this.setState({ reply: true }); + this.props.permalinkRequest(this.props.item); + } + + handleQuote() { + this.setState({ quote: true }); + this.props.permalinkRequest(this.props.item); + } + + handleActionPress = (actionIndex) => { + // reply + if (actionIndex === 1) { + this.handleReply(); + // edit + } else if (actionIndex === 2) { + this.handleEdit(); + // permalink + } else if (actionIndex === 3) { + this.handlePermalink(); + // copy + } else if (actionIndex === 4) { + this.handleCopy(); + // quote + } else if (actionIndex === 5) { + this.handleQuote(); + // star + } else if (actionIndex === 6) { + this.handleStar(); + // toggle pin + } else if (actionIndex === 7) { + this.handleTogglePin(); + // delete + } else if (actionIndex === 8) { + this.handleDelete(); + } + } + + renderMessageContent() { + if (this.isDeleted()) { + return Message removed; + } + + const msg = emojify(this.props.item.msg, { output: 'unicode' }); + return ( + + {msg} + + ); + } + + render() { + const { item } = this.props; + + const extraStyle = {}; + if (item.temp) { + extraStyle.opacity = 0.3; + } + + const username = item.alias || item.u.username; + const isEditing = this.props.message._id === item._id; + + return ( + this.showActions()} + disabled={this.isDeleted()} + style={isEditing ? styles.editing : null} + > + + + + + {this.attachments()} + {this.renderMessageContent(item)} + + this.ActionSheet = o} + title={title} + options={options} + cancelButtonIndex={CANCEL_INDEX} + destructiveButtonIndex={DESTRUCTIVE_INDEX} + onPress={this.handleActionPress} + /> + + + ); + } +} diff --git a/app/lib/realm.js b/app/lib/realm.js index 75a21df17..72481b5ee 100644 --- a/app/lib/realm.js +++ b/app/lib/realm.js @@ -84,6 +84,14 @@ const attachment = { } }; +const messagesEditedBySchema = { + name: 'messagesEditedBy', + properties: { + _id: { type: 'string', optional: true }, + username: { type: 'string', optional: true } + } +}; + const messagesSchema = { name: 'messages', primaryKey: '_id', @@ -102,14 +110,26 @@ const messagesSchema = { avatar: { type: 'string', optional: true }, attachments: { type: 'list', objectType: 'attachment' }, _updatedAt: { type: 'date', optional: true }, - temp: { type: 'bool', optional: true } + temp: { type: 'bool', optional: true }, + pinned: { type: 'bool', optional: true }, + starred: { type: 'bool', optional: true }, + editedBy: 'messagesEditedBy' } }; // // Realm.clearTestState(); // AsyncStorage.clear(); const realm = new Realm({ - schema: [settingsSchema, serversSchema, subscriptionSchema, messagesSchema, usersSchema, roomsSchema, attachment] + schema: [ + settingsSchema, + serversSchema, + subscriptionSchema, + messagesSchema, + usersSchema, + roomsSchema, + attachment, + messagesEditedBySchema + ] }); export default realm; diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index a698cdcec..35a83216c 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -89,6 +89,7 @@ const RocketChat = { const message = ddbMessage.fields.args[0]; message.temp = false; message._server = { id: reduxStore.getState().server.server }; + message.starred = !!message.starred; realm.create('messages', message, true); }); } @@ -285,6 +286,7 @@ const RocketChat = { message.temp = false; message._server = { id: reduxStore.getState().server.server }; // write('messages', message); + message.starred = !!message.starred; realm.create('messages', message, true); }); }); @@ -469,6 +471,58 @@ const RocketChat = { Meteor.disconnect(); AsyncStorage.removeItem(TOKEN_KEY); AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`); + }, + deleteMessage(message) { + return call('deleteMessage', { _id: message._id }); + }, + editMessage(message) { + const { _id, msg, rid } = message; + return call('updateMessage', { _id, msg, rid }); + }, + starMessage(message) { + return call('starMessage', { _id: message._id, rid: message.rid, starred: !message.starred }); + }, + togglePinMessage(message) { + if (message.pinned) { + return call('unpinMessage', message); + } + return call('pinMessage', message); + }, + getRoom(rid) { + return new Promise((resolve, reject) => { + const result = realm.objects('subscriptions').filtered('rid = $0', rid); + + if (result.length === 0) { + return reject(new Error('Room not found')); + } + return resolve(result[0]); + }); + }, + async getPermalink(message) { + return new Promise(async(resolve, reject) => { + let room; + try { + room = await RocketChat.getRoom(message.rid); + } catch (error) { + return reject(error); + } + + let roomType; + switch (room.t) { + case 'p': + roomType = 'group'; + break; + case 'c': + roomType = 'channel'; + break; + case 'd': + roomType = 'direct'; + break; + default: + break; + } + return resolve(`${ room._server.id }/${ roomType }/${ room.name }?msg=${ message._id }`); + }); } }; diff --git a/app/reducers/messages.js b/app/reducers/messages.js index 94f0f4f1f..4c1a73e2d 100644 --- a/app/reducers/messages.js +++ b/app/reducers/messages.js @@ -2,7 +2,10 @@ import * as types from '../actions/actionsTypes'; const initialState = { isFetching: false, - failure: false + failure: false, + message: {}, + editing: false, + permalink: '' }; export default function messages(state = initialState, action) { @@ -24,6 +27,34 @@ export default function messages(state = initialState, action) { failure: true, errorMessage: action.err }; + case types.MESSAGES.EDIT_INIT: + return { + ...state, + message: action.message, + editing: true + }; + case types.MESSAGES.EDIT_SUCCESS: + return { + ...state, + message: {}, + editing: false + }; + case types.MESSAGES.EDIT_FAILURE: + return { + ...state, + message: {}, + editing: false + }; + case types.MESSAGES.PERMALINK_SUCCESS: + return { + ...state, + permalink: action.permalink + }; + case types.MESSAGES.SET_INPUT: + return { + ...state, + message: action.message + }; // case types.LOGOUT: // return initialState; default: diff --git a/app/sagas/messages.js b/app/sagas/messages.js index fc4aff769..336f90261 100644 --- a/app/sagas/messages.js +++ b/app/sagas/messages.js @@ -1,8 +1,27 @@ -import { takeLatest, select, take, put } from 'redux-saga/effects'; +import { takeLatest, select, take, put, call } from 'redux-saga/effects'; import { MESSAGES, LOGIN } from '../actions/actionsTypes'; -import { messagesSuccess, messagesFailure } from '../actions/messages'; +import { + messagesSuccess, + messagesFailure, + deleteSuccess, + deleteFailure, + editSuccess, + editFailure, + starSuccess, + starFailure, + permalinkSuccess, + permalinkFailure, + togglePinSuccess, + togglePinFailure +} from '../actions/messages'; import RocketChat from '../lib/rocketchat'; +const deleteMessage = message => RocketChat.deleteMessage(message); +const editMessage = message => RocketChat.editMessage(message); +const starMessage = message => RocketChat.starMessage(message); +const getPermalink = message => RocketChat.getPermalink(message); +const togglePinMessage = message => RocketChat.togglePinMessage(message); + const get = function* get({ rid }) { const auth = yield select(state => state.login.isAuthenticated); if (!auth) { @@ -17,7 +36,58 @@ const get = function* get({ rid }) { yield put(messagesFailure(err.status)); } }; -const getData = function* getData() { - yield takeLatest(MESSAGES.REQUEST, get); + +const handleDeleteRequest = function* handleDeleteRequest({ message }) { + try { + yield call(deleteMessage, message); + yield put(deleteSuccess()); + } catch (error) { + yield put(deleteFailure()); + } }; -export default getData; + +const handleEditRequest = function* handleEditRequest({ message }) { + try { + yield call(editMessage, message); + yield put(editSuccess()); + } catch (error) { + yield put(editFailure()); + } +}; + +const handleStarRequest = function* handleStarRequest({ message }) { + try { + yield call(starMessage, message); + yield put(starSuccess()); + } catch (error) { + yield put(starFailure()); + } +}; + +const handlePermalinkRequest = function* handlePermalinkRequest({ message }) { + try { + const permalink = yield call(getPermalink, message); + yield put(permalinkSuccess(permalink)); + } catch (error) { + yield put(permalinkFailure(error)); + } +}; + +const handleTogglePinRequest = function* handleTogglePinRequest({ message }) { + try { + yield call(togglePinMessage, message); + yield put(togglePinSuccess()); + } catch (error) { + yield put(togglePinFailure(error)); + } +}; + +const root = function* root() { + yield takeLatest(MESSAGES.REQUEST, get); + yield takeLatest(MESSAGES.DELETE_REQUEST, handleDeleteRequest); + yield takeLatest(MESSAGES.EDIT_REQUEST, handleEditRequest); + yield takeLatest(MESSAGES.STAR_REQUEST, handleStarRequest); + yield takeLatest(MESSAGES.PERMALINK_REQUEST, handlePermalinkRequest); + yield takeLatest(MESSAGES.TOGGLE_PIN_REQUEST, handleTogglePinRequest); +}; +export default root; diff --git a/app/views/RoomView.js b/app/views/RoomView.js index acf6e53e5..1c68d2ab0 100644 --- a/app/views/RoomView.js +++ b/app/views/RoomView.js @@ -9,7 +9,7 @@ import * as actions from '../actions'; import { messagesRequest } from '../actions/messages'; import realm from '../lib/realm'; import RocketChat from '../lib/rocketchat'; -import Message from '../containers/Message'; +import Message from '../containers/message'; import MessageBox from '../containers/MessageBox'; import KeyboardView from '../presentation/KeyboardView'; diff --git a/package-lock.json b/package-lock.json index 641edeab8..bfe469d75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1058,12 +1058,6 @@ "integrity": "sha1-NOmzALFHnd2Y7HfqC76TQt/jloQ=", "dev": true }, - "babel-helper-is-react-class": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/babel-helper-is-react-class/-/babel-helper-is-react-class-1.0.0.tgz", - "integrity": "sha1-7282eLBcdtve7a3q16+YwnJNhDE=", - "dev": true - }, "babel-helper-is-void-0": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/babel-helper-is-void-0/-/babel-helper-is-void-0-0.2.0.tgz", @@ -1807,15 +1801,6 @@ "babel-runtime": "6.26.0" } }, - "babel-plugin-transform-react-inline-elements": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-inline-elements/-/babel-plugin-transform-react-inline-elements-6.22.0.tgz", - "integrity": "sha1-ZochGjK0mlLyLFc6K1UEol7xfFM=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, "babel-plugin-transform-react-jsx": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz", @@ -1845,15 +1830,6 @@ "babel-runtime": "6.26.0" } }, - "babel-plugin-transform-react-pure-class-to-function": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-pure-class-to-function/-/babel-plugin-transform-react-pure-class-to-function-1.0.1.tgz", - "integrity": "sha1-MqZJyX1lMlC0Gc/RSJMxsCkNnuQ=", - "dev": true, - "requires": { - "babel-helper-is-react-class": "1.0.0" - } - }, "babel-plugin-transform-react-remove-prop-types": { "version": "0.4.10", "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.10.tgz", @@ -2178,26 +2154,6 @@ "react-transform-hmr": "1.0.4" } }, - "babel-preset-react-optimize": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-react-optimize/-/babel-preset-react-optimize-1.0.1.tgz", - "integrity": "sha1-wjUJ+6fLx2195wUOfSa80ivDBOg=", - "dev": true, - "requires": { - "babel-plugin-transform-react-constant-elements": "6.23.0", - "babel-plugin-transform-react-inline-elements": "6.22.0", - "babel-plugin-transform-react-pure-class-to-function": "1.0.1", - "babel-plugin-transform-react-remove-prop-types": "0.2.12" - }, - "dependencies": { - "babel-plugin-transform-react-remove-prop-types": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.2.12.tgz", - "integrity": "sha1-NAZpbfC4tFYIn51ybSfn4SPS+Sk=", - "dev": true - } - } - }, "babel-preset-stage-0": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-preset-stage-0/-/babel-preset-stage-0-6.24.1.tgz", @@ -12047,6 +12003,11 @@ "prop-types": "15.6.0" } }, + "react-native-actionsheet": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-native-actionsheet/-/react-native-actionsheet-2.3.0.tgz", + "integrity": "sha1-qVUqNW3Fk5gpBiNTgE1eVgno+Wk=" + }, "react-native-animatable": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.2.4.tgz", diff --git a/package.json b/package.json index 3eefff0e2..12f4c43e2 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "react-emojione": "^5.0.0", "react-native": "0.50.3", "react-native-action-button": "^2.8.1", + "react-native-actionsheet": "^2.3.0", "react-native-animatable": "^1.2.4", "react-native-card-view": "0.0.3", "react-native-easy-markdown": "git+https://github.com/lappalj4/react-native-easy-markdown.git",