@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
|
@@ -0,0 +1,17 @@
|
|
|
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, type NetworkState } from '../../../core';
|
|
6
|
+
|
|
7
|
+
export function useNetworkState(): NetworkState {
|
|
8
|
+
const [networkState, setNetworkState] = useState<NetworkState>({});
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
getNetworkStateAsync().then(setNetworkState);
|
|
12
|
+
const subscription = addNetworkStateListener(event => setNetworkState(event));
|
|
13
|
+
return () => subscription.remove();
|
|
14
|
+
}, []);
|
|
15
|
+
|
|
16
|
+
return networkState;
|
|
17
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Co-located React-driven test (ADR 0025) for useNetworkState. See battery's
|
|
2
|
+
// use-battery-state.test.tsx for the shared rationale (mocks `core`, not expo-modules-core
|
|
3
|
+
// internals).
|
|
4
|
+
|
|
5
|
+
import { createElement, type ReactElement } from 'react';
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
7
|
+
import { mount, unmount, View } from '@symbiote-native/react';
|
|
8
|
+
import { installFabric } from '@symbiote-native/test-utils';
|
|
9
|
+
import { useNetworkState } from './index';
|
|
10
|
+
|
|
11
|
+
type INetworkState = { type?: string; isConnected?: boolean; isInternetReachable?: boolean };
|
|
12
|
+
|
|
13
|
+
const { addListener, getNetworkStateAsync, remove } = vi.hoisted(() => {
|
|
14
|
+
const remove = vi.fn();
|
|
15
|
+
return {
|
|
16
|
+
addListener: vi.fn((_listener: (event: INetworkState) => void) => ({ remove })),
|
|
17
|
+
getNetworkStateAsync: vi.fn(async () => ({
|
|
18
|
+
type: 'WIFI',
|
|
19
|
+
isConnected: true,
|
|
20
|
+
isInternetReachable: true,
|
|
21
|
+
})),
|
|
22
|
+
remove,
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
vi.mock('../../../core', () => ({
|
|
27
|
+
addNetworkStateListener: addListener,
|
|
28
|
+
getNetworkStateAsync,
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
const ROOT_TAG = 953;
|
|
32
|
+
|
|
33
|
+
const results: INetworkState[] = [];
|
|
34
|
+
|
|
35
|
+
function Probe(): ReactElement {
|
|
36
|
+
results.push(useNetworkState());
|
|
37
|
+
return createElement(View);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const fabric = installFabric();
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
fabric.reset();
|
|
44
|
+
results.length = 0;
|
|
45
|
+
vi.clearAllMocks();
|
|
46
|
+
getNetworkStateAsync.mockResolvedValue({
|
|
47
|
+
type: 'WIFI',
|
|
48
|
+
isConnected: true,
|
|
49
|
+
isInternetReachable: true,
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(() => unmount(ROOT_TAG));
|
|
54
|
+
|
|
55
|
+
describe('useNetworkState', () => {
|
|
56
|
+
it('reports an empty object before the initial fetch resolves', () => {
|
|
57
|
+
mount(ROOT_TAG, createElement(Probe));
|
|
58
|
+
|
|
59
|
+
expect(results[results.length - 1]).toEqual({});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('reports the fetched state once the initial getNetworkStateAsync() resolves', async () => {
|
|
63
|
+
mount(ROOT_TAG, createElement(Probe));
|
|
64
|
+
|
|
65
|
+
await vi.waitFor(() =>
|
|
66
|
+
expect(results[results.length - 1]).toEqual({
|
|
67
|
+
type: 'WIFI',
|
|
68
|
+
isConnected: true,
|
|
69
|
+
isInternetReachable: true,
|
|
70
|
+
}),
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('updates when the native listener fires', async () => {
|
|
75
|
+
mount(ROOT_TAG, createElement(Probe));
|
|
76
|
+
await vi.waitFor(() => expect(results[results.length - 1].type).toBe('WIFI'));
|
|
77
|
+
|
|
78
|
+
const listener = addListener.mock.calls[0][0];
|
|
79
|
+
listener({ type: 'CELLULAR', isConnected: true, isInternetReachable: false });
|
|
80
|
+
|
|
81
|
+
await vi.waitFor(() => expect(results[results.length - 1].type).toBe('CELLULAR'));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('unsubscribes from the native listener on unmount', () => {
|
|
85
|
+
mount(ROOT_TAG, createElement(Probe));
|
|
86
|
+
|
|
87
|
+
unmount(ROOT_TAG);
|
|
88
|
+
|
|
89
|
+
expect(remove).toHaveBeenCalledTimes(1);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// @symbiote-native/network/react: the React entry over the framework-agnostic core.
|
|
2
|
+
|
|
3
|
+
export { useNetworkState } from './hooks/use-network-state';
|
|
4
|
+
export {
|
|
5
|
+
getNetworkStateAsync,
|
|
6
|
+
getIpAddressAsync,
|
|
7
|
+
isAirplaneModeEnabledAsync,
|
|
8
|
+
addNetworkStateListener,
|
|
9
|
+
NetworkStateType,
|
|
10
|
+
type NetworkState,
|
|
11
|
+
type NetworkStateEvent,
|
|
12
|
+
type EventSubscription,
|
|
13
|
+
} from '../core';
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
|
|
5
|
+
import { onMounted, onUnmounted, ref, type Ref } from '@vue/runtime-core';
|
|
6
|
+
import {
|
|
7
|
+
addNetworkStateListener,
|
|
8
|
+
getNetworkStateAsync,
|
|
9
|
+
type EventSubscription,
|
|
10
|
+
type NetworkState,
|
|
11
|
+
} from '../../../core';
|
|
12
|
+
|
|
13
|
+
export function useNetworkState(): Ref<NetworkState> {
|
|
14
|
+
const networkState = ref<NetworkState>({});
|
|
15
|
+
let subscription: EventSubscription | undefined;
|
|
16
|
+
|
|
17
|
+
onMounted(() => {
|
|
18
|
+
getNetworkStateAsync().then(state => {
|
|
19
|
+
networkState.value = state;
|
|
20
|
+
});
|
|
21
|
+
subscription = addNetworkStateListener(event => {
|
|
22
|
+
networkState.value = event;
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
onUnmounted(() => {
|
|
27
|
+
subscription?.remove();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return networkState;
|
|
31
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Co-located Vue-driven test (ADR 0025) for useNetworkState. See battery's
|
|
2
|
+
// use-battery-state.test.ts for the shared rationale.
|
|
3
|
+
|
|
4
|
+
import { defineComponent, h, type Ref } from '@vue/runtime-core';
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
6
|
+
import { mount, unmount } from '@symbiote-native/vue';
|
|
7
|
+
import { installFabric } from '@symbiote-native/test-utils';
|
|
8
|
+
import { useNetworkState } from './index';
|
|
9
|
+
|
|
10
|
+
const ROOT_TAG = 9953;
|
|
11
|
+
|
|
12
|
+
type INetworkState = { type?: string; isConnected?: boolean; isInternetReachable?: boolean };
|
|
13
|
+
type IListener = (event: INetworkState) => void;
|
|
14
|
+
|
|
15
|
+
let registeredListener: IListener | undefined;
|
|
16
|
+
const removeMock = vi.fn();
|
|
17
|
+
const addListenerMock = vi.fn((listener: IListener) => {
|
|
18
|
+
registeredListener = listener;
|
|
19
|
+
return { remove: removeMock };
|
|
20
|
+
});
|
|
21
|
+
const getNetworkStateAsyncMock = vi.fn(async () => ({
|
|
22
|
+
type: 'WIFI',
|
|
23
|
+
isConnected: true,
|
|
24
|
+
isInternetReachable: true,
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
vi.mock('../../../core', () => ({
|
|
28
|
+
addNetworkStateListener: (listener: IListener) => addListenerMock(listener),
|
|
29
|
+
getNetworkStateAsync: () => getNetworkStateAsyncMock(),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
const fabric = installFabric();
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
fabric.reset();
|
|
36
|
+
registeredListener = undefined;
|
|
37
|
+
addListenerMock.mockClear();
|
|
38
|
+
removeMock.mockClear();
|
|
39
|
+
getNetworkStateAsyncMock.mockClear();
|
|
40
|
+
getNetworkStateAsyncMock.mockResolvedValue({
|
|
41
|
+
type: 'WIFI',
|
|
42
|
+
isConnected: true,
|
|
43
|
+
isInternetReachable: true,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
afterEach(() => unmount(ROOT_TAG));
|
|
48
|
+
|
|
49
|
+
function mountNetworkState(): Ref<INetworkState> {
|
|
50
|
+
let networkState: Ref<INetworkState> | undefined;
|
|
51
|
+
mount(
|
|
52
|
+
ROOT_TAG,
|
|
53
|
+
defineComponent({
|
|
54
|
+
setup: () => {
|
|
55
|
+
networkState = useNetworkState();
|
|
56
|
+
return () => h('symbiote-text', {}, 'network');
|
|
57
|
+
},
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
if (networkState === undefined) {
|
|
61
|
+
throw new Error('setup() did not run');
|
|
62
|
+
}
|
|
63
|
+
return networkState;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe('useNetworkState (Vue)', () => {
|
|
67
|
+
it('starts at an empty object before the initial fetch resolves', () => {
|
|
68
|
+
const networkState = mountNetworkState();
|
|
69
|
+
|
|
70
|
+
expect(networkState.value).toEqual({});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('updates to the fetched value once getNetworkStateAsync() resolves', async () => {
|
|
74
|
+
const networkState = mountNetworkState();
|
|
75
|
+
|
|
76
|
+
await vi.waitFor(() =>
|
|
77
|
+
expect(networkState.value).toEqual({
|
|
78
|
+
type: 'WIFI',
|
|
79
|
+
isConnected: true,
|
|
80
|
+
isInternetReachable: true,
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('updates the ref when the native listener fires', async () => {
|
|
86
|
+
const networkState = mountNetworkState();
|
|
87
|
+
await vi.waitFor(() => expect(networkState.value.type).toBe('WIFI'));
|
|
88
|
+
|
|
89
|
+
registeredListener?.({ type: 'CELLULAR', isConnected: true, isInternetReachable: false });
|
|
90
|
+
|
|
91
|
+
expect(networkState.value.type).toBe('CELLULAR');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('removes the subscription on unmount', () => {
|
|
95
|
+
mountNetworkState();
|
|
96
|
+
unmount(ROOT_TAG);
|
|
97
|
+
|
|
98
|
+
expect(removeMock).toHaveBeenCalledTimes(1);
|
|
99
|
+
});
|
|
100
|
+
});
|
package/src/vue/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// @symbiote-native/network/vue: the Vue entry over the framework-agnostic core.
|
|
2
|
+
|
|
3
|
+
export { useNetworkState } from './composables/use-network-state';
|
|
4
|
+
export {
|
|
5
|
+
getNetworkStateAsync,
|
|
6
|
+
getIpAddressAsync,
|
|
7
|
+
isAirplaneModeEnabledAsync,
|
|
8
|
+
addNetworkStateListener,
|
|
9
|
+
NetworkStateType,
|
|
10
|
+
type NetworkState,
|
|
11
|
+
type NetworkStateEvent,
|
|
12
|
+
type EventSubscription,
|
|
13
|
+
} from '../core';
|