chatapp3-flutter/lib/modules/room_game/views/baishun_debug_panel.dart
2026-04-16 15:07:47 +08:00

446 lines
15 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class BaishunDebugLogEntry {
const BaishunDebugLogEntry({
required this.timestamp,
required this.tag,
required this.message,
});
final DateTime timestamp;
final String tag;
final String message;
String get formattedTime {
final hh = timestamp.hour.toString().padLeft(2, '0');
final mm = timestamp.minute.toString().padLeft(2, '0');
final ss = timestamp.second.toString().padLeft(2, '0');
return '$hh:$mm:$ss';
}
}
class BaishunDebugSnapshot {
const BaishunDebugSnapshot({
required this.roomId,
required this.gameName,
required this.gameId,
required this.gameSessionId,
required this.entryUrl,
required this.launchMode,
required this.loading,
required this.pageFinished,
required this.receivedBridgeMessage,
required this.injectCount,
required this.configSendCount,
required this.lastBridgeSource,
required this.lastBridgeAction,
required this.lastBridgePayload,
required this.lastJsCallback,
required this.lastConfigSummary,
required this.errorMessage,
required this.logs,
});
final String roomId;
final String gameName;
final String gameId;
final String gameSessionId;
final String entryUrl;
final String launchMode;
final bool loading;
final bool pageFinished;
final bool receivedBridgeMessage;
final int injectCount;
final int configSendCount;
final String lastBridgeSource;
final String lastBridgeAction;
final String lastBridgePayload;
final String lastJsCallback;
final String lastConfigSummary;
final String errorMessage;
final List<BaishunDebugLogEntry> logs;
}
class BaishunDebugPanel extends StatefulWidget {
const BaishunDebugPanel({
super.key,
required this.snapshot,
required this.onReload,
required this.onInjectBridge,
required this.onReplayConfig,
required this.onWalletUpdate,
required this.onClearLogs,
});
final BaishunDebugSnapshot snapshot;
final VoidCallback onReload;
final VoidCallback onInjectBridge;
final VoidCallback onReplayConfig;
final VoidCallback onWalletUpdate;
final VoidCallback onClearLogs;
@override
State<BaishunDebugPanel> createState() => _BaishunDebugPanelState();
}
class _BaishunDebugPanelState extends State<BaishunDebugPanel> {
bool _expanded = false;
@override
Widget build(BuildContext context) {
final maxWidth = (ScreenUtil().screenWidth - 24.w).clamp(220.w, 360.w);
return AnimatedSwitcher(
duration: const Duration(milliseconds: 180),
child:
_expanded
? Container(
key: const ValueKey<String>('baishun_debug_panel_expanded'),
width: maxWidth,
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: const Color(0xE616302B),
borderRadius: BorderRadius.circular(16.w),
border: Border.all(
color: Colors.white.withValues(alpha: 0.14),
width: 1.w,
),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.22),
blurRadius: 18.w,
offset: Offset(0, 8.w),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'BAISHUN DEBUG',
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
fontWeight: FontWeight.w800,
letterSpacing: 0.4,
),
),
const Spacer(),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
setState(() {
_expanded = false;
});
},
child: Padding(
padding: EdgeInsets.all(2.w),
child: Icon(
Icons.close,
size: 16.w,
color: Colors.white70,
),
),
),
],
),
SizedBox(height: 8.w),
_InfoLine(label: 'Room', value: widget.snapshot.roomId),
_InfoLine(
label: 'Game',
value:
'${widget.snapshot.gameName} (${widget.snapshot.gameId})',
),
_InfoLine(
label: 'Session',
value: widget.snapshot.gameSessionId,
),
_InfoLine(
label: 'Launch',
value: widget.snapshot.launchMode,
),
_InfoLine(
label: 'Loading',
value: widget.snapshot.loading ? 'YES' : 'NO',
),
_InfoLine(
label: 'PageFinished',
value: widget.snapshot.pageFinished ? 'YES' : 'NO',
),
_InfoLine(
label: 'BridgeMsg',
value:
widget.snapshot.receivedBridgeMessage ? 'YES' : 'NO',
),
_InfoLine(
label: 'InjectCount',
value: '${widget.snapshot.injectCount}',
),
_InfoLine(
label: 'ConfigSend',
value: '${widget.snapshot.configSendCount}',
),
_InfoLine(
label: 'LastSource',
value:
widget.snapshot.lastBridgeSource.isEmpty
? '--'
: widget.snapshot.lastBridgeSource,
),
_InfoLine(
label: 'LastAction',
value:
widget.snapshot.lastBridgeAction.isEmpty
? '--'
: widget.snapshot.lastBridgeAction,
),
_InfoLine(
label: 'LastCallback',
value:
widget.snapshot.lastJsCallback.isEmpty
? '--'
: widget.snapshot.lastJsCallback,
),
if (widget.snapshot.lastConfigSummary.isNotEmpty)
_MultilineLine(
label: 'Config',
value: widget.snapshot.lastConfigSummary,
),
_MultilineLine(
label: 'EntryUrl',
value: widget.snapshot.entryUrl,
),
if (widget.snapshot.lastBridgePayload.isNotEmpty)
_MultilineLine(
label: 'Payload',
value: widget.snapshot.lastBridgePayload,
),
if (widget.snapshot.errorMessage.isNotEmpty)
_MultilineLine(
label: 'Error',
value: widget.snapshot.errorMessage,
valueColor: const Color(0xFFFFB4B4),
),
SizedBox(height: 10.w),
Wrap(
spacing: 8.w,
runSpacing: 8.w,
children: [
_DebugActionChip(
label: 'Reload',
onTap: widget.onReload,
),
_DebugActionChip(
label: 'Inject',
onTap: widget.onInjectBridge,
),
_DebugActionChip(
label: 'ReplayConfig',
onTap: widget.onReplayConfig,
),
_DebugActionChip(
label: 'Wallet',
onTap: widget.onWalletUpdate,
),
_DebugActionChip(
label: 'ClearLogs',
onTap: widget.onClearLogs,
),
],
),
SizedBox(height: 10.w),
Text(
'Recent Logs',
style: TextStyle(
color: Colors.white,
fontSize: 11.sp,
fontWeight: FontWeight.w700,
),
),
SizedBox(height: 6.w),
Container(
height: 180.w,
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.22),
borderRadius: BorderRadius.circular(12.w),
border: Border.all(
color: Colors.white.withValues(alpha: 0.08),
),
),
child:
widget.snapshot.logs.isEmpty
? Center(
child: Text(
'No bridge logs yet',
style: TextStyle(
color: Colors.white54,
fontSize: 11.sp,
),
),
)
: ListView.separated(
itemCount: widget.snapshot.logs.length,
separatorBuilder:
(_, __) => SizedBox(height: 6.w),
itemBuilder: (context, index) {
final entry = widget.snapshot.logs[index];
return Text(
'[${entry.formattedTime}] ${entry.tag} ${entry.message}',
style: TextStyle(
color: Colors.white70,
fontSize: 10.sp,
height: 1.35,
),
);
},
),
),
],
),
)
: GestureDetector(
key: const ValueKey<String>('baishun_debug_panel_collapsed'),
behavior: HitTestBehavior.opaque,
onTap: () {
setState(() {
_expanded = true;
});
},
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 12.w,
vertical: 8.w,
),
decoration: BoxDecoration(
color: const Color(0xD916302B),
borderRadius: BorderRadius.circular(999.w),
border: Border.all(
color: Colors.white.withValues(alpha: 0.12),
),
),
child: Text(
'BS DEBUG',
style: TextStyle(
color: Colors.white,
fontSize: 11.sp,
fontWeight: FontWeight.w700,
),
),
),
),
);
}
}
class _InfoLine extends StatelessWidget {
const _InfoLine({required this.label, required this.value});
final String label;
final String value;
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(bottom: 4.w),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 82.w,
child: Text(
'$label:',
style: TextStyle(
color: Colors.white60,
fontSize: 10.sp,
fontWeight: FontWeight.w600,
),
),
),
Expanded(
child: Text(
value,
style: TextStyle(
color: Colors.white,
fontSize: 10.sp,
height: 1.3,
),
),
),
],
),
);
}
}
class _MultilineLine extends StatelessWidget {
const _MultilineLine({
required this.label,
required this.value,
this.valueColor = Colors.white,
});
final String label;
final String value;
final Color valueColor;
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 4.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'$label:',
style: TextStyle(
color: Colors.white60,
fontSize: 10.sp,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 2.w),
SelectableText(
value,
style: TextStyle(color: valueColor, fontSize: 10.sp, height: 1.35),
),
],
),
);
}
}
class _DebugActionChip extends StatelessWidget {
const _DebugActionChip({required this.label, required this.onTap});
final String label;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 6.w),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(999.w),
border: Border.all(color: Colors.white.withValues(alpha: 0.08)),
),
child: Text(
label,
style: TextStyle(
color: Colors.white,
fontSize: 10.sp,
fontWeight: FontWeight.w600,
),
),
),
);
}
}