-
Notifications
You must be signed in to change notification settings - Fork 2.9k
/
NetworkConnection.js
162 lines (145 loc) · 5.54 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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import _ from 'underscore';
import Onyx from 'react-native-onyx';
import NetInfo from '@react-native-community/netinfo';
import AppStateMonitor from './AppStateMonitor';
import Log from './Log';
import * as NetworkActions from './actions/Network';
import CONFIG from '../CONFIG';
import CONST from '../CONST';
import ONYXKEYS from '../ONYXKEYS';
let isOffline = false;
let hasPendingNetworkCheck = false;
// Holds all of the callbacks that need to be triggered when the network reconnects
let callbackID = 0;
const reconnectionCallbacks = {};
/**
* Loop over all reconnection callbacks and fire each one
*/
const triggerReconnectionCallbacks = _.throttle(
(reason) => {
Log.info(`[NetworkConnection] Firing reconnection callbacks because ${reason}`);
_.each(reconnectionCallbacks, (callback) => callback());
},
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;
}
// Update the offline status in response to changes in shouldForceOffline
let shouldForceOffline = false;
Onyx.connect({
key: ONYXKEYS.NETWORK,
callback: (network) => {
if (!network) {
return;
}
const currentShouldForceOffline = Boolean(network.shouldForceOffline);
if (currentShouldForceOffline === shouldForceOffline) {
return;
}
shouldForceOffline = currentShouldForceOffline;
if (shouldForceOffline) {
setOfflineStatus(true);
} else {
// If we are no longer forcing offline fetch the NetInfo to set isOffline appropriately
NetInfo.fetch().then((state) => setOfflineStatus(state.isInternetReachable === false));
}
},
});
/**
* 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.DEFAULT_API_ROOT}api?command=Ping`,
reachabilityMethod: 'GET',
reachabilityTest: (response) => {
if (!response.ok) {
return Promise.resolve(false);
}
return response
.json()
.then((json) => Promise.resolve(json.jsonCode === 200))
.catch(() => Promise.resolve(false));
},
// 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.
NetInfo.addEventListener((state) => {
Log.info('[NetworkConnection] NetInfo state change', false, state);
if (shouldForceOffline) {
Log.info('[NetworkConnection] Not setting offline status because shouldForceOffline = true');
return;
}
setOfflineStatus(state.isInternetReachable === false);
});
}
function listenForReconnect() {
Log.info('[NetworkConnection] listenForReconnect called');
AppStateMonitor.addBecameActiveListener(() => {
triggerReconnectionCallbacks('app became active');
});
}
/**
* Register callback to fire when we reconnect
*
* @param {Function} callback - must return a Promise
* @returns {Function} unsubscribe method
*/
function onReconnect(callback) {
const currentID = callbackID;
callbackID++;
reconnectionCallbacks[currentID] = callback;
return () => delete reconnectionCallbacks[currentID];
}
/**
* Delete all queued reconnection callbacks
*/
function clearReconnectionCallbacks() {
_.each(_.keys(reconnectionCallbacks), (key) => delete reconnectionCallbacks[key]);
}
/**
* Refresh NetInfo state.
*/
function recheckNetworkConnection() {
if (hasPendingNetworkCheck) {
return;
}
Log.info('[NetworkConnection] recheck NetInfo');
hasPendingNetworkCheck = true;
NetInfo.refresh().finally(() => (hasPendingNetworkCheck = false));
}
export default {
clearReconnectionCallbacks,
setOfflineStatus,
listenForReconnect,
onReconnect,
triggerReconnectionCallbacks,
recheckNetworkConnection,
subscribeToNetInfo,
};