@react-native-community/netinfo
Version:
React Native Network Info API for iOS & Android
172 lines (154 loc) • 5.54 kB
text/typescript
/**
* 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.
*
* @format
*/
import * as Types from './types';
import * as PrivateTypes from './privateTypes';
interface InternetReachabilityCheckHandler {
promise: Promise<void>;
cancel: () => void;
}
export default class InternetReachability {
private _configuration: Types.NetInfoConfiguration;
private _listener: PrivateTypes.NetInfoInternetReachabilityChangeListener;
private _isInternetReachable: boolean | null | undefined = undefined;
private _currentInternetReachabilityCheckHandler: InternetReachabilityCheckHandler | null = null;
private _currentTimeoutHandle: ReturnType<typeof setTimeout> | null = null;
constructor(
configuration: Types.NetInfoConfiguration,
listener: PrivateTypes.NetInfoInternetReachabilityChangeListener,
) {
this._configuration = configuration;
this._listener = listener;
}
private _setIsInternetReachable = (
isInternetReachable: boolean | null,
): void => {
if (this._isInternetReachable === isInternetReachable) {
return;
}
this._isInternetReachable = isInternetReachable;
this._listener(this._isInternetReachable);
};
private _setExpectsConnection = (expectsConnection: boolean | null): void => {
// Cancel any pending check
if (this._currentInternetReachabilityCheckHandler !== null) {
this._currentInternetReachabilityCheckHandler.cancel();
this._currentInternetReachabilityCheckHandler = null;
}
// Cancel any pending timeout
if (this._currentTimeoutHandle !== null) {
clearTimeout(this._currentTimeoutHandle);
this._currentTimeoutHandle = null;
}
if (expectsConnection && this._configuration.reachabilityShouldRun()) {
// If we expect a connection, start the process for finding if we have one
// Set the state to "null" if it was previously false
if (!this._isInternetReachable) {
this._setIsInternetReachable(null);
}
// Start a network request to check for internet
this._currentInternetReachabilityCheckHandler = this._checkInternetReachability();
} else {
// If we don't expect a connection or don't run reachability check, just change the state to "false"
this._setIsInternetReachable(false);
}
};
private _checkInternetReachability = (): InternetReachabilityCheckHandler => {
const responsePromise = fetch(this._configuration.reachabilityUrl, {
method: 'HEAD',
cache: 'no-cache',
});
// Create promise that will reject after the request timeout has been reached
let timeoutHandle: ReturnType<typeof setTimeout>;
const timeoutPromise = new Promise<Response>(
(_, reject): void => {
timeoutHandle = setTimeout(
(): void => reject('timedout'),
this._configuration.reachabilityRequestTimeout,
);
},
);
// Create promise that makes it possible to cancel a pending request through a reject
// eslint-disable-next-line @typescript-eslint/no-empty-function
let cancel: () => void = (): void => {};
const cancelPromise = new Promise<Response>(
(_, reject): void => {
cancel = (): void => reject('canceled');
},
);
const promise = Promise.race([
responsePromise,
timeoutPromise,
cancelPromise,
])
.then(
(response): Promise<boolean> => {
return this._configuration.reachabilityTest(response);
},
)
.then(
(result): void => {
this._setIsInternetReachable(result);
const nextTimeoutInterval = this._isInternetReachable
? this._configuration.reachabilityLongTimeout
: this._configuration.reachabilityShortTimeout;
this._currentTimeoutHandle = setTimeout(
this._checkInternetReachability,
nextTimeoutInterval,
);
},
)
.catch(
(error: Error | 'timedout' | 'canceled'): void => {
if (error !== 'canceled') {
this._setIsInternetReachable(false);
this._currentTimeoutHandle = setTimeout(
this._checkInternetReachability,
this._configuration.reachabilityShortTimeout,
);
}
},
)
// Clear request timeout and propagate any errors
.then(
(): void => {
clearTimeout(timeoutHandle);
},
(error: Error): void => {
clearTimeout(timeoutHandle);
throw error;
},
);
return {
promise,
cancel,
};
};
public update = (state: PrivateTypes.NetInfoNativeModuleState): void => {
if (typeof state.isInternetReachable === 'boolean') {
this._setIsInternetReachable(state.isInternetReachable);
} else {
this._setExpectsConnection(state.isConnected);
}
};
public currentState = (): boolean | null | undefined => {
return this._isInternetReachable;
};
public tearDown = (): void => {
// Cancel any pending check
if (this._currentInternetReachabilityCheckHandler !== null) {
this._currentInternetReachabilityCheckHandler.cancel();
this._currentInternetReachabilityCheckHandler = null;
}
// Cancel any pending timeout
if (this._currentTimeoutHandle !== null) {
clearTimeout(this._currentTimeoutHandle);
this._currentTimeoutHandle = null;
}
};
}