From f1f900a7240871f068ac888036e051c0912e594d Mon Sep 17 00:00:00 2001 From: TechnicJelle <22576047+TechnicJelle@users.noreply.github.com> Date: Thu, 29 Aug 2024 06:35:46 +0200 Subject: [PATCH] FIX Improved console and process --- README.md | 2 +- lib/console.dart | 26 +++++---- lib/control_panel.dart | 117 ++++++++++++++++++----------------------- 3 files changed, 65 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index c0eca7b..1e1b370 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ Make sure you have **Java 16** or higher installed and on your PATH! ## TODO - make the console better + - fix the scroll; when you scroll up, new logs should not move the view (but it does, rn) - add a scroll down button (only visible if you scroll up) - fix the random line breaks (probably due to the stream) -- make the start/stop buttons a single button - make it not lose the process (orphan) as easily - when going to a config file without stopping the bluemap process, it loses it and you have to restart it - when closing the program, it loses it diff --git a/lib/console.dart b/lib/console.dart index 44815f2..63fbb0b 100644 --- a/lib/console.dart +++ b/lib/console.dart @@ -17,20 +17,18 @@ class ConsoleState extends ConsumerState { @override Widget build(BuildContext context) { - final AsyncValue asyncLog = ref.watch(logProvider); - switch (asyncLog) { - case AsyncData(value: final String message): - print("RECEIVE: $message"); - output.add(message); - break; - case AsyncError(:final error): - print("ERR: $error"); - output.add("ERR: $error"); - break; - default: - print("No new log message"); - break; - } + ref.listen(logNotifierProvider, (_, next) { + switch (next) { + case AsyncData(value: final String? message): + if (message != null) { + setState(() => output.add(message)); + } + break; + case AsyncError(:final error): + setState(() => output.add("ERR: $error")); + break; + } + }); return Container( margin: const EdgeInsets.only(bottom: 8, right: 8), diff --git a/lib/control_panel.dart b/lib/control_panel.dart index 55f7c43..6009636 100644 --- a/lib/control_panel.dart +++ b/lib/control_panel.dart @@ -1,3 +1,4 @@ +import "dart:async"; import "dart:convert"; import "dart:io"; @@ -10,23 +11,12 @@ import "package:url_launcher/url_launcher.dart"; import "console.dart"; import "main.dart"; -class LogNotifier extends AsyncNotifier { +class LogNotifier extends AutoDisposeAsyncNotifier { + Process? _process; + StreamSubscription? _streamSub; @override - String build() { - return ""; - } - - void log(String message) { - state = AsyncValue.data(message); - } -} - -final logProvider = AsyncNotifierProvider(() => LogNotifier()); - -class ProcessNotifier extends AsyncNotifier { - @override - Future build() { - return Future.value(null); + Future build() async { + return null; } Future start() async { @@ -35,63 +25,60 @@ class ProcessNotifier extends AsyncNotifier { final Directory projectDirectory = ref.read(projectDirectoryProvider)!; final String bluemapJarPath = p.join(projectDirectory.path, blueMapCliJarName); - state = await AsyncValue.guard(() async { - Process process = await Process.start( - "java", - ["-jar", bluemapJarPath, "--render", "--watch", "--webserver"], - workingDirectory: projectDirectory.path, - mode: ProcessStartMode.normal, - runInShell: false, - ); - process.exitCode.then((value) { - print("Exit code: $value"); - state = const AsyncValue.data(null); - return; - }); - - var mergedStream = StreamGroup.merge([ - process.stdout.transform(utf8.decoder), - process.stderr.transform(utf8.decoder), - ]); - - mergedStream.listen((String event) { - print("OUTPUT: $event"); - ref.read(logProvider.notifier).log(event); - }); - - // bool hasWebserverStartedYet = false; - // process.stdout.transform(utf8.decoder).listen((String event) { - // if (event.contains("WebServer started")) { - // hasWebserverStartedYet = true; - // } - // }); - // - // while (!hasWebserverStartedYet) { - // await Future.delayed(const Duration(milliseconds: 100)); - // } + final process = await Process.start( + "java", + ["-jar", bluemapJarPath, "--render", "--watch", "--webserver"], + workingDirectory: projectDirectory.path, + mode: ProcessStartMode.normal, + runInShell: false, + ); + process.exitCode.then((value) { + print("Exit code: $value"); + state = const AsyncValue.data(null); + return; + }); - return process; + var mergedStream = StreamGroup.merge([ + process.stdout.transform(utf8.decoder), + process.stderr.transform(utf8.decoder), + ]); + + // bool hasWebserverStartedYet = false; + // process.stdout.transform(utf8.decoder).listen((String event) { + // if (event.contains("WebServer started")) { + // hasWebserverStartedYet = true; + // } + // }); + // + // while (!hasWebserverStartedYet) { + // await Future.delayed(const Duration(milliseconds: 100)); + // } + + _process = process; + _streamSub?.cancel(); + _streamSub = mergedStream.listen((String event) { + print("OUTPUT: $event"); + state = AsyncValue.data(event); }); } void stop() { - state.whenData((Process? process) { - if (process == null) throw Exception("No process to stop!"); - process.kill(); - }); + Process? process = _process; + if (process == null) throw Exception("No process to stop!"); + process.kill(); + _streamSub?.cancel(); } } -final processProvider = - AsyncNotifierProvider(() => ProcessNotifier()); +final logNotifierProvider = + AsyncNotifierProvider.autoDispose(() => LogNotifier()); class ControlPanel extends ConsumerWidget { const ControlPanel({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - final asyncProcess = ref.watch(processProvider); - final Process? process = asyncProcess.asData?.value; + final logMessage = ref.watch(logNotifierProvider).value; return Padding( padding: const EdgeInsets.only(top: 16), @@ -103,18 +90,18 @@ class ControlPanel extends ConsumerWidget { children: [ ElevatedButton.icon( onPressed: () { - if (process == null) { - ref.read(processProvider.notifier).start(); + if (logMessage == null) { + ref.read(logNotifierProvider.notifier).start(); } else { - ref.read(processProvider.notifier).stop(); + ref.read(logNotifierProvider.notifier).stop(); } }, - label: Text(process == null ? "Start" : "Stop"), - icon: Icon(process == null ? Icons.play_arrow : Icons.stop), + label: Text(logMessage == null ? "Start" : "Stop"), + icon: Icon(logMessage == null ? Icons.play_arrow : Icons.stop), ), const SizedBox(width: 16), ElevatedButton.icon( - onPressed: process == null + onPressed: logMessage == null ? null : () async { if (!await launchUrl(Uri.parse("http://localhost:8100"))) {