yumi-flutter/lib/shared/tools/sc_room_top_layer_guard.dart
2026-05-25 19:30:09 +08:00

174 lines
4.3 KiB
Dart

import 'dart:async';
import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:yumi/shared/data_sources/sources/local/floating_screen_manager.dart';
class SCRoomTopLayerGuard {
static const int _maxDeferredActions = 16;
static const String _redPacketOpenDialogTag = 'showRoomRedPacketOpenDialog';
static const String _roomRocketRewardDialogTag = 'showRoomRocketRewardDialog';
static const String _roomRocketPagLaunchDialogTag =
'showRoomRocketPagLaunchDialog';
static final SCRoomTopLayerGuard _instance = SCRoomTopLayerGuard._internal();
factory SCRoomTopLayerGuard() => _instance;
SCRoomTopLayerGuard._internal();
final Queue<VoidCallback> _deferredActions = Queue<VoidCallback>();
final ValueNotifier<bool> _activeListenable = ValueNotifier<bool>(false);
int _activeCount = 0;
bool get isActive => _activeCount > 0;
ValueListenable<bool> get activeListenable => _activeListenable;
SCRoomTopLayerGuardLease acquire({
required String reason,
Set<String> keepDialogTags = const <String>{},
}) {
_begin(reason, keepDialogTags: keepDialogTags);
return SCRoomTopLayerGuardLease._(this, reason);
}
void runWhenInactive(VoidCallback action) {
if (!isActive) {
action();
return;
}
while (_deferredActions.length >= _maxDeferredActions) {
_deferredActions.removeFirst();
}
_deferredActions.add(action);
}
void _begin(String reason, {Set<String> keepDialogTags = const <String>{}}) {
_activeCount += 1;
if (_activeCount != 1) {
return;
}
_activeListenable.value = true;
OverlayManager().beginSuppressFloatingScreens(reason: reason);
if (!keepDialogTags.contains(_redPacketOpenDialogTag)) {
unawaited(SmartDialog.dismiss(tag: _redPacketOpenDialogTag));
}
if (!keepDialogTags.contains(_roomRocketRewardDialogTag)) {
unawaited(SmartDialog.dismiss(tag: _roomRocketRewardDialogTag));
}
if (!keepDialogTags.contains(_roomRocketPagLaunchDialogTag)) {
unawaited(SmartDialog.dismiss(tag: _roomRocketPagLaunchDialogTag));
}
}
void _release(String reason) {
if (_activeCount <= 0) {
return;
}
_activeCount -= 1;
if (_activeCount != 0) {
return;
}
OverlayManager().endSuppressFloatingScreens(reason: reason);
_activeListenable.value = false;
_drainDeferredActions();
}
void _drainDeferredActions() {
if (_deferredActions.isEmpty) {
return;
}
final actions = List<VoidCallback>.from(_deferredActions);
_deferredActions.clear();
scheduleMicrotask(() {
for (final action in actions) {
if (isActive) {
runWhenInactive(action);
continue;
}
action();
}
});
}
}
class SCRoomTopLayerSuppressor extends StatefulWidget {
const SCRoomTopLayerSuppressor({
super.key,
required this.reason,
required this.child,
this.keepDialogTags = const <String>{},
});
final String reason;
final Widget child;
final Set<String> keepDialogTags;
@override
State<SCRoomTopLayerSuppressor> createState() =>
_SCRoomTopLayerSuppressorState();
}
class _SCRoomTopLayerSuppressorState extends State<SCRoomTopLayerSuppressor> {
SCRoomTopLayerGuardLease? _lease;
@override
void initState() {
super.initState();
_acquire();
}
@override
void didUpdateWidget(covariant SCRoomTopLayerSuppressor oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.reason == widget.reason &&
setEquals(oldWidget.keepDialogTags, widget.keepDialogTags)) {
return;
}
_release();
_acquire();
}
@override
void dispose() {
_release();
super.dispose();
}
void _acquire() {
_lease = SCRoomTopLayerGuard().acquire(
reason: widget.reason,
keepDialogTags: widget.keepDialogTags,
);
}
void _release() {
_lease?.release();
_lease = null;
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}
class SCRoomTopLayerGuardLease {
SCRoomTopLayerGuardLease._(this._guard, this.reason);
final SCRoomTopLayerGuard _guard;
final String reason;
bool _released = false;
void release() {
if (_released) {
return;
}
_released = true;
_guard._release(reason);
}
}