Merge branch 'develop' of github.com:RocketChat/Rocket.Chat.ReactNative into typing

This commit is contained in:
Guilherme Gazzo 2017-11-21 15:05:14 -02:00
commit f2076ea6c8
No known key found for this signature in database
GPG Key ID: 1F85C9AD922D0829
17 changed files with 664 additions and 152 deletions

View File

@ -29,7 +29,26 @@ export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD', [
export const ROOMS = createRequestTypes('ROOMS'); export const ROOMS = createRequestTypes('ROOMS');
export const ROOM = createRequestTypes('ROOM', ['ADD_USER_TYPING', 'REMOVE_USER_TYPING', 'USER_TYPING', 'OPEN', 'IM_TYPING']); 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 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', [ export const CREATE_CHANNEL = createRequestTypes('CREATE_CHANNEL', [
...defaultTypes, ...defaultTypes,
'REQUEST_USERS', 'REQUEST_USERS',

View File

@ -19,3 +19,116 @@ export function messagesFailure(err) {
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
};
}

View File

@ -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 ? (
<Card
data={this.props.item.attachments[0]}
/>
) : 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 (
<View style={[styles.message, extraStyle]}>
<Avatar
style={{ marginRight: 10 }}
text={item.avatar ? '' : username}
size={40}
baseUrl={this.props.baseUrl}
avatar={item.avatar}
/>
<View style={[styles.content]}>
<User
onPress={this._onPress}
item={item}
Message_TimeFormat={this.props.Message_TimeFormat}
/>
{this.attachments()}
<Markdown>
{msg}
</Markdown>
</View>
</View>
);
}
}

View File

@ -6,6 +6,7 @@ import ImagePicker from 'react-native-image-picker';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { imTyping } from '../actions/room'; import { imTyping } from '../actions/room';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import { editRequest } from '../actions/messages';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
textBox: { textBox: {
@ -22,7 +23,6 @@ const styles = StyleSheet.create({
textBoxInput: { textBoxInput: {
height: 40, height: 40,
alignSelf: 'stretch', alignSelf: 'stretch',
backgroundColor: '#fff',
flexGrow: 1 flexGrow: 1
}, },
fileButton: { fileButton: {
@ -30,31 +30,50 @@ const styles = StyleSheet.create({
paddingTop: 10, paddingTop: 10,
paddingBottom: 10, paddingBottom: 10,
fontSize: 20 fontSize: 20
},
editing: {
backgroundColor: '#fff5df'
} }
}); });
@connect( @connect(state => ({
null, message: state.messages.message,
dispatch => ({ editing: state.messages.editing
}), dispatch => ({
editRequest: message => dispatch(editRequest(message)),
typing: status => dispatch(imTyping(status)) typing: status => dispatch(imTyping(status))
}) }))
) export default class MessageBox extends React.Component {
export default class MessageBox extends React.PureComponent {
static propTypes = { static propTypes = {
onSubmit: PropTypes.func.isRequired, 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) { submit(message) {
const text = message; const { editing } = this.props;
if (text.trim() === '') { if (message.trim() === '') {
return; 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 = () => { addFile = () => {
@ -86,7 +105,7 @@ export default class MessageBox extends React.PureComponent {
render() { render() {
return ( return (
<View style={styles.textBox}> <View style={[styles.textBox, (this.props.editing ? styles.editing : null)]}>
<SafeAreaView style={styles.safeAreaView}> <SafeAreaView style={styles.safeAreaView}>
<TextInput <TextInput
ref={component => this.component = component} ref={component => this.component = component}

View File

@ -2,6 +2,8 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { View, Text, StyleSheet } from 'react-native'; import { View, Text, StyleSheet } from 'react-native';
import moment from 'moment'; import moment from 'moment';
import Icon from 'react-native-vector-icons/FontAwesome';
import Avatar from '../Avatar';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
username: { username: {
@ -21,6 +23,11 @@ const styles = StyleSheet.create({
fontSize: 10, fontSize: 10,
color: '#888', color: '#888',
paddingLeft: 5 paddingLeft: 5
},
edited: {
marginLeft: 5,
flexDirection: 'row',
alignItems: 'center'
} }
}); });
@ -28,7 +35,26 @@ export default class Message extends React.PureComponent {
static propTypes = { static propTypes = {
item: PropTypes.object.isRequired, item: PropTypes.object.isRequired,
Message_TimeFormat: PropTypes.string.isRequired, Message_TimeFormat: PropTypes.string.isRequired,
onPress: PropTypes.func onPress: PropTypes.func,
baseUrl: PropTypes.string
}
renderEdited(item) {
if (!item.editedBy) {
return null;
}
return (
<View style={styles.edited}>
<Icon name='pencil-square-o' color='#888' size={10} />
<Avatar
style={{ marginLeft: 5 }}
text={item.editedBy.username}
size={20}
baseUrl={this.props.baseUrl}
avatar={item.avatar}
/>
</View>
);
} }
render() { render() {
@ -48,7 +74,9 @@ export default class Message extends React.PureComponent {
<Text onPress={this.props.onPress} style={styles.username}> <Text onPress={this.props.onPress} style={styles.username}>
{username} {username}
</Text> </Text>
{aliasUsername}<Text style={styles.time}>{time}</Text> {aliasUsername}
<Text style={styles.time}>{time}</Text>
{this.renderEdited(item)}
</View> </View>
); );
} }

View File

@ -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 ? (
<Card
data={this.props.item.attachments[0]}
/>
) : 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 <Text style={styles.textInfo}>Message removed</Text>;
}
const msg = emojify(this.props.item.msg, { output: 'unicode' });
return (
<Markdown>
{msg}
</Markdown>
);
}
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 (
<TouchableOpacity
onLongPress={() => this.showActions()}
disabled={this.isDeleted()}
style={isEditing ? styles.editing : null}
>
<View style={[styles.message, extraStyle]}>
<Avatar
style={{ marginRight: 10 }}
text={item.avatar ? '' : username}
size={40}
baseUrl={this.props.baseUrl}
avatar={item.avatar}
/>
<View style={[styles.content]}>
<User
onPress={this._onPress}
item={item}
Message_TimeFormat={this.props.Message_TimeFormat}
baseUrl={this.props.baseUrl}
/>
{this.attachments()}
{this.renderMessageContent(item)}
</View>
<ActionSheet
ref={o => this.ActionSheet = o}
title={title}
options={options}
cancelButtonIndex={CANCEL_INDEX}
destructiveButtonIndex={DESTRUCTIVE_INDEX}
onPress={this.handleActionPress}
/>
</View>
</TouchableOpacity>
);
}
}

View File

@ -86,6 +86,14 @@ const attachment = {
} }
}; };
const messagesEditedBySchema = {
name: 'messagesEditedBy',
properties: {
_id: { type: 'string', optional: true },
username: { type: 'string', optional: true }
}
};
const messagesSchema = { const messagesSchema = {
name: 'messages', name: 'messages',
primaryKey: '_id', primaryKey: '_id',
@ -104,14 +112,26 @@ const messagesSchema = {
avatar: { type: 'string', optional: true }, avatar: { type: 'string', optional: true },
attachments: { type: 'list', objectType: 'attachment' }, attachments: { type: 'list', objectType: 'attachment' },
_updatedAt: { type: 'date', optional: true }, _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(); // Realm.clearTestState();
// AsyncStorage.clear(); // AsyncStorage.clear();
const realm = new Realm({ const realm = new Realm({
schema: [settingsSchema, serversSchema, subscriptionSchema, messagesSchema, usersSchema, roomsSchema, attachment] schema: [
settingsSchema,
serversSchema,
subscriptionSchema,
messagesSchema,
usersSchema,
roomsSchema,
attachment,
messagesEditedBySchema
]
}); });
export default realm; export default realm;

View File

@ -70,6 +70,7 @@ const RocketChat = {
message.temp = false; message.temp = false;
message._server = server; message._server = server;
message.attachments = message.attachments || []; message.attachments = message.attachments || [];
message.starred = !!message.starred;
realm.create('messages', message, true); realm.create('messages', message, true);
}); });
} }
@ -230,7 +231,7 @@ const RocketChat = {
const data = { const data = {
id: `RocketChatRN${ id }`, id: `RocketChatRN${ id }`,
token: { [key]: token }, token: { [key]: token },
appName: 'main', appName: 'chat.rocket.reactnative', // TODO: try to get from config file
userId: id, userId: id,
metadata: {} metadata: {}
}; };
@ -257,6 +258,7 @@ const RocketChat = {
message._server = { id: reduxStore.getState().server.server }; message._server = { id: reduxStore.getState().server.server };
message.attachments = message.attachments || []; message.attachments = message.attachments || [];
// write('messages', message); // write('messages', message);
message.starred = !!message.starred;
realm.create('messages', message, true); realm.create('messages', message, true);
}); });
}); });
@ -433,6 +435,38 @@ const RocketChat = {
}); });
}, },
_filterSettings: settings => settings.filter(setting => settingsType[setting.type] && setting.value), _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) { subscribe(...args) {
return Meteor.subscribe(...args); return Meteor.subscribe(...args);
}, },

View File

@ -2,7 +2,10 @@ import * as types from '../actions/actionsTypes';
const initialState = { const initialState = {
isFetching: false, isFetching: false,
failure: false failure: false,
message: {},
editing: false,
permalink: ''
}; };
export default function messages(state = initialState, action) { export default function messages(state = initialState, action) {
@ -24,6 +27,34 @@ export default function messages(state = initialState, action) {
failure: true, failure: true,
errorMessage: action.err 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: // case types.LOGOUT:
// return initialState; // return initialState;
default: default:

View File

@ -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 { 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'; 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 get = function* get({ rid }) {
const auth = yield select(state => state.login.isAuthenticated); const auth = yield select(state => state.login.isAuthenticated);
if (!auth) { if (!auth) {
@ -16,7 +35,58 @@ const get = function* get({ rid }) {
yield put(messagesFailure(err.status)); 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;

View File

@ -9,7 +9,7 @@ import * as actions from '../actions';
import { openRoom } from '../actions/room'; import { openRoom } from '../actions/room';
import realm from '../lib/realm'; import realm from '../lib/realm';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import Message from '../containers/Message'; import Message from '../containers/message';
import MessageBox from '../containers/MessageBox'; import MessageBox from '../containers/MessageBox';
import KeyboardView from '../presentation/KeyboardView'; import KeyboardView from '../presentation/KeyboardView';

View File

@ -403,6 +403,7 @@
5A0EEFAF8AB14F5B9E796CDD /* libRNVectorIcons.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNVectorIcons.a; sourceTree = "<group>"; }; 5A0EEFAF8AB14F5B9E796CDD /* libRNVectorIcons.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNVectorIcons.a; sourceTree = "<group>"; };
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 = "<group>"; }; 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 = "<group>"; };
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = "<group>"; }; 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = "<group>"; };
60B2A6A31FC4588700BD58E5 /* RocketChatRN.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = RocketChatRN.entitlements; path = RocketChatRN/RocketChatRN.entitlements; sourceTree = "<group>"; };
6533FB90166345D29F1B91C0 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFetchBlob.a; sourceTree = "<group>"; }; 6533FB90166345D29F1B91C0 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFetchBlob.a; sourceTree = "<group>"; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
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 = "<group>"; }; 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 = "<group>"; };
@ -564,6 +565,7 @@
13B07FAE1A68108700A75B9A /* RocketChatRN */ = { 13B07FAE1A68108700A75B9A /* RocketChatRN */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
60B2A6A31FC4588700BD58E5 /* RocketChatRN.entitlements */,
008F07F21AC5B25A0029DE68 /* main.jsbundle */, 008F07F21AC5B25A0029DE68 /* main.jsbundle */,
13B07FAF1A68108700A75B9A /* AppDelegate.h */, 13B07FAF1A68108700A75B9A /* AppDelegate.h */,
13B07FB01A68108700A75B9A /* AppDelegate.m */, 13B07FB01A68108700A75B9A /* AppDelegate.m */,
@ -863,6 +865,11 @@
13B07F861A680F5B00A75B9A = { 13B07F861A680F5B00A75B9A = {
DevelopmentTeam = S6UPZG7ZR3; DevelopmentTeam = S6UPZG7ZR3;
ProvisioningStyle = Manual; ProvisioningStyle = Manual;
SystemCapabilities = {
com.apple.Push = {
enabled = 1;
};
};
}; };
2D02E47A1E0B4A5D006451C7 = { 2D02E47A1E0B4A5D006451C7 = {
CreatedOnToolsVersion = 8.2.1; CreatedOnToolsVersion = 8.2.1;
@ -1483,6 +1490,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = RocketChatRN/RocketChatRN.entitlements;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 100; CURRENT_PROJECT_VERSION = 100;
@ -1519,6 +1527,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = RocketChatRN/RocketChatRN.entitlements;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 100; CURRENT_PROJECT_VERSION = 100;

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>

View File

@ -39,6 +39,7 @@ platform :ios do
type: "appstore", type: "appstore",
git_url: "git@github.com:RocketChat/Rocket.Chat.ReactNative.FastLane.git" git_url: "git@github.com:RocketChat/Rocket.Chat.ReactNative.FastLane.git"
) # more information: https://codesigning.guide ) # more information: https://codesigning.guide
pem()
gym(scheme: "RocketChatRN") # Build your app - more options available gym(scheme: "RocketChatRN") # Build your app - more options available
pilot pilot
@ -66,6 +67,7 @@ platform :ios do
type: "appstore", type: "appstore",
git_url: "git@github.com:RocketChat/Rocket.Chat.ReactNative.FastLane.git" git_url: "git@github.com:RocketChat/Rocket.Chat.ReactNative.FastLane.git"
) # more information: https://codesigning.guide ) # more information: https://codesigning.guide
pem()
gym(scheme: "RocketChatRN") # Build your app - more options available gym(scheme: "RocketChatRN") # Build your app - more options available
# frameit # frameit
end end

View File

@ -12,9 +12,9 @@ xcode-select --install
<table width="100%" > <table width="100%" >
<tr> <tr>
<th width="33%"><a href="http://brew.sh">Homebrew</a></td> <th width="33%"><a href="http://brew.sh">Homebrew</a></th>
<th width="33%">Installer Script</td> <th width="33%">Installer Script</th>
<th width="33%">RubyGems</td> <th width="33%">RubyGems</th>
</tr> </tr>
<tr> <tr>
<td width="33%" align="center">macOS</td> <td width="33%" align="center">macOS</td>

52
package-lock.json generated
View File

@ -1058,12 +1058,6 @@
"integrity": "sha1-NOmzALFHnd2Y7HfqC76TQt/jloQ=", "integrity": "sha1-NOmzALFHnd2Y7HfqC76TQt/jloQ=",
"dev": true "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": { "babel-helper-is-void-0": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/babel-helper-is-void-0/-/babel-helper-is-void-0-0.2.0.tgz", "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-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": { "babel-plugin-transform-react-jsx": {
"version": "6.24.1", "version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz", "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-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": { "babel-plugin-transform-react-remove-prop-types": {
"version": "0.4.10", "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", "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": { "babel-plugin-transform-remove-console": {
"version": "6.8.5", "version": "6.8.5",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.8.5.tgz", "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.8.5.tgz",
"integrity": "sha512-uuCKvtweCyIvvC8fi92EcWRtO2Kt5KMNMRK6BhpDXdeb3sxvGM7453RSmgeu4DlKns3OlvY9Ep5Q9m5a7RQAgg==", "integrity": "sha512-uuCKvtweCyIvvC8fi92EcWRtO2Kt5KMNMRK6BhpDXdeb3sxvGM7453RSmgeu4DlKns3OlvY9Ep5Q9m5a7RQAgg=="
"dev": true
}, },
"babel-plugin-transform-remove-debugger": { "babel-plugin-transform-remove-debugger": {
"version": "6.8.5", "version": "6.8.5",
@ -2178,26 +2153,6 @@
"react-transform-hmr": "1.0.4" "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": { "babel-preset-stage-0": {
"version": "6.24.1", "version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-preset-stage-0/-/babel-preset-stage-0-6.24.1.tgz", "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" "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": { "react-native-animatable": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.2.4.tgz", "resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.2.4.tgz",

View File

@ -23,6 +23,7 @@
"react-emojione": "^5.0.0", "react-emojione": "^5.0.0",
"react-native": "0.50.3", "react-native": "0.50.3",
"react-native-action-button": "^2.8.1", "react-native-action-button": "^2.8.1",
"react-native-actionsheet": "^2.3.0",
"react-native-animatable": "^1.2.4", "react-native-animatable": "^1.2.4",
"react-native-card-view": "0.0.3", "react-native-card-view": "0.0.3",
"react-native-easy-markdown": "git+https://github.com/lappalj4/react-native-easy-markdown.git", "react-native-easy-markdown": "git+https://github.com/lappalj4/react-native-easy-markdown.git",