diff --git a/docs/apis/NetInfo.md b/docs/apis/NetInfo.md index 479e3391..67312985 100644 --- a/docs/apis/NetInfo.md +++ b/docs/apis/NetInfo.md @@ -1,9 +1,19 @@ # NetInfo `NetInfo` asynchronously determines the online/offline status of the -application. +application and depending on browser support via +[NetworkInformation API](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation), +additional information about the connection. -Connection types: +EffectiveConnectionType: + +* `4g` +* `3g` +* `2g` +* `slow-2g` +* `unknown` + +ConnectionType: * `bluetooth` - The user agent is using a Bluetooth connection. * `cellular` - The user agent is using a cellular connection (e.g., EDGE, HSPA, LTE, etc.). @@ -18,12 +28,12 @@ Connection types: ## Methods Note that support for retrieving the connection type depends upon browswer -support (and is limited to mobile browsers). It will default to `unknown` when +support and the current platform. It will default to `unknown` when support is missing. static **addEventListener**(eventName: ChangeEventName, handler: Function) -static **fetch**(): Promise +static **getConnectionInfo**(): Promise static **removeEventListener**(eventName: ChangeEventName, handler: Function) @@ -36,7 +46,7 @@ internet connectivity. Use this if you are only interested with whether the devi **isConnected.addEventListener**(eventName: ChangeEventName, handler: Function) -**isConnected.fetch**(): Promise +**isConnected.getConnectionInfo**(): Promise **isConnected.removeEventListener**(eventName: ChangeEventName, handler: Function) @@ -45,24 +55,25 @@ internet connectivity. Use this if you are only interested with whether the devi Fetching the connection type: ```js -NetInfo.fetch().then((connectionType) => { - console.log('Connection type:', connectionType); +NetInfo.getConnectionInfo().then(({ effectiveType, type }) => { + console.log('Effective connection type:', effectiveType); + console.log('Connection type:', type); }); ``` Subscribing to changes in the connection type: ```js -const handleConnectivityTypeChange = (connectionType) => { - console.log('Current connection type:', connectionType); +const handleConnectivityTypeChange = ({ effectiveType }) => { + console.log('Current effective connection type:', effectiveType); } -NetInfo.addEventListener('change', handleConnectivityTypeChange); +NetInfo.addEventListener('connectionChange', handleConnectivityTypeChange); ``` Fetching the connection status: ```js -NetInfo.isConnected.fetch().then((isConnected) => { +NetInfo.isConnected.getConnectionInfo().then((isConnected) => { console.log('Connection status:', (isConnected ? 'online' : 'offline')); }); ``` @@ -73,5 +84,5 @@ Subscribing to changes in the connection status: const handleConnectivityStatusChange = (isConnected) => { console.log('Current connection status:', (isConnected ? 'online' : 'offline')); } -NetInfo.isConnected.addEventListener('change', handleConnectivityStatusChange); +NetInfo.isConnected.addEventListener('connectionChange', handleConnectivityStatusChange); ``` diff --git a/docs/storybook/2-apis/NetInfo/NetInfoScreen.js b/docs/storybook/2-apis/NetInfo/NetInfoScreen.js index 4d9be16a..76d9df36 100644 --- a/docs/storybook/2-apis/NetInfo/NetInfoScreen.js +++ b/docs/storybook/2-apis/NetInfo/NetInfoScreen.js @@ -8,61 +8,94 @@ import UIExplorer, { Code, Description, DocItem, + ExternalLink, Section, - storiesOf, - TextList + storiesOf } from '../../ui-explorer'; const NetInfoScreen = () => ( - NetInfo asynchronously determines the online/offline status of the application. + NetInfo asynchronously determines the online/offline status and additional connection + information (where available) of the application. - Note that support for retrieving the connection type depends upon browser support (and is - limited to mobile browsers). It will default to unknown when support is - missing. + Note that connection type information is limited to how well the browser supports the{' '} + + NetworkInformation API + . Connection types will be unknown when support is missing. +
+ + One of slow-2g, 2g, 3g, 4g,{' '} + unknown. + + } + name="ConnectionType" + /> + + One of bluebooth, cellular, ethernet,{' '} + mixed, mixed, none, other,{' '} + unknown, wifi, wimax + + } + name="EffectiveConnectionType" + /> + + {`{ + effectiveType: EffectiveConnectionType; + type: ConnectionType; + downlink?: number; + downlinkMax?: number; + rtt?: number; +}`} + } + name="ConnectionEventType" + /> +
+
- Invokes the listener whenever network status changes. The listener receives one of the - following connectivity types (from the DOM connection API): - , - - ]} + Adds an event handler. The connectionChange event fires when the network + status changes. The argument to the event handler is an object of type{' '} + ConnectionEventType. + + } example={{ - code: "NetInfo.addEventListener('change', (connectionType) => {})" + code: `NetInfo.addEventListener('connectionChange', ({ effectiveType, type }) => { + console.log('Effective connection type:', effectiveType); + console.log('Connection type:', type); +})` }} name="static addEventListener" typeInfo="(eventName, handler) => void" /> + Returns a promise that resolves with an object of type ConnectionEventType. + + } example={{ - code: `NetInfo.fetch().then((connectionType) => { - console.log('Connection type:', connectionType); + code: `NetInfo.getConnectionInfo().then(({ effectiveType, type }) => { + console.log('Effective connection type:', effectiveType); + console.log('Connection type:', type); });` }} - name="static fetch" - typeInfo="() => Promise" + name="static getConnectionInfo" + typeInfo="() => Promise" /> ( { + code: `NetInfo.isConnected.getConnectionInfo().then((isConnected) => { console.log('Connection status:', (isConnected ? 'online' : 'offline')); });` }} diff --git a/src/apis/NetInfo/__tests__/index-test.js b/src/apis/NetInfo/__tests__/index-test.js index c25c02b7..f0eddade 100644 --- a/src/apis/NetInfo/__tests__/index-test.js +++ b/src/apis/NetInfo/__tests__/index-test.js @@ -3,31 +3,44 @@ import NetInfo from '..'; describe('apis/NetInfo', () => { + describe('getConnectionInfo', () => { + test('fills out basic fields', done => { + NetInfo.getConnectionInfo().then(result => { + expect(result.effectiveType).toBeDefined(); + expect(result.type).toBeDefined(); + done(); + }); + }); + }); + describe('isConnected', () => { const handler = () => {}; afterEach(() => { try { - NetInfo.isConnected.removeEventListener('change', handler); + NetInfo.isConnected.removeEventListener('connectionChange', handler); } catch (e) {} }); describe('addEventListener', () => { test('throws if the provided "eventType" is not supported', () => { expect(() => NetInfo.isConnected.addEventListener('foo', handler)).toThrow(); - expect(() => NetInfo.isConnected.addEventListener('change', handler)).not.toThrow(); + expect(() => + NetInfo.isConnected.addEventListener('connectionChange', handler) + ).not.toThrow(); }); }); describe('removeEventListener', () => { test('throws if the handler is not registered', () => { - expect(() => NetInfo.isConnected.removeEventListener('change', handler)).toThrow; + expect(() => NetInfo.isConnected.removeEventListener('connectionChange', handler)).toThrow; }); test('throws if the provided "eventType" is not supported', () => { - NetInfo.isConnected.addEventListener('change', handler); + NetInfo.isConnected.addEventListener('connectionChange', handler); expect(() => NetInfo.isConnected.removeEventListener('foo', handler)).toThrow; - expect(() => NetInfo.isConnected.removeEventListener('change', handler)).not.toThrow; + expect(() => NetInfo.isConnected.removeEventListener('connectionChange', handler)).not + .toThrow; }); }); }); diff --git a/src/apis/NetInfo/index.js b/src/apis/NetInfo/index.js index 6f73f457..0877989c 100644 --- a/src/apis/NetInfo/index.js +++ b/src/apis/NetInfo/index.js @@ -20,7 +20,27 @@ const connection = window.navigator.mozConnection || window.navigator.webkitConnection); -const eventTypes = ['change']; +// Prevent the underlying event handlers from leaking and include additional +// properties available in browsers +const getConnectionInfoObject = () => { + const result = {}; + if (!connection) { + return result; + } + for (const prop in connection) { + if (typeof connection[prop] !== 'function') { + result[prop] = connection[prop]; + } + } + return result; +}; + +// Map React Native events to browser equivalents +const eventTypesMap = { + change: 'change', + connectionChange: 'change' +}; +const eventTypes = Object.keys(eventTypesMap); const connectionListeners = []; @@ -31,6 +51,9 @@ const connectionListeners = []; 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.' @@ -40,21 +63,25 @@ const NetInfo = { }; } - connection.addEventListener(type, handler); + connection.addEventListener(eventTypesMap[type], handler); return { - remove: () => NetInfo.removeEventListener(type, handler) + remove: () => NetInfo.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.'); + } if (!connection) { return; } - connection.removeEventListener(type, handler); + connection.removeEventListener(eventTypesMap[type], handler); }, fetch(): Promise { + console.warn('`fetch` is deprecated. Use `getConnectionInfo` instead.'); return new Promise((resolve, reject) => { try { resolve(connection.type); @@ -64,6 +91,16 @@ const NetInfo = { }); }, + getConnectionInfo(): Promise { + return new Promise((resolve, reject) => { + resolve({ + effectiveType: 'unknown', + type: 'unknown', + ...getConnectionInfoObject() + }); + }); + }, + isConnected: { addEventListener(type: string, handler: Function): { remove: () => void } { invariant( @@ -71,6 +108,10 @@ const NetInfo = { '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]); @@ -79,7 +120,7 @@ const NetInfo = { window.addEventListener('offline', offlineCallback, false); return { - remove: () => NetInfo.isConnected.removeEventListener(type, handler) + remove: () => NetInfo.isConnected.removeEventListener(eventTypesMap[type], handler) }; }, @@ -89,6 +130,9 @@ const NetInfo = { '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( @@ -103,7 +147,12 @@ const NetInfo = { connectionListeners.splice(listenerIndex, 1); }, - fetch(): Promise { + fetch(): Promise { + console.warn('`fetch` is deprecated. Use `getConnectionInfo` instead.'); + return NetInfo.isConnected.getConnectionInfo(); + }, + + getConnectionInfo(): Promise { return new Promise((resolve, reject) => { try { resolve(window.navigator.onLine);