初始化提交
This commit is contained in:
+27
@@ -0,0 +1,27 @@
|
||||
// // import 'package:kaer_with_panels/core/directories/directories_provider.dart';
|
||||
// import 'dart:io';
|
||||
|
||||
// import 'package:kaer_with_panels/features/log/data/log_path_resolver.dart';
|
||||
// import 'package:kaer_with_panels/features/log/data/log_repository.dart';
|
||||
// import 'package:kaer_with_panels/singbox/service/singbox_service_provider.dart';
|
||||
// import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
// part 'log_data_providers.g.dart';
|
||||
|
||||
// @Riverpod(keepAlive: true)
|
||||
// Future<LogRepository> logRepository(LogRepositoryRef ref) async {
|
||||
// final repo = LogRepositoryImpl(
|
||||
// singbox: ref.watch(singboxServiceProvider),
|
||||
// logPathResolver: ref.watch(logPathResolverProvider),
|
||||
// );
|
||||
// await repo.init().getOrElse((l) => throw l).run();
|
||||
// return repo;
|
||||
// }
|
||||
|
||||
// @Riverpod(keepAlive: true)
|
||||
// LogPathResolver logPathResolver(LogPathResolverRef ref) {
|
||||
// return LogPathResolver(
|
||||
// Directory("21323"),
|
||||
// // ref.watch(appDirectoriesProvider).requireValue.workingDir,
|
||||
// );
|
||||
// }
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
// ignore_for_file: parameter_assignments
|
||||
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:kaer_with_panels/features/log/model/log_entity.dart';
|
||||
import 'package:kaer_with_panels/features/log/model/log_level.dart';
|
||||
import 'package:tint/tint.dart';
|
||||
|
||||
abstract class LogParser {
|
||||
static LogEntity parseSingbox(String log) {
|
||||
log = log.strip();
|
||||
DateTime? time;
|
||||
if (log.length > 25) {
|
||||
time = DateTime.tryParse(log.substring(6, 25));
|
||||
}
|
||||
if (time != null) {
|
||||
log = log.substring(26);
|
||||
}
|
||||
final level = LogLevel.values.firstOrNullWhere(
|
||||
(e) {
|
||||
if (log.startsWith(e.name.toUpperCase())) {
|
||||
log = log.removePrefix(e.name.toUpperCase());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
);
|
||||
return LogEntity(
|
||||
level: level,
|
||||
time: time,
|
||||
message: log.trim(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
class LogPathResolver {
|
||||
const LogPathResolver(this._workingDir);
|
||||
|
||||
final Directory _workingDir;
|
||||
|
||||
Directory get directory => _workingDir;
|
||||
|
||||
File coreFile() {
|
||||
return File(p.join(directory.path, "box.log"));
|
||||
}
|
||||
|
||||
File appFile() {
|
||||
return File(p.join(directory.path, "app.log"));
|
||||
}
|
||||
}
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
// import 'package:fpdart/fpdart.dart';
|
||||
// import 'package:kaer_with_panels/core/utils/exception_handler.dart';
|
||||
// import 'package:kaer_with_panels/features/log/data/log_parser.dart';
|
||||
// import 'package:kaer_with_panels/features/log/data/log_path_resolver.dart';
|
||||
// import 'package:kaer_with_panels/features/log/model/log_entity.dart';
|
||||
// import 'package:kaer_with_panels/features/log/model/log_failure.dart';
|
||||
// import 'package:kaer_with_panels/singbox/service/singbox_service.dart';
|
||||
// import 'package:kaer_with_panels/utils/custom_loggers.dart';
|
||||
|
||||
// abstract interface class LogRepository {
|
||||
// TaskEither<LogFailure, Unit> init();
|
||||
// Stream<Either<LogFailure, List<LogEntity>>> watchLogs();
|
||||
// TaskEither<LogFailure, Unit> clearLogs();
|
||||
// }
|
||||
|
||||
// class LogRepositoryImpl
|
||||
// with ExceptionHandler, InfraLogger
|
||||
// implements LogRepository {
|
||||
// LogRepositoryImpl({
|
||||
// required this.singbox,
|
||||
// required this.logPathResolver,
|
||||
// });
|
||||
|
||||
// final SingboxService singbox;
|
||||
// final LogPathResolver logPathResolver;
|
||||
|
||||
// @override
|
||||
// TaskEither<LogFailure, Unit> init() {
|
||||
// return exceptionHandler(
|
||||
// () async {
|
||||
// if (!await logPathResolver.directory.exists()) {
|
||||
// await logPathResolver.directory.create(recursive: true);
|
||||
// }
|
||||
// if (await logPathResolver.coreFile().exists()) {
|
||||
// await logPathResolver.coreFile().writeAsString("");
|
||||
// } else {
|
||||
// await logPathResolver.coreFile().create(recursive: true);
|
||||
// }
|
||||
// if (await logPathResolver.appFile().exists()) {
|
||||
// await logPathResolver.appFile().writeAsString("");
|
||||
// } else {
|
||||
// await logPathResolver.appFile().create(recursive: true);
|
||||
// }
|
||||
// return right(unit);
|
||||
// },
|
||||
// LogUnexpectedFailure.new,
|
||||
// );
|
||||
// }
|
||||
|
||||
// @override
|
||||
// Stream<Either<LogFailure, List<LogEntity>>> watchLogs() {
|
||||
// return singbox
|
||||
// .watchLogs(logPathResolver.coreFile().path)
|
||||
// .map((event) => event.map(LogParser.parseSingbox).toList())
|
||||
// .handleExceptions(
|
||||
// (error, stackTrace) {
|
||||
// loggy.warning("error watching logs", error, stackTrace);
|
||||
// return LogFailure.unexpected(error, stackTrace);
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
|
||||
// @override
|
||||
// TaskEither<LogFailure, Unit> clearLogs() {
|
||||
// return exceptionHandler(
|
||||
// () => singbox.clearLogs().mapLeft(LogFailure.unexpected).run(),
|
||||
// LogFailure.unexpected,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:kaer_with_panels/features/log/model/log_level.dart';
|
||||
|
||||
part 'log_entity.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class LogEntity with _$LogEntity {
|
||||
const factory LogEntity({
|
||||
LogLevel? level,
|
||||
DateTime? time,
|
||||
required String message,
|
||||
}) = _LogEntity;
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
// import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
// // import 'package:kaer_with_panels/core/localization/translations.dart';
|
||||
// import 'package:kaer_with_panels/core/model/failures.dart';
|
||||
|
||||
// part 'log_failure.freezed.dart';
|
||||
|
||||
// @freezed
|
||||
// sealed class LogFailure with _$LogFailure, Failure {
|
||||
// const LogFailure._();
|
||||
|
||||
// @With<UnexpectedFailure>()
|
||||
// const factory LogFailure.unexpected([
|
||||
// Object? error,
|
||||
// StackTrace? stackTrace,
|
||||
// ]) = LogUnexpectedFailure;
|
||||
|
||||
// @override
|
||||
// ({String type, String? message}) present(TranslationsEn t) {
|
||||
// return switch (this) {
|
||||
// LogUnexpectedFailure() => (
|
||||
// type: t.failure.unexpected,
|
||||
// message: null,
|
||||
// ),
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
part 'log_level.mapper.dart';
|
||||
|
||||
@MappableEnum()
|
||||
enum LogLevel {
|
||||
trace,
|
||||
debug,
|
||||
info,
|
||||
warn,
|
||||
error,
|
||||
fatal,
|
||||
panic;
|
||||
|
||||
/// [LogLevel] selectable by user as preference
|
||||
static List<LogLevel> get choices => values.takeFirst(4);
|
||||
|
||||
Color? get color => switch (this) {
|
||||
trace => Colors.lightBlueAccent,
|
||||
debug => Colors.grey,
|
||||
info => Colors.lightGreen,
|
||||
warn => Colors.orange,
|
||||
error => Colors.redAccent,
|
||||
fatal => Colors.red,
|
||||
panic => Colors.red,
|
||||
};
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
// import 'dart:async';
|
||||
|
||||
// import 'package:kaer_with_panels/features/log/data/log_data_providers.dart';
|
||||
// import 'package:kaer_with_panels/features/log/model/log_entity.dart';
|
||||
// import 'package:kaer_with_panels/features/log/model/log_level.dart';
|
||||
// import 'package:kaer_with_panels/features/log/overview/logs_overview_state.dart';
|
||||
// import 'package:kaer_with_panels/utils/riverpod_utils.dart';
|
||||
// import 'package:kaer_with_panels/utils/utils.dart';
|
||||
// import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
// import 'package:rxdart/rxdart.dart';
|
||||
|
||||
// part 'logs_overview_notifier.g.dart';
|
||||
|
||||
// @riverpod
|
||||
// class LogsOverviewNotifier extends _$LogsOverviewNotifier with AppLogger {
|
||||
// @override
|
||||
// LogsOverviewState build() {
|
||||
// ref.disposeDelay(const Duration(seconds: 20));
|
||||
// state = const LogsOverviewState();
|
||||
// ref.onDispose(
|
||||
// () {
|
||||
// loggy.debug("disposing");
|
||||
// _listener?.cancel();
|
||||
// _listener = null;
|
||||
// },
|
||||
// );
|
||||
// ref.onCancel(
|
||||
// () {
|
||||
// if (_listener?.isPaused != true) {
|
||||
// loggy.debug("pausing");
|
||||
// _listener?.pause();
|
||||
// }
|
||||
// },
|
||||
// );
|
||||
// ref.onResume(
|
||||
// () {
|
||||
// if (!state.paused && (_listener?.isPaused ?? false)) {
|
||||
// loggy.debug("resuming");
|
||||
// _listener?.resume();
|
||||
// }
|
||||
// },
|
||||
// );
|
||||
|
||||
// _addListeners();
|
||||
// return const LogsOverviewState();
|
||||
// }
|
||||
|
||||
// StreamSubscription? _listener;
|
||||
|
||||
// Future<void> _addListeners() async {
|
||||
// loggy.debug("adding listeners");
|
||||
// await _listener?.cancel();
|
||||
// _listener = ref
|
||||
// .read(logRepositoryProvider)
|
||||
// .requireValue
|
||||
// .watchLogs()
|
||||
// .throttle(
|
||||
// (_) => Stream.value(_listener?.isPaused ?? false),
|
||||
// leading: false,
|
||||
// trailing: true,
|
||||
// )
|
||||
// .throttleTime(
|
||||
// const Duration(milliseconds: 250),
|
||||
// leading: false,
|
||||
// trailing: true,
|
||||
// )
|
||||
// .asyncMap(
|
||||
// (event) async {
|
||||
// await event.fold(
|
||||
// (f) {
|
||||
// _logs = [];
|
||||
// state = state.copyWith(logs: AsyncError(f, StackTrace.current));
|
||||
// },
|
||||
// (a) async {
|
||||
// _logs = a.reversed;
|
||||
// state = state.copyWith(logs: AsyncData(await _computeLogs()));
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// ).listen((event) {});
|
||||
// }
|
||||
|
||||
// Iterable<LogEntity> _logs = [];
|
||||
// final _debouncer = CallbackDebouncer(const Duration(milliseconds: 200));
|
||||
// LogLevel? _levelFilter;
|
||||
// String _filter = "";
|
||||
|
||||
// Future<List<LogEntity>> _computeLogs() async {
|
||||
// if (_levelFilter == null && _filter.isEmpty) return _logs.toList();
|
||||
// return _logs.where((e) {
|
||||
// return (_filter.isEmpty || e.message.contains(_filter)) &&
|
||||
// (_levelFilter == null ||
|
||||
// e.level == null ||
|
||||
// e.level!.index >= _levelFilter!.index);
|
||||
// }).toList();
|
||||
// }
|
||||
|
||||
// void pause() {
|
||||
// loggy.debug("pausing");
|
||||
// _listener?.pause();
|
||||
// state = state.copyWith(paused: true);
|
||||
// }
|
||||
|
||||
// void resume() {
|
||||
// loggy.debug("resuming");
|
||||
// _listener?.resume();
|
||||
// state = state.copyWith(paused: false);
|
||||
// }
|
||||
|
||||
// Future<void> clear() async {
|
||||
// loggy.debug("clearing");
|
||||
// await ref.read(logRepositoryProvider).requireValue.clearLogs().match(
|
||||
// (l) {
|
||||
// loggy.warning("error clearing logs", l);
|
||||
// },
|
||||
// (_) {
|
||||
// _logs = [];
|
||||
// state = state.copyWith(logs: const AsyncData([]));
|
||||
// },
|
||||
// ).run();
|
||||
// }
|
||||
|
||||
// void filterMessage(String? filter) {
|
||||
// _filter = filter ?? '';
|
||||
// _debouncer(
|
||||
// () async {
|
||||
// if (state.logs case AsyncData()) {
|
||||
// state = state.copyWith(
|
||||
// filter: _filter,
|
||||
// logs: AsyncData(await _computeLogs()),
|
||||
// );
|
||||
// }
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
|
||||
// Future<void> filterLevel(LogLevel? level) async {
|
||||
// _levelFilter = level;
|
||||
// if (state.logs case AsyncData()) {
|
||||
// state = state.copyWith(
|
||||
// levelFilter: _levelFilter,
|
||||
// logs: AsyncData(await _computeLogs()),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
// import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
// import 'package:fpdart/fpdart.dart';
|
||||
// import 'package:gap/gap.dart';
|
||||
// import 'package:kaer_with_panels/core/localization/translations.dart';
|
||||
// import 'package:kaer_with_panels/core/model/failures.dart';
|
||||
// import 'package:kaer_with_panels/core/preferences/general_preferences.dart';
|
||||
// import 'package:kaer_with_panels/core/widget/adaptive_icon.dart';
|
||||
// import 'package:kaer_with_panels/features/common/nested_app_bar.dart';
|
||||
// import 'package:kaer_with_panels/features/log/data/log_data_providers.dart';
|
||||
// import 'package:kaer_with_panels/features/log/model/log_level.dart';
|
||||
// import 'package:kaer_with_panels/features/log/overview/logs_overview_notifier.dart';
|
||||
// import 'package:kaer_with_panels/utils/utils.dart';
|
||||
// import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
// import 'package:sliver_tools/sliver_tools.dart';
|
||||
|
||||
// class LogsOverviewPage extends HookConsumerWidget with PresLogger {
|
||||
// const LogsOverviewPage({super.key});
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context, WidgetRef ref) {
|
||||
// final t = ref.watch(translationsProvider);
|
||||
// final state = ref.watch(logsOverviewNotifierProvider);
|
||||
// final notifier = ref.watch(logsOverviewNotifierProvider.notifier);
|
||||
|
||||
// final debug = ref.watch(debugModeNotifierProvider);
|
||||
// final pathResolver = ref.watch(logPathResolverProvider);
|
||||
|
||||
// final filterController = useTextEditingController(text: state.filter);
|
||||
|
||||
// final List<PopupMenuEntry> popupButtons = debug || PlatformUtils.isDesktop
|
||||
// ? [
|
||||
// PopupMenuItem(
|
||||
// child: Text(t.logs.shareCoreLogs),
|
||||
// onTap: () async {
|
||||
// await UriUtils.tryShareOrLaunchFile(
|
||||
// Uri.parse(pathResolver.coreFile().path),
|
||||
// fileOrDir: pathResolver.directory.uri,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// PopupMenuItem(
|
||||
// child: Text(t.logs.shareAppLogs),
|
||||
// onTap: () async {
|
||||
// await UriUtils.tryShareOrLaunchFile(
|
||||
// Uri.parse(pathResolver.appFile().path),
|
||||
// fileOrDir: pathResolver.directory.uri,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ]
|
||||
// : [];
|
||||
|
||||
// return Scaffold(
|
||||
// body: NestedScrollView(
|
||||
// headerSliverBuilder: (context, innerBoxIsScrolled) {
|
||||
// return <Widget>[
|
||||
// SliverOverlapAbsorber(
|
||||
// handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
|
||||
// sliver: MultiSliver(
|
||||
// children: [
|
||||
// NestedAppBar(
|
||||
// forceElevated: innerBoxIsScrolled,
|
||||
// title: Text(t.logs.pageTitle),
|
||||
// actions: [
|
||||
// if (state.paused)
|
||||
// IconButton(
|
||||
// onPressed: notifier.resume,
|
||||
// icon: const Icon(FluentIcons.play_20_regular),
|
||||
// tooltip: t.logs.resumeTooltip,
|
||||
// iconSize: 20,
|
||||
// )
|
||||
// else
|
||||
// IconButton(
|
||||
// onPressed: notifier.pause,
|
||||
// icon: const Icon(FluentIcons.pause_20_regular),
|
||||
// tooltip: t.logs.pauseTooltip,
|
||||
// iconSize: 20,
|
||||
// ),
|
||||
// IconButton(
|
||||
// onPressed: notifier.clear,
|
||||
// icon: const Icon(FluentIcons.delete_lines_20_regular),
|
||||
// tooltip: t.logs.clearTooltip,
|
||||
// iconSize: 20,
|
||||
// ),
|
||||
// if (popupButtons.isNotEmpty)
|
||||
// PopupMenuButton(
|
||||
// icon: Icon(AdaptiveIcon(context).more),
|
||||
// itemBuilder: (context) {
|
||||
// return popupButtons;
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// SliverPinnedHeader(
|
||||
// child: DecoratedBox(
|
||||
// decoration: BoxDecoration(
|
||||
// color: Theme.of(context).colorScheme.background,
|
||||
// ),
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 16,
|
||||
// vertical: 8,
|
||||
// ),
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Flexible(
|
||||
// child: TextFormField(
|
||||
// controller: filterController,
|
||||
// onChanged: notifier.filterMessage,
|
||||
// decoration: InputDecoration(
|
||||
// isDense: true,
|
||||
// hintText: t.logs.filterHint,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// const Gap(16),
|
||||
// DropdownButton<Option<LogLevel>>(
|
||||
// value: optionOf(state.levelFilter),
|
||||
// onChanged: (v) {
|
||||
// if (v == null) return;
|
||||
// notifier.filterLevel(v.toNullable());
|
||||
// },
|
||||
// padding:
|
||||
// const EdgeInsets.symmetric(horizontal: 8),
|
||||
// borderRadius: BorderRadius.circular(4),
|
||||
// items: [
|
||||
// DropdownMenuItem(
|
||||
// value: none(),
|
||||
// child: Text(t.logs.allLevelsFilter),
|
||||
// ),
|
||||
// ...LogLevel.choices.map(
|
||||
// (e) => DropdownMenuItem(
|
||||
// value: some(e),
|
||||
// child: Text(e.name),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ];
|
||||
// },
|
||||
// body: Builder(
|
||||
// builder: (context) {
|
||||
// return CustomScrollView(
|
||||
// primary: false,
|
||||
// reverse: true,
|
||||
// slivers: <Widget>[
|
||||
// switch (state.logs) {
|
||||
// AsyncData(value: final logs) => SliverList.builder(
|
||||
// itemCount: logs.length,
|
||||
// itemBuilder: (context, index) {
|
||||
// final log = logs[index];
|
||||
// return Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 16,
|
||||
// vertical: 4,
|
||||
// ),
|
||||
// child: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// if (log.level != null)
|
||||
// Row(
|
||||
// mainAxisAlignment:
|
||||
// MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// Text(
|
||||
// log.level!.name.toUpperCase(),
|
||||
// style: Theme.of(context)
|
||||
// .textTheme
|
||||
// .labelMedium
|
||||
// ?.copyWith(
|
||||
// color: log.level!.color,
|
||||
// ),
|
||||
// ),
|
||||
// if (log.time != null)
|
||||
// Text(
|
||||
// log.time!.toString(),
|
||||
// style: Theme.of(context)
|
||||
// .textTheme
|
||||
// .labelSmall,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// Text(
|
||||
// log.message,
|
||||
// style:
|
||||
// Theme.of(context).textTheme.bodySmall,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// if (index != 0)
|
||||
// const Divider(
|
||||
// indent: 16,
|
||||
// endIndent: 16,
|
||||
// height: 4,
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// AsyncError(:final error) => SliverErrorBodyPlaceholder(
|
||||
// t.presentShortError(error),
|
||||
// ),
|
||||
// _ => const SliverLoadingBodyPlaceholder(),
|
||||
// },
|
||||
// SliverOverlapInjector(
|
||||
// handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
|
||||
// context,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:kaer_with_panels/features/log/model/log_entity.dart';
|
||||
import 'package:kaer_with_panels/features/log/model/log_level.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'logs_overview_state.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class LogsOverviewState with _$LogsOverviewState {
|
||||
const LogsOverviewState._();
|
||||
|
||||
const factory LogsOverviewState({
|
||||
@Default(AsyncLoading()) AsyncValue<List<LogEntity>> logs,
|
||||
@Default(false) bool paused,
|
||||
@Default("") String filter,
|
||||
LogLevel? levelFilter,
|
||||
}) = _LogsOverviewState;
|
||||
}
|
||||
Reference in New Issue
Block a user