接入原生壳网络状态能力

新增 network.status 与 network.statusChanged HostBridge 能力

Expo 壳通过 expo-network 查询并订阅真实网络状态

Tauri 壳通过主站可达性查询和 WebView online/offline 事件同步网络状态

更新壳能力检查、测试和架构文档
This commit is contained in:
2026-06-18 02:35:48 +08:00
parent 346368f0e7
commit 586e46fa63
19 changed files with 551 additions and 5 deletions

View File

@@ -4,6 +4,7 @@ import {
isHostBridgeCapability,
normalizeHostBridgeBadgeCount,
normalizeHostBridgeColorScheme,
normalizeHostBridgeConnectionType,
normalizeHostBridgeExportFileName,
normalizeHostBridgeExternalUrl,
normalizeHostBridgeLifecycleState,
@@ -51,6 +52,8 @@ describe('HostBridge shared contract helpers', () => {
expect(isHostBridgeCapability('appearance.getColorScheme')).toBe(true);
expect(isHostBridgeCapability('share.open')).toBe(true);
expect(isHostBridgeCapability('app.lifecycle')).toBe(true);
expect(isHostBridgeCapability('network.status')).toBe(true);
expect(isHostBridgeCapability('network.statusChanged')).toBe(true);
expect(isHostBridgeCapability('app.setBadgeCount')).toBe(true);
expect(isHostBridgeCapability('navigation.canGoBack')).toBe(true);
expect(isHostBridgeCapability('unknown.capability')).toBe(false);
@@ -80,4 +83,13 @@ describe('HostBridge shared contract helpers', () => {
expect(normalizeHostBridgeLifecycleState('extension')).toBe('inactive');
expect(normalizeHostBridgeLifecycleState(null)).toBe('inactive');
});
test('归一化宿主网络连接类型', () => {
expect(normalizeHostBridgeConnectionType('WIFI')).toBe('wifi');
expect(normalizeHostBridgeConnectionType('CELLULAR')).toBe('cellular');
expect(normalizeHostBridgeConnectionType('ETHERNET')).toBe('ethernet');
expect(normalizeHostBridgeConnectionType('NONE')).toBe('none');
expect(normalizeHostBridgeConnectionType('satellite')).toBe('unknown');
expect(normalizeHostBridgeConnectionType(null)).toBe('unknown');
});
});

View File

@@ -24,6 +24,7 @@ export const HOST_BRIDGE_METHODS = [
'app.openExternalUrl',
'app.setTitle',
'app.setBadgeCount',
'network.status',
'clipboard.writeText',
'file.exportText',
'file.exportImage',
@@ -36,6 +37,7 @@ export const HOST_BRIDGE_CAPABILITIES = [
...HOST_BRIDGE_METHODS,
'host.events',
'app.lifecycle',
'network.statusChanged',
'navigation.canGoBack',
] as const;
@@ -120,6 +122,49 @@ export function normalizeHostBridgeLifecycleState(
: 'inactive';
}
export type HostNetworkConnectionType =
| 'none'
| 'unknown'
| 'cellular'
| 'wifi'
| 'ethernet'
| 'bluetooth'
| 'vpn'
| 'other';
export type NetworkStatusResult = {
isConnected: boolean;
isInternetReachable: boolean | null;
connectionType: HostNetworkConnectionType;
nativeType?: string;
};
export function normalizeHostBridgeConnectionType(
rawConnectionType: unknown,
): HostNetworkConnectionType {
if (typeof rawConnectionType !== 'string') {
return 'unknown';
}
const normalizedType = rawConnectionType.toLowerCase();
if (
normalizedType === 'none' ||
normalizedType === 'cellular' ||
normalizedType === 'wifi' ||
normalizedType === 'ethernet' ||
normalizedType === 'bluetooth' ||
normalizedType === 'vpn'
) {
return normalizedType;
}
if (normalizedType === 'other') {
return 'other';
}
return 'unknown';
}
export type HostAppearanceColorScheme = 'light' | 'dark' | 'unknown';
export type AppearanceColorSchemeResult = {