UNPKG

7.2 kBPlain TextView Raw
1/**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @format
8 */
9
10import {
11 NetInfoNativeModule,
12 DEVICE_CONNECTIVITY_EVENT,
13 NetInfoNativeModuleState,
14} from './privateTypes';
15import {
16 NetInfoState,
17 NetInfoStateType,
18 NetInfoUnknownState,
19 NetInfoNoConnectionState,
20 NetInfoCellularState,
21 NetInfoBluetoothState,
22 NetInfoEthernetState,
23 NetInfoWifiState,
24 NetInfoWimaxState,
25 NetInfoOtherState,
26 NetInfoCellularGeneration,
27} from './types';
28
29// See https://wicg.github.io/netinfo/#dom-connectiontype
30type ConnectionType =
31 | 'bluetooth'
32 | 'cellular'
33 | 'ethernet'
34 | 'mixed'
35 | 'none'
36 | 'other'
37 | 'unknown'
38 | 'wifi'
39 | 'wimax';
40
41// See https://wicg.github.io/netinfo/#dom-effectiveconnectiontype
42type ConnectionEffectiveType = '2g' | '3g' | '4g' | 'slow-2g';
43
44// https://wicg.github.io/netinfo/#dom-networkinformation-savedata
45type ConnectionSaveData = boolean;
46
47interface Events {
48 change: Event;
49}
50
51interface Connection {
52 type: ConnectionType;
53 effectiveType: ConnectionEffectiveType;
54 saveData: ConnectionSaveData;
55 addEventListener<K extends keyof Events>(
56 type: K,
57 listener: (event: Events[K]) => void,
58 ): void;
59 removeEventListener<K extends keyof Events>(
60 type: K,
61 listener: (event: Events[K]) => void,
62 ): void;
63}
64
65// Create (optional) connection APIs on navigator
66declare global {
67 interface Navigator {
68 connection?: Connection;
69 mozConnection?: Connection;
70 webkitConnection?: Connection;
71 }
72}
73
74// Check if the browser supports the connection API
75const connection =
76 window.navigator.connection ||
77 window.navigator.mozConnection ||
78 window.navigator.webkitConnection;
79
80// Map browser types to native types
81const typeMapping: Record<ConnectionType, NetInfoStateType> = {
82 bluetooth: NetInfoStateType.bluetooth,
83 cellular: NetInfoStateType.cellular,
84 ethernet: NetInfoStateType.ethernet,
85 none: NetInfoStateType.none,
86 other: NetInfoStateType.other,
87 unknown: NetInfoStateType.unknown,
88 wifi: NetInfoStateType.wifi,
89 wimax: NetInfoStateType.wimax,
90 mixed: NetInfoStateType.other,
91};
92const effectiveTypeMapping: Record<
93 ConnectionEffectiveType,
94 NetInfoCellularGeneration
95> = {
96 '2g': NetInfoCellularGeneration['2g'],
97 '3g': NetInfoCellularGeneration['3g'],
98 '4g': NetInfoCellularGeneration['4g'],
99 'slow-2g': NetInfoCellularGeneration['2g'],
100};
101
102// Determine current state of connection
103const getCurrentState = (
104 _requestedInterface?: string,
105): Pick<NetInfoState, Exclude<keyof NetInfoState, 'isInternetReachable'>> => {
106 const isConnected = navigator.onLine;
107 const baseState = {
108 isInternetReachable: null,
109 };
110
111 // If we don't have a connection object, we return minimal information
112 if (!connection) {
113 if (isConnected) {
114 const state: NetInfoOtherState = {
115 ...baseState,
116 isConnected: true,
117 type: NetInfoStateType.other,
118 details: {
119 isConnectionExpensive: false,
120 },
121 };
122 return state;
123 }
124
125 const state: NetInfoNoConnectionState = {
126 ...baseState,
127 isConnected: false,
128 isInternetReachable: false,
129 type: NetInfoStateType.none,
130 details: null,
131 };
132 return state;
133 }
134
135 // Otherwise try to return detailed information
136 const isConnectionExpensive = connection.saveData;
137 const type: NetInfoStateType = connection.type
138 ? typeMapping[connection.type]
139 : isConnected
140 ? NetInfoStateType.other
141 : NetInfoStateType.unknown;
142
143 if (type === NetInfoStateType.bluetooth) {
144 const state: NetInfoBluetoothState = {
145 ...baseState,
146 isConnected: true,
147 type,
148 details: {
149 isConnectionExpensive,
150 },
151 };
152 return state;
153 } else if (type === NetInfoStateType.cellular) {
154 const state: NetInfoCellularState = {
155 ...baseState,
156 isConnected: true,
157 type,
158 details: {
159 isConnectionExpensive,
160 cellularGeneration:
161 effectiveTypeMapping[connection.effectiveType] || null,
162 carrier: null,
163 },
164 };
165 return state;
166 } else if (type === NetInfoStateType.ethernet) {
167 const state: NetInfoEthernetState = {
168 ...baseState,
169 isConnected: true,
170 type,
171 details: {
172 isConnectionExpensive,
173 ipAddress: null,
174 subnet: null,
175 },
176 };
177 return state;
178 } else if (type === NetInfoStateType.wifi) {
179 const state: NetInfoWifiState = {
180 ...baseState,
181 isConnected: true,
182 type,
183 details: {
184 isConnectionExpensive,
185 ssid: null,
186 bssid: null,
187 strength: null,
188 ipAddress: null,
189 subnet: null,
190 frequency: null,
191 },
192 };
193 return state;
194 } else if (type === NetInfoStateType.wimax) {
195 const state: NetInfoWimaxState = {
196 ...baseState,
197 isConnected: true,
198 type,
199 details: {
200 isConnectionExpensive,
201 },
202 };
203 return state;
204 } else if (type === NetInfoStateType.none) {
205 const state: NetInfoNoConnectionState = {
206 ...baseState,
207 isConnected: false,
208 isInternetReachable: false,
209 type,
210 details: null,
211 };
212 return state;
213 } else if (type === NetInfoStateType.unknown) {
214 const state: NetInfoUnknownState = {
215 ...baseState,
216 isConnected: false,
217 isInternetReachable: false,
218 type,
219 details: null,
220 };
221 return state;
222 }
223
224 const state: NetInfoOtherState = {
225 ...baseState,
226 isConnected: true,
227 type: NetInfoStateType.other,
228 details: {
229 isConnectionExpensive,
230 },
231 };
232 return state;
233};
234
235const handlers: ((state: NetInfoNativeModuleState) => void)[] = [];
236const nativeHandlers: (() => void)[] = [];
237
238const RNCNetInfo: NetInfoNativeModule = {
239 addListener(type, handler): void {
240 switch (type) {
241 case DEVICE_CONNECTIVITY_EVENT: {
242 const nativeHandler = (): void => {
243 handler(getCurrentState());
244 };
245
246 if (connection) {
247 connection.addEventListener('change', nativeHandler);
248 } else {
249 window.addEventListener('online', nativeHandler, false);
250 window.addEventListener('offline', nativeHandler, false);
251 }
252
253 // Remember handlers
254 handlers.push(handler);
255 nativeHandlers.push(nativeHandler);
256
257 break;
258 }
259 }
260 },
261
262 removeListeners(type, handler): void {
263 switch (type) {
264 case DEVICE_CONNECTIVITY_EVENT: {
265 // Get native handler
266 const index = handlers.indexOf(handler);
267 const nativeHandler = nativeHandlers[index];
268
269 if (connection) {
270 connection.removeEventListener('change', nativeHandler);
271 } else {
272 window.addEventListener('online', nativeHandler);
273 window.addEventListener('offline', nativeHandler);
274 }
275
276 // Remove handlers
277 handlers.splice(index, 1);
278 nativeHandlers.splice(index, 1);
279
280 break;
281 }
282 }
283 },
284
285 async getCurrentState(requestedInterface): Promise<NetInfoNativeModuleState> {
286 return getCurrentState(requestedInterface);
287 },
288};
289
290export default RNCNetInfo;