mirror of
https://github.com/foss42/apidash.git
synced 2025-06-10 07:09:41 +08:00
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:
@ -664,6 +664,7 @@ const kRaiseIssue =
|
|||||||
|
|
||||||
const kHintTextUrlCard = "Enter API endpoint like https://$kDefaultUri/";
|
const kHintTextUrlCard = "Enter API endpoint like https://$kDefaultUri/";
|
||||||
const kLabelPlusNew = "+ New";
|
const kLabelPlusNew = "+ New";
|
||||||
|
const kLabelImport = "+ Import";
|
||||||
const kLabelSend = "Send";
|
const kLabelSend = "Send";
|
||||||
const kLabelSending = "Sending..";
|
const kLabelSending = "Sending..";
|
||||||
const kLabelBusy = "Busy";
|
const kLabelBusy = "Busy";
|
||||||
|
@ -6,12 +6,17 @@ import 'package:apidash/consts.dart';
|
|||||||
import 'sidebar_save_button.dart';
|
import 'sidebar_save_button.dart';
|
||||||
|
|
||||||
class SidebarHeader extends ConsumerWidget {
|
class SidebarHeader extends ConsumerWidget {
|
||||||
const SidebarHeader({super.key, this.onAddNew});
|
const SidebarHeader({super.key, this.onAddNew, this.onImport});
|
||||||
final Function()? onAddNew;
|
final Function()? onAddNew;
|
||||||
|
final Function()? onImport;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final mobileScaffoldKey = ref.read(mobileScaffoldKeyStateProvider);
|
final mobileScaffoldKey = ref.read(mobileScaffoldKeyStateProvider);
|
||||||
|
final elevatedButtonStyle = ElevatedButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
);
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: kPe8,
|
padding: kPe8,
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -20,9 +25,19 @@ class SidebarHeader extends ConsumerWidget {
|
|||||||
const Spacer(),
|
const Spacer(),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: onAddNew,
|
onPressed: onAddNew,
|
||||||
|
style: elevatedButtonStyle,
|
||||||
child: const Text(
|
child: const Text(
|
||||||
kLabelPlusNew,
|
kLabelPlusNew,
|
||||||
style: kTextStyleButton,
|
style: kTextStyleButtonSmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: onImport,
|
||||||
|
style: elevatedButtonStyle,
|
||||||
|
child: const Text(
|
||||||
|
kLabelImport,
|
||||||
|
style: kTextStyleButtonSmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
context.width <= kMinWindowSize.width
|
context.width <= kMinWindowSize.width
|
||||||
|
@ -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/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:apidash/providers/providers.dart';
|
import 'package:apidash/providers/providers.dart';
|
||||||
@ -32,6 +37,65 @@ class CollectionPane extends ConsumerWidget {
|
|||||||
onAddNew: () {
|
onAddNew: () {
|
||||||
ref.read(collectionStateNotifierProvider.notifier).add();
|
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,
|
kVSpacer10,
|
||||||
SidebarFilter(
|
SidebarFilter(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import '../models/models.dart';
|
import '../models/models.dart';
|
||||||
import '../consts.dart';
|
import '../consts.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
@ -179,3 +180,22 @@ List<NameValueModel>? getEnabledRows(
|
|||||||
rows.where((element) => isRowEnabledList[rows.indexOf(element)]).toList();
|
rows.where((element) => isRowEnabledList[rows.indexOf(element)]).toList();
|
||||||
return finalRows == [] ? null : finalRows;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
51
lib/widgets/drag_and_drop_area.dart
Normal file
51
lib/widgets/drag_and_drop_area.dart
Normal 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")),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,7 @@ class DashboardSplitView extends StatefulWidget {
|
|||||||
class DashboardSplitViewState extends State<DashboardSplitView> {
|
class DashboardSplitViewState extends State<DashboardSplitView> {
|
||||||
final MultiSplitViewController _controller = MultiSplitViewController(
|
final MultiSplitViewController _controller = MultiSplitViewController(
|
||||||
areas: [
|
areas: [
|
||||||
Area(id: "sidebar", size: 250, min: 200),
|
Area(id: "sidebar", size: 270, min: 250),
|
||||||
Area(id: "main", min: 0.7),
|
Area(id: "main", min: 0.7),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
24
pubspec.lock
24
pubspec.lock
@ -233,6 +233,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.0"
|
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:
|
dart_style:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -249,6 +257,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.15"
|
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:
|
eventify:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -64,6 +64,8 @@ dependencies:
|
|||||||
flutter_hooks: ^0.20.5
|
flutter_hooks: ^0.20.5
|
||||||
flutter_portal: ^1.1.4
|
flutter_portal: ^1.1.4
|
||||||
mention_tag_text_field: ^0.0.5
|
mention_tag_text_field: ^0.0.5
|
||||||
|
desktop_drop: ^0.4.4
|
||||||
|
curl_converter: ^1.0.4
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
web: ^0.5.0
|
web: ^0.5.0
|
||||||
|
Reference in New Issue
Block a user