1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | import {
|
11 | NetInfoNativeModule,
|
12 | DEVICE_CONNECTIVITY_EVENT,
|
13 | NetInfoNativeModuleState,
|
14 | } from './privateTypes';
|
15 | import {
|
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 |
|
30 | type ConnectionType =
|
31 | | 'bluetooth'
|
32 | | 'cellular'
|
33 | | 'ethernet'
|
34 | | 'mixed'
|
35 | | 'none'
|
36 | | 'other'
|
37 | | 'unknown'
|
38 | | 'wifi'
|
39 | | 'wimax';
|
40 |
|
41 |
|
42 | type ConnectionEffectiveType = '2g' | '3g' | '4g' | 'slow-2g';
|
43 |
|
44 |
|
45 | type ConnectionSaveData = boolean;
|
46 |
|
47 | interface Events {
|
48 | change: Event;
|
49 | }
|
50 |
|
51 | interface 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 |
|
66 | declare global {
|
67 | interface Navigator {
|
68 | connection?: Connection;
|
69 | mozConnection?: Connection;
|
70 | webkitConnection?: Connection;
|
71 | }
|
72 | }
|
73 |
|
74 |
|
75 | const connection =
|
76 | window.navigator.connection ||
|
77 | window.navigator.mozConnection ||
|
78 | window.navigator.webkitConnection;
|
79 |
|
80 |
|
81 | const 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 | };
|
92 | const 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 |
|
103 | const 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 |
|
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 |
|
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 |
|
235 | const handlers: ((state: NetInfoNativeModuleState) => void)[] = [];
|
236 | const nativeHandlers: (() => void)[] = [];
|
237 |
|
238 | const 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 |
|
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 |
|
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 |
|
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 |
|
290 | export default RNCNetInfo;
|