feat: 添加登录页面 URL 检测功能;更新 H5ShellPage 状态管理以支持路由变化
This commit is contained in:
@@ -83,6 +83,19 @@ class AppConfig {
|
||||
return url;
|
||||
}
|
||||
|
||||
static bool isLoginPageUrl(String? url) {
|
||||
if (url == null || url.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final uri = Uri.tryParse(url);
|
||||
if (uri == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _isLoginPath(uri.path) || _isLoginPath(uri.fragment);
|
||||
}
|
||||
|
||||
static int? lineIndexForUrl(String url) {
|
||||
final uri = Uri.tryParse(url);
|
||||
if (uri == null) {
|
||||
@@ -123,6 +136,21 @@ class AppConfig {
|
||||
return index;
|
||||
}
|
||||
|
||||
static bool _isLoginPath(String path) {
|
||||
final normalized = path.trim();
|
||||
if (normalized.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final pathOnly = normalized.split('?').first.split('#').first;
|
||||
final segments = pathOnly.split('/').where((segment) => segment.isNotEmpty);
|
||||
if (segments.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return segments.last == 'login';
|
||||
}
|
||||
|
||||
static bool _isSameLine(Uri lineUri, Uri uri) {
|
||||
if (lineUri.host.toLowerCase() != uri.host.toLowerCase()) {
|
||||
return false;
|
||||
|
||||
@@ -133,11 +133,12 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
||||
setState(() => _lineSlots[lineIndex].progress = progress);
|
||||
}
|
||||
},
|
||||
onPageStarted: (_) {
|
||||
onPageStarted: (url) {
|
||||
final slot = _lineSlots[lineIndex];
|
||||
slot.shellCoverFallbackTimer?.cancel();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
slot.setCurrentUrl(url);
|
||||
slot.loadError = null;
|
||||
slot.progress = 0;
|
||||
slot.showShellCover = true;
|
||||
@@ -147,6 +148,9 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
||||
onPageFinished: (_) {
|
||||
unawaited(_handlePageFinished(lineIndex));
|
||||
},
|
||||
onUrlChange: (change) {
|
||||
_updateSlotUrl(lineIndex, change.url);
|
||||
},
|
||||
onWebResourceError: (error) {
|
||||
if (error.isForMainFrame ?? true) {
|
||||
final slot = _lineSlots[lineIndex];
|
||||
@@ -183,6 +187,7 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
||||
Future<void> _handlePageFinished(int lineIndex) async {
|
||||
await _loadShellBrandingIfNeeded();
|
||||
await _syncShellBranding(lineIndex);
|
||||
await _installRouteObserver(lineIndex);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_lineSlots[lineIndex].progress = 100;
|
||||
@@ -196,6 +201,8 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
||||
final decoded = jsonDecode(message.message);
|
||||
if (decoded is Map && decoded['type'] == 'first-screen-ready') {
|
||||
_hideShellCover(lineIndex);
|
||||
} else if (decoded is Map && decoded['type'] == 'route-changed') {
|
||||
_updateSlotUrl(lineIndex, decoded['url']?.toString());
|
||||
}
|
||||
} catch (_) {
|
||||
if (message.message == 'first-screen-ready') {
|
||||
@@ -260,6 +267,60 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
||||
return _runJavaScriptSafely(lineIndex, script);
|
||||
}
|
||||
|
||||
Future<void> _installRouteObserver(int lineIndex) {
|
||||
const script = '''
|
||||
(() => {
|
||||
try {
|
||||
const notify = () => {
|
||||
try {
|
||||
window.OpenIMShell.postMessage(JSON.stringify({
|
||||
type: 'route-changed',
|
||||
url: window.location.href
|
||||
}));
|
||||
} catch (_) {}
|
||||
};
|
||||
|
||||
if (window.__OPENIM_FLUTTER_ROUTE_OBSERVER__) {
|
||||
window.__OPENIM_FLUTTER_ROUTE_OBSERVER__.notify();
|
||||
return;
|
||||
}
|
||||
|
||||
const originalPushState = window.history.pushState;
|
||||
const originalReplaceState = window.history.replaceState;
|
||||
|
||||
window.history.pushState = function() {
|
||||
const result = originalPushState.apply(this, arguments);
|
||||
notify();
|
||||
return result;
|
||||
};
|
||||
|
||||
window.history.replaceState = function() {
|
||||
const result = originalReplaceState.apply(this, arguments);
|
||||
notify();
|
||||
return result;
|
||||
};
|
||||
|
||||
window.addEventListener('popstate', notify);
|
||||
window.__OPENIM_FLUTTER_ROUTE_OBSERVER__ = { notify };
|
||||
notify();
|
||||
} catch (_) {}
|
||||
})();
|
||||
''';
|
||||
return _runJavaScriptSafely(lineIndex, script);
|
||||
}
|
||||
|
||||
void _updateSlotUrl(int lineIndex, String? url) {
|
||||
if (url == null || lineIndex < 0 || lineIndex >= _lineSlots.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
final slot = _lineSlots[lineIndex];
|
||||
final changed = slot.setCurrentUrl(url);
|
||||
if (changed && mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _ensureLineLoaded(int lineIndex) async {
|
||||
final slot = _lineSlots[lineIndex];
|
||||
if (slot.hasLoadedInitialRequest) {
|
||||
@@ -270,6 +331,7 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
||||
if (mounted) {
|
||||
slot.shellCoverFallbackTimer?.cancel();
|
||||
setState(() {
|
||||
slot.setCurrentUrl(slot.line.url);
|
||||
slot.loadError = null;
|
||||
slot.progress = 0;
|
||||
slot.showShellCover = true;
|
||||
@@ -369,6 +431,7 @@ class _H5ShellPageState extends State<H5ShellPage> {
|
||||
final bottomInset = MediaQuery.viewInsetsOf(context).bottom;
|
||||
final showLineSwitch = !currentSlot.showShellCover &&
|
||||
currentSlot.loadError == null &&
|
||||
currentSlot.isLoginPage &&
|
||||
bottomInset == 0;
|
||||
|
||||
return PopScope(
|
||||
@@ -448,8 +511,21 @@ class _H5LineWebViewSlot {
|
||||
String? loadError;
|
||||
bool showShellCover = true;
|
||||
bool hasLoadedInitialRequest = false;
|
||||
bool isLoginPage = false;
|
||||
String? currentUrl;
|
||||
Timer? shellCoverFallbackTimer;
|
||||
|
||||
bool setCurrentUrl(String url) {
|
||||
final nextIsLoginPage = AppConfig.isLoginPageUrl(url);
|
||||
if (currentUrl == url && isLoginPage == nextIsLoginPage) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentUrl = url;
|
||||
isLoginPage = nextIsLoginPage;
|
||||
return true;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
shellCoverFallbackTimer?.cancel();
|
||||
}
|
||||
|
||||
@@ -50,6 +50,20 @@ void main() {
|
||||
expect(AppConfig.lineIndexForUrl('${lineUrl}login'), 0);
|
||||
});
|
||||
|
||||
test('detects only H5 login routes for shell line switch display', () {
|
||||
expect(
|
||||
AppConfig.isLoginPageUrl('https://h5-test.imharry.work/login'), isTrue);
|
||||
expect(AppConfig.isLoginPageUrl('https://h5-test.imharry.work/app/login'),
|
||||
isTrue);
|
||||
expect(AppConfig.isLoginPageUrl('https://h5-test.imharry.work/#/login'),
|
||||
isTrue);
|
||||
expect(AppConfig.isLoginPageUrl('https://h5-test.imharry.work/'), isFalse);
|
||||
expect(AppConfig.isLoginPageUrl('https://h5-test.imharry.work/contact'),
|
||||
isFalse);
|
||||
expect(AppConfig.isLoginPageUrl('https://h5-test.imharry.work/getCode'),
|
||||
isFalse);
|
||||
});
|
||||
|
||||
test('refreshes an H5 route URL without adding branding to the URL', () {
|
||||
final uri = Uri.parse(
|
||||
AppConfig.withFreshShellParams(
|
||||
|
||||
Reference in New Issue
Block a user