[FIX] Handle deleted messages (#466)

* [FIX] Handle deleted messages

* Fix rest error

* Fix some connection issues
This commit is contained in:
Diego Mello 2018-09-28 15:57:29 -03:00 committed by GitHub
parent d6c6ac4ae8
commit d5a4ead888
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 93 additions and 60 deletions

View File

@ -59,7 +59,7 @@ jobs:
command: |
brew update
brew tap wix/brew
brew install applesimutils
brew install wix/brew/applesimutils
- run:
name: Install NPM modules

View File

@ -185,11 +185,14 @@ export default class Socket extends EventEmitter {
if (ignore) {
return;
}
const cancel = this.ddp.once('disconnected', reject);
const cancel = this.once('disconnected', () => {
this.removeListener('disconnected', cancel);
reject();
});
this.ddp.once(id, (data) => {
// console.log(data);
this.lastping = new Date();
this.ddp.removeListener('disconnected', cancel);
this.removeListener('disconnected', cancel);
return (data.error ? reject(data.error) : resolve({ id, ...data }));
});
});
@ -273,7 +276,7 @@ export default class Socket extends EventEmitter {
if (this._timer || this.forceDisconnect) {
return;
}
this._close();
// this._close();
this._logged = false;
this._timer = setTimeout(async() => {

View File

@ -1,6 +1,7 @@
import { post } from './helpers/rest';
import database from '../realm';
import log from '../../utils/log';
import store from '../createStore';
// TODO: api fix
const ddpTypes = {
@ -12,7 +13,8 @@ const restTypes = {
async function canOpenRoomREST({ type, rid }) {
try {
const { token, id } = this.ddp._login;
const { user } = store.getState().login;
const { token, id } = user;
const server = this.ddp.url.replace(/^ws/, 'http');
await post({ token, id, server }, `${ restTypes[type] }.open`, { roomId: rid });
return true;
@ -50,7 +52,7 @@ export default async function canOpenRoom({ rid, path }) {
try {
// eslint-disable-next-line
const data = await (this.ddp && this.ddp.status && false ? canOpenRoomDDP.call(this, { rid, type, name }) : canOpenRoomREST.call(this, { type, rid }));
const data = await (this.ddp && this.ddp.status ? canOpenRoomDDP.call(this, { rid, type, name }) : canOpenRoomREST.call(this, { type, rid }));
return data;
} catch (e) {
log('canOpenRoom', e);

View File

@ -15,6 +15,11 @@ const getLastMessage = () => {
export default async function() {
try {
if (!this.ddp) {
// TODO: should implement loop or get from rest?
return;
}
const lastMessage = getLastMessage();
let emojis = await this.ddp.call('listEmojiCustom');
emojis = emojis.filter(emoji => !lastMessage || emoji._updatedAt > lastMessage);

View File

@ -11,6 +11,11 @@ const getLastUpdate = () => {
export default async function() {
try {
if (!this.ddp) {
// TODO: should implement loop or get from rest?
return;
}
const lastUpdate = getLastUpdate();
const result = await (!lastUpdate ? this.ddp.call('permissions/get') : this.ddp.call('permissions/get', new Date(lastUpdate)));
const permissions = (result.update || result).filter(permission => defaultPermissions.includes(permission._id));

View File

@ -5,6 +5,7 @@ import { get } from './helpers/rest';
import mergeSubscriptionsRooms, { merge } from './helpers/mergeSubscriptionsRooms';
import database from '../realm';
import log from '../../utils/log';
import store from '../createStore';
const lastMessage = () => {
const message = database
@ -14,9 +15,9 @@ const lastMessage = () => {
};
const getRoomRest = async function() {
const { ddp } = this;
const updatedSince = lastMessage();
const { token, id } = ddp._login;
const { user } = store.getState().login;
const { token, id } = user;
const server = this.ddp.url.replace(/^ws/, 'http');
const [subscriptions, rooms] = await Promise.all([get({ token, id, server }, 'subscriptions.get', { updatedSince }), get({ token, id, server }, 'rooms.get', { updatedSince })]);
return mergeSubscriptionsRooms(subscriptions, rooms);
@ -39,7 +40,7 @@ export default async function() {
return new Promise(async(resolve, reject) => {
try {
// eslint-disable-next-line
const { subscriptions, rooms } = await (this.ddp.status && false ? getRoomDpp.apply(this) : getRoomRest.apply(this));
const { subscriptions, rooms } = await (this.ddp && this.ddp.status ? getRoomDpp.apply(this) : getRoomRest.apply(this));
const data = rooms.map(room => ({ room, sub: database.objects('subscriptions').filtered('rid == $0', room._id) }));

View File

@ -20,6 +20,11 @@ function updateServer(param) {
export default async function() {
try {
if (!this.ddp) {
// TODO: should implement loop or get from rest?
return;
}
const lastUpdate = getLastUpdate();
const fetchNewSettings = lastUpdate < settingsUpdatedAt;
const result = await ((!lastUpdate || fetchNewSettings) ? this.ddp.call('public-settings/get') : this.ddp.call('public-settings/get', new Date(lastUpdate)));

View File

@ -2,7 +2,7 @@ import toQuery from './toQuery';
const handleSuccess = (msg) => {
if (msg.success !== undefined && !msg.success) {
if ((msg.success !== undefined && !msg.success) || (msg.status && msg.status === 'error')) {
return Promise.reject(msg);
}
return msg;

View File

@ -4,6 +4,7 @@ import { get } from './helpers/rest';
import buildMessage from './helpers/buildMessage';
import database from '../realm';
import log from '../../utils/log';
import store from '../createStore';
// TODO: api fix
const types = {
@ -11,7 +12,8 @@ const types = {
};
async function loadMessagesForRoomRest({ rid: roomId, latest, t }) {
const { token, id } = this.ddp._login;
const { user } = store.getState().login;
const { token, id } = user;
const server = this.ddp.url.replace(/^ws/, 'http');
const data = await get({ token, id, server }, `${ types[t] }.history`, { roomId, latest });
if (!data || data.status === 'error') {
@ -36,11 +38,9 @@ async function loadMessagesForRoomDDP(...args) {
export default async function loadMessagesForRoom(...args) {
const { database: db } = database;
return new Promise(async(resolve, reject) => {
try {
// eslint-disable-next-line
const data = (await (this.ddp.status ? loadMessagesForRoomDDP.call(this, ...args) : loadMessagesForRoomRest.call(this, ...args))).map(buildMessage);
const data = (await (this.ddp && this.ddp.status ? loadMessagesForRoomDDP.call(this, ...args) : loadMessagesForRoomRest.call(this, ...args))).map(buildMessage);
if (data && data.length) {
InteractionManager.runAfterInteractions(() => {

View File

@ -4,57 +4,54 @@ import { get } from './helpers/rest';
import buildMessage from './helpers/buildMessage';
import database from '../realm';
import log from '../../utils/log';
import store from '../createStore';
async function loadMissedMessagesRest({ rid: roomId, lastOpen: lastUpdate }) {
const { token, id } = this.ddp._login;
const { user } = store.getState().login;
const { token, id } = user;
const server = this.ddp.url.replace(/^ws/, 'http');
const { result } = await get({ token, id, server }, 'chat.syncMessages', { roomId, lastUpdate });
// TODO: api fix
if (!result) {
return [];
}
return result.updated || result.messages;
return result;
}
async function loadMissedMessagesDDP(...args) {
const [{ rid, lastOpen: lastUpdate }] = args;
try {
const data = await this.ddp.call('messages/get', rid, { lastUpdate: new Date(lastUpdate) });
return data.updated || data.messages;
const result = await this.ddp.call('messages/get', rid, { lastUpdate: new Date(lastUpdate) });
return result;
} catch (e) {
return loadMissedMessagesRest.call(this, ...args);
}
// }
// if (cb) {
// cb({ end: data && data.messages.length < 20 });
// }
// return data.message;
// }, (err) => {
// if (err) {
// if (cb) {
// cb({ end: true });
// }
// return Promise.reject(err);
// }
// });
}
export default async function(...args) {
export default async function loadMissedMessages(...args) {
const { database: db } = database;
return new Promise(async(resolve, reject) => {
try {
// eslint-disable-next-line
const data = (await (this.ddp.status ? loadMissedMessagesDDP.call(this, ...args) : loadMissedMessagesRest.call(this, ...args)));
const data = (await (this.ddp && this.ddp.status ? loadMissedMessagesDDP.call(this, ...args) : loadMissedMessagesRest.call(this, ...args)));
if (data) {
data.forEach(buildMessage);
return InteractionManager.runAfterInteractions(() => {
db.write(() => data.forEach(message => db.create('messages', message, true)));
resolve(data);
if (data.updated && data.updated.length) {
const { updated } = data;
updated.forEach(buildMessage);
InteractionManager.runAfterInteractions(() => {
db.write(() => updated.forEach(message => db.create('messages', message, true)));
resolve(updated);
});
}
if (data.deleted && data.deleted.length) {
const { deleted } = data;
InteractionManager.runAfterInteractions(() => {
db.write(() => {
deleted.forEach((m) => {
const message = database.objects('messages').filtered('_id = $0', m._id);
database.delete(message);
});
});
});
}
}
resolve([]);
} catch (e) {
log('loadMissedMessages', e);

View File

@ -1,9 +1,11 @@
import { post } from './helpers/rest';
import database from '../realm';
import log from '../../utils/log';
import store from '../createStore';
const readMessagesREST = function readMessagesREST(rid) {
const { token, id } = this.ddp._login;
const { user } = store.getState().login;
const { token, id } = user;
const server = this.ddp.url.replace(/^ws/, 'http');
return post({ token, id, server }, 'subscriptions.read', { rid });
};
@ -20,7 +22,7 @@ export default async function readMessages(rid) {
const { database: db } = database;
try {
// eslint-disable-next-line
const data = await (this.ddp.status && false ? readMessagesDDP.call(this, rid) : readMessagesREST.call(this, rid));
const data = await (this.ddp && this.ddp.status ? readMessagesDDP.call(this, rid) : readMessagesREST.call(this, rid));
const [subscription] = db.objects('subscriptions').filtered('rid = $0', rid);
db.write(() => {
subscription.open = true;

View File

@ -46,7 +46,7 @@ function sendMessageByDDP(message) {
export async function _sendMessageCall(message) {
try {
// eslint-disable-next-line
const data = await (this.ddp.status && false ? sendMessageByDDP.call(this, message) : sendMessageByRest.call(this, message));
const data = await (this.ddp && this.ddp.status ? sendMessageByDDP.call(this, message) : sendMessageByRest.call(this, message));
return data;
} catch (e) {
database.write(() => {

View File

@ -7,7 +7,8 @@ import log from '../../../utils/log';
const subscribe = (ddp, rid) => Promise.all([
ddp.subscribe('stream-room-messages', rid, false),
ddp.subscribe('stream-notify-room', `${ rid }/typing`, false)
ddp.subscribe('stream-notify-room', `${ rid }/typing`, false),
ddp.subscribe('stream-notify-room', `${ rid }/deleteMessage`, false)
]);
const unsubscribe = subscriptions => subscriptions.forEach(sub => sub.unsubscribe().catch((e) => {
log('unsubscribeRoom', e);

View File

@ -171,8 +171,6 @@ const RocketChat = {
this.ddp.on('background', () => this.getRooms().catch(e => log('background getRooms', e)));
this.ddp.on('disconnected', () => console.log('disconnected'));
this.ddp.on('logged', protectedFunction((user) => {
this.loginSuccess(user);
this.getRooms().catch(e => log('logged getRooms', e));
@ -184,7 +182,7 @@ const RocketChat = {
this.ddp.on('disconnected', protectedFunction(() => {
reduxStore.dispatch(disconnect());
console.warn(this.ddp);
console.log('disconnected', this.ddp);
}));
this.ddp.on('stream-room-messages', (ddpMessage) => {
@ -195,10 +193,17 @@ const RocketChat = {
this.ddp.on('stream-notify-room', protectedFunction((ddpMessage) => {
const [_rid, ev] = ddpMessage.fields.eventName.split('/');
if (ev !== 'typing') {
return;
if (ev === 'typing') {
reduxStore.dispatch(someoneTyping({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] }));
} else if (ev === 'deleteMessage') {
database.write(() => {
if (ddpMessage && ddpMessage.fields && ddpMessage.fields.args.length > 0) {
const { _id } = ddpMessage.fields.args[0];
const message = database.objects('messages').filtered('_id = $0', _id);
database.delete(message);
}
});
}
return reduxStore.dispatch(someoneTyping({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] }));
}));
this.ddp.on('rocketchat_starred_message', protectedFunction((ddpMessage) => {

View File

@ -21,14 +21,13 @@ const restore = function* restore() {
const currentServer = yield call([AsyncStorage, 'getItem'], 'currentServer');
if (currentServer) {
yield put(selectServerRequest(currentServer));
const user = yield call([AsyncStorage, 'getItem'], `${ RocketChat.TOKEN_KEY }-${ currentServer }`);
if (user) {
const userParsed = JSON.parse(user);
if (userParsed.language) {
I18n.locale = userParsed.language;
}
yield put(selectServerRequest(currentServer));
yield put(setUser(userParsed));
}
}

View File

@ -46,8 +46,7 @@ export default StyleSheet.create({
flexDirection: 'column'
},
loading: {
flex: 1,
marginVertical: 15
flex: 1
},
imageBackground: {
width: '100%',

View File

@ -3,7 +3,8 @@ import {
View, Text, Animated, Easing, TouchableWithoutFeedback, TouchableOpacity, FlatList, Image, AsyncStorage
} from 'react-native';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { connect, Provider } from 'react-redux';
import { Navigation } from 'react-native-navigation';
import { toggleServerDropdown as toggleServerDropdownAction } from '../../actions/rooms';
import { selectServerRequest as selectServerRequestAction } from '../../actions/server';
@ -13,10 +14,13 @@ import database from '../../lib/realm';
import Touch from '../../utils/touch';
import RocketChat from '../../lib/rocketchat';
import I18n from '../../i18n';
import store from '../../lib/createStore';
const ROW_HEIGHT = 68;
const ANIMATION_DURATION = 200;
let NewServerView = null;
@connect(state => ({
closeServerDropdown: state.rooms.closeServerDropdown,
server: state.server.server
@ -111,6 +115,10 @@ export default class ServerDropdown extends Component {
const token = await AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ server }`);
if (!token) {
appStart();
if (NewServerView == null) {
NewServerView = require('../NewServerView').default;
Navigation.registerComponent('NewServerView', () => NewServerView, store, Provider);
}
setTimeout(() => {
navigator.push({
screen: 'NewServerView',

View File

@ -14,6 +14,7 @@ async function navigateToRoomActions(type) {
} else {
room = `private${ data.random }`;
}
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await element(by.id('rooms-list-view-search')).replaceText(room);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000);
await element(by.id(`rooms-list-view-item-${ room }`)).tap();

View File

@ -14,7 +14,7 @@ async function navigateToRoomInfo(type) {
}
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
await element(by.id('rooms-list-view-search')).replaceText(room);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeVisible().withTimeout(60000);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000);
await element(by.id(`rooms-list-view-item-${ room }`)).tap();
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(2000);
await element(by.id('room-view-header-actions')).tap();