import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:simplecloudnotifier/components/layout/scaffold.dart'; import 'package:simplecloudnotifier/state/request_log.dart'; import 'package:simplecloudnotifier/utils/toaster.dart'; import 'package:simplecloudnotifier/utils/ui.dart'; 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( title: 'Request', showSearch: false, child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.start, children: [ ...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", "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 (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 (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), ], ), ), ), ); } 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), child: Row( children: [ Expanded( child: Text(title, style: TextStyle(fontWeight: FontWeight.bold)), ), UI.buttonIconOnly( iconSize: 14, onPressed: () { Clipboard.setData(new ClipboardData(text: title)); Toaster.info("Clipboard", 'Copied text to Clipboard'); print('================= [CLIPBOARD] =================\n${title}\n================= [/CLIPBOARD] ================='); }, 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: widget.request.type == 'SUCCESS' ? null : Theme.of(context).colorScheme.errorContainer, child: Padding( padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 6.0), child: (isMono || isJson) ? SingleChildScrollView( scrollDirection: Axis.horizontal, child: SelectableText( value, minLines: 1, maxLines: 10, ), ) : SelectableText( value, minLines: 1, maxLines: 10, ), ), ), ]; } }