diff --git a/src/components/TestToolMenu.js b/src/components/TestToolMenu.js
index 908c0a8db8a1..10f12bac71d0 100644
--- a/src/components/TestToolMenu.js
+++ b/src/components/TestToolMenu.js
@@ -47,10 +47,18 @@ const TestToolMenu = props => (
/>
+ {/* When toggled the app will be forced offline. */}
+
+ Network.setShouldForceOffline(!props.network.shouldForceOffline)}
+ />
+
+
{/* When toggled all network requests will fail. */}
Network.setShouldFailAllRequests(!props.network.shouldFailAllRequests)}
/>
diff --git a/src/components/networkPropTypes.js b/src/components/networkPropTypes.js
index ecfcc3b601cb..40cbf990d81f 100644
--- a/src/components/networkPropTypes.js
+++ b/src/components/networkPropTypes.js
@@ -4,6 +4,9 @@ export default PropTypes.shape({
/** Is the network currently offline or not */
isOffline: PropTypes.bool,
+ /** Should the network be forced offline */
+ shouldForceOffline: PropTypes.bool,
+
/** Whether we should fail all network requests */
shouldFailAllRequests: PropTypes.bool,
});
diff --git a/src/libs/HttpUtils.js b/src/libs/HttpUtils.js
index 4c8fe2ee6b30..60971fe1b0b3 100644
--- a/src/libs/HttpUtils.js
+++ b/src/libs/HttpUtils.js
@@ -13,9 +13,16 @@ Onyx.connect({
});
let shouldFailAllRequests = false;
+let shouldForceOffline = false;
Onyx.connect({
key: ONYXKEYS.NETWORK,
- callback: val => shouldFailAllRequests = (val && _.isBoolean(val.shouldFailAllRequests)) ? val.shouldFailAllRequests : false,
+ callback: (network) => {
+ if (!network) {
+ return;
+ }
+ shouldFailAllRequests = Boolean(network.shouldFailAllRequests);
+ shouldForceOffline = Boolean(network.shouldForceOffline);
+ },
});
// We use the AbortController API to terminate pending request in `cancelPendingRequests`
@@ -40,7 +47,7 @@ function processHTTPRequest(url, method = 'get', body = null, canCancel = true)
})
.then((response) => {
// Test mode where all requests will succeed in the server, but fail to return a response
- if (shouldFailAllRequests) {
+ if (shouldFailAllRequests || shouldForceOffline) {
throw new HttpsError({
message: CONST.ERROR.FAILED_TO_FETCH,
});
diff --git a/src/libs/Network/NetworkStore.js b/src/libs/Network/NetworkStore.js
index 67b57f966974..6d4a9dd8856f 100644
--- a/src/libs/Network/NetworkStore.js
+++ b/src/libs/Network/NetworkStore.js
@@ -80,7 +80,7 @@ Onyx.connect({
triggerReconnectCallback();
}
- offline = network.isOffline;
+ offline = Boolean(network.shouldForceOffline) || network.isOffline;
},
});
diff --git a/src/libs/NetworkConnection.js b/src/libs/NetworkConnection.js
index 972c3dcd5179..d6c7932f2a47 100644
--- a/src/libs/NetworkConnection.js
+++ b/src/libs/NetworkConnection.js
@@ -1,10 +1,12 @@
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;
@@ -39,6 +41,29 @@ function setOfflineStatus(isCurrentlyOffline) {
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
@@ -65,6 +90,10 @@ function subscribeToNetInfo() {
// 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);
});
}
diff --git a/src/libs/Pusher/pusher.js b/src/libs/Pusher/pusher.js
index 5fd1e7fa6d26..2483a0c5e3f3 100644
--- a/src/libs/Pusher/pusher.js
+++ b/src/libs/Pusher/pusher.js
@@ -1,8 +1,21 @@
+import Onyx from 'react-native-onyx';
import _ from 'underscore';
+import ONYXKEYS from '../../ONYXKEYS';
import Pusher from './library';
import TYPE from './EventType';
import Log from '../Log';
+let shouldForceOffline = false;
+Onyx.connect({
+ key: ONYXKEYS.NETWORK,
+ callback: (network) => {
+ if (!network) {
+ return;
+ }
+ shouldForceOffline = Boolean(network.shouldForceOffline);
+ },
+});
+
let socket;
const socketEventCallbacks = [];
let customAuthorizer;
@@ -112,6 +125,11 @@ function bindEventToChannel(channel, eventName, eventCallback = () => {}) {
const chunkedDataEvents = {};
const callback = (eventData) => {
+ if (shouldForceOffline) {
+ Log.info('[Pusher] Ignoring a Push event because shouldForceOffline = true');
+ return;
+ }
+
let data;
try {
data = _.isObject(eventData) ? eventData : JSON.parse(eventData);
diff --git a/src/libs/actions/Network.js b/src/libs/actions/Network.js
index 54d732c96e9a..43f6303d20db 100644
--- a/src/libs/actions/Network.js
+++ b/src/libs/actions/Network.js
@@ -8,6 +8,14 @@ function setIsOffline(isOffline) {
Onyx.merge(ONYXKEYS.NETWORK, {isOffline});
}
+/**
+ *
+ * @param {Boolean} shouldForceOffline
+ */
+function setShouldForceOffline(shouldForceOffline) {
+ Onyx.merge(ONYXKEYS.NETWORK, {shouldForceOffline});
+}
+
/**
* Test tool that will fail all network requests when enabled
* @param {Boolean} shouldFailAllRequests
@@ -18,5 +26,6 @@ function setShouldFailAllRequests(shouldFailAllRequests) {
export {
setIsOffline,
+ setShouldForceOffline,
setShouldFailAllRequests,
};
diff --git a/src/libs/actions/SignInRedirect.js b/src/libs/actions/SignInRedirect.js
index d221ba62f6be..9dc63952b5dd 100644
--- a/src/libs/actions/SignInRedirect.js
+++ b/src/libs/actions/SignInRedirect.js
@@ -19,9 +19,16 @@ Onyx.connect({
});
let currentIsOffline;
+let currentShouldForceOffline;
Onyx.connect({
key: ONYXKEYS.NETWORK,
- callback: val => currentIsOffline = val.isOffline,
+ callback: (network) => {
+ if (!network) {
+ return;
+ }
+ currentIsOffline = network.isOffline;
+ currentShouldForceOffline = Boolean(network.shouldForceOffline);
+ },
});
/**
@@ -31,6 +38,7 @@ function clearStorageAndRedirect(errorMessage) {
const activeClients = currentActiveClients;
const preferredLocale = currentPreferredLocale;
const isOffline = currentIsOffline;
+ const shouldForceOffline = currentShouldForceOffline;
// Clearing storage discards the authToken. This causes a redirect to the SignIn screen
Onyx.clear()
@@ -41,7 +49,10 @@ function clearStorageAndRedirect(errorMessage) {
if (activeClients && activeClients.length > 0) {
Onyx.set(ONYXKEYS.ACTIVE_CLIENTS, activeClients);
}
- if (isOffline) {
+
+ // After signing out, set ourselves as offline if we were offline before logging out and we are not forcing it.
+ // If we are forcing offline, ignore it while signed out, otherwise it would require a refresh because there's no way to toggle the switch to go back online while signed out.
+ if (isOffline && !shouldForceOffline) {
Onyx.set(ONYXKEYS.NETWORK, {isOffline});
}