387a1c26e3
清理已跟踪原始日志与个人本地配置 修复前后端有效门禁、测试和构建问题 补齐生产 Jenkins、SpacetimeDB 本地命令与文档约束 收紧图片编辑器状态、附件与媒体引用测试 排除已下线旧创作入口测试并清理 warning
98 lines
2.4 KiB
TypeScript
98 lines
2.4 KiB
TypeScript
import * as Linking from 'expo-linking';
|
|
|
|
import {
|
|
type HostBridgeError,
|
|
type HostBridgeRequest,
|
|
type NavigateNativePagePayload,
|
|
normalizeHostBridgeExternalUrlPayload,
|
|
type OpenExternalUrlPayload,
|
|
} from '../../../../packages/shared/src/contracts/hostBridge';
|
|
import {
|
|
openMobileShellExternalNavigation,
|
|
resolveMobileShellWebViewUrl,
|
|
} from '../shell/navigation';
|
|
import { buildMobileShellUrl } from '../shell/url';
|
|
import {
|
|
invalidRequest,
|
|
type MobileHostBridgeNavigation,
|
|
ok,
|
|
unsupported,
|
|
} from './protocol';
|
|
|
|
function logMobileHostBridgeNavigationFailure(label: string, _error: unknown) {
|
|
console.warn(`mobile HostBridge navigation failed for ${label}`);
|
|
}
|
|
|
|
export async function openMobileHostBridgeExternalUrl(
|
|
request: HostBridgeRequest,
|
|
) {
|
|
const externalUrlPayload = normalizeHostBridgeExternalUrlPayload(
|
|
(request.payload as OpenExternalUrlPayload | undefined)?.url,
|
|
);
|
|
if (!externalUrlPayload) {
|
|
throw invalidRequest('url must use an allowed external protocol');
|
|
}
|
|
|
|
let opened = false;
|
|
try {
|
|
opened = await openMobileShellExternalNavigation(
|
|
Linking,
|
|
externalUrlPayload.url,
|
|
);
|
|
} catch (error) {
|
|
logMobileHostBridgeNavigationFailure('external.open', error);
|
|
opened = false;
|
|
}
|
|
if (!opened) {
|
|
throw {
|
|
code: 'host_error',
|
|
message: 'external URL cannot be opened',
|
|
} satisfies HostBridgeError;
|
|
}
|
|
|
|
return ok(request, true);
|
|
}
|
|
|
|
export function openMobileHostBridgeNativePage(
|
|
request: HostBridgeRequest,
|
|
navigation: MobileHostBridgeNavigation | null,
|
|
) {
|
|
if (!navigation) {
|
|
throw unsupported('navigation.openNativePage');
|
|
}
|
|
|
|
const url = (request.payload as NavigateNativePagePayload | undefined)?.url;
|
|
if (typeof url !== 'string') {
|
|
throw invalidRequest('url is required');
|
|
}
|
|
|
|
const webViewUrl = resolveMobileShellWebViewUrl(
|
|
url,
|
|
navigation.allowedOrigin,
|
|
);
|
|
if (!webViewUrl) {
|
|
throw invalidRequest('url must be an allowed same-origin web path');
|
|
}
|
|
|
|
navigation.openWebViewUrl(
|
|
buildMobileShellUrl(
|
|
webViewUrl,
|
|
navigation.urlOptions,
|
|
navigation.baseWebUrlOptions,
|
|
),
|
|
);
|
|
return ok(request, true);
|
|
}
|
|
|
|
export function reloadMobileHostBridgeWebView(
|
|
request: HostBridgeRequest,
|
|
navigation: MobileHostBridgeNavigation | null,
|
|
) {
|
|
if (!navigation) {
|
|
throw unsupported('app.reloadWebView');
|
|
}
|
|
|
|
navigation.reloadWebView();
|
|
return ok(request, true);
|
|
}
|