feat: 更新 WebView 数据目录,添加持久存储清理逻辑;优化 URL 生成和 H5 路由刷新时的品牌保持

This commit is contained in:
Booker
2026-05-26 19:31:28 +07:00
parent 48e4229cb7
commit 7702dee27f
4 changed files with 165 additions and 24 deletions

View File

@@ -21,33 +21,14 @@ class AppConfig {
final host = environmentHosts.isNotEmpty
? environmentHosts.first
: 'h5-im.imharry.work';
return _withShellBranding(
return withFreshShellParams(
_normalizeHomeUrl(host),
appName: appName,
appLogo: appLogo,
);
}
static String withFreshShellCacheBust(String url) {
final uri = Uri.parse(url);
final queryParameters = Map<String, String>.from(uri.queryParameters)
..['shell_cache_bust'] = DateTime.now().millisecondsSinceEpoch.toString();
return uri.replace(queryParameters: queryParameters).toString();
}
static String _normalizeHomeUrl(String host) {
final value = host.trim();
final normalized =
value.startsWith('http://') || value.startsWith('https://')
? value
: 'https://$value';
final uri = Uri.parse(normalized);
final path = uri.path.isEmpty ? '/' : uri.path;
return uri.replace(path: path).toString();
}
static String _withShellBranding(
static String withFreshShellParams(
String url, {
String? appName,
String? appLogo,
@@ -77,4 +58,23 @@ class AppConfig {
)
.toString();
}
static String withFreshShellCacheBust(String url) {
final uri = Uri.parse(url);
final queryParameters = Map<String, String>.from(uri.queryParameters)
..['shell_cache_bust'] = DateTime.now().millisecondsSinceEpoch.toString();
return uri.replace(queryParameters: queryParameters).toString();
}
static String _normalizeHomeUrl(String host) {
final value = host.trim();
final normalized =
value.startsWith('http://') || value.startsWith('https://')
? value
: 'https://$value';
final uri = Uri.parse(normalized);
final path = uri.path.isEmpty ? '/' : uri.path;
return uri.replace(path: path).toString();
}
}

View File

@@ -105,6 +105,45 @@ const _purgeWebRuntimeCacheScript = r'''
});
})();
''';
const _inspectH5SnapshotScript = r'''
(() => {
const toAbsoluteUrl = (value) => {
try {
return new URL(value, window.location.href).href;
} catch (_) {
return value || '';
}
};
const shrinkAssetUrl = (value) => {
const absolute = toAbsoluteUrl(value);
const match = absolute.match(/\/assets\/[^?#]+/);
return match ? match[0] : absolute;
};
const scripts = Array.from(document.scripts)
.map((script) => script.src)
.filter(Boolean)
.map(shrinkAssetUrl);
const links = Array.from(document.querySelectorAll('link[href]'))
.map((link) => link.href)
.filter(Boolean)
.map(shrinkAssetUrl);
const bodyText = (document.body?.innerText || '')
.replace(/\s+/g, ' ')
.slice(0, 300);
return JSON.stringify({
type: 'h5Snapshot',
href: window.location.href,
title: document.title,
scripts,
links,
bodyText,
userAgent: navigator.userAgent,
});
})();
''';
const _shellMediaPermissionBridgeScript = r'''
(() => {
if (window.__openimShellMediaBridgeInstalled) {
@@ -395,7 +434,10 @@ class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
final platformController = controller.platform;
if (platformController is AndroidWebViewController) {
AndroidWebViewController.enableDebugging(false);
assert(() {
AndroidWebViewController.enableDebugging(true);
return true;
}());
unawaited(platformController.setMediaPlaybackRequiresUserGesture(false));
unawaited(platformController.setGeolocationEnabled(true));
unawaited(
@@ -432,6 +474,7 @@ class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
unawaited(openAppSettings());
}
if (type == 'runtimeCachePurged' && payload != null) {
debugPrint('[H5Shell] runtime cache purged: ${jsonEncode(payload)}');
unawaited(_reloadAfterRuntimeCachePurge(payload['url'] as String?));
}
if (type == 'requestMediaPermissions' && payload != null) {
@@ -512,7 +555,11 @@ class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
}
try {
return AppConfig.withFreshShellCacheBust(value);
return AppConfig.withFreshShellParams(
value,
appName: widget.shellBranding.appName,
appLogo: widget.shellBranding.appLogo,
);
} catch (_) {
return _freshHomeUrl();
}
@@ -527,6 +574,7 @@ class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
unawaited(_syncShellBranding());
unawaited(_injectShellMediaPermissionBridge());
unawaited(_logLoadedH5Snapshot());
if (mounted) {
setState(() {
_progress = 100;
@@ -635,6 +683,35 @@ class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
return _runJavaScriptSafely(_stopWebMediaScript);
}
Future<void> _logLoadedH5Snapshot() async {
try {
final result = await _controller.runJavaScriptReturningResult(
_inspectH5SnapshotScript,
);
debugPrint(
'[H5Shell] loaded H5 snapshot: ${_decodeJavaScriptStringResult(result)}',
);
} catch (error) {
debugPrint('[H5Shell] loaded H5 snapshot failed: $error');
}
}
String _decodeJavaScriptStringResult(Object? result) {
if (result == null) {
return '';
}
if (result is String) {
try {
final decoded = jsonDecode(result);
if (decoded is String) {
return decoded;
}
} catch (_) {}
return result;
}
return result.toString();
}
Future<void> _loadHome() async {
await _loadUrl(_freshHomeUrl());
}