From d669a6c73c8294523f15a48bfd9289a761e91ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Thu, 24 Aug 2017 15:09:53 +0200 Subject: [PATCH] [RN] Keep track of ongoing network requests Works only for XHR requests, which is the only network request mobile performs (WebRTC traffic aside). The fetch API is implemented on top of XHR, so that's covered too. Requests are kept in the redux store until they complete, at which point they are removed. --- react/features/app/components/App.native.js | 1 + .../mobile/net-interceptor/actionTypes.js | 11 +++ .../mobile/net-interceptor/functions.js | 75 +++++++++++++++++++ .../features/mobile/net-interceptor/index.js | 2 + .../mobile/net-interceptor/middleware.js | 25 +++++++ .../mobile/net-interceptor/reducer.js | 15 ++++ 6 files changed, 129 insertions(+) create mode 100644 react/features/mobile/net-interceptor/actionTypes.js create mode 100644 react/features/mobile/net-interceptor/functions.js create mode 100644 react/features/mobile/net-interceptor/index.js create mode 100644 react/features/mobile/net-interceptor/middleware.js create mode 100644 react/features/mobile/net-interceptor/reducer.js diff --git a/react/features/app/components/App.native.js b/react/features/app/components/App.native.js index 8c9d6e2a0..f58bc9b73 100644 --- a/react/features/app/components/App.native.js +++ b/react/features/app/components/App.native.js @@ -8,6 +8,7 @@ import '../../mobile/audio-mode'; import '../../mobile/background'; import '../../mobile/external-api'; import '../../mobile/full-screen'; +import '../../mobile/net-interceptor'; import '../../mobile/permissions'; import '../../mobile/proximity'; import '../../mobile/wake-lock'; diff --git a/react/features/mobile/net-interceptor/actionTypes.js b/react/features/mobile/net-interceptor/actionTypes.js new file mode 100644 index 000000000..1a91cd5d2 --- /dev/null +++ b/react/features/mobile/net-interceptor/actionTypes.js @@ -0,0 +1,11 @@ +/** + * The type of redux action to update the pending network requests mapping. + * + * { + * type: UPDATE_NETWORK_REQUESTS, + * requests: Object + * } + * + * @protected + */ +export const UPDATE_NETWORK_REQUESTS = Symbol('UPDATE_NETWORK_REQUESTS'); diff --git a/react/features/mobile/net-interceptor/functions.js b/react/features/mobile/net-interceptor/functions.js new file mode 100644 index 000000000..931ac73ee --- /dev/null +++ b/react/features/mobile/net-interceptor/functions.js @@ -0,0 +1,75 @@ +import _ from 'lodash'; +import XHRInterceptor from 'react-native/Libraries/Network/XHRInterceptor'; + +import { UPDATE_NETWORK_REQUESTS } from './actionTypes'; + +/** + * Global index for keeping track of XHR requests. + * @type {number} + */ +let reqIndex = 0; + +/** + * Starts intercepting network requests. Only XHR requests are supported at the + * moment. + * + * Ongoing request information is kept in redux, and it's removed as soon as a + * given request is complete. + * + * @param {Object} store - The redux store. + * @returns {void} + */ +export function startNetInterception({ dispatch, getState }) { + XHRInterceptor.setOpenCallback((method, url, xhr) => { + xhr._reqIndex = reqIndex++; + + const requests = getState()['features/net-interceptor'].requests || {}; + const newRequests = _.cloneDeep(requests); + const request = { + method, + url + }; + + newRequests[xhr._reqIndex] = request; + + dispatch({ + type: UPDATE_NETWORK_REQUESTS, + requests: newRequests + }); + }); + + XHRInterceptor.setResponseCallback((...args) => { + const xhr = args.slice(-1)[0]; + + if (typeof xhr._reqIndex === 'undefined') { + return; + } + + const requests = getState()['features/net-interceptor'].requests || {}; + const newRequests = _.cloneDeep(requests); + + delete newRequests[xhr._reqIndex]; + + dispatch({ + type: UPDATE_NETWORK_REQUESTS, + requests: newRequests + }); + }); + + XHRInterceptor.enableInterception(); +} + +/** + * Stops intercepting network requests. + * + * @param {Object} store - The redux store. + * @returns {void} + */ +export function stopNetInterception({ dispatch }) { + XHRInterceptor.disableInterception(); + + dispatch({ + type: UPDATE_NETWORK_REQUESTS, + requests: {} + }); +} diff --git a/react/features/mobile/net-interceptor/index.js b/react/features/mobile/net-interceptor/index.js new file mode 100644 index 000000000..200d25492 --- /dev/null +++ b/react/features/mobile/net-interceptor/index.js @@ -0,0 +1,2 @@ +import './middleware'; +import './reducer'; diff --git a/react/features/mobile/net-interceptor/middleware.js b/react/features/mobile/net-interceptor/middleware.js new file mode 100644 index 000000000..87e80c653 --- /dev/null +++ b/react/features/mobile/net-interceptor/middleware.js @@ -0,0 +1,25 @@ +import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../app'; +import { MiddlewareRegistry } from '../../base/redux'; + +import { startNetInterception, stopNetInterception } from './functions'; + +/** + * Middleware which captures app startup and conference actions in order to + * clear the image cache. + * + * @returns {Function} + */ +MiddlewareRegistry.register(store => next => action => { + const result = next(action); + + switch (action.type) { + case APP_WILL_MOUNT: + startNetInterception(store); + break; + case APP_WILL_UNMOUNT: + stopNetInterception(store); + break; + } + + return result; +}); diff --git a/react/features/mobile/net-interceptor/reducer.js b/react/features/mobile/net-interceptor/reducer.js new file mode 100644 index 000000000..0f9a4df22 --- /dev/null +++ b/react/features/mobile/net-interceptor/reducer.js @@ -0,0 +1,15 @@ +import { ReducerRegistry } from '../../base/redux'; + +import { UPDATE_NETWORK_REQUESTS } from './actionTypes'; + +ReducerRegistry.register('features/net-interceptor', (state = {}, action) => { + switch (action.type) { + case UPDATE_NETWORK_REQUESTS: + return { + ...state, + requests: action.requests + }; + } + + return state; +});