diff --git a/flutter/Makefile b/flutter/Makefile index 481fddb..86e905a 100644 --- a/flutter/Makefile +++ b/flutter/Makefile @@ -9,9 +9,14 @@ # runs app locally (linux) -run: +run-linux: dart run build_runner build - _JAVA_OPTIONS="" flutter run + _JAVA_OPTIONS="" flutter run -d linux + +# runs app locally (web | not really supported) +run-linux: + dart run build_runner build + _JAVA_OPTIONS="" flutter run -d web # runs on android device (must have network adb enabled teh correct IP) run-android: diff --git a/flutter/lib/pages/debug/debug_request_view.dart b/flutter/lib/pages/debug/debug_request_view.dart index 695b292..a91b1f5 100644 --- a/flutter/lib/pages/debug/debug_request_view.dart +++ b/flutter/lib/pages/debug/debug_request_view.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -6,11 +8,19 @@ import 'package:simplecloudnotifier/state/request_log.dart'; import 'package:simplecloudnotifier/utils/toaster.dart'; import 'package:simplecloudnotifier/utils/ui.dart'; -class DebugRequestViewPage extends StatelessWidget { +class DebugRequestViewPage extends StatefulWidget { final SCNRequest request; DebugRequestViewPage({required this.request}); + @override + State createState() => _DebugRequestViewPageState(); +} + +class _DebugRequestViewPageState extends State { + Set _monospaceMode = new Set(); + Set _prettyJson = new Set(); + @override Widget build(BuildContext context) { return SCNScaffold( @@ -23,22 +33,22 @@ class DebugRequestViewPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.start, children: [ - ...buildRow(context, "Name", request.name), - ...buildRow(context, "Timestamp (Start)", request.timestampStart.toString()), - ...buildRow(context, "Timestamp (End)", request.timestampEnd.toString()), - ...buildRow(context, "Duration", request.timestampEnd.difference(request.timestampStart).toString()), + ...buildRow(context, "name", "Name", widget.request.name), + ...buildRow(context, "timestampStart", "Timestamp (Start)", widget.request.timestampStart.toString()), + ...buildRow(context, "timestampEnd", "Timestamp (End)", widget.request.timestampEnd.toString()), + ...buildRow(context, "duration", "Duration", widget.request.timestampEnd.difference(widget.request.timestampStart).toString()), Divider(), - ...buildRow(context, "Method", request.method), - ...buildRow(context, "URL", request.url), - if (request.requestHeaders.isNotEmpty) ...buildRow(context, "Request->Headers", request.requestHeaders.entries.map((v) => '${v.key} = ${v.value}').join('\n')), - if (request.requestBody != '') ...buildRow(context, "Request->Body", request.requestBody), + ...buildRow(context, "method", "Method", widget.request.method), + ...buildRow(context, "url", "URL", widget.request.url, mono: true), + if (widget.request.requestHeaders.isNotEmpty) ...buildRow(context, "request_headers", "Request->Headers", widget.request.requestHeaders.entries.map((v) => '${v.key} = ${v.value}').join('\n'), mono: true), + if (widget.request.requestBody != '') ...buildRow(context, "request_body", "Request->Body", widget.request.requestBody, mono: true, json: true), Divider(), - if (request.responseStatusCode != 0) ...buildRow(context, "Response->Statuscode", request.responseStatusCode.toString()), - if (request.responseBody != '') ...buildRow(context, "Reponse->Body", request.responseBody), - if (request.responseHeaders.isNotEmpty) ...buildRow(context, "Reponse->Headers", request.responseHeaders.entries.map((v) => '${v.key} = ${v.value}').join('\n')), + if (widget.request.responseStatusCode != 0) ...buildRow(context, "response_statuscode", "Response->Statuscode", widget.request.responseStatusCode.toString()), + if (widget.request.responseBody != '') ...buildRow(context, "response_body", "Reponse->Body", widget.request.responseBody, mono: true, json: true), + if (widget.request.responseHeaders.isNotEmpty) ...buildRow(context, "response_headers", "Reponse->Headers", widget.request.responseHeaders.entries.map((v) => '${v.key} = ${v.value}').join('\n'), mono: true, json: true), Divider(), - if (request.error != '') ...buildRow(context, "Error", request.error), - if (request.stackTrace != '') ...buildRow(context, "Stacktrace", request.stackTrace), + if (widget.request.error != '') ...buildRow(context, "error", "Error", widget.request.error, mono: true), + if (widget.request.stackTrace != '') ...buildRow(context, "trace", "Stacktrace", widget.request.stackTrace, mono: true), ], ), ), @@ -46,7 +56,19 @@ class DebugRequestViewPage extends StatelessWidget { ); } - List buildRow(BuildContext context, String title, String value) { + List buildRow(BuildContext context, String key, String title, String value, {bool? json, bool? mono}) { + var isMono = _monospaceMode.contains(key); + var isJson = _prettyJson.contains(key); + + if (isJson) { + try { + var jsonValue = jsonDecode(value); + value = JsonEncoder.withIndent(' ').convert(jsonValue); + } catch (e) { + value = ('Error parsing JSON: $e'); + } + } + return [ Padding( padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 8.0), @@ -64,19 +86,46 @@ class DebugRequestViewPage extends StatelessWidget { }, icon: FontAwesomeIcons.copy, ), + if (mono == true) + UI.buttonIconOnly( + icon: isMono ? FontAwesomeIcons.lineColumns : FontAwesomeIcons.alignLeft, + onPressed: () { + setState(() { + _monospaceMode.contains(key) ? _monospaceMode.remove(key) : _monospaceMode.add(key); + }); + }, + ), + if (json == true) + UI.buttonIconOnly( + icon: isJson ? FontAwesomeIcons.bracketsRound : FontAwesomeIcons.bracketsCurly, + onPressed: () { + setState(() { + _prettyJson.contains(key) ? _prettyJson.remove(key) : _prettyJson.add(key); + }); + }, + ), ], ), ), Card.filled( shape: BeveledRectangleBorder(borderRadius: BorderRadius.circular(0)), - color: request.type == 'SUCCESS' ? null : Theme.of(context).colorScheme.errorContainer, + color: widget.request.type == 'SUCCESS' ? null : Theme.of(context).colorScheme.errorContainer, child: Padding( padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 6.0), - child: SelectableText( - value, - minLines: 1, - maxLines: 10, - ), + child: (isMono || isJson) + ? SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SelectableText( + value, + minLines: 1, + maxLines: 10, + ), + ) + : SelectableText( + value, + minLines: 1, + maxLines: 10, + ), ), ), ];