From 319630949e79c5d30e6e596e192abdf05d691432 Mon Sep 17 00:00:00 2001
From: Diego Mello <diegolmello@gmail.com>
Date: Mon, 17 Jun 2019 10:57:07 -0300
Subject: [PATCH] [FIX] Cancel ongoing tasks on server change (#984)

* Fork selectServer and loginSuccess

* Fork tasks on login

* Refactor

* Remove room sub and requests on logout

* Comment room header disabled rule

* Check server on rooms stream

* Uncomment disabled props

* Fix updating state

* Comment last fetch user presence date
---
 app/actions/actionsTypes.js            |   1 +
 app/actions/server.js                  |   6 ++
 app/lib/methods/getCustomEmojis.js     |  93 ++++++++++----------
 app/lib/methods/getPermissions.js      |  93 ++++++++++----------
 app/lib/methods/getRoles.js            |  46 +++++-----
 app/lib/methods/getSlashCommands.js    |  47 +++++-----
 app/lib/methods/subscriptions/rooms.js |   7 ++
 app/lib/rocketchat.js                  | 113 ++++++++++++-------------
 app/reducers/server.js                 |   7 ++
 app/sagas/login.js                     |  78 ++++++++++++-----
 app/sagas/rooms.js                     |  29 ++++++-
 app/sagas/selectServer.js              |  21 ++++-
 app/views/RoomsListView/index.js       |   4 +-
 13 files changed, 330 insertions(+), 215 deletions(-)

diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js
index 6e704861b..45d2886f7 100644
--- a/app/actions/actionsTypes.js
+++ b/app/actions/actionsTypes.js
@@ -58,6 +58,7 @@ export const SERVER = createRequestTypes('SERVER', [
 	...defaultTypes,
 	'SELECT_SUCCESS',
 	'SELECT_REQUEST',
+	'SELECT_FAILURE',
 	'INIT_ADD',
 	'FINISH_ADD'
 ]);
diff --git a/app/actions/server.js b/app/actions/server.js
index 3a383781c..a5441c7ba 100644
--- a/app/actions/server.js
+++ b/app/actions/server.js
@@ -17,6 +17,12 @@ export function selectServerSuccess(server, version) {
 	};
 }
 
+export function selectServerFailure() {
+	return {
+		type: SERVER.SELECT_FAILURE
+	};
+}
+
 export function serverRequest(server) {
 	return {
 		type: SERVER.REQUEST,
diff --git a/app/lib/methods/getCustomEmojis.js b/app/lib/methods/getCustomEmojis.js
index be2701f6c..e2fa2790d 100644
--- a/app/lib/methods/getCustomEmojis.js
+++ b/app/lib/methods/getCustomEmojis.js
@@ -23,57 +23,62 @@ const create = (customEmojis) => {
 };
 
 
-export default async function() {
-	try {
-		const serverVersion = reduxStore.getState().server.version;
-		const updatedSince = getUpdatedSince();
+export default function() {
+	return new Promise(async(resolve) => {
+		try {
+			const serverVersion = reduxStore.getState().server.version;
+			const updatedSince = getUpdatedSince();
 
-		// if server version is lower than 0.75.0, fetches from old api
-		if (semver.lt(serverVersion, '0.75.0')) {
-			// RC 0.61.0
-			const result = await this.sdk.get('emoji-custom');
+			// if server version is lower than 0.75.0, fetches from old api
+			if (semver.lt(serverVersion, '0.75.0')) {
+				// RC 0.61.0
+				const result = await this.sdk.get('emoji-custom');
 
-			InteractionManager.runAfterInteractions(() => {
-				let { emojis } = result;
-				emojis = emojis.filter(emoji => !updatedSince || emoji._updatedAt > updatedSince);
-				database.write(() => {
-					create(emojis);
+				InteractionManager.runAfterInteractions(() => {
+					let { emojis } = result;
+					emojis = emojis.filter(emoji => !updatedSince || emoji._updatedAt > updatedSince);
+					database.write(() => {
+						create(emojis);
+					});
+					return resolve();
 				});
-			});
-		} else {
-			const params = {};
-			if (updatedSince) {
-				params.updatedSince = updatedSince;
-			}
+			} else {
+				const params = {};
+				if (updatedSince) {
+					params.updatedSince = updatedSince;
+				}
 
-			// RC 0.75.0
-			const result = await this.sdk.get('emoji-custom.list', params);
+				// RC 0.75.0
+				const result = await this.sdk.get('emoji-custom.list', params);
 
-			if (!result.success) {
-				return;
-			}
+				if (!result.success) {
+					return resolve();
+				}
 
-			InteractionManager.runAfterInteractions(
-				() => database.write(() => {
-					const { emojis } = result;
-					create(emojis.update);
+				InteractionManager.runAfterInteractions(
+					() => database.write(() => {
+						const { emojis } = result;
+						create(emojis.update);
 
-					if (emojis.delete && emojis.delete.length) {
-						emojis.delete.forEach((emoji) => {
-							try {
-								const emojiRecord = database.objectForPrimaryKey('customEmojis', emoji._id);
-								if (emojiRecord) {
-									database.delete(emojiRecord);
+						if (emojis.delete && emojis.delete.length) {
+							emojis.delete.forEach((emoji) => {
+								try {
+									const emojiRecord = database.objectForPrimaryKey('customEmojis', emoji._id);
+									if (emojiRecord) {
+										database.delete(emojiRecord);
+									}
+								} catch (e) {
+									log('err_get_emojis_delete', e);
 								}
-							} catch (e) {
-								log('err_get_emojis_delete', e);
-							}
-						});
-					}
-				})
-			);
+							});
+						}
+						return resolve();
+					})
+				);
+			}
+		} catch (e) {
+			log('err_get_custom_emojis', e);
+			return resolve();
 		}
-	} catch (e) {
-		log('err_get_custom_emojis', e);
-	}
+	});
 }
diff --git a/app/lib/methods/getPermissions.js b/app/lib/methods/getPermissions.js
index c4d4a860d..b6c9c1c50 100644
--- a/app/lib/methods/getPermissions.js
+++ b/app/lib/methods/getPermissions.js
@@ -22,55 +22,60 @@ const create = (permissions) => {
 	}
 };
 
-export default async function() {
-	try {
-		const serverVersion = reduxStore.getState().server.version;
+export default function() {
+	return new Promise(async(resolve) => {
+		try {
+			const serverVersion = reduxStore.getState().server.version;
 
-		// if server version is lower than 0.73.0, fetches from old api
-		if (semver.lt(serverVersion, '0.73.0')) {
-			// RC 0.66.0
-			const result = await this.sdk.get('permissions.list');
-			if (!result.success) {
-				return;
-			}
-			InteractionManager.runAfterInteractions(() => {
-				database.write(() => {
-					create(result.permissions);
+			// if server version is lower than 0.73.0, fetches from old api
+			if (semver.lt(serverVersion, '0.73.0')) {
+				// RC 0.66.0
+				const result = await this.sdk.get('permissions.list');
+				if (!result.success) {
+					return resolve();
+				}
+				InteractionManager.runAfterInteractions(() => {
+					database.write(() => {
+						create(result.permissions);
+					});
+					return resolve();
 				});
-			});
-		} else {
-			const params = {};
-			const updatedSince = getUpdatedSince();
-			if (updatedSince) {
-				params.updatedSince = updatedSince;
-			}
-			// RC 0.73.0
-			const result = await this.sdk.get('permissions.listAll', params);
+			} else {
+				const params = {};
+				const updatedSince = getUpdatedSince();
+				if (updatedSince) {
+					params.updatedSince = updatedSince;
+				}
+				// RC 0.73.0
+				const result = await this.sdk.get('permissions.listAll', params);
 
-			if (!result.success) {
-				return;
-			}
+				if (!result.success) {
+					return resolve();
+				}
 
-			InteractionManager.runAfterInteractions(
-				() => database.write(() => {
-					create(result.update);
+				InteractionManager.runAfterInteractions(
+					() => database.write(() => {
+						create(result.update);
 
-					if (result.delete && result.delete.length) {
-						result.delete.forEach((p) => {
-							try {
-								const permission = database.objectForPrimaryKey('permissions', p._id);
-								if (permission) {
-									database.delete(permission);
+						if (result.delete && result.delete.length) {
+							result.delete.forEach((p) => {
+								try {
+									const permission = database.objectForPrimaryKey('permissions', p._id);
+									if (permission) {
+										database.delete(permission);
+									}
+								} catch (e) {
+									log('err_get_permissions_delete', e);
 								}
-							} catch (e) {
-								log('err_get_permissions_delete', e);
-							}
-						});
-					}
-				})
-			);
+							});
+						}
+						return resolve();
+					})
+				);
+			}
+		} catch (e) {
+			log('err_get_permissions', e);
+			return resolve();
 		}
-	} catch (e) {
-		log('err_get_permissions', e);
-	}
+	});
 }
diff --git a/app/lib/methods/getRoles.js b/app/lib/methods/getRoles.js
index 6b7a06da7..c668a55ae 100644
--- a/app/lib/methods/getRoles.js
+++ b/app/lib/methods/getRoles.js
@@ -3,29 +3,33 @@ import { InteractionManager } from 'react-native';
 import database from '../realm';
 import log from '../../utils/log';
 
-export default async function() {
-	try {
-		// RC 0.70.0
-		const result = await this.sdk.get('roles.list');
+export default function() {
+	return new Promise(async(resolve) => {
+		try {
+			// RC 0.70.0
+			const result = await this.sdk.get('roles.list');
 
-		if (!result.success) {
-			return;
-		}
+			if (!result.success) {
+				return resolve();
+			}
 
-		const { roles } = result;
+			const { roles } = result;
 
-		if (roles && roles.length) {
-			InteractionManager.runAfterInteractions(() => {
-				database.write(() => roles.forEach((role) => {
-					try {
-						database.create('roles', role, true);
-					} catch (e) {
-						log('err_get_roles_create', e);
-					}
-				}));
-			});
+			if (roles && roles.length) {
+				InteractionManager.runAfterInteractions(() => {
+					database.write(() => roles.forEach((role) => {
+						try {
+							database.create('roles', role, true);
+						} catch (e) {
+							log('err_get_roles_create', e);
+						}
+					}));
+					return resolve();
+				});
+			}
+		} catch (e) {
+			log('err_get_roles', e);
+			return resolve();
 		}
-	} catch (e) {
-		log('err_get_roles', e);
-	}
+	});
 }
diff --git a/app/lib/methods/getSlashCommands.js b/app/lib/methods/getSlashCommands.js
index 401c11807..6c20624e3 100644
--- a/app/lib/methods/getSlashCommands.js
+++ b/app/lib/methods/getSlashCommands.js
@@ -3,29 +3,34 @@ import { InteractionManager } from 'react-native';
 import database from '../realm';
 import log from '../../utils/log';
 
-export default async function() {
-	try {
-		// RC 0.60.2
-		const result = await this.sdk.get('commands.list');
+export default function() {
+	return new Promise(async(resolve) => {
+		try {
+			// RC 0.60.2
+			const result = await this.sdk.get('commands.list');
 
-		if (!result.success) {
-			return log('getSlashCommand fetch', result);
-		}
+			if (!result.success) {
+				log('getSlashCommand fetch', result);
+				return resolve();
+			}
 
-		const { commands } = result;
+			const { commands } = result;
 
-		if (commands && commands.length) {
-			InteractionManager.runAfterInteractions(() => {
-				database.write(() => commands.forEach((command) => {
-					try {
-						database.create('slashCommand', command, true);
-					} catch (e) {
-						log('get_slash_command', e);
-					}
-				}));
-			});
+			if (commands && commands.length) {
+				InteractionManager.runAfterInteractions(() => {
+					database.write(() => commands.forEach((command) => {
+						try {
+							database.create('slashCommand', command, true);
+						} catch (e) {
+							log('get_slash_command', e);
+						}
+					}));
+					return resolve();
+				});
+			}
+		} catch (e) {
+			log('err_get_slash_command', e);
+			return resolve();
 		}
-	} catch (e) {
-		log('err_get_slash_command', e);
-	}
+	});
 }
diff --git a/app/lib/methods/subscriptions/rooms.js b/app/lib/methods/subscriptions/rooms.js
index 50a35c551..5b23fbda7 100644
--- a/app/lib/methods/subscriptions/rooms.js
+++ b/app/lib/methods/subscriptions/rooms.js
@@ -13,6 +13,7 @@ const removeListener = listener => listener.stop();
 let connectedListener;
 let disconnectedListener;
 let streamListener;
+let subServer;
 
 export default async function subscribeRooms() {
 	let timer = null;
@@ -41,6 +42,10 @@ export default async function subscribeRooms() {
 	};
 
 	const handleStreamMessageReceived = protectedFunction((ddpMessage) => {
+		// check if the server from variable is the same as the js sdk client
+		if (this.sdk && this.sdk.client && this.sdk.client.host !== subServer) {
+			return;
+		}
 		if (ddpMessage.msg === 'added') {
 			return;
 		}
@@ -149,6 +154,8 @@ export default async function subscribeRooms() {
 	streamListener = this.sdk.onStreamData('stream-notify-user', handleStreamMessageReceived);
 
 	try {
+		// set the server that started this task
+		subServer = this.sdk.client.host;
 		await this.sdk.subscribeNotifyUser();
 	} catch (e) {
 		log('err_subscribe_rooms', e);
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index 156e34b72..21ad4b1e4 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -8,7 +8,6 @@ import messagesStatus from '../constants/messagesStatus';
 import database from './realm';
 import log from '../utils/log';
 import { isIOS, getBundleId } from '../utils/deviceInfo';
-import EventEmitter from '../utils/events';
 
 import {
 	setUser, setLoginServices, loginRequest, loginFailure, logout
@@ -24,7 +23,7 @@ import getSettings from './methods/getSettings';
 
 import getRooms from './methods/getRooms';
 import getPermissions from './methods/getPermissions';
-import getCustomEmoji from './methods/getCustomEmojis';
+import getCustomEmojis from './methods/getCustomEmojis';
 import getSlashCommands from './methods/getSlashCommands';
 import getRoles from './methods/getRoles';
 import canOpenRoom from './methods/canOpenRoom';
@@ -37,7 +36,6 @@ import sendMessage, { getMessage, sendMessageCall } from './methods/sendMessage'
 import { sendFileMessage, cancelUpload, isUploadActive } from './methods/sendFileMessage';
 
 import { getDeviceToken } from '../notifications/push';
-import { roomsRequest } from '../actions/rooms';
 
 const TOKEN_KEY = 'reactnativemeteor_usertoken';
 const SORT_PREFS_KEY = 'RC_SORT_PREFS_KEY';
@@ -141,23 +139,6 @@ const RocketChat = {
 			};
 		}
 	},
-	async loginSuccess({ user }) {
-		EventEmitter.emit('connected');
-		reduxStore.dispatch(setUser(user));
-		reduxStore.dispatch(roomsRequest());
-
-		if (this.roomsSub) {
-			this.roomsSub.stop();
-		}
-		this.roomsSub = await this.subscribeRooms();
-
-		this.getPermissions();
-		this.getCustomEmoji();
-		this.getRoles();
-		this.getSlashCommands();
-		this.registerPushToken().catch(e => console.log(e));
-		this.getUserPresence();
-	},
 	connect({ server, user }) {
 		return new Promise((resolve) => {
 			database.setActiveDB(server);
@@ -167,6 +148,10 @@ const RocketChat = {
 				clearTimeout(this.connectTimeout);
 			}
 
+			if (this.roomsSub) {
+				this.roomsSub.stop();
+			}
+
 			if (this.sdk) {
 				this.sdk.disconnect();
 				this.sdk = null;
@@ -195,10 +180,10 @@ const RocketChat = {
 
 			this.sdk.onStreamData('connected', () => {
 				reduxStore.dispatch(connectSuccess());
-				const { isAuthenticated } = reduxStore.getState().login;
-				if (isAuthenticated) {
-					this.getUserPresence();
-				}
+				// const { isAuthenticated } = reduxStore.getState().login;
+				// if (isAuthenticated) {
+				// 	this.getUserPresence();
+				// }
 			});
 
 			this.sdk.onStreamData('close', () => {
@@ -349,7 +334,7 @@ const RocketChat = {
 		}
 	},
 	registerPushToken() {
-		return new Promise((resolve) => {
+		return new Promise(async(resolve) => {
 			const token = getDeviceToken();
 			if (token) {
 				const type = isIOS ? 'apn' : 'gcm';
@@ -358,8 +343,12 @@ const RocketChat = {
 					type,
 					appName: getBundleId
 				};
-				// RC 0.60.0
-				return this.sdk.post('push.token', data);
+				try {
+					// RC 0.60.0
+					await this.sdk.post('push.token', data);
+				} catch (error) {
+					console.log(error);
+				}
 			}
 			return resolve();
 		});
@@ -468,7 +457,7 @@ const RocketChat = {
 	isUploadActive,
 	getSettings,
 	getPermissions,
-	getCustomEmoji,
+	getCustomEmojis,
 	getSlashCommands,
 	getRoles,
 	parseSettings: settings => settings.reduce((ret, item) => {
@@ -824,41 +813,45 @@ const RocketChat = {
 			command, params, roomId, previewItem
 		});
 	},
-	async getUserPresence() {
-		const serverVersion = reduxStore.getState().server.version;
-
-		// if server is lower than 1.1.0
-		if (semver.lt(semver.coerce(serverVersion), '1.1.0')) {
-			if (this.activeUsersSubTimeout) {
-				clearTimeout(this.activeUsersSubTimeout);
-				this.activeUsersSubTimeout = false;
-			}
-			this.activeUsersSubTimeout = setTimeout(() => {
-				this.sdk.subscribe('activeUsers');
-			}, 5000);
-		} else {
-			const params = {};
-			if (this.lastUserPresenceFetch) {
-				params.from = this.lastUserPresenceFetch.toISOString();
-			}
+	getUserPresence() {
+		return new Promise(async(resolve) => {
+			const serverVersion = reduxStore.getState().server.version;
 
-			// RC 1.1.0
-			const result = await this.sdk.get('users.presence', params);
-			if (result.success) {
-				this.lastUserPresenceFetch = new Date();
-				database.memoryDatabase.write(() => {
-					result.users.forEach((item) => {
-						try {
-							item.id = item._id;
-							database.memoryDatabase.create('activeUsers', item, true);
-						} catch (error) {
-							console.log(error);
-						}
+			// if server is lower than 1.1.0
+			if (semver.lt(semver.coerce(serverVersion), '1.1.0')) {
+				if (this.activeUsersSubTimeout) {
+					clearTimeout(this.activeUsersSubTimeout);
+					this.activeUsersSubTimeout = false;
+				}
+				this.activeUsersSubTimeout = setTimeout(() => {
+					this.sdk.subscribe('activeUsers');
+				}, 5000);
+				return resolve();
+			} else {
+				const params = {};
+				// if (this.lastUserPresenceFetch) {
+				// 	params.from = this.lastUserPresenceFetch.toISOString();
+				// }
+
+				// RC 1.1.0
+				const result = await this.sdk.get('users.presence', params);
+				if (result.success) {
+					// this.lastUserPresenceFetch = new Date();
+					database.memoryDatabase.write(() => {
+						result.users.forEach((item) => {
+							try {
+								item.id = item._id;
+								database.memoryDatabase.create('activeUsers', item, true);
+							} catch (error) {
+								console.log(error);
+							}
+						});
 					});
-				});
-				this.sdk.subscribe('stream-notify-logged', 'user-status');
+					this.sdk.subscribe('stream-notify-logged', 'user-status');
+					return resolve();
+				}
 			}
-		}
+		});
 	},
 	getDirectory({
 		query, count, offset, sort
diff --git a/app/reducers/server.js b/app/reducers/server.js
index a2a04bd94..7eda3767f 100644
--- a/app/reducers/server.js
+++ b/app/reducers/server.js
@@ -44,6 +44,13 @@ export default function server(state = initialState, action) {
 				connected: true,
 				loading: false
 			};
+		case SERVER.SELECT_FAILURE:
+			return {
+				...state,
+				connecting: false,
+				connected: false,
+				loading: false
+			};
 		case SERVER.INIT_ADD:
 			return {
 				...state,
diff --git a/app/sagas/login.js b/app/sagas/login.js
index 28c723215..66b4183ad 100644
--- a/app/sagas/login.js
+++ b/app/sagas/login.js
@@ -1,16 +1,18 @@
 import { AsyncStorage } from 'react-native';
 import {
-	put, call, takeLatest, select
+	put, call, takeLatest, select, take, fork, cancel
 } from 'redux-saga/effects';
 
 import * as types from '../actions/actionsTypes';
 import { appStart } from '../actions';
 import { serverFinishAdd, selectServerRequest } from '../actions/server';
-import { loginFailure, loginSuccess } from '../actions/login';
+import { loginFailure, loginSuccess, setUser } from '../actions/login';
+import { roomsRequest } from '../actions/rooms';
 import RocketChat from '../lib/rocketchat';
 import log from '../utils/log';
 import I18n from '../i18n';
 import database from '../lib/realm';
+import EventEmitter from '../utils/events';
 
 const getServer = state => state.server.server;
 const loginWithPasswordCall = args => RocketChat.loginWithPassword(args);
@@ -31,27 +33,59 @@ const handleLoginRequest = function* handleLoginRequest({ credentials }) {
 	}
 };
 
-const handleLoginSuccess = function* handleLoginSuccess({ user }) {
-	const adding = yield select(state => state.server.adding);
-	yield AsyncStorage.setItem(RocketChat.TOKEN_KEY, user.token);
+const fetchPermissions = function* fetchPermissions() {
+	yield RocketChat.getPermissions();
+};
 
-	const server = yield select(getServer);
+const fetchCustomEmojis = function* fetchCustomEmojis() {
+	yield RocketChat.getCustomEmojis();
+};
+
+const fetchRoles = function* fetchRoles() {
+	yield RocketChat.getRoles();
+};
+
+const fetchSlashCommands = function* fetchSlashCommands() {
+	yield RocketChat.getSlashCommands();
+};
+
+const registerPushToken = function* registerPushToken() {
+	yield RocketChat.registerPushToken();
+};
+
+const fetchUserPresence = function* fetchUserPresence() {
+	yield RocketChat.getUserPresence();
+};
+
+const handleLoginSuccess = function* handleLoginSuccess({ user }) {
 	try {
-		RocketChat.loginSuccess({ user });
+		const adding = yield select(state => state.server.adding);
+		yield AsyncStorage.setItem(RocketChat.TOKEN_KEY, user.token);
+
+		const server = yield select(getServer);
+		yield put(roomsRequest());
+		yield fork(fetchPermissions);
+		yield fork(fetchCustomEmojis);
+		yield fork(fetchRoles);
+		yield fork(fetchSlashCommands);
+		yield fork(registerPushToken);
+		yield fork(fetchUserPresence);
+
 		I18n.locale = user.language;
 		yield AsyncStorage.setItem(`${ RocketChat.TOKEN_KEY }-${ server }`, JSON.stringify(user));
-	} catch (error) {
-		console.log('loginSuccess saga -> error', error);
-	}
+		yield put(setUser(user));
+		EventEmitter.emit('connected');
 
-	if (!user.username) {
-		RocketChat.loginSuccess({ user });
-		yield put(appStart('setUsername'));
-	} else if (adding) {
-		yield put(serverFinishAdd());
-		yield put(appStart('inside'));
-	} else {
-		yield put(appStart('inside'));
+		if (!user.username) {
+			yield put(appStart('setUsername'));
+		} else if (adding) {
+			yield put(serverFinishAdd());
+			yield put(appStart('inside'));
+		} else {
+			yield put(appStart('inside'));
+		}
+	} catch (e) {
+		log('err_handle_login_success', e);
 	}
 };
 
@@ -93,8 +127,14 @@ const handleSetUser = function handleSetUser({ user }) {
 
 const root = function* root() {
 	yield takeLatest(types.LOGIN.REQUEST, handleLoginRequest);
-	yield takeLatest(types.LOGIN.SUCCESS, handleLoginSuccess);
 	yield takeLatest(types.LOGOUT, handleLogout);
 	yield takeLatest(types.USER.SET, handleSetUser);
+
+	while (true) {
+		const params = yield take(types.LOGIN.SUCCESS);
+		const loginSuccessTask = yield fork(handleLoginSuccess, params);
+		yield take(types.SERVER.SELECT_REQUEST);
+		yield cancel(loginSuccessTask);
+	}
 };
 export default root;
diff --git a/app/sagas/rooms.js b/app/sagas/rooms.js
index ad5d862f4..bc900f2d2 100644
--- a/app/sagas/rooms.js
+++ b/app/sagas/rooms.js
@@ -1,6 +1,7 @@
 import {
-	put, takeLatest, select
+	put, select, race, take, fork, cancel, takeLatest
 } from 'redux-saga/effects';
+import { BACKGROUND } from 'redux-enhancer-react-native-appstate';
 
 import * as types from '../actions/actionsTypes';
 import { roomsSuccess, roomsFailure } from '../actions/rooms';
@@ -9,8 +10,18 @@ import log from '../utils/log';
 import mergeSubscriptionsRooms from '../lib/methods/helpers/mergeSubscriptionsRooms';
 import RocketChat from '../lib/rocketchat';
 
+let roomsSub;
+
+const removeSub = function removeSub() {
+	if (roomsSub && roomsSub.stop) {
+		roomsSub.stop();
+	}
+};
+
 const handleRoomsRequest = function* handleRoomsRequest() {
 	try {
+		removeSub();
+		roomsSub = yield RocketChat.subscribeRooms();
 		const newRoomsUpdatedAt = new Date();
 		const server = yield select(state => state.server.server);
 		const [serverRecord] = database.databases.serversDB.objects('servers').filtered('id = $0', server);
@@ -42,7 +53,21 @@ const handleRoomsRequest = function* handleRoomsRequest() {
 	}
 };
 
+const handleLogout = function handleLogout() {
+	removeSub();
+};
+
 const root = function* root() {
-	yield takeLatest(types.ROOMS.REQUEST, handleRoomsRequest);
+	yield takeLatest(types.LOGOUT, handleLogout);
+	while (true) {
+		const params = yield take(types.ROOMS.REQUEST);
+		const roomsRequestTask = yield fork(handleRoomsRequest, params);
+		yield race({
+			serverReq: take(types.SERVER.SELECT_REQUEST),
+			background: take(BACKGROUND),
+			logout: take(types.LOGOUT)
+		});
+		yield cancel(roomsRequestTask);
+	}
 };
 export default root;
diff --git a/app/sagas/selectServer.js b/app/sagas/selectServer.js
index 859103901..b6f1efec5 100644
--- a/app/sagas/selectServer.js
+++ b/app/sagas/selectServer.js
@@ -1,10 +1,14 @@
-import { put, takeLatest } from 'redux-saga/effects';
+import {
+	put, take, takeLatest, fork, cancel, race
+} from 'redux-saga/effects';
 import { AsyncStorage, Alert } from 'react-native';
 
 import Navigation from '../lib/Navigation';
 import { SERVER } from '../actions/actionsTypes';
 import * as actions from '../actions';
-import { serverFailure, selectServerRequest, selectServerSuccess } from '../actions/server';
+import {
+	serverFailure, selectServerRequest, selectServerSuccess, selectServerFailure
+} from '../actions/server';
 import { setUser } from '../actions/login';
 import RocketChat from '../lib/rocketchat';
 import database from '../lib/realm';
@@ -58,6 +62,7 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
 		// Return server version even when offline
 		yield put(selectServerSuccess(server, (serverInfo && serverInfo.version) || version));
 	} catch (e) {
+		yield put(selectServerFailure());
 		log('err_select_server', e);
 	}
 };
@@ -81,7 +86,17 @@ const handleServerRequest = function* handleServerRequest({ server }) {
 };
 
 const root = function* root() {
-	yield takeLatest(SERVER.SELECT_REQUEST, handleSelectServer);
 	yield takeLatest(SERVER.REQUEST, handleServerRequest);
+
+	while (true) {
+		const params = yield take(SERVER.SELECT_REQUEST);
+		const selectServerTask = yield fork(handleSelectServer, params);
+		yield race({
+			request: take(SERVER.SELECT_REQUEST),
+			success: take(SERVER.SELECT_SUCCESS),
+			failure: take(SERVER.SELECT_FAILURE)
+		});
+		yield cancel(selectServerTask);
+	}
 };
 export default root;
diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js
index f29d62348..a8297a166 100644
--- a/app/views/RoomsListView/index.js
+++ b/app/views/RoomsListView/index.js
@@ -29,6 +29,7 @@ import RoomsListHeaderView from './Header';
 import { DrawerButton, CustomHeaderButtons, Item } from '../../containers/HeaderButton';
 import StatusBar from '../../containers/StatusBar';
 import ListHeader from './ListHeader';
+import { selectServerRequest as selectServerRequestAction } from '../../actions/server';
 
 const SCROLL_OFFSET = 56;
 
@@ -56,7 +57,8 @@ const keyExtractor = item => item.rid;
 	openSearchHeader: () => dispatch(openSearchHeaderAction()),
 	closeSearchHeader: () => dispatch(closeSearchHeaderAction()),
 	appStart: () => dispatch(appStartAction()),
-	roomsRequest: () => dispatch(roomsRequestAction())
+	roomsRequest: () => dispatch(roomsRequestAction()),
+	selectServerRequest: server => dispatch(selectServerRequestAction(server))
 }))
 export default class RoomsListView extends React.Component {
 	static navigationOptions = ({ navigation }) => {
-- 
GitLab