mirror of
https://github.com/foss42/apidash.git
synced 2025-08-06 13:51:20 +08:00
Updated previewer with downloader
This commit is contained in:
@ -291,7 +291,7 @@ const Map<String, Map<String, List<ResponseBodyView>>>
|
|||||||
kSubTypeDefaultViewOptions: kNoRawBodyViewOptions,
|
kSubTypeDefaultViewOptions: kNoRawBodyViewOptions,
|
||||||
kSubTypeJson: kCodeRawBodyViewOptions,
|
kSubTypeJson: kCodeRawBodyViewOptions,
|
||||||
kSubTypeOctetStream: kNoBodyViewOptions,
|
kSubTypeOctetStream: kNoBodyViewOptions,
|
||||||
kSubTypePdf: kPreviewBodyViewOptions,
|
kSubTypePdf: kNoBodyViewOptions,
|
||||||
kSubTypeSql: kCodeRawBodyViewOptions,
|
kSubTypeSql: kCodeRawBodyViewOptions,
|
||||||
kSubTypeXml: kCodeRawBodyViewOptions,
|
kSubTypeXml: kCodeRawBodyViewOptions,
|
||||||
kSubTypeYaml: kCodeRawBodyViewOptions,
|
kSubTypeYaml: kCodeRawBodyViewOptions,
|
||||||
@ -302,10 +302,10 @@ const Map<String, Map<String, List<ResponseBodyView>>>
|
|||||||
kSubTypeSvg: kCodeRawBodyViewOptions,
|
kSubTypeSvg: kCodeRawBodyViewOptions,
|
||||||
},
|
},
|
||||||
kTypeAudio: {
|
kTypeAudio: {
|
||||||
kSubTypeDefaultViewOptions: kPreviewBodyViewOptions,
|
kSubTypeDefaultViewOptions: kNoBodyViewOptions,
|
||||||
},
|
},
|
||||||
kTypeVideo: {
|
kTypeVideo: {
|
||||||
kSubTypeDefaultViewOptions: kPreviewBodyViewOptions,
|
kSubTypeDefaultViewOptions: kNoBodyViewOptions,
|
||||||
},
|
},
|
||||||
kTypeText: {
|
kTypeText: {
|
||||||
kSubTypeDefaultViewOptions: kRawBodyViewOptions,
|
kSubTypeDefaultViewOptions: kRawBodyViewOptions,
|
||||||
@ -404,14 +404,13 @@ const kResponseCodeReasons = {
|
|||||||
const kIntro =
|
const kIntro =
|
||||||
"""API Dash is a beautiful open-source cross-platform API Client built using Flutter which can help you easily create & customize your API requests, visually inspect responses and generate Dart code on the go.""";
|
"""API Dash is a beautiful open-source cross-platform API Client built using Flutter which can help you easily create & customize your API requests, visually inspect responses and generate Dart code on the go.""";
|
||||||
|
|
||||||
const kMimeTypeRaiseIssueStart =
|
const kMimeTypeRawRaiseIssueStart =
|
||||||
"Please click on 'Raw' to view the unformatted raw results as a visual preview for the response Content-Type - ";
|
"Please click on 'Raw' to view the unformatted raw results as the response preview for Content-Type ";
|
||||||
|
|
||||||
const kMimeTypeRaiseIssueEnd =
|
const kMimeTypeRaiseIssueStart = "Response preview for Content-Type ";
|
||||||
" is currently not available.\nKindly raise an issue in API Dash GitHub repo so that we can add a Visual Preview logic for this content-type.";
|
|
||||||
|
|
||||||
const kMimeTypeRaiseIssue =
|
const kMimeTypeRaiseIssue =
|
||||||
" is currently not supported.\nPlease raise an issue in API Dash GitHub repo so that we can prioritize adding it to the tool.";
|
" is currently not supported.\nPlease raise an issue in API Dash GitHub repo so that we can add a Previewer for this content-type.";
|
||||||
|
|
||||||
const kUnexpectedRaiseIssue =
|
const kUnexpectedRaiseIssue =
|
||||||
"\nIf the behaviour is unexpected, please raise an issue in API Dash GitHub repo so that we can resolve it.";
|
"\nIf the behaviour is unexpected, please raise an issue in API Dash GitHub repo so that we can resolve it.";
|
||||||
|
@ -89,28 +89,27 @@ MediaType? getMediaTypeFromHeaders(Map? headers) {
|
|||||||
return (uri, null);
|
return (uri, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
(List<ResponseBodyView>, String?) getResponseBodyViewOptions(MediaType mediaType){
|
(List<ResponseBodyView>, String?) getResponseBodyViewOptions(MediaType? mediaType){
|
||||||
var type = mediaType.type;
|
if(mediaType != null){
|
||||||
var subtype = mediaType.subtype;
|
var type = mediaType.type;
|
||||||
//print(mediaType);
|
var subtype = mediaType.subtype;
|
||||||
if(kResponseBodyViewOptions.containsKey(type)){
|
if(kResponseBodyViewOptions.containsKey(type)){
|
||||||
if (kResponseBodyViewOptions[type]!.containsKey(subtype)){
|
if (kResponseBodyViewOptions[type]!.containsKey(subtype)){
|
||||||
return (kResponseBodyViewOptions[type]![subtype]!, kCodeHighlighterMap[subtype] ?? subtype);
|
return (kResponseBodyViewOptions[type]![subtype]!, kCodeHighlighterMap[subtype] ?? subtype);
|
||||||
|
}
|
||||||
|
if(subtype.contains(kSubTypeJson)){
|
||||||
|
subtype = kSubTypeJson;
|
||||||
|
}
|
||||||
|
if(subtype.contains(kSubTypeXml)){
|
||||||
|
subtype = kSubTypeXml;
|
||||||
|
}
|
||||||
|
if (kResponseBodyViewOptions[type]!.containsKey(subtype)){
|
||||||
|
return (kResponseBodyViewOptions[type]![subtype]!, kCodeHighlighterMap[subtype] ?? subtype);
|
||||||
|
}
|
||||||
|
return (kResponseBodyViewOptions[type]![kSubTypeDefaultViewOptions]!, subtype);
|
||||||
}
|
}
|
||||||
if(subtype.contains(kSubTypeJson)){
|
|
||||||
subtype = kSubTypeJson;
|
|
||||||
}
|
|
||||||
if(subtype.contains(kSubTypeXml)){
|
|
||||||
subtype = kSubTypeXml;
|
|
||||||
}
|
|
||||||
if (kResponseBodyViewOptions[type]!.containsKey(subtype)){
|
|
||||||
return (kResponseBodyViewOptions[type]![subtype]!, kCodeHighlighterMap[subtype] ?? subtype);
|
|
||||||
}
|
|
||||||
return (kResponseBodyViewOptions[type]![kSubTypeDefaultViewOptions]!, subtype);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return (kNoBodyViewOptions, null);
|
|
||||||
}
|
}
|
||||||
|
return (kNoBodyViewOptions, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
String? formatBody(String? body, MediaType? mediaType){
|
String? formatBody(String? body, MediaType? mediaType){
|
||||||
|
@ -3,9 +3,16 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class ErrorMessage extends StatelessWidget {
|
class ErrorMessage extends StatelessWidget {
|
||||||
const ErrorMessage({super.key, required this.message});
|
const ErrorMessage({
|
||||||
|
super.key,
|
||||||
|
required this.message,
|
||||||
|
this.showIcon = true,
|
||||||
|
this.showIssueButton = true,
|
||||||
|
});
|
||||||
|
|
||||||
final String? message;
|
final String? message;
|
||||||
|
final bool showIcon;
|
||||||
|
final bool showIssueButton;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -16,11 +23,13 @@ class ErrorMessage extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
showIcon
|
||||||
Icons.warning_rounded,
|
? Icon(
|
||||||
size: 40,
|
Icons.warning_rounded,
|
||||||
color: color,
|
size: 40,
|
||||||
),
|
color: color,
|
||||||
|
)
|
||||||
|
: const SizedBox(),
|
||||||
SelectableText(
|
SelectableText(
|
||||||
message ?? 'An error occurred. $kUnexpectedRaiseIssue',
|
message ?? 'An error occurred. $kUnexpectedRaiseIssue',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
@ -30,16 +39,18 @@ class ErrorMessage extends StatelessWidget {
|
|||||||
?.copyWith(color: color),
|
?.copyWith(color: color),
|
||||||
),
|
),
|
||||||
kVSpacer20,
|
kVSpacer20,
|
||||||
FilledButton.tonalIcon(
|
showIssueButton
|
||||||
onPressed: () {
|
? FilledButton.tonalIcon(
|
||||||
launchUrl(Uri.parse(kGitUrl));
|
onPressed: () {
|
||||||
},
|
launchUrl(Uri.parse(kGitUrl));
|
||||||
icon: const Icon(Icons.arrow_outward_rounded),
|
},
|
||||||
label: Text(
|
icon: const Icon(Icons.arrow_outward_rounded),
|
||||||
'Raise Issue',
|
label: Text(
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
'Raise Issue',
|
||||||
),
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -4,15 +4,19 @@ import 'error_message.dart';
|
|||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
|
|
||||||
class Previewer extends StatefulWidget {
|
class Previewer extends StatefulWidget {
|
||||||
const Previewer(
|
const Previewer({
|
||||||
{super.key,
|
super.key,
|
||||||
required this.bytes,
|
required this.bytes,
|
||||||
required this.type,
|
this.type,
|
||||||
required this.subtype});
|
this.subtype,
|
||||||
|
this.hasRaw = false,
|
||||||
|
});
|
||||||
|
|
||||||
final Uint8List bytes;
|
final Uint8List bytes;
|
||||||
final String type;
|
final String? type;
|
||||||
final String subtype;
|
final String? subtype;
|
||||||
|
final bool hasRaw;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<Previewer> createState() => _PreviewerState();
|
State<Previewer> createState() => _PreviewerState();
|
||||||
}
|
}
|
||||||
@ -20,9 +24,6 @@ class Previewer extends StatefulWidget {
|
|||||||
class _PreviewerState extends State<Previewer> {
|
class _PreviewerState extends State<Previewer> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (widget.type == kTypeApplication && widget.subtype == kSubTypePdf) {
|
|
||||||
return const ErrorMessage(message: "PDF viewing $kMimeTypeRaiseIssue");
|
|
||||||
}
|
|
||||||
if (widget.type == kTypeImage) {
|
if (widget.type == kTypeImage) {
|
||||||
return Image.memory(
|
return Image.memory(
|
||||||
widget.bytes,
|
widget.bytes,
|
||||||
@ -31,14 +32,18 @@ class _PreviewerState extends State<Previewer> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (widget.type == kTypeApplication && widget.subtype == kSubTypePdf) {
|
||||||
|
// TODO: PDF Viewer
|
||||||
|
}
|
||||||
if (widget.type == kTypeAudio) {
|
if (widget.type == kTypeAudio) {
|
||||||
return const ErrorMessage(message: "Audio playing $kMimeTypeRaiseIssue");
|
// TODO: Audio Player
|
||||||
}
|
}
|
||||||
if (widget.type == kTypeVideo) {
|
if (widget.type == kTypeVideo) {
|
||||||
return const ErrorMessage(message: "Video playing $kMimeTypeRaiseIssue");
|
// TODO: Video Player
|
||||||
}
|
}
|
||||||
return ErrorMessage(
|
String message = widget.hasRaw
|
||||||
message:
|
? "$kMimeTypeRawRaiseIssueStart${widget.type}/${widget.subtype}$kMimeTypeRaiseIssue"
|
||||||
"${widget.type}/${widget.subtype} mimetype preview $kMimeTypeRaiseIssue");
|
: "$kMimeTypeRaiseIssueStart${widget.type}/${widget.subtype}$kMimeTypeRaiseIssue";
|
||||||
|
return ErrorMessage(message: message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,30 +313,35 @@ class _ResponseBodyState extends State<ResponseBody> {
|
|||||||
final responseModel = widget.activeRequestModel?.responseModel;
|
final responseModel = widget.activeRequestModel?.responseModel;
|
||||||
if (responseModel == null) {
|
if (responseModel == null) {
|
||||||
return const ErrorMessage(
|
return const ErrorMessage(
|
||||||
message: 'Error: No Response Data Found. $kUnexpectedRaiseIssue');
|
|
||||||
}
|
|
||||||
var mediaType = responseModel.mediaType;
|
|
||||||
if (mediaType == null) {
|
|
||||||
return ErrorMessage(
|
|
||||||
message:
|
message:
|
||||||
'Unknown Response content type - ${responseModel.contentType}. $kUnexpectedRaiseIssue');
|
'Error: Response data does not exist. $kUnexpectedRaiseIssue');
|
||||||
}
|
}
|
||||||
|
|
||||||
var body = responseModel.body;
|
var body = responseModel.body;
|
||||||
var formattedBody = responseModel.formattedBody;
|
var formattedBody = responseModel.formattedBody;
|
||||||
if (body == null) {
|
if (body == null) {
|
||||||
return const ErrorMessage(
|
return const ErrorMessage(
|
||||||
message: 'Response body is empty. $kUnexpectedRaiseIssue');
|
message: 'Response body is missing (null). $kUnexpectedRaiseIssue');
|
||||||
}
|
}
|
||||||
var responseBodyView = getResponseBodyViewOptions(mediaType);
|
if (body.isEmpty) {
|
||||||
//print(responseBodyView);
|
return const ErrorMessage(
|
||||||
var options = responseBodyView.$0;
|
message: 'No content',
|
||||||
var highlightLanguage = responseBodyView.$1;
|
showIcon: false,
|
||||||
if (options == kNoBodyViewOptions) {
|
showIssueButton: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var mediaType = responseModel.mediaType;
|
||||||
|
if (mediaType == null) {
|
||||||
return ErrorMessage(
|
return ErrorMessage(
|
||||||
message:
|
message:
|
||||||
"Viewing response data of Content-Type\n'${mediaType.mimeType}' $kMimeTypeRaiseIssue");
|
'Unknown Response Content-Type - ${responseModel.contentType}. $kUnexpectedRaiseIssue');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var responseBodyView = getResponseBodyViewOptions(mediaType);
|
||||||
|
var options = responseBodyView.$0;
|
||||||
|
var highlightLanguage = responseBodyView.$1;
|
||||||
|
|
||||||
if (formattedBody == null) {
|
if (formattedBody == null) {
|
||||||
options = [...options];
|
options = [...options];
|
||||||
options.remove(ResponseBodyView.code);
|
options.remove(ResponseBodyView.code);
|
||||||
@ -456,15 +461,12 @@ class _BodySuccessState extends State<BodySuccess> {
|
|||||||
visible: currentSeg == ResponseBodyView.preview ||
|
visible: currentSeg == ResponseBodyView.preview ||
|
||||||
currentSeg == ResponseBodyView.none,
|
currentSeg == ResponseBodyView.none,
|
||||||
child: Expanded(
|
child: Expanded(
|
||||||
child: currentSeg == ResponseBodyView.preview
|
child: Previewer(
|
||||||
? Previewer(
|
bytes: widget.bytes,
|
||||||
bytes: widget.bytes,
|
type: widget.mediaType.type,
|
||||||
type: widget.mediaType.type,
|
subtype: widget.mediaType.subtype,
|
||||||
subtype: widget.mediaType.subtype,
|
hasRaw: widget.options.contains(ResponseBodyView.raw),
|
||||||
)
|
),
|
||||||
: ErrorMessage(
|
|
||||||
message:
|
|
||||||
"$kMimeTypeRaiseIssueStart'${widget.mediaType.mimeType}' $kMimeTypeRaiseIssueEnd"),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.formattedBody != null)
|
if (widget.formattedBody != null)
|
||||||
|
Reference in New Issue
Block a user