mirror of
https://github.com/foss42/apidash.git
synced 2025-06-04 01:13:30 +08:00
Merge branch 'foss42:main' into main
This commit is contained in:
@ -379,6 +379,7 @@ const Map<String, Map<String, List<ResponseBodyView>>>
|
||||
kSubTypeDefaultViewOptions: kRawBodyViewOptions,
|
||||
kSubTypeCss: kCodeRawBodyViewOptions,
|
||||
kSubTypeHtml: kCodeRawBodyViewOptions,
|
||||
kSubTypeCsv: kPreviewRawBodyViewOptions,
|
||||
kSubTypeJavascript: kCodeRawBodyViewOptions,
|
||||
kSubTypeMarkdown: kCodeRawBodyViewOptions,
|
||||
kSubTypeTextXml: kCodeRawBodyViewOptions,
|
||||
@ -496,6 +497,9 @@ const kAudioError =
|
||||
const kRaiseIssue =
|
||||
"\nPlease raise an issue in API Dash GitHub repo so that we can resolve it.";
|
||||
|
||||
const kCsvError =
|
||||
"There seems to be an issue rendering this CSV. Please raise an issue in API Dash GitHub repo so that we can resolve it.";
|
||||
|
||||
const kHintTextUrlCard = "Enter API endpoint like api.foss42.com/country/codes";
|
||||
const kLabelPlusNew = "+ New";
|
||||
const kLabelSend = "Send";
|
||||
|
@ -2,6 +2,7 @@ Map<String, String> headers = {
|
||||
"Accept": "Specifies the media types that are acceptable for the response.",
|
||||
"Accept-Encoding":
|
||||
"Indicates the encoding methods the client can understand.",
|
||||
"Accept-Charset": "Specifies the character sets that are acceptable.",
|
||||
"Access-Control-Allow-Headers":
|
||||
"Specifies a list of HTTP headers that can be used in an actual request after a preflight request including the Access-Control-Request-Headers header is made.",
|
||||
"Access-Control-Allow-Methods":
|
||||
@ -41,11 +42,16 @@ Map<String, String> headers = {
|
||||
"Cross-Origin-Resource-Policy":
|
||||
"Controls how cross-origin requests for resources are handled.",
|
||||
"Date": "Indicates the date and time at which the message was sent.",
|
||||
"Device-Memory":
|
||||
"Indicates the approximate amount of device memory in gigabytes.",
|
||||
"DNT":
|
||||
"Informs websites whether the user's preference is to opt out of online tracking.",
|
||||
"Expect": "Indicates certain expectations that need to be met by the server.",
|
||||
"Expires":
|
||||
"Contains the date/time after which the response is considered expired",
|
||||
"Forwarded":
|
||||
"Contains information from the client-facing side of proxy servers that is altered or lost when a proxy is involved in the path of the request.",
|
||||
"From": "Contains an Internet email address for a human user who controls the requesting user agent.",
|
||||
"Host": "Specifies the domain name of the server and the port number.",
|
||||
"If-Match":
|
||||
"Used for conditional requests, allows the server to respond based on certain conditions.",
|
||||
@ -57,9 +63,15 @@ Map<String, String> headers = {
|
||||
"Used in conjunction with the Range header to conditionally request a partial resource.",
|
||||
"If-Unmodified-Since":
|
||||
"Used for conditional requests, allows the server to respond based on certain conditions.",
|
||||
"Keep-Alive":
|
||||
"Used to allow the connection to be reused for further requests.",
|
||||
"Location":
|
||||
"Indicates the URL a client should redirect to for further interaction.",
|
||||
"Max-Forwards":
|
||||
"Indicates the remaining number of times a request can be forwarded by proxies.",
|
||||
"Origin": "Specifies the origin of a cross-origin request.",
|
||||
"Proxy-Authorization":
|
||||
"Contains credentials for authenticating a client with a proxy server.",
|
||||
"Range":
|
||||
"Used to request only part of a resource, typically in the context of downloading large files.",
|
||||
"Referer":
|
||||
@ -68,10 +80,14 @@ Map<String, String> headers = {
|
||||
"Specifies how much information the browser should include in the Referer header when navigating to other pages.",
|
||||
"Retry-After":
|
||||
"Informs the client how long it should wait before making another request after a server has responded with a rate-limiting status code.",
|
||||
"Save-Data":
|
||||
"Indicates the client's preference for reduced data usage.",
|
||||
"Server": "Indicates the software used by the origin server.",
|
||||
"Strict-Transport-Security":
|
||||
"Instructs the browser to always use HTTPS for the given domain.",
|
||||
"TE": "Specifies the transfer encodings that are acceptable to the client.",
|
||||
"Upgrade-Insecure-Requests":
|
||||
"Instructs the browser to prefer secure connections when available.",
|
||||
"User-Agent":
|
||||
"Identifies the client software and version making the request.",
|
||||
"Via":
|
||||
|
53
lib/widgets/csv_previewer.dart
Normal file
53
lib/widgets/csv_previewer.dart
Normal file
@ -0,0 +1,53 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:csv/csv.dart';
|
||||
import 'error_message.dart';
|
||||
import '../consts.dart';
|
||||
|
||||
class CsvPreviewer extends StatelessWidget {
|
||||
const CsvPreviewer({super.key, required this.body});
|
||||
|
||||
final String body;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
try {
|
||||
final List<List<dynamic>> csvData =
|
||||
const CsvToListConverter().convert(body, eol: '\n');
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: DataTable(
|
||||
columns: csvData[0]
|
||||
.map(
|
||||
(item) => DataColumn(
|
||||
label: Text(
|
||||
item.toString(),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
rows: csvData
|
||||
.skip(1)
|
||||
.map(
|
||||
(csvrow) => DataRow(
|
||||
cells: csvrow
|
||||
.map(
|
||||
(csvItem) => DataCell(
|
||||
Text(
|
||||
csvItem.toString(),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
return const ErrorMessage(message: kCsvError);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:apidash/utils/header_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||
|
||||
class HeaderField extends StatefulWidget {
|
||||
@ -41,6 +41,7 @@ class _HeaderFieldState extends State<HeaderField> {
|
||||
|
||||
@override
|
||||
void didUpdateWidget(HeaderField oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.initialValue != widget.initialValue) {
|
||||
controller.text = widget.initialValue ?? "";
|
||||
controller.selection =
|
||||
@ -54,8 +55,8 @@ class _HeaderFieldState extends State<HeaderField> {
|
||||
return TypeAheadField(
|
||||
key: Key(widget.keyId),
|
||||
hideOnEmpty: true,
|
||||
minCharsForSuggestions: 1,
|
||||
onSuggestionSelected: (value) {
|
||||
controller: controller,
|
||||
onSelected: (value) {
|
||||
setState(() {
|
||||
controller.text = value;
|
||||
});
|
||||
@ -68,19 +69,17 @@ class _HeaderFieldState extends State<HeaderField> {
|
||||
);
|
||||
},
|
||||
suggestionsCallback: headerSuggestionCallback,
|
||||
suggestionsBoxDecoration: suggestionBoxDecorations(context),
|
||||
textFieldConfiguration: TextFieldConfiguration(
|
||||
decorationBuilder: (context, child) =>
|
||||
suggestionBoxDecorations(context, child, colorScheme),
|
||||
constraints: const BoxConstraints(maxHeight: 400),
|
||||
builder: (context, controller, focusNode) => TextField(
|
||||
onChanged: widget.onChanged,
|
||||
controller: controller,
|
||||
style: kCodeStyle.copyWith(
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
focusNode: focusNode,
|
||||
style: kCodeStyle.copyWith(color: colorScheme.onSurface),
|
||||
decoration: InputDecoration(
|
||||
hintStyle: kCodeStyle.copyWith(
|
||||
color: colorScheme.outline.withOpacity(
|
||||
kHintOpacity,
|
||||
),
|
||||
),
|
||||
color: colorScheme.outline.withOpacity(kHintOpacity)),
|
||||
hintText: widget.hintText,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
@ -99,22 +98,26 @@ class _HeaderFieldState extends State<HeaderField> {
|
||||
);
|
||||
}
|
||||
|
||||
SuggestionsBoxDecoration suggestionBoxDecorations(BuildContext context) {
|
||||
return SuggestionsBoxDecoration(
|
||||
elevation: 4,
|
||||
constraints: const BoxConstraints(maxHeight: 400),
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.2,
|
||||
Theme suggestionBoxDecorations(
|
||||
BuildContext context, Widget child, ColorScheme colorScheme) {
|
||||
return Theme(
|
||||
data: ThemeData(colorScheme: colorScheme),
|
||||
child: Material(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(color: Theme.of(context).dividerColor, width: 1.2),
|
||||
borderRadius: const BorderRadius.vertical(bottom: Radius.circular(8)),
|
||||
),
|
||||
borderRadius: const BorderRadius.vertical(bottom: Radius.circular(8)),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: child,
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<String>> headerSuggestionCallback(String pattern) async {
|
||||
Future<List<String>?> headerSuggestionCallback(String pattern) async {
|
||||
if (pattern.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return getHeaderSuggestions(pattern);
|
||||
}
|
||||
}
|
||||
|
@ -154,6 +154,7 @@ class _JsonPreviewerState extends State<JsonPreviewer> {
|
||||
|
||||
@override
|
||||
void didUpdateWidget(JsonPreviewer oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.code != widget.code) {
|
||||
store.buildNodes(widget.code, areAllCollapsed: true);
|
||||
store.expandAll();
|
||||
|
@ -7,6 +7,7 @@ import 'package:vector_graphics_compiler/vector_graphics_compiler.dart';
|
||||
import 'error_message.dart';
|
||||
import 'uint8_audio_player.dart';
|
||||
import 'json_previewer.dart';
|
||||
import 'csv_previewer.dart';
|
||||
import '../consts.dart';
|
||||
|
||||
class Previewer extends StatefulWidget {
|
||||
@ -81,6 +82,9 @@ class _PreviewerState extends State<Previewer> {
|
||||
},
|
||||
);
|
||||
}
|
||||
if (widget.type == kTypeText && widget.subtype == kSubTypeCsv) {
|
||||
return CsvPreviewer(body: widget.body);
|
||||
}
|
||||
if (widget.type == kTypeVideo) {
|
||||
// TODO: Video Player
|
||||
}
|
||||
|
Reference in New Issue
Block a user