Merge pull request #86 from RocketChat/improves

improvements
This commit is contained in:
Diego Mello 2017-11-21 13:40:48 -02:00 committed by GitHub
commit 5ad676f7dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 126 additions and 175 deletions

View File

@ -1,4 +1,9 @@
{
"presets": ["react-native"],
"plugins": ["transform-decorators-legacy"]
"plugins": ["transform-decorators-legacy"],
"env": {
"production": {
"plugins": ["transform-remove-console"]
}
}
}

View File

@ -18,7 +18,9 @@ const settingsSchema = {
_server: 'servers',
valueAsString: { type: 'string', optional: true },
valueAsBoolean: { type: 'bool', optional: true },
valueAsNumber: { type: 'int', optional: true }
valueAsNumber: { type: 'int', optional: true },
_updatedAt: { type: 'date', optional: true }
}
};

View File

@ -26,9 +26,7 @@ const RocketChat = {
TOKEN_KEY,
createChannel({ name, users, type }) {
return new Promise((resolve, reject) => {
Meteor.call(type ? 'createChannel' : 'createPrivateGroup', name, users, type, (err, res) => (err ? reject(err) : resolve(res)));
});
return call(type ? 'createChannel' : 'createPrivateGroup', name, users, type);
},
async getUserToken() {
@ -52,61 +50,63 @@ const RocketChat = {
const url = `${ _url }/websocket`;
Meteor.connect(url, { autoConnect: true, autoReconnect: true });
Meteor.ddp.on('disconnected', () => {
reduxStore.dispatch(disconnect());
});
Meteor.ddp.on('connected', () => {
reduxStore.dispatch(connectSuccess());
resolve();
});
Meteor.ddp.on('connected', () => {
Meteor.call('public-settings/get', (err, data) => {
if (err) {
console.error(err);
}
const settings = {};
realm.write(() => {
data.forEach((item) => {
const setting = {
_id: item._id
};
setting._server = { id: reduxStore.getState().server.server };
if (settingsType[item.type]) {
setting[settingsType[item.type]] = item.value;
realm.create('settings', setting, true);
}
settings[item._id] = item.value;
});
});
reduxStore.dispatch(actions.setAllSettings(settings));
});
Meteor.ddp.on('connected', async() => {
Meteor.ddp.on('changed', (ddbMessage) => {
const server = { id: reduxStore.getState().server.server };
if (ddbMessage.collection === 'stream-room-messages') {
realm.write(() => {
const message = ddbMessage.fields.args[0];
message.temp = false;
message._server = { id: reduxStore.getState().server.server };
message._server = server;
message.attachments = message.attachments || [];
message.starred = !!message.starred;
realm.create('messages', message, true);
});
}
if (ddbMessage.collection === 'stream-notify-user') {
realm.write(() => {
const data = ddbMessage.fields.args[1];
data._server = { id: reduxStore.getState().server.server };
realm.create('subscriptions', data, true);
});
const [type, data] = ddbMessage.fields.args;
const [, ev] = ddbMessage.fields.eventName.split('/');
if (/subscriptions/.test(ev)) {
switch (type) {
case 'inserted':
data._server = server;
realm.write(() => {
realm.create('subscriptions', data, true);
});
break;
case 'updated':
delete data._updatedAt;
realm.write(() => {
realm.create('subscriptions', data, true);
});
break;
default:
}
}
if (/rooms/.test(ev) && type === 'updated') {
const sub = realm.objects('subscriptions').filtered('rid == $0', data._id)[0];
realm.write(() => {
sub._updatedAt = data._updatedAt;
});
}
}
});
RocketChat.getSettings();
});
})
.catch(e => console.error(e));
},
login(params, callback) {
return new Promise((resolve, reject) => {
Meteor._startLoggingIn();
@ -114,6 +114,11 @@ const RocketChat = {
Meteor._endLoggingIn();
Meteor._handleLoginCallback(err, result);
if (err) {
if (/user not found/i.test(err.reason)) {
err.error = 1;
err.reason = 'User or Password incorrect';
err.message = 'User or Password incorrect';
}
reject(err);
} else {
resolve(result);
@ -137,36 +142,15 @@ const RocketChat = {
},
register({ credentials }) {
return new Promise((resolve, reject) => {
Meteor.call('registerUser', credentials, (err, userId) => {
if (err) {
reject(err);
}
resolve(userId);
});
});
return call('registerUser', credentials);
},
setUsername({ credentials }) {
return new Promise((resolve, reject) => {
Meteor.call('setUsername', credentials.username, (err, result) => {
if (err) {
reject(err);
}
resolve(result);
});
});
return call('setUsername', credentials.username);
},
forgotPassword(email) {
return new Promise((resolve, reject) => {
Meteor.call('sendForgotPasswordEmail', email, (err, result) => {
if (err) {
reject(err);
}
resolve(result);
});
});
return call('sendForgotPasswordEmail', email);
},
loginWithPassword({ username, password, code }, callback) {
@ -211,26 +195,6 @@ const RocketChat = {
return this.login(params, callback);
},
// loadRooms(cb) {
// console.warn('a');
// Meteor.call('rooms/get', (err, data) => {
// if (err) {
// console.error(err);
// }
// console.warn(`rooms ${ data.length }`);
// if (data.length) {
// realm.write(() => {
// data.forEach((room) => {
// room._server = { id: reduxStore.getState().server.server };
// realm.create('rooms', room, true);
// });
// });
// }
// return cb && cb();
// });
// },
loadSubscriptions(cb) {
Meteor.call('subscriptions/get', (err, data) => {
if (err) {
@ -285,6 +249,7 @@ const RocketChat = {
data.messages.forEach((message) => {
message.temp = false;
message._server = { id: reduxStore.getState().server.server };
message.attachments = message.attachments || [];
// write('messages', message);
message.starred = !!message.starred;
realm.create('messages', message, true);
@ -334,38 +299,17 @@ const RocketChat = {
},
spotlight(search, usernames) {
return new Promise((resolve, reject) => {
Meteor.call('spotlight', search, usernames, (error, result) => {
if (error) {
return reject(error);
}
return resolve(result);
});
});
return call('spotlight', search, usernames);
},
createDirectMessage(username) {
return new Promise((resolve, reject) => {
Meteor.call('createDirectMessage', username, (error, result) => {
if (error) {
return reject(error);
}
return resolve(result);
});
});
return call('createDirectMessage', username);
},
readMessages(rid) {
return call('readMessages', rid);
},
joinRoom(rid) {
return new Promise((resolve, reject) => {
Meteor.call('joinRoom', rid, (error, result) => {
if (error) {
return reject(error);
}
return resolve(result);
});
});
return call('joinRoom', rid);
},
@ -379,26 +323,12 @@ const RocketChat = {
*/
_ufsCreate(fileInfo) {
// return call('ufsCreate', fileInfo);
return new Promise((resolve, reject) => {
Meteor.call('ufsCreate', fileInfo, (error, result) => {
if (error) {
return reject(error);
}
return resolve(result);
});
});
return call('ufsCreate', fileInfo);
},
// ["ZTE8CKHJt7LATv7Me","fileSystem","e8E96b2819"
_ufsComplete(fileId, store, token) {
return new Promise((resolve, reject) => {
Meteor.call('ufsComplete', fileId, store, token, (error, result) => {
if (error) {
return reject(error);
}
return resolve(result);
});
});
return call('ufsComplete', fileId, store, token);
},
/*
@ -412,14 +342,7 @@ const RocketChat = {
}
*/
_sendFileMessage(rid, data, msg = {}) {
return new Promise((resolve, reject) => {
Meteor.call('sendFileMessage', rid, null, data, msg, (error, result) => {
if (error) {
return reject(error);
}
return resolve(result);
});
});
return call('sendFileMessage', rid, null, data, msg);
},
async sendFileMessage(rid, fileInfo, data) {
const placeholder = RocketChat.getMessage(rid, 'Sending an image');
@ -448,23 +371,36 @@ const RocketChat = {
});
}
},
getRooms() {
async getRooms() {
const { server, login } = reduxStore.getState();
return Promise.all([call('subscriptions/get'), call('rooms/get')]).then(([subscriptions, rooms]) => {
const data = subscriptions.map((subscription) => {
subscription._updatedAt = (rooms.find(room => room._id === subscription.rid) || {})._updatedAt;
subscription._server = { id: server.server };
return subscription;
});
let lastMessage = realm
.objects('subscriptions')
.filtered('_server.id = $0', server.server)
.sorted('_updatedAt', true)[0];
lastMessage = lastMessage && new Date(lastMessage._updatedAt);
let [subscriptions, rooms] = await Promise.all([call('subscriptions/get', lastMessage), call('rooms/get', lastMessage)]);
realm.write(() => {
data.forEach((subscription) => {
realm.create('subscriptions', subscription, true);
});
});
Meteor.subscribe('stream-notify-user', `${ login.user.id }/subscriptions-changed`, false);
return data;
if (lastMessage) {
subscriptions = subscriptions.update;
rooms = rooms.update;
}
const data = subscriptions.map((subscription) => {
const room = rooms.find(({ _id }) => _id === subscription.rid);
delete subscription._updatedAt;
if (room) {
subscription._updatedAt = room._updatedAt;
}
subscription._server = { id: server.server };
return subscription;
});
realm.write(() => {
data.forEach(subscription =>
realm.create('subscriptions', subscription, true));
});
Meteor.subscribe('stream-notify-user', `${ login.user.id }/subscriptions-changed`, false);
Meteor.subscribe('stream-notify-user', `${ login.user.id }/rooms-changed`, false);
return data;
},
logout({ server }) {
Meteor.logout();
@ -472,6 +408,27 @@ const RocketChat = {
AsyncStorage.removeItem(TOKEN_KEY);
AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`);
},
async getSettings() {
const temp = realm.objects('settings').sorted('_updatedAt', true)[0];
const result = await (!temp ? call('public-settings/get') : call('public-settings/get', new Date(temp._updatedAt)));
const settings = temp ? result.update : result;
const filteredSettings = RocketChat._prepareSettings(RocketChat._filterSettings(settings));
realm.write(() => {
filteredSettings.forEach(setting => realm.create('settings', setting, true));
});
reduxStore.dispatch(actions.setAllSettings(RocketChat.parseSettings(filteredSettings)));
},
parseSettings: settings => settings.reduce((ret, item) => {
ret[item._id] = item[settingsType[item.type]] || item.valueAsString || item.value;
return ret;
}, {}),
_prepareSettings(settings) {
return settings.map((setting) => {
setting[settingsType[setting.type]] = setting.value;
return setting;
});
},
_filterSettings: settings => settings.filter(setting => settingsType[setting.type] && setting.value),
deleteMessage(message) {
return call('deleteMessage', { _id: message._id });
},

View File

@ -87,13 +87,12 @@ export default class RoomItem extends React.PureComponent {
return null;
}
const { color } = avatarInitialsAndColor(name);
if (type === 'd') {
return (
<Avatar text={name} baseUrl={baseUrl} size={40} />
);
}
const { color } = avatarInitialsAndColor(name);
return (
<View style={[styles.iconContainer, { backgroundColor: color }]}>
@ -120,24 +119,13 @@ export default class RoomItem extends React.PureComponent {
render() {
const { unread, name, _updatedAt } = this.props;
if (_updatedAt) {
return (
<TouchableOpacity onPress={this.props.onPress} style={styles.container}>
{this.icon}
<View style={styles.roomNameView}>
<Text style={styles.roomName} ellipsizeMode='tail' numberOfLines={1}>{ name }</Text>
<Text style={styles.update} ellipsizeMode='tail' numberOfLines={1}>{ moment(_updatedAt).format(this.props.dateFormat) }</Text>
</View>
{this.renderNumber(unread)}
</TouchableOpacity>
);
}
return (
<TouchableOpacity onPress={this.props.onPress} style={styles.container}>
{this.icon}
<View style={styles.roomNameView}>
<Text style={styles.roomName} ellipsizeMode='tail' numberOfLines={1}>{ name }</Text>
{_updatedAt ? <Text style={styles.update} ellipsizeMode='tail' numberOfLines={1}>{ moment(_updatedAt).format(this.props.dateFormat) }</Text> : null}
</View>
{this.renderNumber(unread)}
</TouchableOpacity>

View File

@ -4,6 +4,8 @@ import * as actions from '../actions';
import { setServer } from '../actions/server';
import { restoreToken } from '../actions/login';
import { APP } from '../actions/actionsTypes';
import realm from '../lib/realm';
import RocketChat from '../lib/rocketchat';
const restore = function* restore() {
try {
@ -16,6 +18,8 @@ const restore = function* restore() {
const currentServer = yield call([AsyncStorage, 'getItem'], 'currentServer');
if (currentServer) {
yield put(setServer(currentServer));
const tmp = realm.objects('settings');
yield put(actions.setAllSettings(RocketChat.parseSettings(tmp.slice(0, tmp.length))));
}
yield put(actions.appReady({}));
} catch (e) {

View File

@ -1,5 +1,5 @@
import { AsyncStorage } from 'react-native';
import { take, put, call, takeEvery, takeLatest, select, all } from 'redux-saga/effects';
import { take, put, call, takeLatest, select, all } from 'redux-saga/effects';
import * as types from '../actions/actionsTypes';
import {
loginRequest,
@ -9,7 +9,6 @@ import {
loginSuccess,
loginFailure,
setToken,
logout,
registerSuccess,
setUsernameRequest,
setUsernameSuccess,
@ -83,11 +82,7 @@ const handleLoginRequest = function* handleLoginRequest({ credentials }) {
yield put(loginSuccess(user));
} catch (err) {
if (err.error === 403) {
yield put(logout());
} else {
yield put(loginFailure(err));
}
yield put(loginFailure(err));
}
};
@ -147,7 +142,7 @@ const handleForgotPasswordRequest = function* handleForgotPasswordRequest({ emai
};
const root = function* root() {
yield takeEvery(types.SERVER.CHANGED, handleLoginWhenServerChanges);
yield takeLatest(types.SERVER.CHANGED, handleLoginWhenServerChanges);
yield takeLatest(types.LOGIN.REQUEST, handleLoginRequest);
yield takeLatest(types.LOGIN.SUCCESS, saveToken);
yield takeLatest(types.LOGIN.SUBMIT, handleLoginSubmit);

View File

@ -1,6 +1,6 @@
import React from 'react';
// import Spinner from 'react-native-loading-spinner-overlay';
import Spinner from 'react-native-loading-spinner-overlay';
import PropTypes from 'prop-types';
import { Keyboard, Text, TextInput, View, TouchableOpacity, SafeAreaView } from 'react-native';
@ -143,6 +143,7 @@ class LoginView extends React.Component {
</TouchableOpacity>
{this.props.login.failure && <Text style={styles.error}>{this.props.login.error.reason}</Text>}
</View>
<Spinner visible={this.props.login.isFetching} textContent='Loading...' textStyle={{ color: '#FFF' }} />
</SafeAreaView>
</View>
</KeyboardView>

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Text, View, StyleSheet, Button, SafeAreaView } from 'react-native';
import { Text, View, StyleSheet, Button, SafeAreaView, Dimensions } from 'react-native';
import { ListView } from 'realm/react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
@ -38,7 +38,7 @@ const styles = StyleSheet.create({
textAlign: 'center',
color: '#a00'
},
header: {
loadingMore: {
transform: [{ scaleY: -1 }],
textAlign: 'center',
padding: 5,
@ -86,7 +86,7 @@ export default class RoomView extends React.Component {
.sorted('ts', true);
this.state = {
slow: false,
dataSource: [],
dataSource: ds.cloneWithRows([]),
loaded: true,
joined: typeof props.rid === 'undefined'
};
@ -102,10 +102,9 @@ export default class RoomView extends React.Component {
this.timer = setTimeout(() => this.setState({ slow: true }), 5000);
this.props.getMessages(this.rid);
this.data.addListener(this.updateState);
this.state.dataSource = ds.cloneWithRows(this.data);
}
componentDidMount() {
this.updateState();
}
componentDidUpdate() {
return !this.props.loading && clearTimeout(this.timer);
@ -124,14 +123,12 @@ export default class RoomView extends React.Component {
this.state.end !== true
) {
this.setState({
// ...this.state,
loadingMore: true
});
const lastRowData = this.data[rowCount - 1];
RocketChat.loadMessagesForRoom(this.rid, lastRowData.ts, ({ end }) => {
this.setState({
// ...this.state,
loadingMore: false,
end
});
@ -186,15 +183,16 @@ export default class RoomView extends React.Component {
renderHeader = () => {
if (this.state.loadingMore) {
return <Text style={styles.header}>Loading more messages...</Text>;
return <Text style={styles.loadingMore}>Loading more messages...</Text>;
}
if (this.state.end) {
return <Text style={styles.header}>Start of conversation</Text>;
return <Text style={styles.loadingMore}>Start of conversation</Text>;
}
};
render() {
const { height } = Dimensions.get('window');
return (
<KeyboardView contentContainerStyle={styles.container} keyboardVerticalOffset={64}>
{this.renderBanner()}
@ -202,7 +200,7 @@ export default class RoomView extends React.Component {
<ListView
enableEmptySections
style={styles.list}
onEndReachedThreshold={10}
onEndReachedThreshold={height / 2}
renderFooter={this.renderHeader}
onEndReached={this.onEndReached}
dataSource={this.state.dataSource}

View File

@ -231,7 +231,7 @@ export default class RoomsListView extends React.Component {
return;
}
navigateToRoom({ sid: id });
navigateToRoom({ sid: id, ...item });
clearSearch();
}

View File

@ -15,6 +15,7 @@
},
"dependencies": {
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-plugin-transform-remove-console": "^6.8.5",
"babel-polyfill": "^6.26.0",
"moment": "^2.19.2",
"prop-types": "^15.6.0",