[IMPROVEMENT] Change server while connecting/updating (#1981)

* [IMPROVEMENT] Change server while connecting

* [FIX] Not login/reconnect to previous server

* [FIX] Abort all fetch while connecting

* [FIX] Abort sdk fetch

* [FIX] Patch-package

* Add comments

Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
Djorkaeff Alexandre 2020-05-05 10:11:28 -03:00 committed by GitHub
parent ee5b7592b4
commit c578f1bbe8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 120 additions and 22 deletions

View File

@ -47,6 +47,7 @@ import { getDeviceToken } from '../notifications/push';
import { setActiveUsers } from '../actions/activeUsers'; import { setActiveUsers } from '../actions/activeUsers';
import I18n from '../i18n'; import I18n from '../i18n';
import { twoFactor } from '../utils/twoFactor'; import { twoFactor } from '../utils/twoFactor';
import { selectServerFailure } from '../actions/server';
import { useSsl } from '../utils/url'; import { useSsl } from '../utils/url';
const TOKEN_KEY = 'reactnativemeteor_usertoken'; const TOKEN_KEY = 'reactnativemeteor_usertoken';
@ -142,6 +143,10 @@ const RocketChat = {
} }
return result; return result;
} catch (e) { } catch (e) {
if (e.message === 'Aborted') {
reduxStore.dispatch(selectServerFailure());
throw e;
}
log(e); log(e);
} }
return { return {
@ -155,6 +160,16 @@ const RocketChat = {
stopListener(listener) { stopListener(listener) {
return listener && listener.stop(); return listener && listener.stop();
}, },
// Abort all requests and create a new AbortController
abort() {
if (this.controller) {
this.controller.abort();
if (this.sdk) {
this.sdk.abort();
}
}
this.controller = new AbortController();
},
connect({ server, user, logoutOnError = false }) { connect({ server, user, logoutOnError = false }) {
return new Promise((resolve) => { return new Promise((resolve) => {
if (!this.sdk || this.sdk.client.host !== server) { if (!this.sdk || this.sdk.client.host !== server) {
@ -201,17 +216,21 @@ const RocketChat = {
const sdkConnect = () => this.sdk.connect() const sdkConnect = () => this.sdk.connect()
.then(() => { .then(() => {
if (user && user.token) { const { server: currentServer } = reduxStore.getState().server;
if (user && user.token && server === currentServer) {
reduxStore.dispatch(loginRequest({ resume: user.token }, logoutOnError)); reduxStore.dispatch(loginRequest({ resume: user.token }, logoutOnError));
} }
}) })
.catch((err) => { .catch((err) => {
console.log('connect error', err); console.log('connect error', err);
// when `connect` raises an error, we try again in 10 seconds const { server: currentServer } = reduxStore.getState().server;
this.connectTimeout = setTimeout(() => { if (server === currentServer) {
sdkConnect(); // when `connect` raises an error, we try again in 10 seconds
}, 10000); this.connectTimeout = setTimeout(() => {
sdkConnect();
}, 10000);
}
}); });
sdkConnect(); sdkConnect();

View File

@ -1,6 +1,4 @@
import { import { put, takeLatest } from 'redux-saga/effects';
put, take, takeLatest, fork, cancel, race
} from 'redux-saga/effects';
import { Alert } from 'react-native'; import { Alert } from 'react-native';
import RNUserDefaults from 'rn-user-defaults'; import RNUserDefaults from 'rn-user-defaults';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
@ -98,6 +96,9 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
const basicAuth = yield RNUserDefaults.get(`${ BASIC_AUTH_KEY }-${ server }`); const basicAuth = yield RNUserDefaults.get(`${ BASIC_AUTH_KEY }-${ server }`);
setBasicAuth(basicAuth); setBasicAuth(basicAuth);
// Check for running requests and abort them before connecting to the server
RocketChat.abort();
if (user) { if (user) {
yield put(clearSettings()); yield put(clearSettings());
yield RocketChat.connect({ server, user, logoutOnError: true }); yield RocketChat.connect({ server, user, logoutOnError: true });
@ -152,16 +153,6 @@ const handleServerRequest = function* handleServerRequest({ server, certificate
const root = function* root() { const root = function* root() {
yield takeLatest(SERVER.REQUEST, handleServerRequest); yield takeLatest(SERVER.REQUEST, handleServerRequest);
yield takeLatest(SERVER.SELECT_REQUEST, handleSelectServer);
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; export default root;

View File

@ -1,6 +1,7 @@
import { Platform } from 'react-native'; import { Platform } from 'react-native';
import DeviceInfo from 'react-native-device-info'; import DeviceInfo from 'react-native-device-info';
import { settings as RocketChatSettings } from '@rocket.chat/sdk'; import { settings as RocketChatSettings } from '@rocket.chat/sdk';
import RocketChat from '../lib/rocketchat';
// this form is required by Rocket.Chat's parser in "app/statistics/server/lib/UAParserCustom.js" // this form is required by Rocket.Chat's parser in "app/statistics/server/lib/UAParserCustom.js"
export const headers = { export const headers = {
@ -25,5 +26,9 @@ export default (url, options = {}) => {
if (options && options.headers) { if (options && options.headers) {
customOptions = { ...customOptions, headers: { ...options.headers, ...customOptions.headers } }; customOptions = { ...customOptions, headers: { ...options.headers, ...customOptions.headers } };
} }
if (RocketChat.controller) {
const { signal } = RocketChat.controller;
customOptions = { ...customOptions, signal };
}
return fetch(url, customOptions); return fetch(url, customOptions);
}; };

View File

@ -56,7 +56,7 @@ const Header = React.memo(({
onPress={onPress} onPress={onPress}
testID='rooms-list-header-server-dropdown-button' testID='rooms-list-header-server-dropdown-button'
style={styles.container} style={styles.container}
disabled={connecting || isFetching} // disabled={connecting || isFetching}
> >
<HeaderTitle connecting={connecting} isFetching={isFetching} theme={theme} /> <HeaderTitle connecting={connecting} isFetching={isFetching} theme={theme} />
<View style={styles.button}> <View style={styles.button}>

View File

@ -85,7 +85,7 @@
"react-native-responsive-ui": "^1.1.1", "react-native-responsive-ui": "^1.1.1",
"react-native-screens": "^2.0.0-alpha.3", "react-native-screens": "^2.0.0-alpha.3",
"react-native-scrollable-tab-view": "^1.0.0", "react-native-scrollable-tab-view": "^1.0.0",
"react-native-slowlog": "^1.0.2", "react-native-slowlog": "1.0.2",
"react-native-unimodules": "0.5.3", "react-native-unimodules": "0.5.3",
"react-native-vector-icons": "6.6.0", "react-native-vector-icons": "6.6.0",
"react-native-webview": "7.5.1", "react-native-webview": "7.5.1",

View File

@ -0,0 +1,83 @@
diff --git a/node_modules/@rocket.chat/sdk/lib/api/api.ts b/node_modules/@rocket.chat/sdk/lib/api/api.ts
index 17c2c2b..cb094e8 100644
--- a/node_modules/@rocket.chat/sdk/lib/api/api.ts
+++ b/node_modules/@rocket.chat/sdk/lib/api/api.ts
@@ -117,24 +117,31 @@ class Client implements IClient {
JSON.stringify(data)
}
+ getSignal (options?: any): AbortSignal {
+ return options && options.signal;
+ }
+
get (url: string, data: any, options?: any): Promise<any> {
return fetch(`${this.host}/api/v1/${encodeURI(url)}?${this.getParams(data)}`, {
method: 'GET',
- headers: this.getHeaders(options)
+ headers: this.getHeaders(options),
+ signal: this.getSignal(options)
}).then(this.handle)
}
post (url: string, data: any, options?: any): Promise<any> {
return fetch(`${this.host}/api/v1/${encodeURI(url)}`, {
method: 'POST',
body: this.getBody(data),
- headers: this.getHeaders(options)
+ headers: this.getHeaders(options),
+ signal: this.getSignal(options)
}).then(this.handle)
}
put (url: string, data: any, options?: any): Promise<any> {
return fetch(`${this.host}/api/v1/${encodeURI(url)}`, {
method: 'PUT',
body: this.getBody(data),
- headers: this.getHeaders(options)
+ headers: this.getHeaders(options),
+ signal: this.getSignal(options)
}).then(this.handle)
}
@@ -142,7 +149,8 @@ class Client implements IClient {
return fetch(`${this.host}/api/v1/${encodeURI(url)}`, {
method: 'DELETE',
body: this.getBody(data),
- headers: this.getHeaders(options)
+ headers: this.getHeaders(options),
+ signal: this.getSignal(options)
}).then(this.handle)
}
private async handle (r: any) {
@@ -176,11 +184,13 @@ export default class Api extends EventEmitter {
authToken: string,
result: ILoginResultAPI
} | null = null
+ controller: AbortController
constructor ({ client, host, logger = Logger }: any) {
super()
this.client = client || new Client({ host } as any)
this.logger = Logger
+ this.controller = new AbortController();
}
get username () {
@@ -212,6 +222,10 @@ export default class Api extends EventEmitter {
if (auth && !this.loggedIn()) {
throw new Error('')
}
+
+ const { signal } = this.controller;
+ options = { ...options, signal };
+
let result
switch (method) {
case 'GET': result = await this.client.get(endpoint, data, options); break
@@ -242,6 +256,8 @@ export default class Api extends EventEmitter {
/** Do a DELETE request to an API endpoint. */
del: IAPIRequest = (endpoint, data, auth, ignore, options = {}) => this.request('DELETE', endpoint, data, auth, ignore, options)
+ abort = (): void => this.controller.abort()
+
/** Check result data for success, allowing override to ignore some errors */
success (result: any, ignore?: RegExp) {
return (

View File

@ -10072,7 +10072,7 @@ react-native-scrollable-tab-view@^1.0.0:
prop-types "^15.6.0" prop-types "^15.6.0"
react-timer-mixin "^0.13.3" react-timer-mixin "^0.13.3"
react-native-slowlog@^1.0.2: react-native-slowlog@1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/react-native-slowlog/-/react-native-slowlog-1.0.2.tgz#5520979e3ef9d5273495d431ff3be34f02e35c89" resolved "https://registry.yarnpkg.com/react-native-slowlog/-/react-native-slowlog-1.0.2.tgz#5520979e3ef9d5273495d431ff3be34f02e35c89"
integrity sha1-VSCXnj751Sc0ldQx/zvjTwLjXIk= integrity sha1-VSCXnj751Sc0ldQx/zvjTwLjXIk=