Add the functionality to import a curl file into the app.

The user can click on the button saying "+ Import" after which, they
will be allow to drop a curl file and import the curl command in the
app.
This commit is contained in:
Ketan Sonar
2024-07-04 23:35:59 +05:30
parent 063638b8ef
commit 7720611cd7
8 changed files with 180 additions and 3 deletions

View File

@ -664,6 +664,7 @@ const kRaiseIssue =
const kHintTextUrlCard = "Enter API endpoint like https://$kDefaultUri/";
const kLabelPlusNew = "+ New";
const kLabelImport = "+ Import";
const kLabelSend = "Send";
const kLabelSending = "Sending..";
const kLabelBusy = "Busy";

View File

@ -6,12 +6,17 @@ import 'package:apidash/consts.dart';
import 'sidebar_save_button.dart';
class SidebarHeader extends ConsumerWidget {
const SidebarHeader({super.key, this.onAddNew});
const SidebarHeader({super.key, this.onAddNew, this.onImport});
final Function()? onAddNew;
final Function()? onImport;
@override
Widget build(BuildContext context, WidgetRef ref) {
final mobileScaffoldKey = ref.read(mobileScaffoldKeyStateProvider);
final elevatedButtonStyle = ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 12),
);
return Padding(
padding: kPe8,
child: Row(
@ -20,9 +25,19 @@ class SidebarHeader extends ConsumerWidget {
const Spacer(),
ElevatedButton(
onPressed: onAddNew,
style: elevatedButtonStyle,
child: const Text(
kLabelPlusNew,
style: kTextStyleButton,
style: kTextStyleButtonSmall,
),
),
const SizedBox(width: 12),
ElevatedButton(
onPressed: onImport,
style: elevatedButtonStyle,
child: const Text(
kLabelImport,
style: kTextStyleButtonSmall,
),
),
context.width <= kMinWindowSize.width

View File

@ -1,3 +1,8 @@
import 'dart:io';
import 'package:apidash/utils/convert_utils.dart';
import 'package:apidash/widgets/drag_and_drop_area.dart';
import 'package:curl_converter/curl_converter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/providers/providers.dart';
@ -32,6 +37,65 @@ class CollectionPane extends ConsumerWidget {
onAddNew: () {
ref.read(collectionStateNotifierProvider.notifier).add();
},
onImport: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
contentPadding: const EdgeInsets.all(12),
content: DragAndDropArea(
onFileDropped: (file) {
final i = file.path.lastIndexOf('.') + 1;
final String ext = file.path.substring(i);
switch (ext) {
case 'curl':
file.readAsString().then((value) {
if (value.endsWith('\n')) {
value = value.substring(0, value.length - 1);
}
try {
final curl = Curl.parse(value);
ref
.read(
collectionStateNotifierProvider.notifier)
.add();
final selectedId =
ref.read(selectedIdStateProvider)!;
ref
.read(
collectionStateNotifierProvider.notifier)
.update(
selectedId,
method: httpVerbFromString(curl.method),
url: curl.uri.toString().substring(0,
curl.uri.toString().lastIndexOf('?')),
headers: curl.headers?.entries
.map((entry) => NameValueModel(
name: entry.key,
value: entry.value,
))
.toList(),
params: curl.uri.queryParameters.entries
.map((entry) => NameValueModel(
name: entry.key,
value: entry.value,
))
.toList(),
body: curl.data,
);
} catch (e) {
throw Error();
}
Navigator.of(context).pop();
});
break;
default:
throw Error();
}
},
),
),
);
},
),
kVSpacer10,
SidebarFilter(

View File

@ -1,6 +1,7 @@
import 'dart:typed_data';
import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/models.dart';
import '../consts.dart';
import 'package:http/http.dart' as http;
@ -179,3 +180,22 @@ List<NameValueModel>? getEnabledRows(
rows.where((element) => isRowEnabledList[rows.indexOf(element)]).toList();
return finalRows == [] ? null : finalRows;
}
HTTPVerb? httpVerbFromString(String value) {
switch (value) {
case "GET":
return HTTPVerb.get;
case "POST":
return HTTPVerb.post;
case "PUT":
return HTTPVerb.put;
case "DELETE":
return HTTPVerb.delete;
case "HEAD":
return HTTPVerb.head;
case "PATCH":
return HTTPVerb.patch;
default:
return null;
}
}

View File

@ -0,0 +1,51 @@
import 'package:desktop_drop/desktop_drop.dart';
import 'package:file_selector/file_selector.dart';
import 'package:flutter/material.dart';
class DragAndDropArea extends StatefulWidget {
final Function(XFile) onFileDropped;
const DragAndDropArea({super.key, required this.onFileDropped});
@override
State<DragAndDropArea> createState() => _DragAndDropAreaState();
}
class _DragAndDropAreaState extends State<DragAndDropArea> {
final List<XFile> _list = [];
bool _dragging = false;
@override
Widget build(BuildContext context) {
return DropTarget(
onDragDone: (detail) {
setState(() {
_list.addAll(detail.files);
});
widget.onFileDropped(detail.files[0]);
},
onDragEntered: (detail) {
setState(() {
_dragging = true;
});
},
onDragExited: (detail) {
setState(() {
_dragging = false;
});
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
color: _dragging ? Colors.blue.withOpacity(0.4) : Colors.black26,
border: Border.all(color: Colors.white24),
),
width: 600,
height: 400,
child: _list.isEmpty
? const Center(child: Text("Drop here"))
: Text(_list.map((e) => e.path).join("\n")),
),
);
}
}

View File

@ -19,7 +19,7 @@ class DashboardSplitView extends StatefulWidget {
class DashboardSplitViewState extends State<DashboardSplitView> {
final MultiSplitViewController _controller = MultiSplitViewController(
areas: [
Area(id: "sidebar", size: 250, min: 200),
Area(id: "sidebar", size: 270, min: 250),
Area(id: "main", min: 0.7),
],
);

View File

@ -233,6 +233,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.0.0"
curl_converter:
dependency: "direct main"
description:
name: curl_converter
sha256: "32c541b875c6ddea275dfb35ff58c95f0d997f51e92232ff8fa4431b96c62dab"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
dart_style:
dependency: "direct main"
description:
@ -249,6 +257,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.5.15"
desktop_drop:
dependency: "direct main"
description:
name: desktop_drop
sha256: d55a010fe46c8e8fcff4ea4b451a9ff84a162217bdb3b2a0aa1479776205e15d
url: "https://pub.dev"
source: hosted
version: "0.4.4"
equatable:
dependency: transitive
description:
name: equatable
sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2
url: "https://pub.dev"
source: hosted
version: "2.0.5"
eventify:
dependency: transitive
description:

View File

@ -64,6 +64,8 @@ dependencies:
flutter_hooks: ^0.20.5
flutter_portal: ^1.1.4
mention_tag_text_field: ^0.0.5
desktop_drop: ^0.4.4
curl_converter: ^1.0.4
dependency_overrides:
web: ^0.5.0