收口移动导航响应边界
将 Expo 外链打开、WebView 刷新和受控导航成功响应包装收口到 navigation 模块 让移动 dispatch 只委托导航请求 同步移动壳配置门禁和共享记忆
This commit is contained in:
@@ -1952,9 +1952,12 @@ for (const snippet of [
|
||||
}
|
||||
|
||||
if (
|
||||
!dispatchSource.includes('openMobileHostBridgeExternalUrl(request.payload)') ||
|
||||
!dispatchSource.includes('openMobileHostBridgeNativePage(navigation, request.payload)') ||
|
||||
!dispatchSource.includes('reloadMobileHostBridgeWebView(navigation)') ||
|
||||
!dispatchSource.includes('openMobileHostBridgeExternalUrl(request)') ||
|
||||
!dispatchSource.includes('openMobileHostBridgeNativePage(request, navigation)') ||
|
||||
!dispatchSource.includes('reloadMobileHostBridgeWebView(request, navigation)') ||
|
||||
dispatchSource.includes('ok(request, await openMobileHostBridgeExternalUrl') ||
|
||||
dispatchSource.includes('ok(request, reloadMobileHostBridgeWebView') ||
|
||||
dispatchSource.includes('ok(\\n request,\\n openMobileHostBridgeNativePage') ||
|
||||
dispatchSource.includes("from 'expo-linking'") ||
|
||||
dispatchSource.includes('Linking.') ||
|
||||
dispatchSource.includes('openMobileShellExternalNavigation') ||
|
||||
@@ -1966,13 +1969,17 @@ if (
|
||||
}
|
||||
for (const snippet of [
|
||||
'openMobileHostBridgeExternalUrl',
|
||||
'request: HostBridgeRequest',
|
||||
"import * as Linking from 'expo-linking'",
|
||||
'openMobileShellExternalNavigation(Linking, externalUrlPayload.url)',
|
||||
'(request.payload as OpenExternalUrlPayload | undefined)?.url',
|
||||
'normalizeHostBridgeExternalUrlPayload',
|
||||
'openMobileHostBridgeNativePage',
|
||||
'(request.payload as NavigateNativePagePayload | undefined)?.url',
|
||||
'resolveMobileShellWebViewUrl',
|
||||
'buildMobileShellUrl(webViewUrl, navigation.urlOptions)',
|
||||
'reloadMobileHostBridgeWebView',
|
||||
'ok(request, true)',
|
||||
]) {
|
||||
if (!hostBridgeNavigationSource.includes(snippet)) {
|
||||
throw new Error(`mobile shell navigation module is missing ${snippet}`);
|
||||
|
||||
@@ -54,9 +54,9 @@ export async function dispatchMobileHostBridgeRequest(
|
||||
case 'appearance.getColorScheme':
|
||||
return getMobileHostBridgeAppearanceColorScheme(request);
|
||||
case 'app.openExternalUrl':
|
||||
return ok(request, await openMobileHostBridgeExternalUrl(request.payload));
|
||||
return openMobileHostBridgeExternalUrl(request);
|
||||
case 'app.reloadWebView':
|
||||
return ok(request, reloadMobileHostBridgeWebView(navigation));
|
||||
return reloadMobileHostBridgeWebView(request, navigation);
|
||||
case 'network.status':
|
||||
return getMobileHostBridgeNetworkStatus(request);
|
||||
case 'clipboard.writeText':
|
||||
@@ -92,10 +92,7 @@ export async function dispatchMobileHostBridgeRequest(
|
||||
case 'share.setTarget':
|
||||
return setMobileHostBridgeShareTarget(request);
|
||||
case 'navigation.openNativePage':
|
||||
return ok(
|
||||
request,
|
||||
openMobileHostBridgeNativePage(navigation, request.payload),
|
||||
);
|
||||
return openMobileHostBridgeNativePage(request, navigation);
|
||||
case 'auth.requestLogin':
|
||||
case 'payment.request':
|
||||
return failure(request, unsupported(request.method));
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as Linking from 'expo-linking';
|
||||
|
||||
import {
|
||||
type HostBridgeError,
|
||||
type HostBridgeRequest,
|
||||
type NavigateNativePagePayload,
|
||||
normalizeHostBridgeExternalUrlPayload,
|
||||
type OpenExternalUrlPayload,
|
||||
@@ -15,14 +16,15 @@ import { buildMobileShellUrl } from '../shell/url';
|
||||
import {
|
||||
type MobileHostBridgeNavigation,
|
||||
invalidRequest,
|
||||
ok,
|
||||
unsupported,
|
||||
} from './protocol';
|
||||
|
||||
export async function openMobileHostBridgeExternalUrl(
|
||||
payload: unknown,
|
||||
request: HostBridgeRequest,
|
||||
) {
|
||||
const externalUrlPayload = normalizeHostBridgeExternalUrlPayload(
|
||||
(payload as OpenExternalUrlPayload | undefined)?.url,
|
||||
(request.payload as OpenExternalUrlPayload | undefined)?.url,
|
||||
);
|
||||
if (!externalUrlPayload) {
|
||||
throw invalidRequest('url must use an allowed external protocol');
|
||||
@@ -37,18 +39,18 @@ export async function openMobileHostBridgeExternalUrl(
|
||||
} satisfies HostBridgeError;
|
||||
}
|
||||
|
||||
return true;
|
||||
return ok(request, true);
|
||||
}
|
||||
|
||||
export function openMobileHostBridgeNativePage(
|
||||
request: HostBridgeRequest,
|
||||
navigation: MobileHostBridgeNavigation | null,
|
||||
payload: unknown,
|
||||
) {
|
||||
if (!navigation) {
|
||||
throw unsupported('navigation.openNativePage');
|
||||
}
|
||||
|
||||
const url = (payload as NavigateNativePagePayload | undefined)?.url;
|
||||
const url = (request.payload as NavigateNativePagePayload | undefined)?.url;
|
||||
if (typeof url !== 'string') {
|
||||
throw invalidRequest('url is required');
|
||||
}
|
||||
@@ -64,10 +66,11 @@ export function openMobileHostBridgeNativePage(
|
||||
navigation.openWebViewUrl(
|
||||
buildMobileShellUrl(webViewUrl, navigation.urlOptions),
|
||||
);
|
||||
return true;
|
||||
return ok(request, true);
|
||||
}
|
||||
|
||||
export function reloadMobileHostBridgeWebView(
|
||||
request: HostBridgeRequest,
|
||||
navigation: MobileHostBridgeNavigation | null,
|
||||
) {
|
||||
if (!navigation) {
|
||||
@@ -75,5 +78,5 @@ export function reloadMobileHostBridgeWebView(
|
||||
}
|
||||
|
||||
navigation.reloadWebView();
|
||||
return true;
|
||||
return ok(request, true);
|
||||
}
|
||||
|
||||
@@ -2794,7 +2794,7 @@
|
||||
## 2026-06-19 原生壳导航桥接边界
|
||||
|
||||
- 背景:`app.openExternalUrl`、`navigation.openNativePage` 和 `app.reloadWebView` 已是受控 HostBridge 能力,但移动端和桌面端 `dispatch` 仍直接承接外链归一、同源 H5 跳转、宿主上下文补写和 WebView 刷新细节,后续继续补壳能力时容易让分发层重新变厚。
|
||||
- 决策:Expo 移动壳新增 `apps/mobile-shell/src/host-bridge/navigation.ts`,统一承接 HostBridge 外链打开、受控 H5 跳转和 WebView 刷新,底层继续复用 `src/shell/navigation.ts` 与 `src/shell/url.ts`;Tauri 桌面壳新增 `apps/desktop-shell/src-tauri/src/host_bridge/navigation.rs`,统一承接外链打开、同源 H5 跳转和主窗口刷新,底层继续复用 `shell::navigation` 的 URL 归一和宿主上下文补写。两端 `dispatch` 只保留 method 委托和响应包装,配置检查会拒绝分发层直接调用外链 opener、URL 归一或 WebView navigate / reload 细节。
|
||||
- 决策:Expo 移动壳新增 `apps/mobile-shell/src/host-bridge/navigation.ts`,统一承接 HostBridge 外链打开、受控 H5 跳转、WebView 刷新和成功响应包装,底层继续复用 `src/shell/navigation.ts` 与 `src/shell/url.ts`;Tauri 桌面壳新增 `apps/desktop-shell/src-tauri/src/host_bridge/navigation.rs`,统一承接外链打开、同源 H5 跳转和主窗口刷新,底层继续复用 `shell::navigation` 的 URL 归一和宿主上下文补写。两端 `dispatch` 只保留 method 委托,配置检查会拒绝分发层直接调用外链 opener、URL 归一、WebView navigate / reload 细节或包装导航成功响应。
|
||||
- 影响范围:`apps/mobile-shell/src/host-bridge/navigation.ts`、`apps/mobile-shell/src/host-bridge/dispatch.ts`、`apps/mobile-shell/scripts/check-config.mjs`、`apps/desktop-shell/src-tauri/src/host_bridge/navigation.rs`、`apps/desktop-shell/src-tauri/src/host_bridge/dispatch.rs`、`apps/desktop-shell/scripts/check-config.mjs`、`scripts/check-native-shells.mjs`、宿主壳能力统一协议文档、Expo / Tauri HostBridge 方案文档。
|
||||
- 验证方式:`npm run mobile-shell:typecheck`、`npm run mobile-shell:test -- src/host-bridge/bridge.test.ts`、`npm run desktop-shell:typecheck`、`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user