feat: add listSortReverse option to ToolsLogAnalyze provider

This commit is contained in:
xkeyC 2025-05-08 23:16:27 +08:00
parent 3c061f995c
commit cb35c400f9
5 changed files with 75 additions and 60 deletions

View File

@ -22,7 +22,7 @@ final routerProvider = AutoDisposeProvider<GoRouter>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead') @Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element // ignore: unused_element
typedef RouterRef = AutoDisposeProviderRef<GoRouter>; typedef RouterRef = AutoDisposeProviderRef<GoRouter>;
String _$appGlobalModelHash() => r'eb06413ab3a70f26712d897cee745ee62e89e75e'; String _$appGlobalModelHash() => r'4e372bc744903960e4e7b146dbb394ded15e2c18';
/// See also [AppGlobalModel]. /// See also [AppGlobalModel].
@ProviderFor(AppGlobalModel) @ProviderFor(AppGlobalModel)

View File

@ -6,7 +6,7 @@ part of 'nav_state.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$navHash() => r'2019b3f675fbaec4be794049d900bf2dcc8d5e37'; String _$navHash() => r'00c4da8fdd37214cda179a81ece3676add7aab53';
/// See also [Nav]. /// See also [Nav].
@ProviderFor(Nav) @ProviderFor(Nav)

View File

@ -38,11 +38,10 @@ class LogAnalyzeLineData with _$LogAnalyzeLineData {
@riverpod @riverpod
class ToolsLogAnalyze extends _$ToolsLogAnalyze { class ToolsLogAnalyze extends _$ToolsLogAnalyze {
static const String unknownValue = "<Unknown>"; static const String unknownValue = "<Unknown>";
@override @override
Future<List<LogAnalyzeLineData>> build(String gameInstallPath) async { Future<List<LogAnalyzeLineData>> build(String gameInstallPath, bool listSortReverse) async {
final logFile = File("$gameInstallPath/Game.log"); final logFile = File("$gameInstallPath/Game.log");
debugPrint("[ToolsLogAnalyze] logFile: ${logFile.absolute.path}"); debugPrint("[ToolsLogAnalyze] logFile: ${logFile.absolute.path}");
if (gameInstallPath.isEmpty || !(await logFile.exists())) { if (gameInstallPath.isEmpty || !(await logFile.exists())) {
@ -99,7 +98,7 @@ class ToolsLogAnalyze extends _$ToolsLogAnalyze {
} }
final lastLineDateTime = final lastLineDateTime =
_gameStartTime != null ? _getLogLineDateTime(logLines.lastWhere((e) => e.startsWith("<20"))) : null; _gameStartTime != null ? _getLogLineDateTime(logLines.lastWhere((e) => e.startsWith("<20"))) : null;
// ${S.current.log_analyzer_filter_game_crash} // ${S.current.log_analyzer_filter_game_crash}
if (_gameCrashLineNumber > 0) { if (_gameCrashLineNumber > 0) {
@ -173,9 +172,13 @@ class ToolsLogAnalyze extends _$ToolsLogAnalyze {
debugPrint("[ToolsLogAnalyze] logFile change: ${change.type}"); debugPrint("[ToolsLogAnalyze] logFile change: ${change.type}");
switch (change.type) { switch (change.type) {
case ChangeType.MODIFY: case ChangeType.MODIFY:
// ${S.current.log_analyzer_filter_statistics} // ${S.current.log_analyzer_filter_statistics}
final newList = state.value?.where((e) => e.type != "statistics").toList(); final newList = state.value?.where((e) => e.type != "statistics").toList();
state = AsyncData(newList ?? []); if (listSortReverse) {
state = AsyncData(newList?.reversed.toList() ?? []);
} else {
state = AsyncData(newList ?? []);
}
return _launchLogAnalyze(logFile, startLine: _currentLineNumber); return _launchLogAnalyze(logFile, startLine: _currentLineNumber);
case ChangeType.ADD: case ChangeType.ADD:
case ChangeType.REMOVE: case ChangeType.REMOVE:
@ -217,19 +220,19 @@ class ToolsLogAnalyze extends _$ToolsLogAnalyze {
if (baseEvent != null) { if (baseEvent != null) {
switch (baseEvent) { switch (baseEvent) {
case "AccountLoginCharacterStatus_Character": case "AccountLoginCharacterStatus_Character":
// //
return _logGetCharacterName(line); return _logGetCharacterName(line);
case "FatalCollision": case "FatalCollision":
// ${S.current.log_analyzer_filter_fatal_collision} // ${S.current.log_analyzer_filter_fatal_collision}
return _logGetFatalCollision(line); return _logGetFatalCollision(line);
case "Vehicle Destruction": case "Vehicle Destruction":
// ${S.current.log_analyzer_filter_vehicle_damaged} // ${S.current.log_analyzer_filter_vehicle_damaged}
return _logGetVehicleDestruction(line); return _logGetVehicleDestruction(line);
case "Actor Death": case "Actor Death":
// ${S.current.log_analyzer_filter_character_death} // ${S.current.log_analyzer_filter_character_death}
return _logGetActorDeath(line); return _logGetActorDeath(line);
case "RequestLocationInventory": case "RequestLocationInventory":
// ${S.current.log_analyzer_filter_local_inventory} // ${S.current.log_analyzer_filter_local_inventory}
return _logGetRequestLocationInventory(line); return _logGetRequestLocationInventory(line);
} }
} }
@ -253,7 +256,12 @@ class ToolsLogAnalyze extends _$ToolsLogAnalyze {
// state // state
final currentState = state.value; final currentState = state.value;
if (currentState != null) { if (currentState != null) {
state = AsyncData([...currentState, data]); if (listSortReverse) {
//
state = AsyncData([data, ...currentState]);
} else {
state = AsyncData([...currentState, data]);
}
} else { } else {
state = AsyncData([data]); state = AsyncData([data]);
} }
@ -272,7 +280,7 @@ class ToolsLogAnalyze extends _$ToolsLogAnalyze {
} }
final _gameLoadingRegExp = final _gameLoadingRegExp =
RegExp(r'<[^>]+>\s+Loading screen for\s+(\w+)\s+:\s+SC_Frontend closed after\s+(\d+\.\d+)\s+seconds'); RegExp(r'<[^>]+>\s+Loading screen for\s+(\w+)\s+:\s+SC_Frontend closed after\s+(\d+\.\d+)\s+seconds');
(String, String)? _logGetGameLoading(String line) { (String, String)? _logGetGameLoading(String line) {
final match = _gameLoadingRegExp.firstMatch(line); final match = _gameLoadingRegExp.firstMatch(line);
@ -339,10 +347,10 @@ class ToolsLogAnalyze extends _$ToolsLogAnalyze {
LogAnalyzeLineData? _logGetVehicleDestruction(String line) { LogAnalyzeLineData? _logGetVehicleDestruction(String line) {
final pattern = RegExp(r"Vehicle\s+'([^']+)'.*?" // final pattern = RegExp(r"Vehicle\s+'([^']+)'.*?" //
r"in zone\s+'([^']+)'.*?" // Zone r"in zone\s+'([^']+)'.*?" // Zone
r"destroy level \d+ to (\d+).*?" // r"destroy level \d+ to (\d+).*?" //
r"caused by\s+'([^']+)'" // r"caused by\s+'([^']+)'" //
); );
final match = pattern.firstMatch(line); final match = pattern.firstMatch(line);
if (match != null) { if (match != null) {
final vehicleModel = match.group(1) ?? unknownValue; final vehicleModel = match.group(1) ?? unknownValue;
@ -378,10 +386,10 @@ class ToolsLogAnalyze extends _$ToolsLogAnalyze {
LogAnalyzeLineData? _logGetActorDeath(String line) { LogAnalyzeLineData? _logGetActorDeath(String line) {
final pattern = RegExp(r"CActor::Kill: '([^']+)'.*?" // ID final pattern = RegExp(r"CActor::Kill: '([^']+)'.*?" // ID
r"in zone '([^']+)'.*?" // r"in zone '([^']+)'.*?" //
r"killed by '([^']+)'.*?" // ID r"killed by '([^']+)'.*?" // ID
r"with damage type '([^']+)'" // r"with damage type '([^']+)'" //
); );
final match = pattern.firstMatch(line); final match = pattern.firstMatch(line);
if (match != null) { if (match != null) {

View File

@ -6,7 +6,7 @@ part of 'log_analyze_provider.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$toolsLogAnalyzeHash() => r'cc8aed5b4eeb6c8feb35c59ef484dc61c92a5549'; String _$toolsLogAnalyzeHash() => r'5666c3f882e22e2192593629164bc53f8ce4aabe';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {
@ -32,9 +32,11 @@ class _SystemHash {
abstract class _$ToolsLogAnalyze abstract class _$ToolsLogAnalyze
extends BuildlessAutoDisposeAsyncNotifier<List<LogAnalyzeLineData>> { extends BuildlessAutoDisposeAsyncNotifier<List<LogAnalyzeLineData>> {
late final String gameInstallPath; late final String gameInstallPath;
late final bool listSortReverse;
FutureOr<List<LogAnalyzeLineData>> build( FutureOr<List<LogAnalyzeLineData>> build(
String gameInstallPath, String gameInstallPath,
bool listSortReverse,
); );
} }
@ -51,9 +53,11 @@ class ToolsLogAnalyzeFamily
/// See also [ToolsLogAnalyze]. /// See also [ToolsLogAnalyze].
ToolsLogAnalyzeProvider call( ToolsLogAnalyzeProvider call(
String gameInstallPath, String gameInstallPath,
bool listSortReverse,
) { ) {
return ToolsLogAnalyzeProvider( return ToolsLogAnalyzeProvider(
gameInstallPath, gameInstallPath,
listSortReverse,
); );
} }
@ -63,6 +67,7 @@ class ToolsLogAnalyzeFamily
) { ) {
return call( return call(
provider.gameInstallPath, provider.gameInstallPath,
provider.listSortReverse,
); );
} }
@ -87,8 +92,11 @@ class ToolsLogAnalyzeProvider extends AutoDisposeAsyncNotifierProviderImpl<
/// See also [ToolsLogAnalyze]. /// See also [ToolsLogAnalyze].
ToolsLogAnalyzeProvider( ToolsLogAnalyzeProvider(
String gameInstallPath, String gameInstallPath,
bool listSortReverse,
) : this._internal( ) : this._internal(
() => ToolsLogAnalyze()..gameInstallPath = gameInstallPath, () => ToolsLogAnalyze()
..gameInstallPath = gameInstallPath
..listSortReverse = listSortReverse,
from: toolsLogAnalyzeProvider, from: toolsLogAnalyzeProvider,
name: r'toolsLogAnalyzeProvider', name: r'toolsLogAnalyzeProvider',
debugGetCreateSourceHash: debugGetCreateSourceHash:
@ -99,6 +107,7 @@ class ToolsLogAnalyzeProvider extends AutoDisposeAsyncNotifierProviderImpl<
allTransitiveDependencies: allTransitiveDependencies:
ToolsLogAnalyzeFamily._allTransitiveDependencies, ToolsLogAnalyzeFamily._allTransitiveDependencies,
gameInstallPath: gameInstallPath, gameInstallPath: gameInstallPath,
listSortReverse: listSortReverse,
); );
ToolsLogAnalyzeProvider._internal( ToolsLogAnalyzeProvider._internal(
@ -109,9 +118,11 @@ class ToolsLogAnalyzeProvider extends AutoDisposeAsyncNotifierProviderImpl<
required super.debugGetCreateSourceHash, required super.debugGetCreateSourceHash,
required super.from, required super.from,
required this.gameInstallPath, required this.gameInstallPath,
required this.listSortReverse,
}) : super.internal(); }) : super.internal();
final String gameInstallPath; final String gameInstallPath;
final bool listSortReverse;
@override @override
FutureOr<List<LogAnalyzeLineData>> runNotifierBuild( FutureOr<List<LogAnalyzeLineData>> runNotifierBuild(
@ -119,6 +130,7 @@ class ToolsLogAnalyzeProvider extends AutoDisposeAsyncNotifierProviderImpl<
) { ) {
return notifier.build( return notifier.build(
gameInstallPath, gameInstallPath,
listSortReverse,
); );
} }
@ -127,13 +139,16 @@ class ToolsLogAnalyzeProvider extends AutoDisposeAsyncNotifierProviderImpl<
return ProviderOverride( return ProviderOverride(
origin: this, origin: this,
override: ToolsLogAnalyzeProvider._internal( override: ToolsLogAnalyzeProvider._internal(
() => create()..gameInstallPath = gameInstallPath, () => create()
..gameInstallPath = gameInstallPath
..listSortReverse = listSortReverse,
from: from, from: from,
name: null, name: null,
dependencies: null, dependencies: null,
allTransitiveDependencies: null, allTransitiveDependencies: null,
debugGetCreateSourceHash: null, debugGetCreateSourceHash: null,
gameInstallPath: gameInstallPath, gameInstallPath: gameInstallPath,
listSortReverse: listSortReverse,
), ),
); );
} }
@ -147,13 +162,15 @@ class ToolsLogAnalyzeProvider extends AutoDisposeAsyncNotifierProviderImpl<
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return other is ToolsLogAnalyzeProvider && return other is ToolsLogAnalyzeProvider &&
other.gameInstallPath == gameInstallPath; other.gameInstallPath == gameInstallPath &&
other.listSortReverse == listSortReverse;
} }
@override @override
int get hashCode { int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode); var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, gameInstallPath.hashCode); hash = _SystemHash.combine(hash, gameInstallPath.hashCode);
hash = _SystemHash.combine(hash, listSortReverse.hashCode);
return _SystemHash.finish(hash); return _SystemHash.finish(hash);
} }
@ -165,6 +182,9 @@ mixin ToolsLogAnalyzeRef
on AutoDisposeAsyncNotifierProviderRef<List<LogAnalyzeLineData>> { on AutoDisposeAsyncNotifierProviderRef<List<LogAnalyzeLineData>> {
/// The parameter `gameInstallPath` of this provider. /// The parameter `gameInstallPath` of this provider.
String get gameInstallPath; String get gameInstallPath;
/// The parameter `listSortReverse` of this provider.
bool get listSortReverse;
} }
class _ToolsLogAnalyzeProviderElement class _ToolsLogAnalyzeProviderElement
@ -175,6 +195,9 @@ class _ToolsLogAnalyzeProviderElement
@override @override
String get gameInstallPath => String get gameInstallPath =>
(origin as ToolsLogAnalyzeProvider).gameInstallPath; (origin as ToolsLogAnalyzeProvider).gameInstallPath;
@override
bool get listSortReverse =>
(origin as ToolsLogAnalyzeProvider).listSortReverse;
} }
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@ -15,14 +15,13 @@ class ToolsLogAnalyzeDialogUI extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final selectedPath = useState<String?>(appState.gameInstallPaths.firstOrNull); final selectedPath = useState<String?>(appState.gameInstallPaths.firstOrNull);
final logResp = ref.watch(toolsLogAnalyzeProvider(selectedPath.value ?? "")); final listSortReverse = useState<bool>(false);
final provider = toolsLogAnalyzeProvider(selectedPath.value ?? "", listSortReverse.value);
final logResp = ref.watch(provider);
final searchText = useState<String>(""); final searchText = useState<String>("");
final searchType = useState<String?>(null); final searchType = useState<String?>(null);
final lastListSize = useState<int>(0);
final listCtrl = useScrollController(); final listCtrl = useScrollController();
_diffData(logResp, lastListSize, listCtrl);
return ScaffoldPage( return ScaffoldPage(
content: Column( content: Column(
children: [ children: [
@ -56,7 +55,7 @@ class ToolsLogAnalyzeDialogUI extends HookConsumerWidget {
child: const Icon(FluentIcons.refresh), child: const Icon(FluentIcons.refresh),
), ),
onPressed: () { onPressed: () {
ref.invalidate(toolsLogAnalyzeProvider(selectedPath.value ?? "")); ref.invalidate(provider);
}, },
), ),
], ],
@ -81,6 +80,7 @@ class ToolsLogAnalyzeDialogUI extends HookConsumerWidget {
}, },
), ),
), ),
SizedBox(width: 6), SizedBox(width: 6),
// ComboBox // ComboBox
ComboBox<String>( ComboBox<String>(
@ -97,6 +97,18 @@ class ToolsLogAnalyzeDialogUI extends HookConsumerWidget {
searchType.value = value; searchType.value = value;
}, },
), ),
const SizedBox(width: 6),
// Icon
Button(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 6),
child: Transform.rotate(
angle: listSortReverse.value ? 3.14 : 0, child: const Icon(FluentIcons.sort_lines)),
),
onPressed: () {
listSortReverse.value = !listSortReverse.value;
},
),
], ],
), ),
), ),
@ -226,32 +238,4 @@ class ToolsLogAnalyzeDialogUI extends HookConsumerWidget {
return Colors.white.withValues(alpha: .06); return Colors.white.withValues(alpha: .06);
} }
} }
void _diffData(
AsyncValue<List<LogAnalyzeLineData>> logResp, ValueNotifier<int> lastListSize, ScrollController listCtrl) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (lastListSize.value == 0) {
lastListSize.value = logResp.value?.length ?? 0;
} else {
//
if (listCtrl.position.maxScrollExtent > listCtrl.position.viewportDimension) {
//
if (listCtrl.position.pixels >= listCtrl.position.maxScrollExtent) {
//
if ((logResp.value?.length ?? 0) > lastListSize.value) {
Future.delayed(Duration(milliseconds: 100)).then((_) {
listCtrl.jumpTo(listCtrl.position.maxScrollExtent);
});
lastListSize.value = logResp.value?.length ?? 0;
} else {
//
if (listCtrl.position.pixels > 0) {
listCtrl.jumpTo(0);
}
}
}
}
}
});
}
} }