-
Notifications
You must be signed in to change notification settings - Fork 3.1k
/
Copy pathNetworkConnection.js
135 lines (116 loc) · 4.94 KB
/
NetworkConnection.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import _ from 'underscore';
import NetInfo from '@react-native-community/netinfo';
import AppStateMonitor from './AppStateMonitor';
import promiseAllSettled from './promiseAllSettled';
import Log from './Log';
import * as NetworkActions from './actions/Network';
import * as NetworkEvents from './Network/NetworkEvents';
import CONFIG from '../CONFIG';
import CONST from '../CONST';
// NetInfo.addEventListener() returns a function used to unsubscribe the
// listener so we must create a reference to it and call it in stopListeningForReconnect()
let unsubscribeFromNetInfo;
let unsubscribeFromAppState;
let isOffline = false;
let hasPendingNetworkCheck = true;
// Holds all of the callbacks that need to be triggered when the network reconnects
const reconnectionCallbacks = [];
/**
* Loop over all reconnection callbacks and fire each one
*/
const triggerReconnectionCallbacks = _.throttle((reason) => {
Log.info(`[NetworkConnection] Firing reconnection callbacks because ${reason}`);
NetworkActions.setIsLoadingAfterReconnect(true);
promiseAllSettled(_.map(reconnectionCallbacks, callback => callback()))
.then(() => NetworkActions.setIsLoadingAfterReconnect(false));
}, 5000, {trailing: false});
/**
* Called when the offline status of the app changes and if the network is "reconnecting" (going from offline to online)
* then all of the reconnection callbacks are triggered
*
* @param {Boolean} isCurrentlyOffline
*/
function setOfflineStatus(isCurrentlyOffline) {
NetworkActions.setIsOffline(isCurrentlyOffline);
// When reconnecting, ie, going from offline to online, all the reconnection callbacks
// are triggered (this is usually Actions that need to re-download data from the server)
if (isOffline && !isCurrentlyOffline) {
triggerReconnectionCallbacks('offline status changed');
}
isOffline = isCurrentlyOffline;
}
/**
* Set up the event listener for NetInfo to tell whether the user has
* internet connectivity or not. This is more reliable than the Pusher
* `disconnected` event which takes about 10-15 seconds to emit.
*/
function subscribeToNetInfo() {
// Note: We are disabling the configuration for NetInfo when using the local web API since requests can get stuck in a 'Pending' state and are not reliable indicators for "offline".
// If you need to test the "recheck" feature then switch to the production API proxy server.
if (!CONFIG.IS_USING_LOCAL_WEB) {
// Calling NetInfo.configure (re)checks current state. We use it to force a recheck whenever we (re)subscribe
NetInfo.configure({
// By default, NetInfo uses `/` for `reachabilityUrl`
// When App is served locally (or from Electron) this address is always reachable - even offline
// Using the API url ensures reachability is tested over internet
reachabilityUrl: `${CONFIG.EXPENSIFY.URL_API_ROOT}api`,
reachabilityTest: response => Promise.resolve(response.status === 200),
// If a check is taking longer than this time we're considered offline
reachabilityRequestTimeout: CONST.NETWORK.MAX_PENDING_TIME_MS,
});
}
// Subscribe to the state change event via NetInfo so we can update
// whether a user has internet connectivity or not.
unsubscribeFromNetInfo = NetInfo.addEventListener((state) => {
Log.info('[NetworkConnection] NetInfo state change', false, state);
setOfflineStatus(state.isInternetReachable === false);
// When internet state is indeterminate a check is already running. Set the flag to prevent duplicate checks
hasPendingNetworkCheck = state.isInternetReachable === null;
});
}
function listenForReconnect() {
Log.info('[NetworkConnection] listenForReconnect called');
unsubscribeFromAppState = AppStateMonitor.addBecameActiveListener(() => {
triggerReconnectionCallbacks('app became active');
});
subscribeToNetInfo();
}
/**
* Tear down the event listeners when we are finished with them.
*/
function stopListeningForReconnect() {
Log.info('[NetworkConnection] stopListeningForReconnect called');
if (unsubscribeFromNetInfo) {
unsubscribeFromNetInfo();
unsubscribeFromNetInfo = undefined;
}
if (unsubscribeFromAppState) {
unsubscribeFromAppState();
unsubscribeFromAppState = undefined;
}
}
/**
* Register callback to fire when we reconnect
*
* @param {Function} callback - must return a Promise
*/
function onReconnect(callback) {
reconnectionCallbacks.push(callback);
}
function recheckNetworkConnection() {
if (hasPendingNetworkCheck) {
return;
}
Log.info('[NetworkConnection] recheck NetInfo');
hasPendingNetworkCheck = true;
unsubscribeFromNetInfo();
subscribeToNetInfo();
}
NetworkEvents.onRecheckNeeded(recheckNetworkConnection);
export default {
setOfflineStatus,
listenForReconnect,
stopListeningForReconnect,
onReconnect,
triggerReconnectionCallbacks,
};