diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js
index b727d935f..4250f88bc 100644
--- a/app/actions/actionsTypes.js
+++ b/app/actions/actionsTypes.js
@@ -29,7 +29,26 @@ export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD', [
export const ROOMS = createRequestTypes('ROOMS');
export const ROOM = createRequestTypes('ROOM', ['ADD_USER_TYPING', 'REMOVE_USER_TYPING', 'USER_TYPING', 'OPEN', 'IM_TYPING']);
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 9eb32435c..ce8474e0c 100644
--- a/app/containers/MessageBox.js
+++ b/app/containers/MessageBox.js
@@ -6,6 +6,7 @@ import ImagePicker from 'react-native-image-picker';
import { connect } from 'react-redux';
import { imTyping } from '../actions/room';
import RocketChat from '../lib/rocketchat';
+import { editRequest } from '../actions/messages';
const styles = StyleSheet.create({
textBox: {
@@ -22,7 +23,6 @@ const styles = StyleSheet.create({
textBoxInput: {
height: 40,
alignSelf: 'stretch',
- backgroundColor: '#fff',
flexGrow: 1
},
fileButton: {
@@ -30,31 +30,50 @@ const styles = StyleSheet.create({
paddingTop: 10,
paddingBottom: 10,
fontSize: 20
+ },
+ editing: {
+ backgroundColor: '#fff5df'
}
});
-@connect(
- null,
- dispatch => ({
- typing: status => dispatch(imTyping(status))
- })
-)
-
-export default class MessageBox extends React.PureComponent {
+@connect(state => ({
+ message: state.messages.message,
+ editing: state.messages.editing
+}), dispatch => ({
+ editRequest: message => dispatch(editRequest(message)),
+ typing: status => dispatch(imTyping(status))
+}))
+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 = () => {
@@ -86,7 +105,7 @@ export default class MessageBox extends React.PureComponent {
render() {
return (
-
+
this.component = component}
diff --git a/app/containers/message/User.js b/app/containers/message/User.js
index c75d88a37..873e96888 100644
--- a/app/containers/message/User.js
+++ b/app/containers/message/User.js
@@ -2,6 +2,8 @@ import React from 'react';
import PropTypes from 'prop-types';
import { View, Text, StyleSheet } from 'react-native';
import moment from 'moment';
+import Icon from 'react-native-vector-icons/FontAwesome';
+import Avatar from '../Avatar';
const styles = StyleSheet.create({
username: {
@@ -21,6 +23,11 @@ const styles = StyleSheet.create({
fontSize: 10,
color: '#888',
paddingLeft: 5
+ },
+ edited: {
+ marginLeft: 5,
+ flexDirection: 'row',
+ alignItems: 'center'
}
});
@@ -28,7 +35,26 @@ export default class Message extends React.PureComponent {
static propTypes = {
item: PropTypes.object.isRequired,
Message_TimeFormat: PropTypes.string.isRequired,
- onPress: PropTypes.func
+ onPress: PropTypes.func,
+ baseUrl: PropTypes.string
+ }
+
+ renderEdited(item) {
+ if (!item.editedBy) {
+ return null;
+ }
+ 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 adf877feb..ee44086da 100644
--- a/app/lib/realm.js
+++ b/app/lib/realm.js
@@ -86,6 +86,14 @@ const attachment = {
}
};
+const messagesEditedBySchema = {
+ name: 'messagesEditedBy',
+ properties: {
+ _id: { type: 'string', optional: true },
+ username: { type: 'string', optional: true }
+ }
+};
+
const messagesSchema = {
name: 'messages',
primaryKey: '_id',
@@ -104,14 +112,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 481c10cd9..376a62798 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -70,6 +70,7 @@ const RocketChat = {
message.temp = false;
message._server = server;
message.attachments = message.attachments || [];
+ message.starred = !!message.starred;
realm.create('messages', message, true);
});
}
@@ -230,7 +231,7 @@ const RocketChat = {
const data = {
id: `RocketChatRN${ id }`,
token: { [key]: token },
- appName: 'main',
+ appName: 'chat.rocket.reactnative', // TODO: try to get from config file
userId: id,
metadata: {}
};
@@ -257,6 +258,7 @@ const RocketChat = {
message._server = { id: reduxStore.getState().server.server };
message.attachments = message.attachments || [];
// write('messages', message);
+ message.starred = !!message.starred;
realm.create('messages', message, true);
});
});
@@ -433,6 +435,38 @@ const RocketChat = {
});
},
_filterSettings: settings => settings.filter(setting => settingsType[setting.type] && setting.value),
+ 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) {
+ const result = realm.objects('subscriptions').filtered('rid = $0', rid);
+ if (result.length === 0) {
+ return Promise.reject(new Error('Room not found'));
+ }
+ return Promise.resolve(result[0]);
+ },
+ async getPermalink(message) {
+ const room = await RocketChat.getRoom(message.rid);
+ const roomType = {
+ p: 'group',
+ c: 'channel',
+ d: 'direct'
+ }[room.t];
+ return `${ room._server.id }/${ roomType }/${ room.name }?msg=${ message._id }`;
+ },
subscribe(...args) {
return Meteor.subscribe(...args);
},
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 e21b28ce9..b3d54db3e 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) {
@@ -16,7 +35,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 883a48ac2..307824611 100644
--- a/app/views/RoomView.js
+++ b/app/views/RoomView.js
@@ -9,7 +9,7 @@ import * as actions from '../actions';
import { openRoom } from '../actions/room';
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/ios/RocketChatRN.xcodeproj/project.pbxproj b/ios/RocketChatRN.xcodeproj/project.pbxproj
index 265cb70e5..ccca670e4 100644
--- a/ios/RocketChatRN.xcodeproj/project.pbxproj
+++ b/ios/RocketChatRN.xcodeproj/project.pbxproj
@@ -403,6 +403,7 @@
5A0EEFAF8AB14F5B9E796CDD /* libRNVectorIcons.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNVectorIcons.a; sourceTree = ""; };
5A8684E7C27E426C9206E980 /* RealmReact.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RealmReact.xcodeproj; path = "../node_modules/realm/react-native/ios/RealmReact.xcodeproj"; sourceTree = ""; };
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; };
+ 60B2A6A31FC4588700BD58E5 /* RocketChatRN.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = RocketChatRN.entitlements; path = RocketChatRN/RocketChatRN.entitlements; sourceTree = ""; };
6533FB90166345D29F1B91C0 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFetchBlob.a; sourceTree = ""; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; };
7A30DA4B2D474348824CD05B /* FontAwesome.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = ""; };
@@ -564,6 +565,7 @@
13B07FAE1A68108700A75B9A /* RocketChatRN */ = {
isa = PBXGroup;
children = (
+ 60B2A6A31FC4588700BD58E5 /* RocketChatRN.entitlements */,
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.m */,
@@ -863,6 +865,11 @@
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = S6UPZG7ZR3;
ProvisioningStyle = Manual;
+ SystemCapabilities = {
+ com.apple.Push = {
+ enabled = 1;
+ };
+ };
};
2D02E47A1E0B4A5D006451C7 = {
CreatedOnToolsVersion = 8.2.1;
@@ -1483,6 +1490,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_ENTITLEMENTS = RocketChatRN/RocketChatRN.entitlements;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 100;
@@ -1519,6 +1527,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_ENTITLEMENTS = RocketChatRN/RocketChatRN.entitlements;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 100;
diff --git a/ios/RocketChatRN/RocketChatRN.entitlements b/ios/RocketChatRN/RocketChatRN.entitlements
new file mode 100644
index 000000000..903def2af
--- /dev/null
+++ b/ios/RocketChatRN/RocketChatRN.entitlements
@@ -0,0 +1,8 @@
+
+
+
+
+ aps-environment
+ development
+
+
diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile
index 3a2a79fe4..780a629ae 100644
--- a/ios/fastlane/Fastfile
+++ b/ios/fastlane/Fastfile
@@ -39,6 +39,7 @@ platform :ios do
type: "appstore",
git_url: "git@github.com:RocketChat/Rocket.Chat.ReactNative.FastLane.git"
) # more information: https://codesigning.guide
+ pem()
gym(scheme: "RocketChatRN") # Build your app - more options available
pilot
@@ -66,6 +67,7 @@ platform :ios do
type: "appstore",
git_url: "git@github.com:RocketChat/Rocket.Chat.ReactNative.FastLane.git"
) # more information: https://codesigning.guide
+ pem()
gym(scheme: "RocketChatRN") # Build your app - more options available
# frameit
end
diff --git a/ios/fastlane/README.md b/ios/fastlane/README.md
index ade625c4f..25c79d48b 100644
--- a/ios/fastlane/README.md
+++ b/ios/fastlane/README.md
@@ -12,9 +12,9 @@ xcode-select --install
-Homebrew
- | Installer Script
- | RubyGems
+ | Homebrew |
+Installer Script |
+RubyGems |
macOS |
diff --git a/package-lock.json b/package-lock.json
index 641edeab8..ffe974829 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",
@@ -1877,8 +1853,7 @@
"babel-plugin-transform-remove-console": {
"version": "6.8.5",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.8.5.tgz",
- "integrity": "sha512-uuCKvtweCyIvvC8fi92EcWRtO2Kt5KMNMRK6BhpDXdeb3sxvGM7453RSmgeu4DlKns3OlvY9Ep5Q9m5a7RQAgg==",
- "dev": true
+ "integrity": "sha512-uuCKvtweCyIvvC8fi92EcWRtO2Kt5KMNMRK6BhpDXdeb3sxvGM7453RSmgeu4DlKns3OlvY9Ep5Q9m5a7RQAgg=="
},
"babel-plugin-transform-remove-debugger": {
"version": "6.8.5",
@@ -2178,26 +2153,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 +12002,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 a023642ab..eaf2cf1be 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,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",