8bdfd1c629
新增同源主页面加载失败归一模型与测试 Expo WebView 接入 onError/onHttpError 原生重试层 更新原生壳门禁与宿主壳方案文档
111 lines
2.5 KiB
TypeScript
111 lines
2.5 KiB
TypeScript
import { shouldOpenInMobileShellWebView } from './navigation';
|
|
|
|
export type MobileShellLoadFailureInput =
|
|
| {
|
|
type: 'native';
|
|
url: string;
|
|
code?: number | null;
|
|
description?: string | null;
|
|
}
|
|
| {
|
|
type: 'http';
|
|
url: string;
|
|
statusCode?: number | null;
|
|
description?: string | null;
|
|
};
|
|
|
|
export type MobileShellLoadFailure = {
|
|
type: 'native' | 'http';
|
|
url: string;
|
|
title: string;
|
|
detail: string;
|
|
retryLabel: string;
|
|
};
|
|
|
|
function normalizeDescription(value: string | null | undefined) {
|
|
const normalized = value?.replace(/\s+/g, ' ').trim();
|
|
if (!normalized) {
|
|
return null;
|
|
}
|
|
|
|
return normalized.length > 96
|
|
? `${normalized.slice(0, 96).trim()}...`
|
|
: normalized;
|
|
}
|
|
|
|
function sameDocumentUrl(
|
|
left: URL,
|
|
right: string | null | undefined,
|
|
allowedOrigin: string,
|
|
) {
|
|
if (!right) {
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
const current = new URL(right, allowedOrigin);
|
|
return (
|
|
left.origin === current.origin &&
|
|
left.pathname === current.pathname &&
|
|
left.search === current.search
|
|
);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function shouldShowLoadFailure(
|
|
rawUrl: string,
|
|
allowedOrigin: string,
|
|
currentPageUrl?: string | null,
|
|
) {
|
|
if (!shouldOpenInMobileShellWebView(rawUrl, allowedOrigin)) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
const url = new URL(rawUrl, allowedOrigin);
|
|
return (
|
|
url.origin === allowedOrigin &&
|
|
url.pathname !== '/favicon.ico' &&
|
|
sameDocumentUrl(url, currentPageUrl, allowedOrigin)
|
|
);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function normalizeMobileShellLoadFailure(
|
|
input: MobileShellLoadFailureInput,
|
|
allowedOrigin: string,
|
|
currentPageUrl?: string | null,
|
|
): MobileShellLoadFailure | null {
|
|
if (!shouldShowLoadFailure(input.url, allowedOrigin, currentPageUrl)) {
|
|
return null;
|
|
}
|
|
|
|
const url = new URL(input.url, allowedOrigin).toString();
|
|
const description = normalizeDescription(input.description);
|
|
if (input.type === 'http') {
|
|
const statusCode =
|
|
Number.isInteger(input.statusCode) && input.statusCode
|
|
? input.statusCode
|
|
: null;
|
|
return {
|
|
type: 'http',
|
|
url,
|
|
title: statusCode ? `加载失败 ${statusCode}` : '加载失败',
|
|
detail: description ?? '服务器暂时没有返回可用页面',
|
|
retryLabel: '重试',
|
|
};
|
|
}
|
|
|
|
return {
|
|
type: 'native',
|
|
url,
|
|
title: '网络不可用',
|
|
detail: description ?? '当前页面没有加载成功',
|
|
retryLabel: '重试',
|
|
};
|
|
}
|