feat: 对齐常驻商城与七鱼客服交互
- 同步商城 H5 登录态与原生 token - 完善商城/原生 tab 切换、动画及返回交互 - 修复七鱼客服键盘避让和状态栏恢复 - 调整开屏跳过按钮与登录入口交互
This commit is contained in:
1 parent
b4343ea528
commit
3b089e2205
37 files changed
+2970
-375
No files matched your search
@@ -1,30 +1,121 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class MallWebHostState {
|
||||
const MallWebHostState({this.url, this.visible = false});
|
||||
const MallWebHostState({
|
||||
this.url,
|
||||
this.visible = false,
|
||||
this.isShowingQiyuPage = false,
|
||||
this.isQiyuKeyboardActive = false,
|
||||
});
|
||||
|
||||
final String? url;
|
||||
final bool visible;
|
||||
final bool isShowingQiyuPage;
|
||||
final bool isQiyuKeyboardActive;
|
||||
}
|
||||
|
||||
/// 进程常驻的商城 WebView 状态,切换原生 Tab 时不销毁商城页面。
|
||||
/// Process-lifetime presentation state for the resident mall WebView.
|
||||
class MallWebHostController {
|
||||
MallWebHostController._();
|
||||
static final instance = MallWebHostController._();
|
||||
final state = ValueNotifier(const MallWebHostState());
|
||||
Future<void> Function()? _backHandler;
|
||||
|
||||
void preload(String url) {
|
||||
final old = state.value;
|
||||
if (old.url == url) return;
|
||||
state.value = MallWebHostState(url: url, visible: old.visible);
|
||||
static final instance = MallWebHostController._();
|
||||
|
||||
final state = ValueNotifier(const MallWebHostState());
|
||||
String? _pendingPreloadUrl;
|
||||
Future<void> Function()? _clearCache;
|
||||
Future<void> Function()? _handleSystemBack;
|
||||
|
||||
void open(Uri url) {
|
||||
_pendingPreloadUrl = null;
|
||||
state.value = MallWebHostState(url: url.toString(), visible: true);
|
||||
}
|
||||
|
||||
void open(String url) =>
|
||||
state.value = MallWebHostState(url: url, visible: true);
|
||||
void dismiss() => state.value = MallWebHostState(url: state.value.url);
|
||||
void clear() => state.value = const MallWebHostState();
|
||||
void attachBackHandler(Future<void> Function() handler) =>
|
||||
_backHandler = handler;
|
||||
void detachBackHandler() => _backHandler = null;
|
||||
Future<void> handleBack() => _backHandler?.call() ?? Future.value();
|
||||
void preload(Uri url) {
|
||||
final current = state.value;
|
||||
if (current.visible) {
|
||||
if (current.url == null ||
|
||||
cacheKeyFor(current.url!) != cacheKeyFor(url.toString())) {
|
||||
_pendingPreloadUrl = url.toString();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (current.url != null &&
|
||||
cacheKeyFor(current.url!) == cacheKeyFor(url.toString())) {
|
||||
return;
|
||||
}
|
||||
state.value = MallWebHostState(
|
||||
url: url.toString(),
|
||||
visible: current.visible,
|
||||
);
|
||||
}
|
||||
|
||||
void dismiss() {
|
||||
final current = state.value;
|
||||
final pending = _pendingPreloadUrl;
|
||||
_pendingPreloadUrl = null;
|
||||
state.value = MallWebHostState(url: pending ?? current.url);
|
||||
}
|
||||
|
||||
void setShowingQiyuPage(bool isShowingQiyuPage) {
|
||||
final current = state.value;
|
||||
if (current.isShowingQiyuPage == isShowingQiyuPage) return;
|
||||
state.value = MallWebHostState(
|
||||
url: current.url,
|
||||
visible: current.visible,
|
||||
isShowingQiyuPage: isShowingQiyuPage,
|
||||
isQiyuKeyboardActive: isShowingQiyuPage
|
||||
? current.isQiyuKeyboardActive
|
||||
: false,
|
||||
);
|
||||
}
|
||||
|
||||
void setQiyuKeyboardActive(bool active) {
|
||||
final current = state.value;
|
||||
if (current.isQiyuKeyboardActive == active) return;
|
||||
state.value = MallWebHostState(
|
||||
url: current.url,
|
||||
visible: current.visible,
|
||||
isShowingQiyuPage: current.isShowingQiyuPage,
|
||||
isQiyuKeyboardActive: active,
|
||||
);
|
||||
}
|
||||
|
||||
void attachCacheClearer(Future<void> Function() clearer) =>
|
||||
_clearCache = clearer;
|
||||
|
||||
void detachCacheClearer() => _clearCache = null;
|
||||
|
||||
Future<void> clear() async {
|
||||
await _clearCache?.call();
|
||||
_pendingPreloadUrl = null;
|
||||
state.value = const MallWebHostState();
|
||||
}
|
||||
|
||||
static Uri uriForTheme(Uri uri, {required bool isDark}) {
|
||||
final query = Map<String, List<String>>.from(uri.queryParametersAll);
|
||||
query['isNight'] = [isDark ? '1' : '0'];
|
||||
return uri.replace(queryParameters: query);
|
||||
}
|
||||
|
||||
void attachSystemBackHandler(Future<void> Function() handler) {
|
||||
_handleSystemBack = handler;
|
||||
}
|
||||
|
||||
void detachSystemBackHandler() => _handleSystemBack = null;
|
||||
|
||||
Future<void> handleSystemBack() =>
|
||||
_handleSystemBack?.call() ?? Future<void>.value();
|
||||
|
||||
static String cacheKeyFor(String rawUrl) {
|
||||
final uri = Uri.tryParse(rawUrl);
|
||||
if (uri == null) return rawUrl;
|
||||
return uri
|
||||
.replace(
|
||||
queryParameters: uri.queryParameters['isNight'] == null
|
||||
? const <String, String>{}
|
||||
: <String, String>{'isNight': uri.queryParameters['isNight']!},
|
||||
fragment: '',
|
||||
)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user