UNPKG

5.39 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 * as Types from './types';
11import * as PrivateTypes from './privateTypes';
12
13interface InternetReachabilityCheckHandler {
14 promise: Promise<void>;
15 cancel: () => void;
16}
17
18export default class InternetReachability {
19 private _configuration: Types.NetInfoConfiguration;
20 private _listener: PrivateTypes.NetInfoInternetReachabilityChangeListener;
21 private _isInternetReachable: boolean | null | undefined = undefined;
22 private _currentInternetReachabilityCheckHandler: InternetReachabilityCheckHandler | null = null;
23 private _currentTimeoutHandle: ReturnType<typeof setTimeout> | null = null;
24
25 constructor(
26 configuration: Types.NetInfoConfiguration,
27 listener: PrivateTypes.NetInfoInternetReachabilityChangeListener,
28 ) {
29 this._configuration = configuration;
30 this._listener = listener;
31 }
32
33 private _setIsInternetReachable = (
34 isInternetReachable: boolean | null | undefined,
35 ): void => {
36 if (this._isInternetReachable === isInternetReachable) {
37 return;
38 }
39
40 this._isInternetReachable = isInternetReachable;
41 this._listener(this._isInternetReachable);
42 };
43
44 private _setExpectsConnection = (expectsConnection: boolean): void => {
45 // Cancel any pending check
46 if (this._currentInternetReachabilityCheckHandler !== null) {
47 this._currentInternetReachabilityCheckHandler.cancel();
48 this._currentInternetReachabilityCheckHandler = null;
49 }
50 // Cancel any pending timeout
51 if (this._currentTimeoutHandle !== null) {
52 clearTimeout(this._currentTimeoutHandle);
53 this._currentTimeoutHandle = null;
54 }
55
56 if (expectsConnection) {
57 // If we expect a connection, start the process for finding if we have one
58 // Set the state to "null" if it was previously false
59 if (!this._isInternetReachable) {
60 this._setIsInternetReachable(null);
61 }
62 // Start a network request to check for internet
63 this._currentInternetReachabilityCheckHandler = this._checkInternetReachability();
64 } else {
65 // If we don't expect a connection, just change the state to "false"
66 this._setIsInternetReachable(false);
67 }
68 };
69
70 private _checkInternetReachability = (): InternetReachabilityCheckHandler => {
71 const responsePromise = fetch(this._configuration.reachabilityUrl, {
72 method: 'HEAD',
73 cache: 'no-cache',
74 });
75
76 // Create promise that will reject after the request timeout has been reached
77 let timeoutHandle: ReturnType<typeof setTimeout>;
78 const timeoutPromise = new Promise<Response>(
79 (_, reject): void => {
80 timeoutHandle = setTimeout(
81 (): void => reject('timedout'),
82 this._configuration.reachabilityRequestTimeout,
83 );
84 },
85 );
86
87 // Create promise that makes it possible to cancel a pending request through a reject
88 let cancel: () => void = (): void => {};
89 const cancelPromise = new Promise<Response>(
90 (_, reject): void => {
91 cancel = (): void => reject('canceled');
92 },
93 );
94
95 const promise = Promise.race([
96 responsePromise,
97 timeoutPromise,
98 cancelPromise,
99 ])
100 .then(
101 (response): Promise<boolean> => {
102 return this._configuration.reachabilityTest(response);
103 },
104 )
105 .then(
106 (result): void => {
107 this._setIsInternetReachable(result);
108 const nextTimeoutInterval = this._isInternetReachable
109 ? this._configuration.reachabilityLongTimeout
110 : this._configuration.reachabilityShortTimeout;
111 this._currentTimeoutHandle = setTimeout(
112 this._checkInternetReachability,
113 nextTimeoutInterval,
114 );
115 },
116 )
117 .catch(
118 (error: Error | 'timedout' | 'canceled'): void => {
119 if (error !== 'canceled') {
120 this._setIsInternetReachable(false);
121 this._currentTimeoutHandle = setTimeout(
122 this._checkInternetReachability,
123 this._configuration.reachabilityShortTimeout,
124 );
125 }
126 },
127 )
128 // Clear request timeout and propagate any errors
129 .then(
130 (): void => {
131 clearTimeout(timeoutHandle);
132 },
133 (error: Error): void => {
134 clearTimeout(timeoutHandle);
135 throw error;
136 },
137 );
138
139 return {
140 promise,
141 cancel,
142 };
143 };
144
145 public update = (state: PrivateTypes.NetInfoNativeModuleState): void => {
146 if (typeof state.isInternetReachable === 'boolean') {
147 this._setIsInternetReachable(state.isInternetReachable);
148 } else {
149 this._setExpectsConnection(state.isConnected);
150 }
151 };
152
153 public currentState = (): boolean | null | undefined => {
154 return this._isInternetReachable;
155 };
156
157 public tearDown = (): void => {
158 // Cancel any pending check
159 if (this._currentInternetReachabilityCheckHandler !== null) {
160 this._currentInternetReachabilityCheckHandler.cancel();
161 this._currentInternetReachabilityCheckHandler = null;
162 }
163
164 // Cancel any pending timeout
165 if (this._currentTimeoutHandle !== null) {
166 clearTimeout(this._currentTimeoutHandle);
167 this._currentTimeoutHandle = null;
168 }
169 };
170}