diff --git a/.babelrc b/.babelrc
index d0cf03df..34739bdc 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,4 +1,9 @@
{
"presets": ["react-native"],
- "plugins": ["transform-decorators-legacy"]
+ "plugins": ["transform-decorators-legacy"],
+ "env": {
+ "production": {
+ "plugins": ["transform-remove-console"]
+ }
+ }
}
diff --git a/app/lib/realm.js b/app/lib/realm.js
index 72481b5e..ee44086d 100644
--- a/app/lib/realm.js
+++ b/app/lib/realm.js
@@ -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 }
}
};
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index 35a83216..da645a95 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -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 });
},
diff --git a/app/presentation/RoomItem.js b/app/presentation/RoomItem.js
index 6f143076..3d97e491 100644
--- a/app/presentation/RoomItem.js
+++ b/app/presentation/RoomItem.js
@@ -87,13 +87,12 @@ export default class RoomItem extends React.PureComponent {
return null;
}
- const { color } = avatarInitialsAndColor(name);
-
if (type === 'd') {
return (
);
}
+ const { color } = avatarInitialsAndColor(name);
return (
@@ -120,24 +119,13 @@ export default class RoomItem extends React.PureComponent {
render() {
const { unread, name, _updatedAt } = this.props;
- if (_updatedAt) {
- return (
-
- {this.icon}
-
- { name }
- { moment(_updatedAt).format(this.props.dateFormat) }
-
- {this.renderNumber(unread)}
-
- );
- }
return (
{this.icon}
{ name }
+ {_updatedAt ? { moment(_updatedAt).format(this.props.dateFormat) } : null}
{this.renderNumber(unread)}
diff --git a/app/sagas/init.js b/app/sagas/init.js
index 26255156..78938dd8 100644
--- a/app/sagas/init.js
+++ b/app/sagas/init.js
@@ -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) {
diff --git a/app/sagas/login.js b/app/sagas/login.js
index b02ff994..0838aea0 100644
--- a/app/sagas/login.js
+++ b/app/sagas/login.js
@@ -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);
diff --git a/app/views/LoginView.js b/app/views/LoginView.js
index 9af008fb..5928a448 100644
--- a/app/views/LoginView.js
+++ b/app/views/LoginView.js
@@ -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 {
{this.props.login.failure && {this.props.login.error.reason}}
+
diff --git a/app/views/RoomView.js b/app/views/RoomView.js
index 1c68d2ab..41b02dcf 100644
--- a/app/views/RoomView.js
+++ b/app/views/RoomView.js
@@ -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 Loading more messages...;
+ return Loading more messages...;
}
if (this.state.end) {
- return Start of conversation;
+ return Start of conversation;
}
};
render() {
+ const { height } = Dimensions.get('window');
return (
{this.renderBanner()}
@@ -202,7 +200,7 @@ export default class RoomView extends React.Component {