@akbeniwal/react-native-smart-netinfo 1.0.2 β 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/ReactNativeSmartNetinfo.podspec +20 -0
- package/android/build.gradle +67 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/akbeniwal/reactnativesmartnetinfo/ReactNativeSmartNetinfoModule.kt +73 -0
- package/android/src/main/java/com/akbeniwal/reactnativesmartnetinfo/ReactNativeSmartNetinfoPackage.kt +31 -0
- package/dist/NativeReactNativeSmartNetinfo.d.ts +8 -0
- package/dist/NativeReactNativeSmartNetinfo.d.ts.map +1 -0
- package/dist/NativeReactNativeSmartNetinfo.js +3 -0
- package/dist/NativeReactNativeSmartNetinfo.js.map +1 -0
- package/dist/SmartNetInfo.d.ts +4 -0
- package/dist/SmartNetInfo.d.ts.map +1 -1
- package/dist/SmartNetInfo.js +68 -7
- package/dist/SmartNetInfo.js.map +1 -1
- package/dist/utils/getLatency.d.ts.map +1 -1
- package/dist/utils/getLatency.js +10 -2
- package/dist/utils/getLatency.js.map +1 -1
- package/dist/utils/runSpeedTest.d.ts +1 -1
- package/dist/utils/runSpeedTest.d.ts.map +1 -1
- package/dist/utils/runSpeedTest.js +14 -2
- package/dist/utils/runSpeedTest.js.map +1 -1
- package/ios/ReactNativeSmartNetinfo.h +6 -0
- package/ios/ReactNativeSmartNetinfo.mm +73 -0
- package/package.json +12 -2
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ A lightweight, zero-dependency, smart network connection monitor for React Nativ
|
|
|
11
11
|
- π **Auto Speed Test**: Automatically run speed test when transitioning from offline to online.
|
|
12
12
|
- π± **Foreground Check**: Recheck connectivity instantly when the app transitions back to the foreground.
|
|
13
13
|
- πΈοΈ **Web Compatibility**: Smooth fallback and support for browser online/offline events (perfect for React Native Web).
|
|
14
|
-
- π§© **
|
|
14
|
+
- π§© **Hybrid Architecture (Native + JS)**: Uses iOS (`NWPathMonitor`) and Android (`ConnectivityManager`) native modules for **Zero Battery Drain** when offline, and JS Polling for real-time latency when online.
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
@@ -25,6 +25,18 @@ npm install @akbeniwal/react-native-smart-netinfo
|
|
|
25
25
|
yarn add @akbeniwal/react-native-smart-netinfo
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
### iOS Setup
|
|
29
|
+
|
|
30
|
+
Since this package uses native modules for optimal battery performance, you must run `pod install` for iOS:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cd ios
|
|
34
|
+
pod install
|
|
35
|
+
cd ..
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
*(Android is auto-linked, no extra steps required).*
|
|
39
|
+
|
|
28
40
|
---
|
|
29
41
|
|
|
30
42
|
## Usage
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "ReactNativeSmartNetinfo"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
+
s.source = { :git => "https://github.com/AKBeniwal1700/react-native-smart-netinfo.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
|
|
17
|
+
s.private_header_files = "ios/**/*.h"
|
|
18
|
+
|
|
19
|
+
install_modules_dependencies(s)
|
|
20
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.ReactNativeSmartNetinfo = [
|
|
3
|
+
kotlinVersion: "2.0.21",
|
|
4
|
+
minSdkVersion: 24,
|
|
5
|
+
compileSdkVersion: 36,
|
|
6
|
+
targetSdkVersion: 36
|
|
7
|
+
]
|
|
8
|
+
|
|
9
|
+
ext.getExtOrDefault = { prop ->
|
|
10
|
+
if (rootProject.ext.has(prop)) {
|
|
11
|
+
return rootProject.ext.get(prop)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return ReactNativeSmartNetinfo[prop]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
repositories {
|
|
18
|
+
google()
|
|
19
|
+
mavenCentral()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
dependencies {
|
|
23
|
+
classpath "com.android.tools.build:gradle:8.7.2"
|
|
24
|
+
// noinspection DifferentKotlinGradleVersion
|
|
25
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
apply plugin: "com.android.library"
|
|
31
|
+
apply plugin: "kotlin-android"
|
|
32
|
+
|
|
33
|
+
apply plugin: "com.facebook.react"
|
|
34
|
+
|
|
35
|
+
android {
|
|
36
|
+
namespace "com.akbeniwal.reactnativesmartnetinfo"
|
|
37
|
+
|
|
38
|
+
compileSdkVersion getExtOrDefault("compileSdkVersion")
|
|
39
|
+
|
|
40
|
+
defaultConfig {
|
|
41
|
+
minSdkVersion getExtOrDefault("minSdkVersion")
|
|
42
|
+
targetSdkVersion getExtOrDefault("targetSdkVersion")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
buildFeatures {
|
|
46
|
+
buildConfig true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
buildTypes {
|
|
50
|
+
release {
|
|
51
|
+
minifyEnabled false
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
lint {
|
|
56
|
+
disable "GradleCompatible"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
compileOptions {
|
|
60
|
+
sourceCompatibility JavaVersion.VERSION_1_8
|
|
61
|
+
targetCompatibility JavaVersion.VERSION_1_8
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
dependencies {
|
|
66
|
+
implementation "com.facebook.react:react-android"
|
|
67
|
+
}
|
package/android/src/main/java/com/akbeniwal/reactnativesmartnetinfo/ReactNativeSmartNetinfoModule.kt
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
package com.akbeniwal.reactnativesmartnetinfo
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.net.ConnectivityManager
|
|
5
|
+
import android.net.Network
|
|
6
|
+
import android.net.NetworkCapabilities
|
|
7
|
+
import android.net.NetworkRequest
|
|
8
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
9
|
+
import com.facebook.react.bridge.WritableMap
|
|
10
|
+
import com.facebook.react.bridge.Arguments
|
|
11
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
12
|
+
|
|
13
|
+
class ReactNativeSmartNetinfoModule(private val reactContext: ReactApplicationContext) :
|
|
14
|
+
NativeReactNativeSmartNetinfoSpec(reactContext) {
|
|
15
|
+
|
|
16
|
+
private var connectivityManager: ConnectivityManager? = null
|
|
17
|
+
private var networkCallback: ConnectivityManager.NetworkCallback? = null
|
|
18
|
+
private var isConnected = false
|
|
19
|
+
|
|
20
|
+
init {
|
|
21
|
+
connectivityManager = reactContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
|
22
|
+
registerNetworkCallback()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private fun registerNetworkCallback() {
|
|
26
|
+
try {
|
|
27
|
+
networkCallback = object : ConnectivityManager.NetworkCallback() {
|
|
28
|
+
override fun onAvailable(network: Network) {
|
|
29
|
+
super.onAvailable(network)
|
|
30
|
+
isConnected = true
|
|
31
|
+
sendEvent("NetworkStatusChanged", true)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
override fun onLost(network: Network) {
|
|
35
|
+
super.onLost(network)
|
|
36
|
+
isConnected = false
|
|
37
|
+
sendEvent("NetworkStatusChanged", false)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
val builder = NetworkRequest.Builder()
|
|
42
|
+
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
|
43
|
+
|
|
44
|
+
connectivityManager?.registerNetworkCallback(builder.build(), networkCallback!!)
|
|
45
|
+
} catch (e: Exception) {
|
|
46
|
+
// Fallback or ignore
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private fun sendEvent(eventName: String, isConnected: Boolean) {
|
|
51
|
+
try {
|
|
52
|
+
val params: WritableMap = Arguments.createMap()
|
|
53
|
+
params.putBoolean("isConnected", isConnected)
|
|
54
|
+
reactContext
|
|
55
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
56
|
+
.emit(eventName, params)
|
|
57
|
+
} catch (e: Exception) {
|
|
58
|
+
// Ignored
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
override fun addListener(eventName: String) {
|
|
63
|
+
// Required for RN built-in Event Emitter Calls
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
override fun removeListeners(count: Double) {
|
|
67
|
+
// Required for RN built-in Event Emitter Calls
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
companion object {
|
|
71
|
+
const val NAME = NativeReactNativeSmartNetinfoSpec.NAME
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package com.akbeniwal.reactnativesmartnetinfo
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
|
+
import java.util.HashMap
|
|
9
|
+
|
|
10
|
+
class ReactNativeSmartNetinfoPackage : BaseReactPackage() {
|
|
11
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
12
|
+
return if (name == ReactNativeSmartNetinfoModule.NAME) {
|
|
13
|
+
ReactNativeSmartNetinfoModule(reactContext)
|
|
14
|
+
} else {
|
|
15
|
+
null
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
|
|
20
|
+
mapOf(
|
|
21
|
+
ReactNativeSmartNetinfoModule.NAME to ReactModuleInfo(
|
|
22
|
+
name = ReactNativeSmartNetinfoModule.NAME,
|
|
23
|
+
className = ReactNativeSmartNetinfoModule.NAME,
|
|
24
|
+
canOverrideExistingModule = false,
|
|
25
|
+
needsEagerInit = false,
|
|
26
|
+
isCxxModule = false,
|
|
27
|
+
isTurboModule = true
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type TurboModule } from 'react-native';
|
|
2
|
+
export interface Spec extends TurboModule {
|
|
3
|
+
addListener(eventName: string): void;
|
|
4
|
+
removeListeners(count: number): void;
|
|
5
|
+
}
|
|
6
|
+
declare const _default: Spec;
|
|
7
|
+
export default _default;
|
|
8
|
+
//# sourceMappingURL=NativeReactNativeSmartNetinfo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NativeReactNativeSmartNetinfo.d.ts","sourceRoot":"","sources":["../src/NativeReactNativeSmartNetinfo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;;AAED,wBAAiF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NativeReactNativeSmartNetinfo.js","sourceRoot":"","sources":["../src/NativeReactNativeSmartNetinfo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAoB,MAAM,cAAc,CAAC;AAOrE,eAAe,mBAAmB,CAAC,YAAY,CAAO,yBAAyB,CAAC,CAAC"}
|
package/dist/SmartNetInfo.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { NetworkState, SmartNetInfoConfig, NetworkStateListener } from './types';
|
|
2
2
|
declare class SmartNetInfoManager {
|
|
3
|
+
private pingUrlIndex;
|
|
3
4
|
private config;
|
|
4
5
|
private state;
|
|
5
6
|
private listeners;
|
|
6
7
|
private timeoutId;
|
|
7
8
|
private appStateSubscription;
|
|
9
|
+
private nativeEventEmitter;
|
|
10
|
+
private nativeEventSubscription;
|
|
8
11
|
private isMonitoring;
|
|
9
12
|
private isChecking;
|
|
10
13
|
/**
|
|
@@ -45,6 +48,7 @@ declare class SmartNetInfoManager {
|
|
|
45
48
|
* Stop monitoring network status and clean up timers and listeners.
|
|
46
49
|
*/
|
|
47
50
|
stopMonitoring(): void;
|
|
51
|
+
private handleNativeNetworkChange;
|
|
48
52
|
private handleAppStateChange;
|
|
49
53
|
private handleWebOnline;
|
|
50
54
|
private handleWebOffline;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmartNetInfo.d.ts","sourceRoot":"","sources":["../src/SmartNetInfo.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SmartNetInfo.d.ts","sourceRoot":"","sources":["../src/SmartNetInfo.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAcjF,cAAM,mBAAmB;IACvB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,MAAM,CAKZ;IAEF,OAAO,CAAC,KAAK,CAOX;IAEF,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,SAAS,CAA8C;IAC/D,OAAO,CAAC,oBAAoB,CAAuC;IACnE,OAAO,CAAC,kBAAkB,CAAmC;IAC7D,OAAO,CAAC,uBAAuB,CAAuC;IACtE,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,UAAU,CAAS;IAE3B;;;OAGG;IACI,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI;IAQ3D;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,YAAY,CAAC;IAK3C;;;;;;OAMG;IACI,gBAAgB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI;IAanE;;;;;OAKG;IACI,mBAAmB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI;IAOhE;;;;OAIG;IACU,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAkBnD;;OAEG;IACI,eAAe,IAAI,IAAI;IA6C9B;;OAEG;IACI,cAAc,IAAI,IAAI;IA+B7B,OAAO,CAAC,yBAAyB,CAkB/B;IAEF,OAAO,CAAC,oBAAoB,CAM1B;IAEF,OAAO,CAAC,eAAe,CAKrB;IAEF,OAAO,CAAC,gBAAgB,CAOtB;IAEF,OAAO,CAAC,iBAAiB;YA6BX,iBAAiB;IA0C/B,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,eAAe;CASxB;AAED,eAAO,MAAM,YAAY,qBAA4B,CAAC;AACtD,eAAe,YAAY,CAAC"}
|
package/dist/SmartNetInfo.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import { AppState } from 'react-native';
|
|
1
|
+
import { AppState, NativeEventEmitter } from 'react-native';
|
|
2
|
+
import NativeReactNativeSmartNetinfo from './NativeReactNativeSmartNetinfo';
|
|
2
3
|
import { getConnectionQuality } from './utils/getConnectionQuality';
|
|
3
4
|
import { getLatency } from './utils/getLatency';
|
|
4
5
|
import { runSpeedTest as executeSpeedTest } from './utils/runSpeedTest';
|
|
5
|
-
const
|
|
6
|
+
const PING_URLS = [
|
|
7
|
+
'https://clients3.google.com/generate_204',
|
|
8
|
+
'https://www.apple.com/library/test/success.html',
|
|
9
|
+
'https://cloudflare-dns.com/dns-query',
|
|
10
|
+
'https://google.com/generate_204'
|
|
11
|
+
];
|
|
6
12
|
const SPEED_TEST_URL = 'https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js';
|
|
7
13
|
class SmartNetInfoManager {
|
|
8
14
|
constructor() {
|
|
15
|
+
this.pingUrlIndex = 0;
|
|
9
16
|
this.config = {
|
|
10
17
|
pingIntervalMs: 30000,
|
|
11
18
|
timeoutMs: 5000,
|
|
@@ -23,16 +30,42 @@ class SmartNetInfoManager {
|
|
|
23
30
|
this.listeners = new Set();
|
|
24
31
|
this.timeoutId = null;
|
|
25
32
|
this.appStateSubscription = null;
|
|
33
|
+
this.nativeEventEmitter = null;
|
|
34
|
+
this.nativeEventSubscription = null;
|
|
26
35
|
this.isMonitoring = false;
|
|
27
36
|
this.isChecking = false;
|
|
37
|
+
this.handleNativeNetworkChange = (event) => {
|
|
38
|
+
if (!event.isConnected) {
|
|
39
|
+
this.updateState({
|
|
40
|
+
isConnected: false,
|
|
41
|
+
isInternetReachable: false,
|
|
42
|
+
latencyMs: null,
|
|
43
|
+
connectionQuality: null,
|
|
44
|
+
});
|
|
45
|
+
if (this.timeoutId) {
|
|
46
|
+
clearTimeout(this.timeoutId);
|
|
47
|
+
this.timeoutId = null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.updateState({ isConnected: true });
|
|
52
|
+
this.checkConnectivity().finally(() => {
|
|
53
|
+
this.scheduleNextCheck();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
};
|
|
28
57
|
this.handleAppStateChange = (nextAppState) => {
|
|
29
58
|
if (nextAppState === 'active') {
|
|
30
|
-
this.checkConnectivity()
|
|
59
|
+
this.checkConnectivity().finally(() => {
|
|
60
|
+
this.scheduleNextCheck();
|
|
61
|
+
});
|
|
31
62
|
}
|
|
32
63
|
};
|
|
33
64
|
this.handleWebOnline = () => {
|
|
34
65
|
this.updateState({ isConnected: true, isInternetReachable: true });
|
|
35
|
-
this.checkConnectivity()
|
|
66
|
+
this.checkConnectivity().finally(() => {
|
|
67
|
+
this.scheduleNextCheck();
|
|
68
|
+
});
|
|
36
69
|
};
|
|
37
70
|
this.handleWebOffline = () => {
|
|
38
71
|
this.updateState({
|
|
@@ -99,7 +132,8 @@ class SmartNetInfoManager {
|
|
|
99
132
|
return this.state.internetSpeed;
|
|
100
133
|
}
|
|
101
134
|
this.updateState({ isTestingSpeed: true });
|
|
102
|
-
const
|
|
135
|
+
const timeout = Math.max(this.config.timeoutMs * 3, 15000);
|
|
136
|
+
const speed = await executeSpeedTest(SPEED_TEST_URL, this.config.speedTestFileSizeInBytes, timeout);
|
|
103
137
|
this.updateState({
|
|
104
138
|
internetSpeed: speed,
|
|
105
139
|
isTestingSpeed: false,
|
|
@@ -113,6 +147,16 @@ class SmartNetInfoManager {
|
|
|
113
147
|
if (this.isMonitoring)
|
|
114
148
|
return;
|
|
115
149
|
this.isMonitoring = true;
|
|
150
|
+
// Set up Native Event Emitter if available
|
|
151
|
+
try {
|
|
152
|
+
if (NativeReactNativeSmartNetinfo) {
|
|
153
|
+
this.nativeEventEmitter = new NativeEventEmitter(NativeReactNativeSmartNetinfo);
|
|
154
|
+
this.nativeEventSubscription = this.nativeEventEmitter.addListener('NetworkStatusChanged', this.handleNativeNetworkChange);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (e) {
|
|
158
|
+
console.warn('SmartNetInfo native module not found, falling back to pure polling');
|
|
159
|
+
}
|
|
116
160
|
// Run initial check immediately and then schedule subsequent checks
|
|
117
161
|
this.checkConnectivity().then(() => {
|
|
118
162
|
// Auto run speed test on initial connect if online
|
|
@@ -148,6 +192,10 @@ class SmartNetInfoManager {
|
|
|
148
192
|
clearTimeout(this.timeoutId);
|
|
149
193
|
this.timeoutId = null;
|
|
150
194
|
}
|
|
195
|
+
if (this.nativeEventSubscription) {
|
|
196
|
+
this.nativeEventSubscription.remove();
|
|
197
|
+
this.nativeEventSubscription = null;
|
|
198
|
+
}
|
|
151
199
|
if (this.appStateSubscription) {
|
|
152
200
|
if (typeof this.appStateSubscription.remove === 'function') {
|
|
153
201
|
this.appStateSubscription.remove();
|
|
@@ -173,7 +221,12 @@ class SmartNetInfoManager {
|
|
|
173
221
|
}
|
|
174
222
|
if (this.config.pingIntervalMs <= 0)
|
|
175
223
|
return;
|
|
176
|
-
//
|
|
224
|
+
// If native module explicitly told us we are offline (isConnected: false),
|
|
225
|
+
// we DO NOT poll at all to save battery. We wait for native event.
|
|
226
|
+
if (this.state.isConnected === false && this.nativeEventEmitter) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
// When offline (without native fallback), check more frequently (every 3 seconds) to detect recovery quickly.
|
|
177
230
|
// When online, use the configured pingIntervalMs.
|
|
178
231
|
const isOffline = this.state.isInternetReachable === false;
|
|
179
232
|
const interval = isOffline
|
|
@@ -189,7 +242,15 @@ class SmartNetInfoManager {
|
|
|
189
242
|
return;
|
|
190
243
|
this.isChecking = true;
|
|
191
244
|
try {
|
|
192
|
-
|
|
245
|
+
// Add a buffer to timeout when we are currently offline to allow the radio to wake up
|
|
246
|
+
const isCurrentlyOffline = this.state.isInternetReachable === false;
|
|
247
|
+
const actualTimeout = isCurrentlyOffline
|
|
248
|
+
? Math.max(this.config.timeoutMs, 4000)
|
|
249
|
+
: this.config.timeoutMs;
|
|
250
|
+
const currentPingUrl = PING_URLS[this.pingUrlIndex];
|
|
251
|
+
// Cycle to the next URL for the next check to avoid DNS caching issues if it fails
|
|
252
|
+
this.pingUrlIndex = (this.pingUrlIndex + 1) % PING_URLS.length;
|
|
253
|
+
const { isReachable, latencyMs } = await getLatency(currentPingUrl, actualTimeout);
|
|
193
254
|
const wasInternetReachable = this.state.isInternetReachable;
|
|
194
255
|
this.updateState({
|
|
195
256
|
isConnected: isReachable,
|
package/dist/SmartNetInfo.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmartNetInfo.js","sourceRoot":"","sources":["../src/SmartNetInfo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAkB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"SmartNetInfo.js","sourceRoot":"","sources":["../src/SmartNetInfo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAkB,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAC5E,OAAO,6BAA6B,MAAM,iCAAiC,CAAC;AAE5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAExE,MAAM,SAAS,GAAG;IAChB,0CAA0C;IAC1C,iDAAiD;IACjD,sCAAsC;IACtC,iCAAiC;CAClC,CAAC;AAEF,MAAM,cAAc,GAAG,kEAAkE,CAAC;AAE1F,MAAM,mBAAmB;IAAzB;QACU,iBAAY,GAAG,CAAC,CAAC;QACjB,WAAM,GAAiC;YAC7C,cAAc,EAAE,KAAK;YACrB,SAAS,EAAE,IAAI;YACf,wBAAwB,EAAE,KAAK;YAC/B,oBAAoB,EAAE,KAAK;SAC5B,CAAC;QAEM,UAAK,GAAiB;YAC5B,WAAW,EAAE,IAAI;YACjB,mBAAmB,EAAE,IAAI;YACzB,SAAS,EAAE,IAAI;YACf,iBAAiB,EAAE,IAAI;YACvB,aAAa,EAAE,IAAI;YACnB,cAAc,EAAE,KAAK;SACtB,CAAC;QAEM,cAAS,GAAG,IAAI,GAAG,EAAwB,CAAC;QAC5C,cAAS,GAAyC,IAAI,CAAC;QACvD,yBAAoB,GAAkC,IAAI,CAAC;QAC3D,uBAAkB,GAA8B,IAAI,CAAC;QACrD,4BAAuB,GAAkC,IAAI,CAAC;QAC9D,iBAAY,GAAG,KAAK,CAAC;QACrB,eAAU,GAAG,KAAK,CAAC;QAgKnB,8BAAyB,GAAG,CAAC,KAA+B,EAAE,EAAE;YACtE,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;gBACvB,IAAI,CAAC,WAAW,CAAC;oBACf,WAAW,EAAE,KAAK;oBAClB,mBAAmB,EAAE,KAAK;oBAC1B,SAAS,EAAE,IAAI;oBACf,iBAAiB,EAAE,IAAI;iBACxB,CAAC,CAAC;gBACH,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;oBACnB,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;gBACxB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;gBACxC,IAAI,CAAC,iBAAiB,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;oBACpC,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBAC3B,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC;QAEM,yBAAoB,GAAG,CAAC,YAA4B,EAAQ,EAAE;YACpE,IAAI,YAAY,KAAK,QAAQ,EAAE,CAAC;gBAC9B,IAAI,CAAC,iBAAiB,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;oBACpC,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBAC3B,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC;QAEM,oBAAe,GAAG,GAAS,EAAE;YACnC,IAAI,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC,CAAC;YACnE,IAAI,CAAC,iBAAiB,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;gBACpC,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3B,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QAEM,qBAAgB,GAAG,GAAS,EAAE;YACpC,IAAI,CAAC,WAAW,CAAC;gBACf,WAAW,EAAE,KAAK;gBAClB,mBAAmB,EAAE,KAAK;gBAC1B,SAAS,EAAE,IAAI;gBACf,iBAAiB,EAAE,IAAI;aACxB,CAAC,CAAC;QACL,CAAC,CAAC;IAgGJ,CAAC;IAxSC;;;OAGG;IACI,SAAS,CAAC,MAAmC;QAClD,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;QAC5C,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,IAAI,CAAC,cAAc,EAAE,CAAC;YACtB,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,CAAC;IACH,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,KAAK;QAChB,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED;;;;;;OAMG;IACI,gBAAgB,CAAC,QAA8B;QACpD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAE7B,4DAA4D;QAC5D,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAErB,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,CAAC;QAED,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAClD,CAAC;IAED;;;;;OAKG;IACI,mBAAmB,CAAC,QAA8B;QACvD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChC,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACnD,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,CAAC;IACH,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,YAAY;QACvB,IAAI,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;QAClC,CAAC;QAED,IAAI,CAAC,WAAW,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QAE3C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;QAC3D,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,wBAAwB,EAAE,OAAO,CAAC,CAAC;QAEpG,IAAI,CAAC,WAAW,CAAC;YACf,aAAa,EAAE,KAAK;YACpB,cAAc,EAAE,KAAK;SACtB,CAAC,CAAC;QAEH,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACI,eAAe;QACpB,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO;QAC9B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAEzB,2CAA2C;QAC3C,IAAI,CAAC;YACH,IAAI,6BAA6B,EAAE,CAAC;gBAClC,IAAI,CAAC,kBAAkB,GAAG,IAAI,kBAAkB,CAAC,6BAAoC,CAAC,CAAC;gBACvF,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAChE,sBAAsB,EACtB,IAAI,CAAC,yBAAyB,CAC/B,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,oEAAoE,CAAC,CAAC;QACrF,CAAC;QAED,oEAAoE;QACpE,IAAI,CAAC,iBAAiB,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;YACjC,mDAAmD;YACnD,IACE,IAAI,CAAC,KAAK,CAAC,mBAAmB;gBAC9B,IAAI,CAAC,KAAK,CAAC,aAAa,KAAK,IAAI;gBACjC,CAAC,IAAI,CAAC,MAAM,CAAC,oBAAoB,EACjC,CAAC;gBACD,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,CAAC;YACD,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,CAAC,CAAC,CAAC;QAEH,kEAAkE;QAClE,IAAI,CAAC;YACH,IAAI,CAAC,oBAAoB,GAAG,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;QAC7F,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,oDAAoD,EAAE,KAAK,CAAC,CAAC;QAC5E,CAAC;QAED,wEAAwE;QACxE,MAAM,kBAAkB,GAAG,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,gBAAgB,KAAK,UAAU,CAAC;QAC1G,IAAI,kBAAkB,EAAE,CAAC;YACvB,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;YACxD,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAED;;OAEG;IACI,cAAc;QACnB,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO;QAC/B,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAE1B,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;QAED,IAAI,IAAI,CAAC,uBAAuB,EAAE,CAAC;YACjC,IAAI,CAAC,uBAAuB,CAAC,MAAM,EAAE,CAAC;YACtC,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC;QACtC,CAAC;QAED,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC9B,IAAI,OAAO,IAAI,CAAC,oBAAoB,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;gBAC3D,IAAI,CAAC,oBAAoB,CAAC,MAAM,EAAE,CAAC;YACrC,CAAC;iBAAM,CAAC;gBACN,2CAA2C;gBAC1C,QAAgB,CAAC,mBAAmB,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;YAC/E,CAAC;YACD,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;QACnC,CAAC;QAED,MAAM,kBAAkB,GAAG,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,mBAAmB,KAAK,UAAU,CAAC;QAC7G,IAAI,kBAAkB,EAAE,CAAC;YACvB,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;YAC3D,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IA8CO,iBAAiB;QACvB,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO;QAE/B,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACxB,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,CAAC,cAAc,IAAI,CAAC;YAAE,OAAO;QAE5C,2EAA2E;QAC3E,mEAAmE;QACnE,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,KAAK,KAAK,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAChE,OAAO;QACT,CAAC;QAED,8GAA8G;QAC9G,kDAAkD;QAClD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,KAAK,KAAK,CAAC;QAC3D,MAAM,QAAQ,GAAG,SAAS;YACxB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,IAAI,CAAC;YAC5C,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC;QAE/B,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,KAAK,IAAI,EAAE;YACrC,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC/B,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3B,CAAC,EAAE,QAAQ,CAAC,CAAC;IACf,CAAC;IAEO,KAAK,CAAC,iBAAiB;QAC7B,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC5B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QAEvB,IAAI,CAAC;YACH,sFAAsF;YACtF,MAAM,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,KAAK,KAAK,CAAC;YACpE,MAAM,aAAa,GAAG,kBAAkB;gBACtC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC;gBACvC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;YAE1B,MAAM,cAAc,GAAG,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACpD,mFAAmF;YACnF,IAAI,CAAC,YAAY,GAAG,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC;YAE/D,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,MAAM,UAAU,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;YAEnF,MAAM,oBAAoB,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC;YAE5D,IAAI,CAAC,WAAW,CAAC;gBACf,WAAW,EAAE,WAAW;gBACxB,mBAAmB,EAAE,WAAW;gBAChC,SAAS;gBACT,iBAAiB,EAAE,oBAAoB,CAAC,SAAS,CAAC;aACnD,CAAC,CAAC;YAEH,+FAA+F;YAC/F,IACE,WAAW;gBACX,oBAAoB,KAAK,KAAK;gBAC9B,IAAI,CAAC,KAAK,CAAC,aAAa,KAAK,IAAI;gBACjC,CAAC,IAAI,CAAC,MAAM,CAAC,oBAAoB,EACjC,CAAC;gBACD,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,+CAA+C,EAAE,KAAK,CAAC,CAAC;QACvE,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QAC1B,CAAC;IACH,CAAC;IAEO,WAAW,CAAC,YAAmC;QACrD,MAAM,SAAS,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,YAAY,EAAE,CAAC;QAErD,qCAAqC;QACrC,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAC5C,CAAC,GAAG,EAAE,EAAE,CAAE,SAAiB,CAAC,GAAG,CAAC,KAAM,IAAI,CAAC,KAAa,CAAC,GAAG,CAAC,CAC9D,CAAC;QAEF,IAAI,UAAU,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;YACvB,IAAI,CAAC,eAAe,EAAE,CAAC;QACzB,CAAC;IACH,CAAC;IAEO,eAAe;QACrB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;YAClC,IAAI,CAAC;gBACH,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,mBAAmB,EAAE,CAAC;AACtD,eAAe,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getLatency.d.ts","sourceRoot":"","sources":["../../src/utils/getLatency.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,WAAW,EAAE,OAAO,CAAC;IACrB,wEAAwE;IACxE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"getLatency.d.ts","sourceRoot":"","sources":["../../src/utils/getLatency.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,WAAW,EAAE,OAAO,CAAC;IACrB,wEAAwE;IACxE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA6C3F"}
|
package/dist/utils/getLatency.js
CHANGED
|
@@ -14,8 +14,8 @@ export async function getLatency(pingUrl, timeoutMs) {
|
|
|
14
14
|
timeoutId = setTimeout(() => {
|
|
15
15
|
controller.abort();
|
|
16
16
|
}, timeoutMs);
|
|
17
|
-
// Append dynamic timestamp to bypass
|
|
18
|
-
const uniqueUrl = `${pingUrl}${pingUrl.includes('?') ? '&' : '?'}t=${Date.now()}`;
|
|
17
|
+
// Append dynamic timestamp and random string to strictly bypass network/DNS caching
|
|
18
|
+
const uniqueUrl = `${pingUrl}${pingUrl.includes('?') ? '&' : '?'}t=${Date.now()}&r=${Math.random().toString().slice(2)}`;
|
|
19
19
|
// Perform GET request to verify connectivity.
|
|
20
20
|
// We use GET instead of HEAD for maximum compatibility across various CDNs and proxies.
|
|
21
21
|
// Since clients3.google.com/generate_204 returns a 204 No Content response,
|
|
@@ -29,11 +29,19 @@ export async function getLatency(pingUrl, timeoutMs) {
|
|
|
29
29
|
'Expires': '0',
|
|
30
30
|
},
|
|
31
31
|
});
|
|
32
|
+
// Fully consume the response body to prevent connection leaks in React Native
|
|
33
|
+
try {
|
|
34
|
+
await response.text();
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
// ignore
|
|
38
|
+
}
|
|
32
39
|
const isReachable = response.ok || response.status < 400;
|
|
33
40
|
const latencyMs = Date.now() - startTime;
|
|
34
41
|
return { isReachable, latencyMs };
|
|
35
42
|
}
|
|
36
43
|
catch (error) {
|
|
44
|
+
console.warn(`[SmartNetInfo] getLatency failed:`, error instanceof Error ? error.message : error);
|
|
37
45
|
// If it fails due to network/timeout, we are offline.
|
|
38
46
|
return { isReachable: false, latencyMs: null };
|
|
39
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getLatency.js","sourceRoot":"","sources":["../../src/utils/getLatency.ts"],"names":[],"mappings":"AAOA;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,SAAiB;IACjE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,IAAI,SAAS,GAAyC,IAAI,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAC1B,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,
|
|
1
|
+
{"version":3,"file":"getLatency.js","sourceRoot":"","sources":["../../src/utils/getLatency.ts"],"names":[],"mappings":"AAOA;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,SAAiB;IACjE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,IAAI,SAAS,GAAyC,IAAI,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAC1B,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,oFAAoF;QACpF,MAAM,SAAS,GAAG,GAAG,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,EAAE,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAEzH,8CAA8C;QAC9C,wFAAwF;QACxF,4EAA4E;QAC5E,4CAA4C;QAC5C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;YACtC,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,OAAO,EAAE;gBACP,eAAe,EAAE,qCAAqC;gBACtD,QAAQ,EAAE,UAAU;gBACpB,SAAS,EAAE,GAAG;aACf;SACF,CAAC,CAAC;QAEH,8EAA8E;QAC9E,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACxB,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,SAAS;QACX,CAAC;QAED,MAAM,WAAW,GAAG,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC;QACzD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QACzC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,mCAAmC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAClG,sDAAsD;QACtD,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACjD,CAAC;YAAS,CAAC;QACT,IAAI,SAAS,EAAE,CAAC;YACd,YAAY,CAAC,SAAS,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
* @param speedTestFileSizeInBytes Known size of the remote asset in bytes
|
|
7
7
|
* @returns A promise resolving to the estimated download speed in Mbps, or null if the test fails
|
|
8
8
|
*/
|
|
9
|
-
export declare function runSpeedTest(speedTestUrl: string, speedTestFileSizeInBytes: number): Promise<number | null>;
|
|
9
|
+
export declare function runSpeedTest(speedTestUrl: string, speedTestFileSizeInBytes: number, timeoutMs?: number): Promise<number | null>;
|
|
10
10
|
//# sourceMappingURL=runSpeedTest.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runSpeedTest.d.ts","sourceRoot":"","sources":["../../src/utils/runSpeedTest.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,wBAAwB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"runSpeedTest.d.ts","sourceRoot":"","sources":["../../src/utils/runSpeedTest.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,wBAAwB,EAAE,MAAM,EAChC,SAAS,GAAE,MAAc,GACxB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+CxB"}
|
|
@@ -6,11 +6,18 @@
|
|
|
6
6
|
* @param speedTestFileSizeInBytes Known size of the remote asset in bytes
|
|
7
7
|
* @returns A promise resolving to the estimated download speed in Mbps, or null if the test fails
|
|
8
8
|
*/
|
|
9
|
-
export async function runSpeedTest(speedTestUrl, speedTestFileSizeInBytes) {
|
|
9
|
+
export async function runSpeedTest(speedTestUrl, speedTestFileSizeInBytes, timeoutMs = 15000) {
|
|
10
|
+
let timeoutId = null;
|
|
10
11
|
try {
|
|
11
12
|
const startTime = Date.now();
|
|
12
|
-
const
|
|
13
|
+
const controller = new AbortController();
|
|
14
|
+
timeoutId = setTimeout(() => {
|
|
15
|
+
controller.abort();
|
|
16
|
+
}, timeoutMs);
|
|
17
|
+
const uniqueUrl = `${speedTestUrl}${speedTestUrl.includes('?') ? '&' : '?'}t=${Date.now()}&r=${Math.random().toString().slice(2)}`;
|
|
18
|
+
const response = await fetch(uniqueUrl, {
|
|
13
19
|
method: 'GET',
|
|
20
|
+
signal: controller.signal,
|
|
14
21
|
headers: {
|
|
15
22
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
16
23
|
'Pragma': 'no-cache',
|
|
@@ -35,5 +42,10 @@ export async function runSpeedTest(speedTestUrl, speedTestFileSizeInBytes) {
|
|
|
35
42
|
console.warn('Network speed test failed:', error);
|
|
36
43
|
return null;
|
|
37
44
|
}
|
|
45
|
+
finally {
|
|
46
|
+
if (timeoutId) {
|
|
47
|
+
clearTimeout(timeoutId);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
38
50
|
}
|
|
39
51
|
//# sourceMappingURL=runSpeedTest.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runSpeedTest.js","sourceRoot":"","sources":["../../src/utils/runSpeedTest.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,YAAoB,EACpB,wBAAgC;
|
|
1
|
+
{"version":3,"file":"runSpeedTest.js","sourceRoot":"","sources":["../../src/utils/runSpeedTest.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,YAAoB,EACpB,wBAAgC,EAChC,YAAoB,KAAK;IAEzB,IAAI,SAAS,GAAyC,IAAI,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QAEzC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAC1B,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,MAAM,SAAS,GAAG,GAAG,YAAY,GAAG,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,EAAE,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAEnI,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;YACtC,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,OAAO,EAAE;gBACP,eAAe,EAAE,qCAAqC;gBACtD,QAAQ,EAAE,UAAU;gBACpB,SAAS,EAAE,GAAG;aACf;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,2CAA2C,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAChF,CAAC;QAED,oFAAoF;QACpF,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEtB,MAAM,WAAW,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC;QACpD,IAAI,WAAW,IAAI,CAAC,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,YAAY,GAAG,wBAAwB,GAAG,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,OAAO,CAAC;QAEvD,yCAAyC;QACzC,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;YAAS,CAAC;QACT,IAAI,SAAS,EAAE,CAAC;YACd,YAAY,CAAC,SAAS,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#import "ReactNativeSmartNetinfo.h"
|
|
2
|
+
#import <Network/Network.h>
|
|
3
|
+
|
|
4
|
+
@implementation ReactNativeSmartNetinfo {
|
|
5
|
+
nw_path_monitor_t _monitor;
|
|
6
|
+
dispatch_queue_t _queue;
|
|
7
|
+
BOOL _hasListeners;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
RCT_EXPORT_MODULE()
|
|
11
|
+
|
|
12
|
+
- (instancetype)init {
|
|
13
|
+
if (self = [super init]) {
|
|
14
|
+
_queue = dispatch_queue_create("com.smartnetinfo.networkmonitor", NULL);
|
|
15
|
+
_monitor = nw_path_monitor_create();
|
|
16
|
+
|
|
17
|
+
__weak typeof(self) weakSelf = self;
|
|
18
|
+
nw_path_monitor_set_update_handler(_monitor, ^(nw_path_t path) {
|
|
19
|
+
nw_path_status_t status = nw_path_get_status(path);
|
|
20
|
+
BOOL isConnected = (status == nw_path_status_satisfied);
|
|
21
|
+
[weakSelf sendNetworkStatusEvent:isConnected];
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
nw_path_monitor_set_queue(_monitor, _queue);
|
|
25
|
+
nw_path_monitor_start(_monitor);
|
|
26
|
+
}
|
|
27
|
+
return self;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
- (void)dealloc {
|
|
31
|
+
if (_monitor) {
|
|
32
|
+
nw_path_monitor_cancel(_monitor);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
- (NSArray<NSString *> *)supportedEvents {
|
|
37
|
+
return @[@"NetworkStatusChanged"];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
- (void)startObserving {
|
|
41
|
+
_hasListeners = YES;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
- (void)stopObserving {
|
|
45
|
+
_hasListeners = NO;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
- (void)sendNetworkStatusEvent:(BOOL)isConnected {
|
|
49
|
+
if (_hasListeners) {
|
|
50
|
+
[self sendEventWithName:@"NetworkStatusChanged" body:@{@"isConnected": @(isConnected)}];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
- (void)addListener:(NSString *)eventName {
|
|
55
|
+
// Required by TurboModules spec
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
- (void)removeListeners:(double)count {
|
|
59
|
+
// Required by TurboModules spec
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
63
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
64
|
+
{
|
|
65
|
+
return std::make_shared<facebook::react::NativeReactNativeSmartNetinfoSpecJSI>(params);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
+ (NSString *)moduleName
|
|
69
|
+
{
|
|
70
|
+
return @"ReactNativeSmartNetinfo";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@end
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akbeniwal/react-native-smart-netinfo",
|
|
3
|
-
|
|
4
|
-
"version": "1.0.2",
|
|
3
|
+
"version": "1.0.4",
|
|
5
4
|
"description": "A lightweight smart network monitoring library for React Native with internet reachability, latency monitoring, connection quality detection and speed testing.",
|
|
6
5
|
"main": "dist/index.js",
|
|
7
6
|
"types": "dist/index.d.ts",
|
|
8
7
|
"files": [
|
|
9
8
|
"dist",
|
|
9
|
+
"android",
|
|
10
|
+
"ios",
|
|
11
|
+
"*.podspec",
|
|
10
12
|
"README.md",
|
|
11
13
|
"LICENSE"
|
|
12
14
|
],
|
|
@@ -52,5 +54,13 @@
|
|
|
52
54
|
},
|
|
53
55
|
"engines": {
|
|
54
56
|
"node": ">=18"
|
|
57
|
+
},
|
|
58
|
+
"codegenConfig": {
|
|
59
|
+
"name": "ReactNativeSmartNetinfoSpec",
|
|
60
|
+
"type": "modules",
|
|
61
|
+
"jsSrcsDir": "src",
|
|
62
|
+
"android": {
|
|
63
|
+
"javaPackageName": "com.akbeniwal.reactnativesmartnetinfo"
|
|
64
|
+
}
|
|
55
65
|
}
|
|
56
66
|
}
|