feat: 移除 H5ShellPage 中的多余媒体控制逻辑;优化 ShellBranding 加载方式
This commit is contained in:
@@ -45,14 +45,6 @@ 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 =
|
||||
|
||||
163
lib/main.dart
163
lib/main.dart
@@ -11,61 +11,8 @@ import 'config/app_config.dart';
|
||||
const _shellBackground = Color(0xFFF8FBFF);
|
||||
const _shellAccent = Color(0xFF0089FF);
|
||||
const _shellSubText = Color(0xFF8E9AB0);
|
||||
const _resumeCoverDuration = Duration(milliseconds: 700);
|
||||
const _shellBrandingChannel =
|
||||
MethodChannel('io.openim.flutter.im_webview_app/shell_branding');
|
||||
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 _stopWebMediaScript = r'''
|
||||
(() => {
|
||||
try {
|
||||
window.__stopOpenIMVoicePlayback?.();
|
||||
} catch (_) {}
|
||||
document.querySelectorAll('audio, video').forEach((media) => {
|
||||
try {
|
||||
media.pause();
|
||||
media.currentTime = 0;
|
||||
} catch (_) {}
|
||||
});
|
||||
})();
|
||||
''';
|
||||
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -77,8 +24,7 @@ Future<void> main() async {
|
||||
systemNavigationBarIconBrightness: Brightness.dark,
|
||||
),
|
||||
);
|
||||
final shellBranding = await ShellBranding.load();
|
||||
runApp(ImWebViewApp(shellBranding: shellBranding));
|
||||
runApp(const ImWebViewApp());
|
||||
}
|
||||
|
||||
class ShellBranding {
|
||||
@@ -132,55 +78,37 @@ class ImWebViewApp extends StatelessWidget {
|
||||
scaffoldBackgroundColor: _shellBackground,
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: H5ShellPage(shellBranding: shellBranding),
|
||||
home: H5ShellPage(initialShellBranding: shellBranding),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class H5ShellPage extends StatefulWidget {
|
||||
const H5ShellPage({super.key, required this.shellBranding});
|
||||
const H5ShellPage({super.key, required this.initialShellBranding});
|
||||
|
||||
final ShellBranding shellBranding;
|
||||
final ShellBranding initialShellBranding;
|
||||
|
||||
@override
|
||||
State<H5ShellPage> createState() => _H5ShellPageState();
|
||||
}
|
||||
|
||||
class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
|
||||
class _H5ShellPageState extends State<H5ShellPage> {
|
||||
late final WebViewController _controller;
|
||||
|
||||
int _progress = 0;
|
||||
String? _loadError;
|
||||
bool _showShellCover = true;
|
||||
Timer? _shellCoverTimer;
|
||||
bool _shellBrandingLoaded = false;
|
||||
late ShellBranding _shellBranding;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
_shellBranding = widget.initialShellBranding;
|
||||
_controller = _buildController();
|
||||
unawaited(_loadHome());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_shellCoverTimer?.cancel();
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.inactive ||
|
||||
state == AppLifecycleState.hidden ||
|
||||
state == AppLifecycleState.paused ||
|
||||
state == AppLifecycleState.detached) {
|
||||
unawaited(_stopWebMedia());
|
||||
} else if (state == AppLifecycleState.resumed) {
|
||||
_showTransientShellCover();
|
||||
}
|
||||
}
|
||||
|
||||
WebViewController _buildController() {
|
||||
return WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
@@ -214,26 +142,11 @@ class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
|
||||
}
|
||||
}
|
||||
},
|
||||
onUrlChange: (_) {
|
||||
unawaited(_stopWebMedia());
|
||||
},
|
||||
onNavigationRequest: _handleNavigationRequest,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showTransientShellCover() {
|
||||
_shellCoverTimer?.cancel();
|
||||
if (mounted) {
|
||||
setState(() => _showShellCover = true);
|
||||
}
|
||||
_shellCoverTimer = Timer(_resumeCoverDuration, () {
|
||||
if (mounted) {
|
||||
setState(() => _showShellCover = _progress < 100);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _runJavaScriptSafely(String source) async {
|
||||
try {
|
||||
await _controller.runJavaScript(source);
|
||||
@@ -247,8 +160,8 @@ class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
|
||||
}
|
||||
|
||||
Future<void> _handlePageFinished() async {
|
||||
await _loadShellBrandingIfNeeded();
|
||||
await _syncShellBranding();
|
||||
unawaited(_logLoadedH5Snapshot());
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_progress = 100;
|
||||
@@ -257,10 +170,27 @@ class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadShellBrandingIfNeeded() async {
|
||||
if (_shellBrandingLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
final shellBranding = await ShellBranding.load();
|
||||
_shellBrandingLoaded = true;
|
||||
if (!mounted) {
|
||||
_shellBranding = shellBranding;
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_shellBranding = shellBranding;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _syncShellBranding() {
|
||||
final payload = jsonEncode({
|
||||
'name': widget.shellBranding.appName,
|
||||
'logo': widget.shellBranding.appLogo,
|
||||
'name': _shellBranding.appName,
|
||||
'logo': _shellBranding.appLogo,
|
||||
});
|
||||
final script = '''
|
||||
(() => {
|
||||
@@ -274,40 +204,6 @@ class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
|
||||
return _runJavaScriptSafely(script);
|
||||
}
|
||||
|
||||
Future<void> _stopWebMedia() {
|
||||
return _runJavaScriptSafely(_stopWebMediaScript);
|
||||
}
|
||||
|
||||
Future<void> _logLoadedH5Snapshot() async {
|
||||
try {
|
||||
final result = await _controller.runJavaScriptReturningResult(
|
||||
_inspectH5SnapshotScript,
|
||||
);
|
||||
final snapshot = _decodeJavaScriptStringResult(result);
|
||||
debugPrint(
|
||||
'[H5Shell] loaded H5 snapshot: $snapshot',
|
||||
);
|
||||
} 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());
|
||||
}
|
||||
@@ -327,8 +223,6 @@ class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
|
||||
Future<NavigationDecision> _handleNavigationRequest(
|
||||
NavigationRequest request,
|
||||
) async {
|
||||
unawaited(_stopWebMedia());
|
||||
|
||||
final uri = Uri.tryParse(request.url);
|
||||
if (uri == null) {
|
||||
return NavigationDecision.prevent;
|
||||
@@ -349,7 +243,6 @@ class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
|
||||
}
|
||||
|
||||
Future<void> _handleBackNavigation() async {
|
||||
await _stopWebMedia();
|
||||
if (await _controller.canGoBack()) {
|
||||
await _controller.goBack();
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user