/**
* Copyright (c) Nicolas Gallagher.
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
import findIndex from 'array-find-index';
import invariant from 'fbjs/lib/invariant';
const connection =
ExecutionEnvironment.canUseDOM &&
(window.navigator.connection ||
window.navigator.mozConnection ||
window.navigator.webkitConnection);
// Prevent the underlying event handlers from leaking and include additional
// properties available in browsers
const getConnectionInfoObject = () => {
const result = {
effectiveType: 'unknown',
type: 'unknown'
};
if (!connection) {
return result;
}
for (const prop in connection) {
const value = connection[prop];
if (typeof value !== 'function' && value != null) {
result[prop] = value;
}
}
return result;
};
// Map React Native events to browser equivalents
const eventTypesMap = {
change: 'change',
connectionChange: 'change'
};
const eventTypes = Object.keys(eventTypesMap);
const connectionListeners = [];
const netInfoListeners = [];
/**
* Navigator online: https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine
* Network Connection API: https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation
*/
const NetInfo = {
addEventListener(type: string, handler: Function): { remove: () => void } {
invariant(eventTypes.indexOf(type) !== -1, 'Trying to subscribe to unknown event: "%s"', type);
if (type === 'change') {
console.warn('Listening to event `change` is deprecated. Use `connectionChange` instead.');
}
if (!connection) {
console.error(
'Network Connection API is not supported. Not listening for connection type changes.'
);
return {
remove: () => {}
};
}
const wrappedHandler = () => handler(getConnectionInfoObject());
netInfoListeners.push([handler, wrappedHandler]);
connection.addEventListener(eventTypesMap[type], wrappedHandler);
return {
remove: () => NetInfo.removeEventListener(eventTypesMap[type], handler)
};
},
removeEventListener(type: string, handler: Function): void {
invariant(
eventTypes.indexOf(type) !== -1,
'Trying to unsubscribe from unknown event: "%s"',
type
);
if (type === 'change') {
console.warn('Listening to event `change` is deprecated. Use `connectionChange` instead.');
}
const listenerIndex = findIndex(netInfoListeners, pair => pair[0] === handler);
invariant(listenerIndex !== -1, 'Trying to remove NetInfo listener for unregistered handler');
const [, wrappedHandler] = netInfoListeners[listenerIndex];
connection.removeEventListener(eventTypesMap[type], wrappedHandler);
netInfoListeners.splice(listenerIndex, 1);
},
fetch(): Promise<any> {
console.warn('`fetch` is deprecated. Use `getConnectionInfo` instead.');
return new Promise((resolve, reject) => {
try {
resolve(connection.type);
} catch (err) {
resolve('unknown');
}
});
},
getConnectionInfo(): Promise<Object> {
return new Promise((resolve, reject) => {
resolve(getConnectionInfoObject());
});
},
isConnected: {
addEventListener(type: string, handler: Function): { remove: () => void } {
invariant(
eventTypes.indexOf(type) !== -1,
'Trying to subscribe to unknown event: "%s"',
type
);
if (type === 'change') {
console.warn('Listening to event `change` is deprecated. Use `connectionChange` instead.');
}
const onlineCallback = () => handler(true);
const offlineCallback = () => handler(false);
connectionListeners.push([handler, onlineCallback, offlineCallback]);
window.addEventListener('online', onlineCallback, false);
window.addEventListener('offline', offlineCallback, false);
return {
remove: () => NetInfo.isConnected.removeEventListener(eventTypesMap[type], handler)
};
},
removeEventListener(type: string, handler: Function): void {
invariant(
eventTypes.indexOf(type) !== -1,
'Trying to subscribe to unknown event: "%s"',
type
);
if (type === 'change') {
console.warn('Listening to event `change` is deprecated. Use `connectionChange` instead.');
}
const listenerIndex = findIndex(connectionListeners, pair => pair[0] === handler);
invariant(
listenerIndex !== -1,
'Trying to remove NetInfo connection listener for unregistered handler'
);
const [, onlineCallback, offlineCallback] = connectionListeners[listenerIndex];
window.removeEventListener('online', onlineCallback, false);
window.removeEventListener('offline', offlineCallback, false);
connectionListeners.splice(listenerIndex, 1);
},
fetch(): Promise<boolean> {
return new Promise((resolve, reject) => {
try {
resolve(window.navigator.onLine);
} catch (err) {
resolve(true);
}
});
}
}
};
export default NetInfo;