/** * 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;