From 5a3ba40a9f78a12d3d7c441aed81bbf3016000c7 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Mon, 15 Oct 2018 17:22:42 -0300 Subject: [PATCH] Use Rocket.Chat JS SDK (#481) * JS SDK * API working * Multiple servers --- .eslintrc.js | 3 +- app/i18n/locales/pt-BR.js | 2 +- app/lib/ddp.js | 338 ------------------- app/lib/methods/canOpenRoom.js | 14 +- app/lib/methods/getCustomEmojis.js | 11 +- app/lib/methods/getPermissions.js | 20 +- app/lib/methods/getRooms.js | 20 +- app/lib/methods/getSettings.js | 19 +- app/lib/methods/helpers/normalizeMessage.js | 11 +- app/lib/methods/helpers/protectedFunction.js | 4 +- app/lib/methods/helpers/rest.js | 40 --- app/lib/methods/helpers/toQuery.js | 3 - app/lib/methods/loadMessagesForRoom.js | 22 +- app/lib/methods/loadMissedMessages.js | 18 +- app/lib/methods/readMessages.js | 21 +- app/lib/methods/sendFileMessage.js | 7 +- app/lib/methods/sendMessage.js | 11 +- app/lib/methods/subscriptions/room.js | 42 +-- app/lib/methods/subscriptions/rooms.js | 21 +- app/lib/realm.js | 3 +- app/lib/rocketchat.js | 200 ++++++----- app/sagas/connect.js | 2 +- app/sagas/init.js | 4 + app/sagas/roomFiles.js | 4 +- app/views/RoomInfoEditView/index.js | 2 +- app/views/RoomsListView/ServerDropdown.js | 14 +- package-lock.json | 90 ++++- package.json | 1 + 28 files changed, 335 insertions(+), 612 deletions(-) delete mode 100644 app/lib/ddp.js delete mode 100644 app/lib/methods/helpers/rest.js delete mode 100644 app/lib/methods/helpers/toQuery.js diff --git a/.eslintrc.js b/.eslintrc.js index ae902b8c6..bec3d1784 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -128,7 +128,8 @@ module.exports = { "consistent-return": 0, "global-require": "off", "react-native/no-unused-styles": 2, - "react/jsx-one-expression-per-line": 0 + "react/jsx-one-expression-per-line": 0, + "func-names": 0 }, "globals": { "__DEV__": true diff --git a/app/i18n/locales/pt-BR.js b/app/i18n/locales/pt-BR.js index 5929f227b..41088e5f6 100644 --- a/app/i18n/locales/pt-BR.js +++ b/app/i18n/locales/pt-BR.js @@ -97,7 +97,7 @@ export default { Allow_Reactions: 'Permitir reagir', Alphabetical: 'Alfabético', and_more: 'e mais', - and: 'a', + and: 'e', announcement: 'anúncio', Announcement: 'Anúncio', ARCHIVE: 'ARQUIVAR', diff --git a/app/lib/ddp.js b/app/lib/ddp.js deleted file mode 100644 index 28c460ee3..000000000 --- a/app/lib/ddp.js +++ /dev/null @@ -1,338 +0,0 @@ -import EJSON from 'ejson'; -import { AppState } from 'react-native'; - -import debounce from '../utils/debounce'; -import log from '../utils/log'; -// import { AppState, NativeModules } from 'react-native'; -// const { WebSocketModule, BlobManager } = NativeModules; - -// class WS extends WebSocket { -// _close(code?: number, reason?: string): void { -// if (Platform.OS === 'android') { -// WebSocketModule.close(code, reason, this._socketId); -// } else { -// WebSocketModule.close(this._socketId); -// } -// -// if (BlobManager.isAvailable && this._binaryType === 'blob') { -// BlobManager.removeWebSocketHandler(this._socketId); -// } -// } -// } - -class EventEmitter { - constructor() { - this.events = {}; - } - - on(event, listener) { - if (typeof this.events[event] !== 'object') { - this.events[event] = []; - } - this.events[event].push(listener); - return listener; - } - - removeListener(event, listener) { - if (typeof this.events[event] === 'object') { - const idx = this.events[event].indexOf(listener); - if (idx > -1) { - this.events[event].splice(idx, 1); - } - if (this.events[event].length === 0) { - delete this.events[event]; - } - } - } - - emit(event, ...args) { - if (typeof this.events[event] === 'object') { - this.events[event].forEach((listener) => { - try { - listener.apply(this, args); - } catch (e) { - log('EventEmitter.emit', e); - } - }); - } - } - - once(event, listener) { - return this.on(event, function g(...args) { - this.removeListener(event, g); - listener.apply(this, args); - }); - } -} - - -export default class Socket extends EventEmitter { - constructor(url, login) { - super(); - this.state = 'active'; - this.lastping = new Date(); - this._login = login; - this.url = url;// .replace(/^http/, 'ws'); - this.id = 0; - this.subscriptions = {}; - this.ddp = new EventEmitter(); - this._logged = false; - this.forceDisconnect = false; - this.connected = false; - const waitTimeout = () => setTimeout(() => { - // this.connection.ping(); - this.send({ msg: 'ping' }).catch(e => log('ping', e)); - this.timeout = setTimeout(() => this.reconnect(), 1000); - }, 40000); - const handlePing = () => { - this.lastping = new Date(); - this.send({ msg: 'pong' }, true).catch(e => log('pong', e)); - if (this.timeout) { - clearTimeout(this.timeout); - } - this.timeout = waitTimeout(); - }; - const handlePong = () => { - this.lastping = new Date(); - if (this.timeout) { - clearTimeout(this.timeout); - } - this.timeout = waitTimeout(); - }; - - - AppState.addEventListener('change', async(nextAppState) => { - if (this.state && this.state.match(/inactive/) && nextAppState === 'active') { - try { - await this.send({ msg: 'ping' }, true); - // this.connection.ping(); - } catch (e) { - this.reconnect(); - } - } - if (this.state && this.state.match(/background/) && nextAppState === 'active') { - this.emit('background'); - } - this.state = nextAppState; - }); - - this.on('pong', handlePong); - this.on('ping', handlePing); - - this.on('result', data => this.ddp.emit(data.id, { id: data.id, result: data.result, error: data.error })); - this.on('ready', data => this.ddp.emit(data.subs[0], data)); - // this.on('error', () => this.reconnect()); - this.on('disconnected', debounce(() => this.reconnect(), 300)); - - this.on('logged', () => { - this._logged = true; - Object.keys(this.subscriptions || {}).forEach((key) => { - const { name, params } = this.subscriptions[key]; - this.subscriptions[key].unsubscribe().catch(e => log('this.on(logged) unsub', e)); - this.subscribe(name, ...params).catch(e => log('this.on(logged) sub', e)); - }); - }); - this.on('open', async() => { - this._logged = false; - this.send({ msg: 'connect', version: '1', support: ['1', 'pre2', 'pre1'] }).catch(e => log('this.on(open)', e)); - }); - - this._connect().catch(e => log('ddp.constructor._connect', e)); - } - - check() { - if (!this.lastping) { - return false; - } - if ((Math.abs(this.lastping.getTime() - new Date().getTime()) / 1000) > 50) { - return false; - } - return true; - } - - async login(params) { - try { - this.emit('login', params); - const result = await this.call('login', params); - // this._login = { resume: result.token, ...result }; - this._login = { resume: result.token, ...result, ...params }; - this._logged = true; - // this.emit('logged', result); - this.emit('logged', this._login); - return result; - } catch (err) { - const error = { ...err }; - if (/user not found/i.test(error.reason)) { - error.error = 1; - error.reason = 'User or Password incorrect'; - error.message = 'User or Password incorrect'; - } - this.emit('loginError', error); - return Promise.reject(error); - } - } - - async send(obj, ignore) { - console.log('send', obj); - return new Promise((resolve, reject) => { - if (!this.connected) { - return reject(); - } - this.id += 1; - const id = obj.id || `ddp-react-native-${ this.id }`; - // console.log('send', { ...obj, id }); - this.connection.send(EJSON.stringify({ ...obj, id })); - if (ignore) { - return; - } - const cancel = this.once('disconnected', () => { - this.removeListener('disconnected', cancel); - reject(); - }); - this.ddp.once(id, (data) => { - // console.log(data); - this.lastping = new Date(); - this.removeListener('disconnected', cancel); - return (data.error ? reject(data.error) : resolve({ id, ...data })); - }); - }); - } - - get status() { - return this.connection && this.connection.readyState === 1 && this.check() && !!this._logged; - } - - _close() { - try { - // this.connection && this.connection.readyState > 1 && this.connection.close && this.connection.close(300, 'disconnect'); - if (this.connection && this.connection.close) { - this.connection.close(); - delete this.connection; - } - } catch (e) { - // console.log(e); - } - } - - _connect() { - return new Promise((resolve) => { - this.lastping = new Date(); - this._close(); - clearInterval(this.reconnect_timeout); - this.reconnect_timeout = setInterval(() => { - if (!this.connection || this.connection.readyState > 1 || !this.check()) { - this.reconnect(); - } - }, 5000); - this.connection = new WebSocket(`${ this.url }/websocket`, null); - - this.connection.onopen = async() => { - this.connected = true; - this.forceDisconnect = false; - this.emit('open'); - resolve(); - this.ddp.emit('open'); - console.log(`Connected to: ${ this.url }`); - if (this._login) { - return this.login(this._login).catch(e => console.warn(e)); - } - }; - this.connection.onclose = debounce((e) => { - this.emit('disconnected', e); - this.connected = false; - }, 300); - this.connection.onmessage = (e) => { - try { - // console.log('received', e.data, e.target.readyState); - const data = EJSON.parse(e.data); - this.emit(data.msg, data); - return data.collection && this.emit(data.collection, data); - } catch (err) { - log('EJSON parse', err); - } - }; - }); - } - - logout() { - this._login = null; - return this.call('logout') - .catch(e => log('logout', e)) - .finally(() => this.subscriptions = {}); - } - - disconnect() { - this._logged = false; - this._login = null; - this.subscriptions = {}; - this.forceDisconnect = true; - this._close(); - if (this.timeout) { - clearTimeout(this.timeout); - } - } - - async reconnect() { - if (this._timer || this.forceDisconnect) { - return; - } - // this._close(); - this._logged = false; - - this._timer = setTimeout(async() => { - delete this._timer; - try { - await this._connect(); - } catch (e) { - log('ddp.reconnect._connect', e); - } - }, 1000); - } - - call(method, ...params) { - return this.send({ - msg: 'method', method, params - }).then(data => data.result || data.subs).catch((err) => { - log('DDP call Error', err); - if (err && /you've been logged out by the server/i.test(err.reason)) { - return this.emit('forbidden'); - } - return Promise.reject(err); - }); - } - - unsubscribe(id) { - if (!this.subscriptions[id]) { - return Promise.reject(id); - } - delete this.subscriptions[id]; - return this.send({ - msg: 'unsub', - id - }).then(data => data.result || data.subs).catch((err) => { - log('DDP unsubscribe Error', err); - return Promise.reject(err); - }); - } - - subscribe(name, ...params) { - console.log(name, params); - return this.send({ - msg: 'sub', name, params - }).then(({ id }) => { - const args = { - id, - name, - params, - unsubscribe: () => this.unsubscribe(id) - }; - - this.subscriptions[id] = args; - // console.log(args); - return args; - }).catch((err) => { - log('DDP subscribe Error', err); - return Promise.reject(err); - }); - } -} diff --git a/app/lib/methods/canOpenRoom.js b/app/lib/methods/canOpenRoom.js index 17736665c..f576b2e15 100644 --- a/app/lib/methods/canOpenRoom.js +++ b/app/lib/methods/canOpenRoom.js @@ -1,7 +1,7 @@ -import { post } from './helpers/rest'; +import * as SDK from '@rocket.chat/sdk'; + import database from '../realm'; import log from '../../utils/log'; -import store from '../createStore'; // TODO: api fix const ddpTypes = { @@ -13,10 +13,7 @@ const restTypes = { async function canOpenRoomREST({ type, rid }) { try { - 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 }); + await SDK.api.post(`${ restTypes[type] }.open`, { roomId: rid }); return true; } catch (error) { // TODO: workround for 'already open for the sender' error @@ -30,7 +27,7 @@ async function canOpenRoomREST({ type, rid }) { async function canOpenRoomDDP(...args) { try { const [{ type, name }] = args; - await this.ddp.call('getRoomByTypeAndName', ddpTypes[type], name); + await SDK.driver.asyncCall('getRoomByTypeAndName', ddpTypes[type], name); return true; } catch (error) { if (error.isClientSafe) { @@ -51,8 +48,7 @@ export default async function canOpenRoom({ rid, path }) { const [type, name] = path.split('/'); try { - // eslint-disable-next-line - const data = await (this.ddp && this.ddp.status ? canOpenRoomDDP.call(this, { rid, type, name }) : canOpenRoomREST.call(this, { type, rid })); + const data = await (SDK.driver.ddp ? canOpenRoomDDP.call(this, { rid, type, name }) : canOpenRoomREST.call(this, { type, rid })); return data; } catch (e) { log('canOpenRoom', e); diff --git a/app/lib/methods/getCustomEmojis.js b/app/lib/methods/getCustomEmojis.js index bea2fbde9..cded086d4 100644 --- a/app/lib/methods/getCustomEmojis.js +++ b/app/lib/methods/getCustomEmojis.js @@ -1,7 +1,7 @@ import { InteractionManager } from 'react-native'; +import * as SDK from '@rocket.chat/sdk'; import reduxStore from '../createStore'; -// import { get } from './helpers/rest'; import database from '../realm'; import * as actions from '../../actions'; @@ -12,16 +12,11 @@ const getLastMessage = () => { return setting && setting._updatedAt; }; - +// TODO: fix api (get emojis by date/version....) 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'); + let emojis = await SDK.driver.asyncCall('listEmojiCustom'); emojis = emojis.filter(emoji => !lastMessage || emoji._updatedAt > lastMessage); emojis = this._prepareEmojis(emojis); InteractionManager.runAfterInteractions(() => database.write(() => { diff --git a/app/lib/methods/getPermissions.js b/app/lib/methods/getPermissions.js index 7a6758c16..e0398c484 100644 --- a/app/lib/methods/getPermissions.js +++ b/app/lib/methods/getPermissions.js @@ -1,4 +1,5 @@ import { InteractionManager } from 'react-native'; +import * as SDK from '@rocket.chat/sdk'; import database from '../realm'; import log from '../../utils/log'; @@ -11,13 +12,10 @@ 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 result = await (!lastUpdate + ? SDK.driver.asyncCall('permissions/get') + : SDK.driver.asyncCall('permissions/get', new Date(lastUpdate))); const permissions = (result.update || result).filter(permission => defaultPermissions.includes(permission._id)); permissions .map((permission) => { @@ -27,9 +25,13 @@ export default async function() { }); InteractionManager.runAfterInteractions( - () => database.write( - () => permissions.forEach(permission => database.create('permissions', permission, true)) - ) + () => database.write(() => permissions.forEach((permission) => { + try { + database.create('permissions', permission, true); + } catch (e) { + log('getPermissions create', e); + } + })) ); } catch (e) { log('getPermissions', e); diff --git a/app/lib/methods/getRooms.js b/app/lib/methods/getRooms.js index dc1c0ef25..ffc090509 100644 --- a/app/lib/methods/getRooms.js +++ b/app/lib/methods/getRooms.js @@ -1,33 +1,30 @@ import { InteractionManager } from 'react-native'; +import * as SDK from '@rocket.chat/sdk'; -// import { showToast } from '../../utils/info'; -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 .objects('subscriptions') .sorted('roomUpdatedAt', true)[0]; - return message && new Date(message.roomUpdatedAt); + return message && new Date(message.roomUpdatedAt).toISOString(); }; const getRoomRest = async function() { const updatedSince = lastMessage(); - 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 })]); + const [subscriptions, rooms] = await (updatedSince + ? Promise.all([SDK.api.get('subscriptions.get', { updatedSince }), SDK.api.get('rooms.get', { updatedSince })]) + : Promise.all([SDK.api.get('subscriptions.get'), SDK.api.get('rooms.get')]) + ); return mergeSubscriptionsRooms(subscriptions, rooms); }; const getRoomDpp = async function() { try { - const { ddp } = this; const updatedSince = lastMessage(); - const [subscriptions, rooms] = await Promise.all([ddp.call('subscriptions/get', updatedSince), ddp.call('rooms/get', updatedSince)]); + const [subscriptions, rooms] = await Promise.all([SDK.driver.asyncCall('subscriptions/get', updatedSince), SDK.driver.asyncCall('rooms/get', updatedSince)]); return mergeSubscriptionsRooms(subscriptions, rooms); } catch (e) { return getRoomRest.apply(this); @@ -39,8 +36,7 @@ export default async function() { return new Promise(async(resolve, reject) => { try { - // eslint-disable-next-line - const { subscriptions, rooms } = await (this.ddp && this.ddp.status ? getRoomDpp.apply(this) : getRoomRest.apply(this)); + const { subscriptions, rooms } = await (SDK.driver.ddp ? getRoomDpp.apply(this) : getRoomRest.apply(this)); const data = rooms.map(room => ({ room, sub: database.objects('subscriptions').filtered('rid == $0', room._id) })); diff --git a/app/lib/methods/getSettings.js b/app/lib/methods/getSettings.js index 13d7d7f16..ace906851 100644 --- a/app/lib/methods/getSettings.js +++ b/app/lib/methods/getSettings.js @@ -1,7 +1,7 @@ import { InteractionManager } from 'react-native'; +import * as SDK from '@rocket.chat/sdk'; import reduxStore from '../createStore'; -// import { get } from './helpers/rest'; import database from '../realm'; import * as actions from '../../actions'; import log from '../../utils/log'; @@ -14,20 +14,22 @@ const getLastUpdate = () => { function updateServer(param) { database.databases.serversDB.write(() => { - database.databases.serversDB.create('servers', { id: this.ddp.url, ...param }, true); + database.databases.serversDB.create('servers', { id: reduxStore.getState().server.server, ...param }, true); }); } export default async function() { try { - if (!this.ddp) { - // TODO: should implement loop or get from rest? - return; - } + // if (!SDK.driver.dd) { + // // 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))); + const result = await ((!lastUpdate || fetchNewSettings) + ? SDK.driver.asyncCall('public-settings/get') + : SDK.driver.asyncCall('public-settings/get', new Date(lastUpdate))); const data = result.update || result || []; const filteredSettings = this._prepareSettings(this._filterSettings(data)); @@ -47,7 +49,8 @@ export default async function() { const iconSetting = data.find(item => item._id === 'Assets_favicon_512'); if (iconSetting) { - const iconURL = `${ this.ddp.url }/${ iconSetting.value.url || iconSetting.value.defaultUrl }`; + const baseUrl = reduxStore.getState().server.server; + const iconURL = `${ baseUrl }/${ iconSetting.value.url || iconSetting.value.defaultUrl }`; updateServer.call(this, { iconURL }); } } catch (e) { diff --git a/app/lib/methods/helpers/normalizeMessage.js b/app/lib/methods/helpers/normalizeMessage.js index 390a8e5a9..7b77c9ad8 100644 --- a/app/lib/methods/helpers/normalizeMessage.js +++ b/app/lib/methods/helpers/normalizeMessage.js @@ -22,10 +22,13 @@ export default (msg) => { msg = normalizeAttachments(msg); msg.reactions = msg.reactions || []; // TODO: api problems - if (Array.isArray(msg.reactions)) { - msg.reactions = msg.reactions.map((value, key) => ({ emoji: key, usernames: value.usernames.map(username => ({ value: username })) })); - } else { - msg.reactions = Object.keys(msg.reactions).map(key => ({ emoji: key, usernames: msg.reactions[key].usernames.map(username => ({ value: username })) })); + // if (Array.isArray(msg.reactions)) { + // msg.reactions = msg.reactions.map((value, key) => ({ emoji: key, usernames: value.usernames.map(username => ({ value: username })) })); + // } else { + // msg.reactions = Object.keys(msg.reactions).map(key => ({ emoji: key, usernames: msg.reactions[key].usernames.map(username => ({ value: username })) })); + // } + if (!Array.isArray(msg.reactions)) { + msg.reactions = Object.keys(msg.reactions).map(key => ({ _id: `${ msg._id }${ key }`, emoji: key, usernames: msg.reactions[key].usernames.map(username => ({ value: username })) })); } msg.urls = msg.urls ? parseUrls(msg.urls) : []; msg._updatedAt = new Date(); diff --git a/app/lib/methods/helpers/protectedFunction.js b/app/lib/methods/helpers/protectedFunction.js index f6808531d..a5454cbe1 100644 --- a/app/lib/methods/helpers/protectedFunction.js +++ b/app/lib/methods/helpers/protectedFunction.js @@ -1,8 +1,8 @@ import { Answers } from 'react-native-fabric'; -export default fn => (params) => { +export default fn => (...params) => { try { - fn(params); + fn(...params); } catch (e) { let error = e; if (typeof error !== 'object') { diff --git a/app/lib/methods/helpers/rest.js b/app/lib/methods/helpers/rest.js deleted file mode 100644 index d155ef3b4..000000000 --- a/app/lib/methods/helpers/rest.js +++ /dev/null @@ -1,40 +0,0 @@ -import toQuery from './toQuery'; - - -const handleSuccess = (msg) => { - if ((msg.success !== undefined && !msg.success) || (msg.status && msg.status === 'error')) { - return Promise.reject(msg); - } - return msg; -}; - -export const get = function({ - token, id, server -}, method, params = {}) { - return fetch(`${ server }/api/v1/${ method }/?${ toQuery(params) }`, { - method: 'get', - headers: { - // 'Accept-Encoding': 'gzip', - 'Content-Type': 'application/json', - 'X-Auth-Token': token, - 'X-User-Id': id - } - }).then(response => response.json()).then(handleSuccess); -}; - - -export const post = function({ - token, id, server -}, method, params = {}) { - return fetch(`${ server }/api/v1/${ method }`, { - method: 'post', - body: JSON.stringify(params), - headers: { - // 'Accept-Encoding': 'gzip', - 'Content-Type': 'application/json', - Accept: 'application/json', - 'X-Auth-Token': token, - 'X-User-Id': id - } - }).then(response => response.json()).then(handleSuccess); -}; diff --git a/app/lib/methods/helpers/toQuery.js b/app/lib/methods/helpers/toQuery.js deleted file mode 100644 index 4fb9b7776..000000000 --- a/app/lib/methods/helpers/toQuery.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function(obj) { - return Object.keys(obj).filter(p => obj[p] !== undefined && obj[p] !== null).map(p => `${ encodeURIComponent(p) }=${ encodeURIComponent(obj[p]) }`).join('&'); -} diff --git a/app/lib/methods/loadMessagesForRoom.js b/app/lib/methods/loadMessagesForRoom.js index b1241425c..48f816162 100644 --- a/app/lib/methods/loadMessagesForRoom.js +++ b/app/lib/methods/loadMessagesForRoom.js @@ -1,10 +1,9 @@ import { InteractionManager } from 'react-native'; +import * as SDK from '@rocket.chat/sdk'; -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 = { @@ -12,10 +11,10 @@ const types = { }; async function loadMessagesForRoomRest({ rid: roomId, latest, t }) { - 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 (latest) { + latest = new Date(latest).toISOString(); + } + const data = await SDK.api.get(`${ types[t] }.history`, { roomId, latest }); if (!data || data.status === 'error') { return []; } @@ -25,13 +24,12 @@ async function loadMessagesForRoomRest({ rid: roomId, latest, t }) { async function loadMessagesForRoomDDP(...args) { const [{ rid: roomId, latest }] = args; try { - const data = await this.ddp.call('loadHistory', roomId, latest, 50); + const data = await SDK.driver.asyncCall('loadHistory', roomId, latest, 50); if (!data || !data.messages.length) { return []; } return data.messages; } catch (e) { - console.warn('loadMessagesForRoomDDP', e); return loadMessagesForRoomRest.call(this, ...args); } } @@ -40,11 +38,15 @@ export default async function loadMessagesForRoom(...args) { const { database: db } = database; return new Promise(async(resolve, reject) => { try { - const data = (await (this.ddp && this.ddp.status ? loadMessagesForRoomDDP.call(this, ...args) : loadMessagesForRoomRest.call(this, ...args))).map(buildMessage); + const data = (await (SDK.driver.ddp + ? loadMessagesForRoomDDP.call(this, ...args) + : loadMessagesForRoomRest.call(this, ...args))).map(buildMessage); if (data && data.length) { InteractionManager.runAfterInteractions(() => { - db.write(() => data.forEach(message => db.create('messages', message, true))); + db.write(() => data.forEach((message) => { + db.create('messages', message, true); + })); return resolve(data); }); } else { diff --git a/app/lib/methods/loadMissedMessages.js b/app/lib/methods/loadMissedMessages.js index 92c7b72ee..c12ab34ef 100644 --- a/app/lib/methods/loadMissedMessages.js +++ b/app/lib/methods/loadMissedMessages.js @@ -1,16 +1,16 @@ import { InteractionManager } from 'react-native'; +import * as SDK from '@rocket.chat/sdk'; -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 { 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 }); +async function loadMissedMessagesRest({ rid: roomId, lastOpen }) { + let lastUpdate; + if (lastOpen) { + lastUpdate = new Date(lastOpen).toISOString(); + } + const { result } = await SDK.api.get('chat.syncMessages', { roomId, lastUpdate }); return result; } @@ -18,7 +18,7 @@ async function loadMissedMessagesDDP(...args) { const [{ rid, lastOpen: lastUpdate }] = args; try { - const result = await this.ddp.call('messages/get', rid, { lastUpdate: new Date(lastUpdate) }); + const result = await SDK.driver.asyncCall('messages/get', rid, { lastUpdate: new Date(lastUpdate) }); return result; } catch (e) { return loadMissedMessagesRest.call(this, ...args); @@ -29,7 +29,7 @@ export default async function loadMissedMessages(...args) { const { database: db } = database; return new Promise(async(resolve, reject) => { try { - const data = (await (this.ddp && this.ddp.status ? loadMissedMessagesDDP.call(this, ...args) : loadMissedMessagesRest.call(this, ...args))); + const data = (await (SDK.driver.ddp ? loadMissedMessagesDDP.call(this, ...args) : loadMissedMessagesRest.call(this, ...args))); if (data) { if (data.updated && data.updated.length) { diff --git a/app/lib/methods/readMessages.js b/app/lib/methods/readMessages.js index 89ca9c691..4cce4ea7f 100644 --- a/app/lib/methods/readMessages.js +++ b/app/lib/methods/readMessages.js @@ -1,28 +1,25 @@ -import { post } from './helpers/rest'; +import * as SDK from '@rocket.chat/sdk'; + import database from '../realm'; import log from '../../utils/log'; -import store from '../createStore'; -const readMessagesREST = function readMessagesREST(rid) { - 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 }); +const readMessagesREST = function readMessagesREST(rid) { + return SDK.api.post('subscriptions.read', { rid }); }; const readMessagesDDP = function readMessagesDDP(rid) { try { - return this.ddp.call('readMessages', rid); + return SDK.driver.asyncCall('readMessages', rid); } catch (e) { return readMessagesREST.call(this, rid); } }; export default async function readMessages(rid) { + const ls = new Date(); const { database: db } = database; try { - // eslint-disable-next-line - const data = await (this.ddp && this.ddp.status ? readMessagesDDP.call(this, rid) : readMessagesREST.call(this, rid)); + const data = await (SDK.driver.ddp ? readMessagesDDP.call(this, rid) : readMessagesREST.call(this, rid)); const [subscription] = db.objects('subscriptions').filtered('rid = $0', rid); db.write(() => { subscription.open = true; @@ -30,8 +27,8 @@ export default async function readMessages(rid) { subscription.unread = 0; subscription.userMentions = 0; subscription.groupMentions = 0; - subscription.ls = new Date(); - subscription.lastOpen = new Date(); + subscription.ls = ls; + subscription.lastOpen = ls; }); return data; } catch (e) { diff --git a/app/lib/methods/sendFileMessage.js b/app/lib/methods/sendFileMessage.js index 4e813eaf7..57102eeb3 100644 --- a/app/lib/methods/sendFileMessage.js +++ b/app/lib/methods/sendFileMessage.js @@ -1,4 +1,5 @@ import RNFetchBlob from 'rn-fetch-blob'; +import * as SDK from '@rocket.chat/sdk'; import reduxStore from '../createStore'; import database from '../realm'; @@ -6,15 +7,15 @@ import database from '../realm'; const promises = {}; function _ufsCreate(fileInfo) { - return this.ddp.call('ufsCreate', fileInfo); + return SDK.driver.asyncCall('ufsCreate', fileInfo); } function _ufsComplete(fileId, store, token) { - return this.ddp.call('ufsComplete', fileId, store, token); + return SDK.driver.asyncCall('ufsComplete', fileId, store, token); } function _sendFileMessage(rid, data, msg = {}) { - return this.ddp.call('sendFileMessage', rid, null, data, msg); + return SDK.driver.asyncCall('sendFileMessage', rid, null, data, msg); } export function isUploadActive(path) { diff --git a/app/lib/methods/sendMessage.js b/app/lib/methods/sendMessage.js index e5dc0829d..aced6738d 100644 --- a/app/lib/methods/sendMessage.js +++ b/app/lib/methods/sendMessage.js @@ -1,8 +1,8 @@ import Random from 'react-native-meteor/lib/Random'; +import * as SDK from '@rocket.chat/sdk'; import messagesStatus from '../../constants/messagesStatus'; import buildMessage from './helpers/buildMessage'; -import { post } from './helpers/rest'; import database from '../realm'; import reduxStore from '../createStore'; import log from '../../utils/log'; @@ -32,21 +32,18 @@ export const getMessage = (rid, msg = {}) => { }; function sendMessageByRest(message) { - const { token, id } = this.ddp._login; - const server = this.ddp.url.replace(/^ws/, 'http'); const { _id, rid, msg } = message; - return post({ token, id, server }, 'chat.sendMessage', { message: { _id, rid, msg } }); + return SDK.api.post('chat.sendMessage', { message: { _id, rid, msg } }); } function sendMessageByDDP(message) { const { _id, rid, msg } = message; - return this.ddp.call('sendMessage', { _id, rid, msg }); + return SDK.driver.asyncCall('sendMessage', { _id, rid, msg }); } export async function _sendMessageCall(message) { try { - // eslint-disable-next-line - const data = await (this.ddp && this.ddp.status ? sendMessageByDDP.call(this, message) : sendMessageByRest.call(this, message)); + const data = await (SDK.driver.ddp ? sendMessageByDDP.call(this, message) : sendMessageByRest.call(this, message)); return data; } catch (e) { database.write(() => { diff --git a/app/lib/methods/subscriptions/room.js b/app/lib/methods/subscriptions/room.js index c9d2107b6..4331bcdaa 100644 --- a/app/lib/methods/subscriptions/room.js +++ b/app/lib/methods/subscriptions/room.js @@ -1,14 +1,11 @@ -// import database from '../../realm'; -// import reduxStore from '../../createStore'; -// import normalizeMessage from '../helpers/normalizeMessage'; -// import _buildMessage from '../helpers/buildMessage'; -// import protectedFunction from '../helpers/protectedFunction'; +import * as SDK from '@rocket.chat/sdk'; + 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 }/deleteMessage`, false) +const subscribe = rid => Promise.all([ + SDK.driver.subscribe('stream-room-messages', rid, false), + SDK.driver.subscribe('stream-notify-room', `${ rid }/typing`, false), + SDK.driver.subscribe('stream-notify-room', `${ rid }/deleteMessage`, false) ]); const unsubscribe = subscriptions => subscriptions.forEach(sub => sub.unsubscribe().catch((e) => { log('unsubscribeRoom', e); @@ -16,23 +13,13 @@ const unsubscribe = subscriptions => subscriptions.forEach(sub => sub.unsubscrib let timer = null; let promises; -let logged; -let disconnected; -const stop = (ddp) => { +const stop = () => { if (promises) { promises.then(unsubscribe); promises = false; } - if (ddp) { - ddp.removeListener('logged', logged); - ddp.removeListener('disconnected', disconnected); - } - - logged = false; - disconnected = false; - clearTimeout(timer); }; @@ -47,7 +34,7 @@ export default async function subscribeRoom({ rid, t }) { } timer = setTimeout(async() => { try { - await this.loadMissedMessages({ rid, t, lastOpen: timer }); + await this.loadMissedMessages({ rid, t }); timer = false; loop(); } catch (e) { @@ -56,29 +43,28 @@ export default async function subscribeRoom({ rid, t }) { }, 5000); }; - if (!this.ddp || !this.ddp.status) { + if (!SDK.driver.ddp && SDK.driver.userId) { loop(); } else { - logged = this.ddp.on('logged', () => { + SDK.driver.on('logged', () => { clearTimeout(timer); timer = false; - // promises = subscribe(this.ddp, rid); }); - disconnected = this.ddp.on('disconnected', () => { - if (this._login) { + SDK.driver.on('disconnected', () => { + if (SDK.driver.userId) { loop(); } }); try { - promises = subscribe(this.ddp, rid); + promises = subscribe(rid); } catch (e) { log('subscribeRoom', e); } } return { - stop: () => stop(this.ddp) + stop: () => stop() }; } diff --git a/app/lib/methods/subscriptions/rooms.js b/app/lib/methods/subscriptions/rooms.js index 33f3af0f8..752f8deff 100644 --- a/app/lib/methods/subscriptions/rooms.js +++ b/app/lib/methods/subscriptions/rooms.js @@ -1,4 +1,6 @@ import Random from 'react-native-meteor/lib/Random'; +import * as SDK from '@rocket.chat/sdk'; + import database from '../../realm'; import { merge } from '../helpers/mergeSubscriptionsRooms'; import protectedFunction from '../helpers/protectedFunction'; @@ -7,9 +9,9 @@ import log from '../../../utils/log'; export default async function subscribeRooms(id) { const promises = Promise.all([ - this.ddp.subscribe('stream-notify-user', `${ id }/subscriptions-changed`, false), - this.ddp.subscribe('stream-notify-user', `${ id }/rooms-changed`, false), - this.ddp.subscribe('stream-notify-user', `${ id }/message`, false) + SDK.driver.subscribe('stream-notify-user', `${ id }/subscriptions-changed`, false), + SDK.driver.subscribe('stream-notify-user', `${ id }/rooms-changed`, false), + SDK.driver.subscribe('stream-notify-user', `${ id }/message`, false) ]); let timer = null; @@ -28,26 +30,29 @@ export default async function subscribeRooms(id) { }, 5000); }; - if (!this.ddp && this._login) { + if (!SDK.driver.ddp && SDK.driver.userId) { loop(); } else { - this.ddp.on('logged', () => { + SDK.driver.on('logged', () => { clearTimeout(timer); timer = false; }); - this.ddp.on('logout', () => { + SDK.driver.on('logout', () => { clearTimeout(timer); timer = true; }); - this.ddp.on('disconnected', () => { + SDK.driver.on('disconnected', () => { if (this._login) { loop(); } }); - this.ddp.on('stream-notify-user', protectedFunction((ddpMessage) => { + SDK.driver.on('stream-notify-user', protectedFunction((e, ddpMessage) => { + if (ddpMessage.msg === 'added') { + return; + } const [type, data] = ddpMessage.fields.args; const [, ev] = ddpMessage.fields.eventName.split('/'); if (/subscriptions/.test(ev)) { diff --git a/app/lib/realm.js b/app/lib/realm.js index 8d02cce11..3365acce8 100644 --- a/app/lib/realm.js +++ b/app/lib/realm.js @@ -177,8 +177,9 @@ const messagesReactionsUsernamesSchema = { const messagesReactionsSchema = { name: 'messagesReactions', - primaryKey: 'emoji', + primaryKey: '_id', properties: { + _id: 'string', emoji: 'string', usernames: { type: 'list', objectType: 'messagesReactionsUsernames' } } diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index cbd4c6888..aac9da0ff 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -1,7 +1,7 @@ import { AsyncStorage, Platform } from 'react-native'; -import { hashPassword } from 'react-native-meteor/lib/utils'; import foreach from 'lodash/forEach'; import RNFetchBlob from 'rn-fetch-blob'; +import * as SDK from '@rocket.chat/sdk'; import reduxStore from './createStore'; import defaultSettings from '../constants/settings'; @@ -13,7 +13,7 @@ import log from '../utils/log'; import { setUser, setLoginServices, removeLoginServices, loginRequest, loginSuccess, loginFailure, logout } from '../actions/login'; -import { disconnect, connectSuccess, connectFailure } from '../actions/connect'; +import { disconnect, connectSuccess } from '../actions/connect'; import { setActiveUser } from '../actions/activeUsers'; import { starredMessagesReceived, starredMessageUnstarred } from '../actions/starredMessages'; import { pinnedMessagesReceived, pinnedMessageUnpinned } from '../actions/pinnedMessages'; @@ -22,7 +22,6 @@ import { snippetedMessagesReceived } from '../actions/snippetedMessages'; import { roomFilesReceived } from '../actions/roomFiles'; import { someoneTyping, roomMessageReceived } from '../actions/room'; import { setRoles } from '../actions/roles'; -import Ddp from './ddp'; import subscribeRooms from './methods/subscriptions/rooms'; import subscribeRoom from './methods/subscriptions/room'; @@ -47,7 +46,7 @@ import { getDeviceToken } from '../push'; const TOKEN_KEY = 'reactnativemeteor_usertoken'; const SORT_PREFS_KEY = 'RC_SORT_PREFS_KEY'; -const call = (method, ...params) => RocketChat.ddp.call(method, ...params); // eslint-disable-line +const call = (method, ...params) => SDK.driver.asyncCall(method, ...params); const returnAnArray = obj => obj || []; const RocketChat = { @@ -135,63 +134,84 @@ const RocketChat = { // TODO: one api call // call /me only one time if (!user.username) { - const me = await this.me({ token: user.token, userId: user.id }); + const me = await SDK.api.get('me'); user = { ...user, ...me }; } if (user.username) { - const userInfo = await this.userInfo({ token: user.token, userId: user.id }); + const userInfo = await SDK.api.get('users.info', { userId: user.id }); user = { ...user, ...userInfo.user }; } + RocketChat.registerPushToken(user.id); + reduxStore.dispatch(setUser(user)); reduxStore.dispatch(loginSuccess(user)); this.ddp.subscribe('userData'); } catch (e) { - log('rocketchat.loginSuccess', e); + log('SDK.loginSuccess', e); } }, connect(url, login) { - return new Promise((resolve) => { + return new Promise(async() => { if (this.ddp) { - this.ddp.disconnect(); - delete this.ddp; + RocketChat.disconnect(); + this.ddp = null; } - this.ddp = new Ddp(url, login); if (login) { - protectedFunction(() => RocketChat.getRooms()); + SDK.api.setAuth({ authToken: login.token, userId: login.id }); } - this.ddp.on('login', protectedFunction(() => reduxStore.dispatch(loginRequest()))); + SDK.api.setBaseUrl(url); + SDK.driver.connect({ host: url, useSsl: true }, (err, ddp) => { + if (err) { + return console.warn(err); + } + this.ddp = ddp; + if (login) { + SDK.driver.ddp.login({ resume: login.resume }); + } + }); - this.ddp.on('loginError', protectedFunction(err => reduxStore.dispatch(loginFailure(err)))); + SDK.driver.on('connected', () => { + reduxStore.dispatch(connectSuccess()); + SDK.driver.subscribe('meteor.loginServiceConfiguration'); + SDK.driver.subscribe('activeUsers'); + SDK.driver.subscribe('roles'); + RocketChat.getSettings(); + RocketChat.getPermissions(); + RocketChat.getCustomEmoji(); + }); - this.ddp.on('forbidden', protectedFunction(() => reduxStore.dispatch(logout()))); + SDK.driver.on('login', protectedFunction(() => reduxStore.dispatch(loginRequest()))); - this.ddp.on('users', protectedFunction(ddpMessage => RocketChat._setUser(ddpMessage))); + SDK.driver.on('forbidden', protectedFunction(() => reduxStore.dispatch(logout()))); - this.ddp.on('background', () => this.getRooms().catch(e => log('background getRooms', e))); + SDK.driver.on('users', protectedFunction((error, ddpMessage) => RocketChat._setUser(ddpMessage))); - this.ddp.on('logged', protectedFunction((user) => { + // SDK.driver.on('background', () => this.getRooms().catch(e => log('background getRooms', e))); + + SDK.driver.on('logged', protectedFunction((error, user) => { + SDK.api.setAuth({ authToken: user.token, userId: user.id }); + SDK.api.currentLogin = { + userId: user.id, + authToken: user.token + }; this.loginSuccess(user); this.getRooms().catch(e => log('logged getRooms', e)); - })); - this.ddp.once('logged', protectedFunction(({ id }) => { - this.subscribeRooms(id); - // this.ddp.subscribe('stream-notify-logged', 'updateAvatar', false); + this.subscribeRooms(user.id); })); - this.ddp.on('disconnected', protectedFunction(() => { + SDK.driver.on('disconnected', protectedFunction(() => { reduxStore.dispatch(disconnect()); - console.log('disconnected', this.ddp); })); - this.ddp.on('stream-room-messages', (ddpMessage) => { + SDK.driver.on('stream-room-messages', (error, ddpMessage) => { // TODO: debounce const message = _buildMessage(ddpMessage.fields.args[0]); requestAnimationFrame(() => reduxStore.dispatch(roomMessageReceived(message))); }); - this.ddp.on('stream-notify-room', protectedFunction((ddpMessage) => { + SDK.driver.on('stream-notify-room', protectedFunction((error, ddpMessage) => { const [_rid, ev] = ddpMessage.fields.eventName.split('/'); if (ev === 'typing') { reduxStore.dispatch(someoneTyping({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] })); @@ -206,7 +226,7 @@ const RocketChat = { } })); - this.ddp.on('rocketchat_starred_message', protectedFunction((ddpMessage) => { + SDK.driver.on('rocketchat_starred_message', protectedFunction((error, ddpMessage) => { if (ddpMessage.msg === 'added') { this.starredMessages = this.starredMessages || []; @@ -232,7 +252,7 @@ const RocketChat = { } })); - this.ddp.on('rocketchat_pinned_message', protectedFunction((ddpMessage) => { + SDK.driver.on('rocketchat_pinned_message', protectedFunction((error, ddpMessage) => { if (ddpMessage.msg === 'added') { this.pinnedMessages = this.pinnedMessages || []; @@ -258,7 +278,7 @@ const RocketChat = { } })); - this.ddp.on('rocketchat_mentioned_message', protectedFunction((ddpMessage) => { + SDK.driver.on('rocketchat_mentioned_message', protectedFunction((error, ddpMessage) => { if (ddpMessage.msg === 'added') { this.mentionedMessages = this.mentionedMessages || []; @@ -279,7 +299,7 @@ const RocketChat = { } })); - this.ddp.on('rocketchat_snippeted_message', protectedFunction((ddpMessage) => { + SDK.driver.on('rocketchat_snippeted_message', protectedFunction((error, ddpMessage) => { if (ddpMessage.msg === 'added') { this.snippetedMessages = this.snippetedMessages || []; @@ -300,7 +320,7 @@ const RocketChat = { } })); - this.ddp.on('room_files', protectedFunction((ddpMessage) => { + SDK.driver.on('room_files', protectedFunction((error, ddpMessage) => { if (ddpMessage.msg === 'added') { this.roomFiles = this.roomFiles || []; @@ -344,7 +364,7 @@ const RocketChat = { } })); - this.ddp.on('meteor_accounts_loginServiceConfiguration', protectedFunction((ddpMessage) => { + SDK.driver.on('meteor_accounts_loginServiceConfiguration', (error, ddpMessage) => { if (ddpMessage.msg === 'added') { this.loginServices = this.loginServices || {}; if (this.loginServiceTimer) { @@ -364,9 +384,9 @@ const RocketChat = { } this.loginServiceTimer = setTimeout(() => reduxStore.dispatch(removeLoginServices()), 1000); } - })); + }); - this.ddp.on('rocketchat_roles', protectedFunction((ddpMessage) => { + SDK.driver.on('rocketchat_roles', protectedFunction((error, ddpMessage) => { this.roles = this.roles || {}; if (this.roleTimer) { @@ -388,27 +408,25 @@ const RocketChat = { this.roles[ddpMessage.id] = (ddpMessage.fields && ddpMessage.fields.description) || undefined; })); - this.ddp.on('error', (err) => { - log('rocketchat.onerror', err); - reduxStore.dispatch(connectFailure()); - }); + // SDK.driver.on('error', (err) => { + // log('SDK.onerror', err); + // reduxStore.dispatch(connectFailure()); + // }); - // TODO: fix api (get emojis by date/version....) + // SDK.driver.on('open', protectedFunction(() => { + // RocketChat.getSettings(); + // RocketChat.getPermissions(); + // reduxStore.dispatch(connectSuccess()); + // resolve(); + // })); - this.ddp.on('open', protectedFunction(() => { - RocketChat.getSettings(); - RocketChat.getPermissions(); - reduxStore.dispatch(connectSuccess()); - resolve(); - })); - - this.ddp.once('open', protectedFunction(() => { - this.ddp.subscribe('activeUsers'); - this.ddp.subscribe('roles'); - RocketChat.getCustomEmoji(); - })); + // this.ddp.once('open', protectedFunction(() => { + // this.ddp.subscribe('activeUsers'); + // this.ddp.subscribe('roles'); + // RocketChat.getCustomEmoji(); + // })); }).catch((e) => { - log('rocketchat.connect catch', e); + log('SDK.connect catch', e); }); }, @@ -424,7 +442,7 @@ const RocketChat = { return call('sendForgotPasswordEmail', email); }, - loginWithPassword({ username, password, code }, callback) { + async loginWithPassword({ username, password, code }) { let params = {}; const state = reduxStore.getState(); @@ -443,14 +461,12 @@ const RocketChat = { }; } else { params = { - password: hashPassword(password), - user: { - username - } + username, password }; if (typeof username === 'string' && username.indexOf('@') !== -1) { - params.user = { email: username }; + params.email = username; + delete params.username; } } @@ -463,25 +479,45 @@ const RocketChat = { }; } - return this.login(params, callback); + try { + return await this.login(params); + } catch (error) { + throw error; + } }, - login(params) { - return this.ddp.login(params); + async login(params) { + try { + await SDK.driver.login(params); + } catch (e) { + reduxStore.dispatch(loginFailure(e)); + throw e; + } }, logout({ server }) { - if (this.ddp) { - try { - this.ddp.logout(); - } catch (e) { - log('rocketchat.logout', e); - } + try { + RocketChat.disconnect(); + SDK.driver.logout(); + } catch (error) { + console.warn(error); } AsyncStorage.removeItem(TOKEN_KEY); AsyncStorage.removeItem(`${ TOKEN_KEY }-${ server }`); setTimeout(() => { database.deleteAll(); - }, 1000); + }, 300); + }, + disconnect() { + try { + SDK.driver.unsubscribeAll(); + } catch (error) { + console.warn(error); + } + SDK.api.setAuth({ authToken: null, userId: null }); + SDK.api.currentLogin = { + userId: null, + authToken: null + }; }, registerPushToken(userId) { @@ -508,27 +544,6 @@ const RocketChat = { sendMessage, getRooms, readMessages, - me({ server = reduxStore.getState().server.server, token, userId }) { - return fetch(`${ server }/api/v1/me`, { - method: 'get', - headers: { - 'Content-Type': 'application/json', - 'X-Auth-Token': token, - 'X-User-Id': userId - } - }).then(response => response.json()); - }, - - userInfo({ server = reduxStore.getState().server.server, token, userId }) { - return fetch(`${ server }/api/v1/users.info?userId=${ userId }`, { - method: 'get', - headers: { - 'Content-Type': 'application/json', - 'X-Auth-Token': token, - 'X-User-Id': userId - } - }).then(response => response.json()); - }, async resendMessage(messageId) { const message = await database.objects('messages').filtered('_id = $0', messageId)[0]; database.write(() => { @@ -657,7 +672,7 @@ const RocketChat = { try { room = await RocketChat.getRoom(message.rid); } catch (e) { - log('rocketchat.getPermalink', e); + log('SDK.getPermalink', e); return null; } const { server } = reduxStore.getState().server; @@ -669,7 +684,10 @@ const RocketChat = { return `${ server }/${ roomType }/${ room.name }?msg=${ message._id }`; }, subscribe(...args) { - return this.ddp.subscribe(...args); + return SDK.driver.subscribe(...args); + }, + unsubscribe(subscription) { + return SDK.driver.unsubscribe(subscription); }, emitTyping(room, t = true) { const { login } = reduxStore.getState(); diff --git a/app/sagas/connect.js b/app/sagas/connect.js index b340d15b4..bac42bbaa 100644 --- a/app/sagas/connect.js +++ b/app/sagas/connect.js @@ -34,7 +34,7 @@ const test = function* test() { const user = yield call(getToken); // const response = // yield all([call(connect, server, user && user.token ? { resume: user.token, ...user.user } : undefined)]);// , put(loginRequest({ resume: user.token }))]); - yield call(connect, server, user && user.token ? { resume: user.token, ...user.user } : undefined); + yield call(connect, server, user && user.token ? { resume: user.token, ...user } : null); // yield put(connectSuccess(response)); } catch (err) { console.warn('test', err); diff --git a/app/sagas/init.js b/app/sagas/init.js index 5bef66d82..022cba0be 100644 --- a/app/sagas/init.js +++ b/app/sagas/init.js @@ -29,7 +29,11 @@ const restore = function* restore() { } yield put(selectServerRequest(currentServer)); yield put(setUser(userParsed)); + } else { + yield put(actions.appStart('outside')); } + } else { + yield put(actions.appStart('outside')); } const sortPreferences = yield RocketChat.getSortPreferences(); diff --git a/app/sagas/roomFiles.js b/app/sagas/roomFiles.js index e258b6c63..42a01988d 100644 --- a/app/sagas/roomFiles.js +++ b/app/sagas/roomFiles.js @@ -10,7 +10,7 @@ let newSub; const openRoomFiles = function* openRoomFiles({ rid, limit }) { try { - newSub = yield RocketChat.subscribe('roomFiles', rid, limit); + sub = yield RocketChat.subscribe('roomFiles', rid, limit); yield put(readyRoomFiles()); if (sub) { sub.unsubscribe().catch(err => console.warn(err)); @@ -23,6 +23,8 @@ const openRoomFiles = function* openRoomFiles({ rid, limit }) { const closeRoomFiles = function* closeRoomFiles() { try { + // yield sub.unsubscribe(sub); + // sub = null; if (sub) { yield sub.unsubscribe(); } diff --git a/app/views/RoomInfoEditView/index.js b/app/views/RoomInfoEditView/index.js index b86f8e22c..8728c270f 100644 --- a/app/views/RoomInfoEditView/index.js +++ b/app/views/RoomInfoEditView/index.js @@ -68,10 +68,10 @@ export default class RoomInfoEditView extends LoggedView { async componentDidMount() { - const { room } = this.state; await this.updateRoom(); this.init(); this.rooms.addListener(this.updateRoom); + const { room } = this.state; this.permissions = RocketChat.hasPermission(PERMISSIONS_ARRAY, room.rid); } diff --git a/app/views/RoomsListView/ServerDropdown.js b/app/views/RoomsListView/ServerDropdown.js index 35df68bd9..4573ceb87 100644 --- a/app/views/RoomsListView/ServerDropdown.js +++ b/app/views/RoomsListView/ServerDropdown.js @@ -5,6 +5,7 @@ import { import PropTypes from 'prop-types'; import { connect, Provider } from 'react-redux'; import { Navigation } from 'react-native-navigation'; +import * as SDK from '@rocket.chat/sdk'; import { toggleServerDropdown as toggleServerDropdownAction } from '../../actions/rooms'; import { selectServerRequest as selectServerRequestAction } from '../../actions/server'; @@ -106,15 +107,19 @@ export default class ServerDropdown extends Component { select = async(server) => { const { - server: serverProp, selectServerRequest, appStart, navigator + server: currentServer, selectServerRequest, appStart, navigator } = this.props; this.close(); - if (serverProp !== server) { - selectServerRequest(server); + if (currentServer !== server) { const token = await AsyncStorage.getItem(`${ RocketChat.TOKEN_KEY }-${ server }`); if (!token) { appStart(); + try { + SDK.driver.disconnect(); + } catch (error) { + console.warn(error); + } if (NewServerView == null) { NewServerView = require('../NewServerView').default; Navigation.registerComponent('NewServerView', () => NewServerView, store, Provider); @@ -131,6 +136,8 @@ export default class ServerDropdown extends Component { } }); }, 1000); + } else { + selectServerRequest(server); } } } @@ -149,6 +156,7 @@ export default class ServerDropdown extends Component { source={{ uri: item.iconURL }} defaultSource={{ uri: 'logo' }} style={styles.serverIcon} + onError={() => console.warn('error loading serverIcon')} /> ) : ( diff --git a/package-lock.json b/package-lock.json index 713a3c3d0..3971ed803 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1820,6 +1820,30 @@ "resolved": "https://registry.npmjs.org/@remobile/react-native-toast/-/react-native-toast-1.0.7.tgz", "integrity": "sha512-iOD1PRnTSVr9sDWQdesIpfRrwJhHfeEQe5BpalQxC5OhM9thpiE6cu2NlW1KBWl0RJG4ZiJaF1xLlCo9YxU6dA==" }, + "@rocket.chat/sdk": { + "version": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#a2af4675174ce53076ba587693e5210c05ee767d", + "from": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#ddp", + "requires": { + "@types/lru-cache": "^4.1.0", + "@types/node": "^9.4.6", + "axios": "^0.18.0", + "ejson": "^2.1.2", + "isomorphic-ws": "^4.0.1", + "lru-cache": "^4.1.1", + "universal-websocket-client": "^1.0.2", + "ws": "^5.2.0" + }, + "dependencies": { + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, "@storybook/addon-actions": { "version": "3.4.11", "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-3.4.11.tgz", @@ -2210,11 +2234,21 @@ "react-treebeard": "2.1.0" } }, + "@types/lru-cache": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-4.1.1.tgz", + "integrity": "sha512-8mNEUG6diOrI6pMqOHrHPDBB1JsrpedeMK9AWGzVCQ7StRRribiT9BRvUmF8aUws9iBbVlgVekOT5Sgzc1MTKw==" + }, "@types/markdown-it": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-0.0.4.tgz", "integrity": "sha512-FWR7QB7EqBRq1s9BMk0ccOSOuRLfVEWYpHQYpFPaXtCoqN6dJx2ttdsdQbUxLLnAlKpYeVjveGGhQ3583TTa7g==" }, + "@types/node": { + "version": "9.6.34", + "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.34.tgz", + "integrity": "sha512-PzJpSs2afoYqBA4yLBgaKUdZRk8+1yvkxcUBW6958h4vYOC+pc4k4C+QmQ6AO5Pt7uA4EIIboFog6YNCuITD0g==" + }, "@types/react": { "version": "16.4.6", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.4.6.tgz", @@ -2934,6 +2968,15 @@ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, + "axios": { + "version": "0.18.0", + "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", + "requires": { + "follow-redirects": "^1.3.0", + "is-buffer": "^1.1.5" + } + }, "axobject-query": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.1.tgz", @@ -8354,6 +8397,24 @@ "readable-stream": "2.3.5" } }, + "follow-redirects": { + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.8.tgz", + "integrity": "sha512-sy1mXPmv7kLAMKW/8XofG7o9T+6gAjzdZK4AJF6ryqQYUa/hnzgiypoeUecZ53x7XiqKNEpNqLtS97MshW2nxg==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -10697,6 +10758,11 @@ "whatwg-fetch": "2.0.3" } }, + "isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==" + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -13737,7 +13803,7 @@ }, "rimraf": { "version": "2.4.5", - "resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", "dev": true, "optional": true, @@ -13873,7 +13939,7 @@ }, "ncp": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "dev": true, "optional": true @@ -20472,6 +20538,26 @@ "crypto-random-string": "1.0.0" } }, + "universal-websocket-client": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/universal-websocket-client/-/universal-websocket-client-1.0.2.tgz", + "integrity": "sha512-Pi6BdJtEAISb77GTbOLBLIWdYGezXgnJejrVBYKXxzNTsLcjJS+mWIJ2BRZElSlOG/wc7+yfOe5y30bzTu3Qqg==", + "requires": { + "ws": "^3.3.3" + }, + "dependencies": { + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + } + } + }, "universalify": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", diff --git a/package.json b/package.json index 868c1c11d..11e9abac9 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ }, "dependencies": { "@remobile/react-native-toast": "^1.0.7", + "@rocket.chat/sdk": "git+https://github.com/RocketChat/Rocket.Chat.js.SDK.git#ddp", "deep-equal": "^1.0.1", "ejson": "^2.1.2", "js-base64": "^2.4.9",