@symbiote-native/network 0.0.1
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/LICENSE +21 -0
- package/README.md +202 -0
- package/build/angular/index.d.ts +2 -0
- package/build/angular/index.js +4 -0
- package/build/angular/services/network-state.service/index.d.ts +6 -0
- package/build/angular/services/network-state.service/index.js +68 -0
- package/build/core/index.d.ts +3 -0
- package/build/core/index.js +2 -0
- package/build/core/native-module.d.ts +9 -0
- package/build/core/native-module.js +3 -0
- package/build/core/network.d.ts +13 -0
- package/build/core/network.js +34 -0
- package/build/core/types.d.ts +55 -0
- package/build/core/types.js +40 -0
- package/build/react/hooks/use-network-state/index.d.ts +2 -0
- package/build/react/hooks/use-network-state/index.js +14 -0
- package/build/react/index.d.ts +2 -0
- package/build/react/index.js +3 -0
- package/build/vue/composables/use-network-state/index.d.ts +3 -0
- package/build/vue/composables/use-network-state/index.js +21 -0
- package/build/vue/index.d.ts +2 -0
- package/build/vue/index.js +3 -0
- package/build-ngc/angular/index.d.ts +2 -0
- package/build-ngc/angular/index.js +5 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/angular/services/network-state.service/index.d.ts +9 -0
- package/build-ngc/angular/services/network-state.service/index.js +27 -0
- package/build-ngc/angular/services/network-state.service/index.js.map +1 -0
- package/build-ngc/core/index.d.ts +3 -0
- package/build-ngc/core/index.js +3 -0
- package/build-ngc/core/index.js.map +1 -0
- package/build-ngc/core/native-module.d.ts +9 -0
- package/build-ngc/core/native-module.js +4 -0
- package/build-ngc/core/native-module.js.map +1 -0
- package/build-ngc/core/network.d.ts +13 -0
- package/build-ngc/core/network.js +35 -0
- package/build-ngc/core/network.js.map +1 -0
- package/build-ngc/core/types.d.ts +55 -0
- package/build-ngc/core/types.js +41 -0
- package/build-ngc/core/types.js.map +1 -0
- package/native-link.json +12 -0
- package/package.json +107 -0
- package/src/angular/index.ts +13 -0
- package/src/angular/services/network-state.service/index.ts +26 -0
- package/src/angular/services/network-state.service/network-state.service.test.ts +103 -0
- package/src/core/index.ts +8 -0
- package/src/core/native-module.ts +19 -0
- package/src/core/network.test.ts +103 -0
- package/src/core/network.ts +42 -0
- package/src/core/types.ts +60 -0
- package/src/react/hooks/use-network-state/index.ts +17 -0
- package/src/react/hooks/use-network-state/use-network-state.test.tsx +91 -0
- package/src/react/index.ts +13 -0
- package/src/vue/composables/use-network-state/index.ts +31 -0
- package/src/vue/composables/use-network-state/use-network-state.test.ts +100 -0
- package/src/vue/index.ts +13 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 A. Prokopenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# @symbiote-native/network
|
|
2
|
+
|
|
3
|
+
Port of [`expo-network`](https://docs.expo.dev/versions/latest/sdk/network/) for
|
|
4
|
+
[SymbioteNative](../../README.md) — network connection state (type/connected/internet-reachable)
|
|
5
|
+
with a change listener, device IP address, and airplane-mode detection, reachable from every
|
|
6
|
+
adapter (React, Vue, Angular), not just React.
|
|
7
|
+
|
|
8
|
+
Built the same way as [`@symbiote-native/battery`](../battery) and
|
|
9
|
+
[`@symbiote-native/local-auth`](../local-auth), an `expo-modules-core`-based wrapper (see the
|
|
10
|
+
`symbiote-expo-native-module` project skill for the full mechanism: why `expo-modules-core` is
|
|
11
|
+
depended on directly and never the `expo` meta-package, why the upstream JS is hand-ported into
|
|
12
|
+
`core/` rather than imported, and how autolinking picks up the native module).
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @symbiote-native/network
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Depends on `expo-network` and `expo-modules-core` directly (regular dependencies, pinned to exact
|
|
21
|
+
versions — never a caret range, since this package's `core/` is hand-ported against one specific
|
|
22
|
+
native API shape and a newer resolve could silently drift the two apart). Never install
|
|
23
|
+
`expo-network` yourself, and never add the `expo` package to this project — it bundles its own
|
|
24
|
+
Metro/Babel pipeline that conflicts with this project's own.
|
|
25
|
+
|
|
26
|
+
### Required one-time step: native autolinking wiring
|
|
27
|
+
|
|
28
|
+
Unlike a plain RN native module, `expo-network`'s native code is discovered by
|
|
29
|
+
`expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism — this needs wiring
|
|
30
|
+
into the native host app **once**, covering this package and every other `expo-modules-core`
|
|
31
|
+
package (`@symbiote-native/battery`, `@symbiote-native/sensors`, `@symbiote-native/local-auth`)
|
|
32
|
+
with zero further changes:
|
|
33
|
+
|
|
34
|
+
| Platform | Touches |
|
|
35
|
+
|---|---|
|
|
36
|
+
| iOS | `ios/Podfile` — add `use_expo_modules!` |
|
|
37
|
+
| iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
|
|
38
|
+
| Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
|
|
39
|
+
| Android | `MainApplication.kt` — Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
|
|
40
|
+
|
|
41
|
+
Full mechanics — the Podfile pieces that normally ship inside the `expo` package, the `expo`
|
|
42
|
+
peer-dependency exclusion list — live in the `symbiote-expo-native-module` skill. Reference
|
|
43
|
+
implementation: `.examples/expo-react/ios/Podfile` and
|
|
44
|
+
`.examples/expo-react/android/app/src/main/java/com/canary/MainApplication.kt`.
|
|
45
|
+
|
|
46
|
+
No platform permission string is needed for network's surface — connection state, IP address, and
|
|
47
|
+
airplane-mode detection all read system state with no runtime permission prompt on either
|
|
48
|
+
platform.
|
|
49
|
+
|
|
50
|
+
## Shape
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
src/core/ network.ts — get*Async functions + addNetworkStateListener subscription.
|
|
54
|
+
native-module.ts resolves the native module through expo-modules-core's
|
|
55
|
+
requireNativeModule. types.ts — NetworkStateType enum, NetworkState,
|
|
56
|
+
NetworkStateEvent, hand-ported from Network.types.ts.
|
|
57
|
+
src/react/hooks/ @symbiote-native/network/react — useNetworkState
|
|
58
|
+
src/vue/composables/ @symbiote-native/network/vue — useNetworkState (same name)
|
|
59
|
+
src/angular/services/ @symbiote-native/network/angular — NetworkStateService (`.connect()`
|
|
60
|
+
returns a Signal)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Each adapter's hook/composable/service is a thin lifecycle wrapper (seed from the one-shot
|
|
64
|
+
`getNetworkStateAsync()` call, subscribe to `addNetworkStateListener`, unsubscribe on unmount) over
|
|
65
|
+
the same `core` functions — the subscription logic is written once and shared by all three.
|
|
66
|
+
|
|
67
|
+
## Use it
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
// React — examples/expo-react/screens/NetworkScreen.tsx
|
|
71
|
+
import { useEffect, useState } from 'react';
|
|
72
|
+
import { getIpAddressAsync, isAirplaneModeEnabledAsync } from '@symbiote-native/network';
|
|
73
|
+
import { useNetworkState } from '@symbiote-native/network/react';
|
|
74
|
+
|
|
75
|
+
function NetworkScreen() {
|
|
76
|
+
const networkState = useNetworkState(); // { type, isConnected, isInternetReachable }
|
|
77
|
+
const [ipAddress, setIpAddress] = useState<string | null>(null);
|
|
78
|
+
const [isAirplaneMode, setIsAirplaneMode] = useState<boolean | null>(null);
|
|
79
|
+
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
Promise.all([getIpAddressAsync(), isAirplaneModeEnabledAsync()]).then(([ip, airplaneMode]) => {
|
|
82
|
+
setIpAddress(ip);
|
|
83
|
+
setIsAirplaneMode(airplaneMode);
|
|
84
|
+
});
|
|
85
|
+
}, [networkState]);
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<>
|
|
89
|
+
<Text>{networkState.isConnected ? `Connected via ${networkState.type}` : 'Offline'}</Text>
|
|
90
|
+
<Text>{ipAddress ?? 'checking…'}</Text>
|
|
91
|
+
<Text>{isAirplaneMode ? 'Airplane mode: On' : 'Airplane mode: Off'}</Text>
|
|
92
|
+
</>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
```vue
|
|
98
|
+
<!-- Vue — examples/expo-vue-sfc/screens/NetworkScreen.vue -->
|
|
99
|
+
<script setup lang="ts">
|
|
100
|
+
import { ref, watch } from 'vue';
|
|
101
|
+
import { Text } from '@symbiote-native/vue';
|
|
102
|
+
import { getIpAddressAsync, isAirplaneModeEnabledAsync } from '@symbiote-native/network';
|
|
103
|
+
import { useNetworkState } from '@symbiote-native/network/vue';
|
|
104
|
+
|
|
105
|
+
const networkState = useNetworkState(); // Ref<NetworkState>
|
|
106
|
+
const ipAddress = ref<string | null>(null);
|
|
107
|
+
const isAirplaneMode = ref<boolean | null>(null);
|
|
108
|
+
|
|
109
|
+
watch(
|
|
110
|
+
networkState,
|
|
111
|
+
() => {
|
|
112
|
+
void Promise.all([getIpAddressAsync(), isAirplaneModeEnabledAsync()]).then(([ip, airplaneMode]) => {
|
|
113
|
+
ipAddress.value = ip;
|
|
114
|
+
isAirplaneMode.value = airplaneMode;
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
{ immediate: true },
|
|
118
|
+
);
|
|
119
|
+
</script>
|
|
120
|
+
<template>
|
|
121
|
+
<Text>{{ networkState.isConnected ? `Connected via ${networkState.type}` : 'Offline' }}</Text>
|
|
122
|
+
<Text>{{ ipAddress ?? 'checking…' }}</Text>
|
|
123
|
+
</template>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
// Angular — examples/expo-angular/src/screens/NetworkScreen.ts
|
|
128
|
+
import { Component, effect, inject, signal } from '@angular/core';
|
|
129
|
+
import { Text } from '@symbiote-native/angular';
|
|
130
|
+
import {
|
|
131
|
+
NetworkStateService,
|
|
132
|
+
getIpAddressAsync,
|
|
133
|
+
isAirplaneModeEnabledAsync,
|
|
134
|
+
} from '@symbiote-native/network/angular';
|
|
135
|
+
|
|
136
|
+
@Component({
|
|
137
|
+
standalone: true,
|
|
138
|
+
imports: [Text],
|
|
139
|
+
template: `<Text>{{ ipAddress() }}</Text>`,
|
|
140
|
+
})
|
|
141
|
+
export class NetworkScreen {
|
|
142
|
+
readonly networkState = inject(NetworkStateService).connect(); // Signal<NetworkState>
|
|
143
|
+
readonly ipAddress = signal<string | null>(null);
|
|
144
|
+
|
|
145
|
+
constructor() {
|
|
146
|
+
effect(() => {
|
|
147
|
+
this.networkState();
|
|
148
|
+
Promise.all([getIpAddressAsync(), isAirplaneModeEnabledAsync()]).then(([ip]) => {
|
|
149
|
+
this.ipAddress.set(ip);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Each of the three demo screens above also re-fetches the IP address and airplane-mode card
|
|
157
|
+
whenever the live network state changes (toggling Wi-Fi/airplane mode on the device updates both
|
|
158
|
+
cards together) — see the linked files for the full version, including the connection-type label
|
|
159
|
+
switch and the layout around it.
|
|
160
|
+
|
|
161
|
+
## API
|
|
162
|
+
|
|
163
|
+
A mix of stateless async functions and one listener-based subscription
|
|
164
|
+
(`addNetworkStateListener`), with its own adapter-level lifecycle hook.
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
getNetworkStateAsync(): Promise<NetworkState> // { type, isConnected, isInternetReachable }
|
|
168
|
+
getIpAddressAsync(): Promise<string> // device's IPv4 address, "0.0.0.0" if unavailable
|
|
169
|
+
isAirplaneModeEnabledAsync(): Promise<boolean> // Android only
|
|
170
|
+
addNetworkStateListener(listener): EventSubscription // fires whenever the network state changes
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Plus `NetworkStateType` (enum: `NONE`/`UNKNOWN`/`CELLULAR`/`WIFI`/`BLUETOOTH`/`ETHERNET`/`WIMAX`/
|
|
174
|
+
`VPN`/`OTHER`), `NetworkState`, `NetworkStateEvent` — ported from upstream's `Network.types.ts`.
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import { getNetworkStateAsync, addNetworkStateListener } from '@symbiote-native/network';
|
|
178
|
+
|
|
179
|
+
// framework-scoped entry points re-export the same free functions, plus a lifecycle
|
|
180
|
+
// hook/composable/service:
|
|
181
|
+
import { useNetworkState } from '@symbiote-native/network/react';
|
|
182
|
+
import { useNetworkState } from '@symbiote-native/network/vue';
|
|
183
|
+
import { NetworkStateService } from '@symbiote-native/network/angular';
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`useNetworkState` seeds its initial value from a one-shot `getNetworkStateAsync()` call, then
|
|
187
|
+
subscribes to `addNetworkStateListener` for updates, and unsubscribes on unmount — mirroring
|
|
188
|
+
upstream's own `useNetworkState`.
|
|
189
|
+
|
|
190
|
+
## Test it
|
|
191
|
+
|
|
192
|
+
No Fabric/Descriptor angle at all — network is a pure async-function + `EventEmitter` listener
|
|
193
|
+
surface, never a view. Tests inject a fake native-module object in place of the real
|
|
194
|
+
`requireNativeModule` resolution (`src/core/network.test.ts`,
|
|
195
|
+
`src/{react,vue,angular}/**/*.test.{ts,tsx}`, `vitest`), the same pattern
|
|
196
|
+
`@symbiote-native/battery`/`@symbiote-native/sensors`/`@symbiote-native/local-auth` use — no
|
|
197
|
+
`installFabric()`, no ViewConfig. Native rendering itself is verified on-device — see the parent
|
|
198
|
+
[README](../../README.md).
|
|
199
|
+
|
|
200
|
+
The Android/iOS native wiring is done across all four `examples/expo-*` canary apps
|
|
201
|
+
(`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-angular`);
|
|
202
|
+
this package isn't yet in the public, non-Expo `examples/react` canary.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { NetworkStateService } from './services/network-state.service';
|
|
2
|
+
export { getNetworkStateAsync, getIpAddressAsync, isAirplaneModeEnabledAsync, addNetworkStateListener, NetworkStateType, type NetworkState, type NetworkStateEvent, type EventSubscription, } from '../core';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// NetworkStateService is the Angular-only lifecycle half; the free functions and event
|
|
2
|
+
// subscription plumbing all live in core, shared with React/Vue.
|
|
3
|
+
export { NetworkStateService } from './services/network-state.service/index.js';
|
|
4
|
+
export { getNetworkStateAsync, getIpAddressAsync, isAirplaneModeEnabledAsync, addNetworkStateListener, NetworkStateType, } from '../core/index.js';
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
2
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
3
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
4
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
5
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
6
|
+
var _, done = false;
|
|
7
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
8
|
+
var context = {};
|
|
9
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
10
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
11
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
12
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
13
|
+
if (kind === "accessor") {
|
|
14
|
+
if (result === void 0) continue;
|
|
15
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
16
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
17
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
18
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
19
|
+
}
|
|
20
|
+
else if (_ = accept(result)) {
|
|
21
|
+
if (kind === "field") initializers.unshift(_);
|
|
22
|
+
else descriptor[key] = _;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
26
|
+
done = true;
|
|
27
|
+
};
|
|
28
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
29
|
+
var useValue = arguments.length > 2;
|
|
30
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
31
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
32
|
+
}
|
|
33
|
+
return useValue ? value : void 0;
|
|
34
|
+
};
|
|
35
|
+
import { effect, inject, Injectable, Injector, signal } from '@angular/core';
|
|
36
|
+
import { addNetworkStateListener, getNetworkStateAsync } from '../../../core/index.js';
|
|
37
|
+
// Angular twin of React's `useNetworkState` hook and Vue's `useNetworkState` composable.
|
|
38
|
+
//
|
|
39
|
+
// readonly networkState = inject(NetworkStateService).connect();
|
|
40
|
+
// // template: {{ networkState().type }}
|
|
41
|
+
let NetworkStateService = (() => {
|
|
42
|
+
let _classDecorators = [Injectable({ providedIn: 'root' })];
|
|
43
|
+
let _classDescriptor;
|
|
44
|
+
let _classExtraInitializers = [];
|
|
45
|
+
let _classThis;
|
|
46
|
+
var NetworkStateService = class {
|
|
47
|
+
static { _classThis = this; }
|
|
48
|
+
static {
|
|
49
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
50
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
51
|
+
NetworkStateService = _classThis = _classDescriptor.value;
|
|
52
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
53
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
54
|
+
}
|
|
55
|
+
injector = inject(Injector);
|
|
56
|
+
connect() {
|
|
57
|
+
const networkState = signal({});
|
|
58
|
+
effect(onCleanup => {
|
|
59
|
+
getNetworkStateAsync().then(state => networkState.set(state));
|
|
60
|
+
const subscription = addNetworkStateListener(event => networkState.set(event));
|
|
61
|
+
onCleanup(() => subscription.remove());
|
|
62
|
+
}, { injector: this.injector });
|
|
63
|
+
return networkState.asReadonly();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
return NetworkStateService = _classThis;
|
|
67
|
+
})();
|
|
68
|
+
export { NetworkStateService };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EventSubscription } from 'expo-modules-core';
|
|
2
|
+
import type { NetworkStateEvent } from './types';
|
|
3
|
+
export type INativeNetworkModule = {
|
|
4
|
+
getNetworkStateAsync?(): Promise<NetworkStateEvent>;
|
|
5
|
+
getIpAddressAsync?(): Promise<string>;
|
|
6
|
+
isAirplaneModeEnabledAsync?(): Promise<boolean>;
|
|
7
|
+
addListener(eventName: 'onNetworkStateChanged', listener: (event: NetworkStateEvent) => void): EventSubscription;
|
|
8
|
+
};
|
|
9
|
+
export declare const expoNetwork: INativeNetworkModule;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type EventSubscription } from 'expo-modules-core';
|
|
2
|
+
import type { NetworkState, NetworkStateEvent } from './types';
|
|
3
|
+
/** Gets the device's current network connection state. */
|
|
4
|
+
export declare function getNetworkStateAsync(): Promise<NetworkState>;
|
|
5
|
+
/** Gets the device's current IPv4 address. Returns `"0.0.0.0"` if it could not be retrieved. */
|
|
6
|
+
export declare function getIpAddressAsync(): Promise<string>;
|
|
7
|
+
/**
|
|
8
|
+
* Tells if the device is in airplane mode.
|
|
9
|
+
* @platform android
|
|
10
|
+
*/
|
|
11
|
+
export declare function isAirplaneModeEnabledAsync(): Promise<boolean>;
|
|
12
|
+
/** Subscribe to network state changes (connection type, connected, internet reachable). */
|
|
13
|
+
export declare function addNetworkStateListener(listener: (event: NetworkStateEvent) => void): EventSubscription;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Hand-ported from .vendors/expo/packages/expo-network/src/Network.ts (sdk-57). Unlike
|
|
2
|
+
// battery, upstream Network throws UnavailabilityError when the native method is absent —
|
|
3
|
+
// same convention as packages/haptics/src/core/haptics.ts.
|
|
4
|
+
import { UnavailabilityError } from 'expo-modules-core';
|
|
5
|
+
import { expoNetwork } from './native-module.js';
|
|
6
|
+
const NATIVE_MODULE_NAME = 'expo-network';
|
|
7
|
+
/** Gets the device's current network connection state. */
|
|
8
|
+
export async function getNetworkStateAsync() {
|
|
9
|
+
if (!expoNetwork.getNetworkStateAsync) {
|
|
10
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getNetworkStateAsync');
|
|
11
|
+
}
|
|
12
|
+
return expoNetwork.getNetworkStateAsync();
|
|
13
|
+
}
|
|
14
|
+
/** Gets the device's current IPv4 address. Returns `"0.0.0.0"` if it could not be retrieved. */
|
|
15
|
+
export async function getIpAddressAsync() {
|
|
16
|
+
if (!expoNetwork.getIpAddressAsync) {
|
|
17
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'getIpAddressAsync');
|
|
18
|
+
}
|
|
19
|
+
return expoNetwork.getIpAddressAsync();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Tells if the device is in airplane mode.
|
|
23
|
+
* @platform android
|
|
24
|
+
*/
|
|
25
|
+
export async function isAirplaneModeEnabledAsync() {
|
|
26
|
+
if (!expoNetwork.isAirplaneModeEnabledAsync) {
|
|
27
|
+
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'isAirplaneModeEnabledAsync');
|
|
28
|
+
}
|
|
29
|
+
return expoNetwork.isAirplaneModeEnabledAsync();
|
|
30
|
+
}
|
|
31
|
+
/** Subscribe to network state changes (connection type, connected, internet reachable). */
|
|
32
|
+
export function addNetworkStateListener(listener) {
|
|
33
|
+
return expoNetwork.addListener('onNetworkStateChanged', listener);
|
|
34
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export declare enum NetworkStateType {
|
|
2
|
+
/** No active network connection detected. */
|
|
3
|
+
NONE = "NONE",
|
|
4
|
+
/** The connection type could not be determined. */
|
|
5
|
+
UNKNOWN = "UNKNOWN",
|
|
6
|
+
/** Active network connection over mobile data.
|
|
7
|
+
* @platform android
|
|
8
|
+
* @platform ios
|
|
9
|
+
*/
|
|
10
|
+
CELLULAR = "CELLULAR",
|
|
11
|
+
/** Active network connection over Wi-Fi.
|
|
12
|
+
* @platform android
|
|
13
|
+
* @platform ios
|
|
14
|
+
*/
|
|
15
|
+
WIFI = "WIFI",
|
|
16
|
+
/** Active network connection over Bluetooth.
|
|
17
|
+
* @platform android
|
|
18
|
+
*/
|
|
19
|
+
BLUETOOTH = "BLUETOOTH",
|
|
20
|
+
/** Active network connection over Ethernet.
|
|
21
|
+
* @platform android
|
|
22
|
+
* @platform ios
|
|
23
|
+
*/
|
|
24
|
+
ETHERNET = "ETHERNET",
|
|
25
|
+
/** Active network connection over WiMAX.
|
|
26
|
+
* @platform android
|
|
27
|
+
*/
|
|
28
|
+
WIMAX = "WIMAX",
|
|
29
|
+
/** Active network connection over VPN.
|
|
30
|
+
* @platform android
|
|
31
|
+
*/
|
|
32
|
+
VPN = "VPN",
|
|
33
|
+
/** Active network connection over other network connection types.
|
|
34
|
+
* @platform android
|
|
35
|
+
*/
|
|
36
|
+
OTHER = "OTHER"
|
|
37
|
+
}
|
|
38
|
+
export type NetworkState = {
|
|
39
|
+
/** The current network connection type. */
|
|
40
|
+
type?: NetworkStateType;
|
|
41
|
+
/**
|
|
42
|
+
* If there is an active network connection. Note that this does not mean internet is
|
|
43
|
+
* reachable. `false` when `type` is `NONE` or `UNKNOWN`, `true` otherwise.
|
|
44
|
+
*/
|
|
45
|
+
isConnected?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* If the internet is reachable with the currently active network connection. On Android this
|
|
48
|
+
* requires internet capability, confirmed internet access, and a usable connection state; VPN
|
|
49
|
+
* connections also require non-zero downstream bandwidth. On iOS this always matches
|
|
50
|
+
* `isConnected`.
|
|
51
|
+
*/
|
|
52
|
+
isInternetReachable?: boolean;
|
|
53
|
+
};
|
|
54
|
+
/** Passed as the argument to listeners registered with `addNetworkStateListener()`. */
|
|
55
|
+
export type NetworkStateEvent = NetworkState;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Hand-ported from .vendors/expo/packages/expo-network/src/Network.types.ts (sdk-57) — plain
|
|
2
|
+
// data shapes, no `expo` meta-package import to swap out (unlike local-auth's types.ts).
|
|
3
|
+
export var NetworkStateType;
|
|
4
|
+
(function (NetworkStateType) {
|
|
5
|
+
/** No active network connection detected. */
|
|
6
|
+
NetworkStateType["NONE"] = "NONE";
|
|
7
|
+
/** The connection type could not be determined. */
|
|
8
|
+
NetworkStateType["UNKNOWN"] = "UNKNOWN";
|
|
9
|
+
/** Active network connection over mobile data.
|
|
10
|
+
* @platform android
|
|
11
|
+
* @platform ios
|
|
12
|
+
*/
|
|
13
|
+
NetworkStateType["CELLULAR"] = "CELLULAR";
|
|
14
|
+
/** Active network connection over Wi-Fi.
|
|
15
|
+
* @platform android
|
|
16
|
+
* @platform ios
|
|
17
|
+
*/
|
|
18
|
+
NetworkStateType["WIFI"] = "WIFI";
|
|
19
|
+
/** Active network connection over Bluetooth.
|
|
20
|
+
* @platform android
|
|
21
|
+
*/
|
|
22
|
+
NetworkStateType["BLUETOOTH"] = "BLUETOOTH";
|
|
23
|
+
/** Active network connection over Ethernet.
|
|
24
|
+
* @platform android
|
|
25
|
+
* @platform ios
|
|
26
|
+
*/
|
|
27
|
+
NetworkStateType["ETHERNET"] = "ETHERNET";
|
|
28
|
+
/** Active network connection over WiMAX.
|
|
29
|
+
* @platform android
|
|
30
|
+
*/
|
|
31
|
+
NetworkStateType["WIMAX"] = "WIMAX";
|
|
32
|
+
/** Active network connection over VPN.
|
|
33
|
+
* @platform android
|
|
34
|
+
*/
|
|
35
|
+
NetworkStateType["VPN"] = "VPN";
|
|
36
|
+
/** Active network connection over other network connection types.
|
|
37
|
+
* @platform android
|
|
38
|
+
*/
|
|
39
|
+
NetworkStateType["OTHER"] = "OTHER";
|
|
40
|
+
})(NetworkStateType || (NetworkStateType = {}));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// React lifecycle wiring over the framework-agnostic core (core/network.ts). Seeds the initial
|
|
2
|
+
// value with a one-shot getNetworkStateAsync() call before the first native event fires, matching
|
|
3
|
+
// upstream's own useNetworkState.
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
import { addNetworkStateListener, getNetworkStateAsync } from '../../../core/index.js';
|
|
6
|
+
export function useNetworkState() {
|
|
7
|
+
const [networkState, setNetworkState] = useState({});
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
getNetworkStateAsync().then(setNetworkState);
|
|
10
|
+
const subscription = addNetworkStateListener(event => setNetworkState(event));
|
|
11
|
+
return () => subscription.remove();
|
|
12
|
+
}, []);
|
|
13
|
+
return networkState;
|
|
14
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// @symbiote-native/network/react: the React entry over the framework-agnostic core.
|
|
2
|
+
export { useNetworkState } from './hooks/use-network-state/index.js';
|
|
3
|
+
export { getNetworkStateAsync, getIpAddressAsync, isAirplaneModeEnabledAsync, addNetworkStateListener, NetworkStateType, } from '../core/index.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Vue lifecycle wiring over the framework-agnostic core (core/network.ts). Seeds the initial
|
|
2
|
+
// value with a one-shot getNetworkStateAsync() call before the first native event fires, matching
|
|
3
|
+
// upstream's own useNetworkState.
|
|
4
|
+
import { onMounted, onUnmounted, ref } from '@vue/runtime-core';
|
|
5
|
+
import { addNetworkStateListener, getNetworkStateAsync, } from '../../../core/index.js';
|
|
6
|
+
export function useNetworkState() {
|
|
7
|
+
const networkState = ref({});
|
|
8
|
+
let subscription;
|
|
9
|
+
onMounted(() => {
|
|
10
|
+
getNetworkStateAsync().then(state => {
|
|
11
|
+
networkState.value = state;
|
|
12
|
+
});
|
|
13
|
+
subscription = addNetworkStateListener(event => {
|
|
14
|
+
networkState.value = event;
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
onUnmounted(() => {
|
|
18
|
+
subscription?.remove();
|
|
19
|
+
});
|
|
20
|
+
return networkState;
|
|
21
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// @symbiote-native/network/vue: the Vue entry over the framework-agnostic core.
|
|
2
|
+
export { useNetworkState } from './composables/use-network-state/index.js';
|
|
3
|
+
export { getNetworkStateAsync, getIpAddressAsync, isAirplaneModeEnabledAsync, addNetworkStateListener, NetworkStateType, } from '../core/index.js';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { NetworkStateService } from './services/network-state.service';
|
|
2
|
+
export { getNetworkStateAsync, getIpAddressAsync, isAirplaneModeEnabledAsync, addNetworkStateListener, NetworkStateType, type NetworkState, type NetworkStateEvent, type EventSubscription, } from '../core';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// NetworkStateService is the Angular-only lifecycle half; the free functions and event
|
|
2
|
+
// subscription plumbing all live in core, shared with React/Vue.
|
|
3
|
+
export { NetworkStateService } from './services/network-state.service';
|
|
4
|
+
export { getNetworkStateAsync, getIpAddressAsync, isAirplaneModeEnabledAsync, addNetworkStateListener, NetworkStateType, } from '../core';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/angular/index.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,iEAAiE;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,0BAA0B,EAC1B,uBAAuB,EACvB,gBAAgB,GAIjB,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Signal } from '@angular/core';
|
|
2
|
+
import { type NetworkState } from '../../../core';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
export declare class NetworkStateService {
|
|
5
|
+
private readonly injector;
|
|
6
|
+
connect(): Signal<NetworkState>;
|
|
7
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<NetworkStateService, never>;
|
|
8
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<NetworkStateService>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { effect, inject, Injectable, Injector, signal } from '@angular/core';
|
|
2
|
+
import { addNetworkStateListener, getNetworkStateAsync } from '../../../core';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
// Angular twin of React's `useNetworkState` hook and Vue's `useNetworkState` composable.
|
|
5
|
+
//
|
|
6
|
+
// readonly networkState = inject(NetworkStateService).connect();
|
|
7
|
+
// // template: {{ networkState().type }}
|
|
8
|
+
export class NetworkStateService {
|
|
9
|
+
injector = inject(Injector);
|
|
10
|
+
connect() {
|
|
11
|
+
const networkState = signal({}, /* @ts-ignore */
|
|
12
|
+
...(ngDevMode ? [{ debugName: "networkState" }] : /* istanbul ignore next */ []));
|
|
13
|
+
effect(onCleanup => {
|
|
14
|
+
getNetworkStateAsync().then(state => networkState.set(state));
|
|
15
|
+
const subscription = addNetworkStateListener(event => networkState.set(event));
|
|
16
|
+
onCleanup(() => subscription.remove());
|
|
17
|
+
}, { injector: this.injector });
|
|
18
|
+
return networkState.asReadonly();
|
|
19
|
+
}
|
|
20
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NetworkStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
21
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NetworkStateService, providedIn: 'root' });
|
|
22
|
+
}
|
|
23
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NetworkStateService, decorators: [{
|
|
24
|
+
type: Injectable,
|
|
25
|
+
args: [{ providedIn: 'root' }]
|
|
26
|
+
}] });
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/angular/services/network-state.service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAe,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAqB,MAAM,eAAe,CAAC;;AAEjG,yFAAyF;AACzF,EAAE;AACF,mEAAmE;AACnE,2CAA2C;AAE3C,MAAM,OAAO,mBAAmB;IACb,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,OAAO;QACL,MAAM,YAAY,GAAG,MAAM,CAAe,EAAE;yFAAC,CAAC;QAE9C,MAAM,CACJ,SAAS,CAAC,EAAE;YACV,oBAAoB,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YAC9D,MAAM,YAAY,GAAG,uBAAuB,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YAC/E,SAAS,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC;QACzC,CAAC,EACD,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAC5B,CAAC;QAEF,OAAO,YAAY,CAAC,UAAU,EAAE,CAAC;IACnC,CAAC;uGAhBU,mBAAmB;2GAAnB,mBAAmB,cADN,MAAM;;2FACnB,mBAAmB;kBAD/B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,gBAAgB,EAA6C,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EventSubscription } from 'expo-modules-core';
|
|
2
|
+
import type { NetworkStateEvent } from './types';
|
|
3
|
+
export type INativeNetworkModule = {
|
|
4
|
+
getNetworkStateAsync?(): Promise<NetworkStateEvent>;
|
|
5
|
+
getIpAddressAsync?(): Promise<string>;
|
|
6
|
+
isAirplaneModeEnabledAsync?(): Promise<boolean>;
|
|
7
|
+
addListener(eventName: 'onNetworkStateChanged', listener: (event: NetworkStateEvent) => void): EventSubscription;
|
|
8
|
+
};
|
|
9
|
+
export declare const expoNetwork: INativeNetworkModule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAIxD,MAAM,wBAAwB,GAAG,aAAa,CAAC;AAc/C,MAAM,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAAuB,wBAAwB,CAAC,CAAC"}
|