From e1ae22bdcd85ddcf5510f8a08acdce91949662b9 Mon Sep 17 00:00:00 2001 From: vidya-hub Date: Tue, 19 Dec 2023 20:55:44 +0530 Subject: [PATCH] wip: added basic methods --- lib/consts.dart | 14 +- lib/models/form_data_model.dart | 23 +++ lib/models/form_data_model.freezed.dart | 188 ++++++++++++++++++ lib/models/form_data_model.g.dart | 26 +++ lib/models/name_value_model.freezed.dart | 43 ++-- lib/models/name_value_model.g.dart | 7 +- lib/models/request_model.dart | 16 +- .../request_pane/request_body.dart | 121 +++++++++-- lib/utils/convert_utils.dart | 33 ++- lib/widgets/dropdowns.dart | 4 +- 10 files changed, 429 insertions(+), 46 deletions(-) create mode 100644 lib/models/form_data_model.dart create mode 100644 lib/models/form_data_model.freezed.dart create mode 100644 lib/models/form_data_model.g.dart diff --git a/lib/consts.dart b/lib/consts.dart index 2dcb12ab..ef7ed6f4 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -1,9 +1,10 @@ -import 'dart:io'; import 'dart:convert'; +import 'dart:io'; + +import 'package:davi/davi.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:davi/davi.dart'; const kDiscordUrl = "https://bit.ly/heyfoss"; const kGitUrl = "https://github.com/foss42/apidash"; @@ -224,7 +225,9 @@ enum RequestItemMenuOption { edit, delete, duplicate } enum HTTPVerb { get, head, post, put, patch, delete } -enum ContentType { json, text } +enum ContentType { json, text, formdata } + +enum FormDataType { text, file } const kSupportedUriSchemes = ["https", "http"]; const kDefaultUriScheme = "https"; @@ -294,6 +297,11 @@ const kSubTypeDefaultViewOptions = 'all'; const kContentTypeMap = { ContentType.json: "$kTypeApplication/$kSubTypeJson", ContentType.text: "$kTypeText/$kSubTypePlain", + ContentType.formdata: "multipart/form-data", +}; +const kFormDataTypeMap = { + FormDataType.file: "File", + FormDataType.text: "Text", }; enum ResponseBodyView { preview, code, raw, none } diff --git a/lib/models/form_data_model.dart b/lib/models/form_data_model.dart new file mode 100644 index 00000000..edbcd113 --- /dev/null +++ b/lib/models/form_data_model.dart @@ -0,0 +1,23 @@ +import 'package:apidash/consts.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'form_data_model.freezed.dart'; +part 'form_data_model.g.dart'; + +@freezed +class FormDataModel with _$FormDataModel { + const factory FormDataModel({ + required String name, + required dynamic value, + required FormDataType type, + }) = _FormDataModel; + + factory FormDataModel.fromJson(Map json) => + _$FormDataModelFromJson(json); +} + +const kNameValueEmptyModel = FormDataModel( + name: "", + value: "", + type: FormDataType.text, +); diff --git a/lib/models/form_data_model.freezed.dart b/lib/models/form_data_model.freezed.dart new file mode 100644 index 00000000..e1babd78 --- /dev/null +++ b/lib/models/form_data_model.freezed.dart @@ -0,0 +1,188 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'form_data_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +FormDataModel _$FormDataModelFromJson(Map json) { + return _FormDataModel.fromJson(json); +} + +/// @nodoc +mixin _$FormDataModel { + String get name => throw _privateConstructorUsedError; + dynamic get value => throw _privateConstructorUsedError; + FormDataType get type => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $FormDataModelCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $FormDataModelCopyWith<$Res> { + factory $FormDataModelCopyWith( + FormDataModel value, $Res Function(FormDataModel) then) = + _$FormDataModelCopyWithImpl<$Res, FormDataModel>; + @useResult + $Res call({String name, dynamic value, FormDataType type}); +} + +/// @nodoc +class _$FormDataModelCopyWithImpl<$Res, $Val extends FormDataModel> + implements $FormDataModelCopyWith<$Res> { + _$FormDataModelCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? name = null, + Object? value = freezed, + Object? type = null, + }) { + return _then(_value.copyWith( + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + value: freezed == value + ? _value.value + : value // ignore: cast_nullable_to_non_nullable + as dynamic, + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as FormDataType, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$FormDataModelImplCopyWith<$Res> + implements $FormDataModelCopyWith<$Res> { + factory _$$FormDataModelImplCopyWith( + _$FormDataModelImpl value, $Res Function(_$FormDataModelImpl) then) = + __$$FormDataModelImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String name, dynamic value, FormDataType type}); +} + +/// @nodoc +class __$$FormDataModelImplCopyWithImpl<$Res> + extends _$FormDataModelCopyWithImpl<$Res, _$FormDataModelImpl> + implements _$$FormDataModelImplCopyWith<$Res> { + __$$FormDataModelImplCopyWithImpl( + _$FormDataModelImpl _value, $Res Function(_$FormDataModelImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? name = null, + Object? value = freezed, + Object? type = null, + }) { + return _then(_$FormDataModelImpl( + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + value: freezed == value + ? _value.value + : value // ignore: cast_nullable_to_non_nullable + as dynamic, + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as FormDataType, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$FormDataModelImpl implements _FormDataModel { + const _$FormDataModelImpl( + {required this.name, required this.value, required this.type}); + + factory _$FormDataModelImpl.fromJson(Map json) => + _$$FormDataModelImplFromJson(json); + + @override + final String name; + @override + final dynamic value; + @override + final FormDataType type; + + @override + String toString() { + return 'FormDataModel(name: $name, value: $value, type: $type)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$FormDataModelImpl && + (identical(other.name, name) || other.name == name) && + const DeepCollectionEquality().equals(other.value, value) && + (identical(other.type, type) || other.type == type)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, name, const DeepCollectionEquality().hash(value), type); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$FormDataModelImplCopyWith<_$FormDataModelImpl> get copyWith => + __$$FormDataModelImplCopyWithImpl<_$FormDataModelImpl>(this, _$identity); + + @override + Map toJson() { + return _$$FormDataModelImplToJson( + this, + ); + } +} + +abstract class _FormDataModel implements FormDataModel { + const factory _FormDataModel( + {required final String name, + required final dynamic value, + required final FormDataType type}) = _$FormDataModelImpl; + + factory _FormDataModel.fromJson(Map json) = + _$FormDataModelImpl.fromJson; + + @override + String get name; + @override + dynamic get value; + @override + FormDataType get type; + @override + @JsonKey(ignore: true) + _$$FormDataModelImplCopyWith<_$FormDataModelImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/models/form_data_model.g.dart b/lib/models/form_data_model.g.dart new file mode 100644 index 00000000..7d9d353b --- /dev/null +++ b/lib/models/form_data_model.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'form_data_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$FormDataModelImpl _$$FormDataModelImplFromJson(Map json) => + _$FormDataModelImpl( + name: json['name'] as String, + value: json['value'], + type: $enumDecode(_$FormDataTypeEnumMap, json['type']), + ); + +Map _$$FormDataModelImplToJson(_$FormDataModelImpl instance) => + { + 'name': instance.name, + 'value': instance.value, + 'type': _$FormDataTypeEnumMap[instance.type]!, + }; + +const _$FormDataTypeEnumMap = { + FormDataType.text: 'text', + FormDataType.file: 'file', +}; diff --git a/lib/models/name_value_model.freezed.dart b/lib/models/name_value_model.freezed.dart index e7d9e8e0..b224f596 100644 --- a/lib/models/name_value_model.freezed.dart +++ b/lib/models/name_value_model.freezed.dart @@ -68,22 +68,22 @@ class _$NameValueModelCopyWithImpl<$Res, $Val extends NameValueModel> } /// @nodoc -abstract class _$$_NameValueModelCopyWith<$Res> +abstract class _$$NameValueModelImplCopyWith<$Res> implements $NameValueModelCopyWith<$Res> { - factory _$$_NameValueModelCopyWith( - _$_NameValueModel value, $Res Function(_$_NameValueModel) then) = - __$$_NameValueModelCopyWithImpl<$Res>; + factory _$$NameValueModelImplCopyWith(_$NameValueModelImpl value, + $Res Function(_$NameValueModelImpl) then) = + __$$NameValueModelImplCopyWithImpl<$Res>; @override @useResult $Res call({String name, dynamic value}); } /// @nodoc -class __$$_NameValueModelCopyWithImpl<$Res> - extends _$NameValueModelCopyWithImpl<$Res, _$_NameValueModel> - implements _$$_NameValueModelCopyWith<$Res> { - __$$_NameValueModelCopyWithImpl( - _$_NameValueModel _value, $Res Function(_$_NameValueModel) _then) +class __$$NameValueModelImplCopyWithImpl<$Res> + extends _$NameValueModelCopyWithImpl<$Res, _$NameValueModelImpl> + implements _$$NameValueModelImplCopyWith<$Res> { + __$$NameValueModelImplCopyWithImpl( + _$NameValueModelImpl _value, $Res Function(_$NameValueModelImpl) _then) : super(_value, _then); @pragma('vm:prefer-inline') @@ -92,7 +92,7 @@ class __$$_NameValueModelCopyWithImpl<$Res> Object? name = null, Object? value = freezed, }) { - return _then(_$_NameValueModel( + return _then(_$NameValueModelImpl( name: null == name ? _value.name : name // ignore: cast_nullable_to_non_nullable @@ -107,13 +107,13 @@ class __$$_NameValueModelCopyWithImpl<$Res> /// @nodoc @JsonSerializable() -class _$_NameValueModel +class _$NameValueModelImpl with DiagnosticableTreeMixin implements _NameValueModel { - const _$_NameValueModel({required this.name, required this.value}); + const _$NameValueModelImpl({required this.name, required this.value}); - factory _$_NameValueModel.fromJson(Map json) => - _$$_NameValueModelFromJson(json); + factory _$NameValueModelImpl.fromJson(Map json) => + _$$NameValueModelImplFromJson(json); @override final String name; @@ -138,7 +138,7 @@ class _$_NameValueModel bool operator ==(dynamic other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$_NameValueModel && + other is _$NameValueModelImpl && (identical(other.name, name) || other.name == name) && const DeepCollectionEquality().equals(other.value, value)); } @@ -151,12 +151,13 @@ class _$_NameValueModel @JsonKey(ignore: true) @override @pragma('vm:prefer-inline') - _$$_NameValueModelCopyWith<_$_NameValueModel> get copyWith => - __$$_NameValueModelCopyWithImpl<_$_NameValueModel>(this, _$identity); + _$$NameValueModelImplCopyWith<_$NameValueModelImpl> get copyWith => + __$$NameValueModelImplCopyWithImpl<_$NameValueModelImpl>( + this, _$identity); @override Map toJson() { - return _$$_NameValueModelToJson( + return _$$NameValueModelImplToJson( this, ); } @@ -165,10 +166,10 @@ class _$_NameValueModel abstract class _NameValueModel implements NameValueModel { const factory _NameValueModel( {required final String name, - required final dynamic value}) = _$_NameValueModel; + required final dynamic value}) = _$NameValueModelImpl; factory _NameValueModel.fromJson(Map json) = - _$_NameValueModel.fromJson; + _$NameValueModelImpl.fromJson; @override String get name; @@ -176,6 +177,6 @@ abstract class _NameValueModel implements NameValueModel { dynamic get value; @override @JsonKey(ignore: true) - _$$_NameValueModelCopyWith<_$_NameValueModel> get copyWith => + _$$NameValueModelImplCopyWith<_$NameValueModelImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/lib/models/name_value_model.g.dart b/lib/models/name_value_model.g.dart index 25867df8..5fb91b66 100644 --- a/lib/models/name_value_model.g.dart +++ b/lib/models/name_value_model.g.dart @@ -6,13 +6,14 @@ part of 'name_value_model.dart'; // JsonSerializableGenerator // ************************************************************************** -_$_NameValueModel _$$_NameValueModelFromJson(Map json) => - _$_NameValueModel( +_$NameValueModelImpl _$$NameValueModelImplFromJson(Map json) => + _$NameValueModelImpl( name: json['name'] as String, value: json['value'], ); -Map _$$_NameValueModelToJson(_$_NameValueModel instance) => +Map _$$NameValueModelImplToJson( + _$NameValueModelImpl instance) => { 'name': instance.name, 'value': instance.value, diff --git a/lib/models/request_model.dart b/lib/models/request_model.dart index a928dac1..fda03ea1 100644 --- a/lib/models/request_model.dart +++ b/lib/models/request_model.dart @@ -1,6 +1,8 @@ -import 'package:flutter/foundation.dart'; import 'package:apidash/consts.dart'; -import 'package:apidash/utils/utils.dart' show mapToRows, rowsToMap; +import 'package:apidash/models/form_data_model.dart'; +import 'package:apidash/utils/convert_utils.dart'; +import 'package:flutter/foundation.dart'; + import 'name_value_model.dart'; import 'response_model.dart'; @@ -20,6 +22,7 @@ class RequestModel { this.responseStatus, this.message, this.responseModel, + this.formDataList, }); final String id; @@ -30,6 +33,7 @@ class RequestModel { final int requestTabIndex; final List? requestHeaders; final List? requestParams; + final List? formDataList; final ContentType requestBodyContentType; final String? requestBody; final int? responseStatus; @@ -52,6 +56,7 @@ class RequestModel { requestParams: requestParams != null ? [...requestParams!] : null, requestBodyContentType: requestBodyContentType, requestBody: requestBody, + formDataList: formDataList != null ? [...formDataList!] : null, ); } @@ -69,6 +74,7 @@ class RequestModel { int? responseStatus, String? message, ResponseModel? responseModel, + List? formDataList, }) { var headers = requestHeaders ?? this.requestHeaders; var params = requestParams ?? this.requestParams; @@ -87,6 +93,7 @@ class RequestModel { responseStatus: responseStatus ?? this.responseStatus, message: message ?? this.message, responseModel: responseModel ?? this.responseModel, + formDataList: formDataList ?? this.formDataList, ); } @@ -116,6 +123,8 @@ class RequestModel { final responseStatus = data["responseStatus"] as int?; final message = data["message"] as String?; final responseModelJson = data["responseModel"]; + final formDataList = data["formDataList"]; + if (responseModelJson != null) { responseModel = ResponseModel.fromJson(Map.from(responseModelJson)); @@ -141,6 +150,9 @@ class RequestModel { responseStatus: responseStatus, message: message, responseModel: responseModel, + formDataList: formDataList != null + ? mapToFormRows(Map.from(formDataList)) + : null, ); } diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_body.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_body.dart index 07c5bf53..07a3eee4 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_body.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_body.dart @@ -1,8 +1,12 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'dart:math'; + +import 'package:apidash/consts.dart'; +import 'package:apidash/models/name_value_model.dart'; import 'package:apidash/providers/providers.dart'; import 'package:apidash/widgets/widgets.dart'; -import 'package:apidash/consts.dart'; +import 'package:davi/davi.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; class EditRequestBody extends ConsumerStatefulWidget { const EditRequestBody({super.key}); @@ -12,12 +16,99 @@ class EditRequestBody extends ConsumerStatefulWidget { } class _EditRequestBodyState extends ConsumerState { + late List rows; + final random = Random.secure(); + late int seed; + @override + void initState() { + super.initState(); + seed = random.nextInt(kRandMax); + } + @override Widget build(BuildContext context) { final activeId = ref.watch(activeIdStateProvider); final requestModel = ref .read(collectionStateNotifierProvider.notifier) .getRequestModel(activeId!); + ContentType? requestBodyStateWatcher = (ref + .watch(collectionStateNotifierProvider)![activeId] + ?.requestBodyContentType) ?? + ContentType.values.first; + DaviModel model = DaviModel( + rows: rows, + columns: [ + DaviColumn( + name: 'Header Name', + grow: 1, + cellBuilder: (_, row) { + int idx = row.index; + return HeaderField( + keyId: "$activeId-$idx-headers-k-$seed", + initialValue: rows[idx].name, + hintText: "Add Header Name", + onChanged: (value) { + rows[idx] = rows[idx].copyWith(name: value); + // _onFieldChange(activeId); + }, + colorScheme: Theme.of(context).colorScheme, + ); + }, + sortable: false, + ), + DaviColumn( + width: 30, + cellBuilder: (_, row) { + return Text( + "=", + style: kCodeStyle, + ); + }, + ), + DaviColumn( + name: 'Header Value', + grow: 1, + cellBuilder: (_, row) { + int idx = row.index; + return CellField( + keyId: "$activeId-$idx-headers-v-$seed", + initialValue: rows[idx].value, + hintText: " Add Header Value", + onChanged: (value) { + rows[idx] = rows[idx].copyWith(value: value); + // _onFieldChange(activeId); + }, + colorScheme: Theme.of(context).colorScheme, + ); + }, + sortable: false, + ), + DaviColumn( + pinStatus: PinStatus.none, + width: 30, + cellBuilder: (_, row) { + return InkWell( + child: Theme.of(context).brightness == Brightness.dark + ? kIconRemoveDark + : kIconRemoveLight, + onTap: () { + seed = random.nextInt(kRandMax); + if (rows.length == 1) { + setState(() { + rows = [ + kNameValueEmptyModel, + ]; + }); + } else { + rows.removeAt(row.index); + } + // _onFieldChange(activeId); + }, + ); + }, + ), + ], + ); return Container( decoration: BoxDecoration( color: Theme.of(context).colorScheme.background, @@ -38,16 +129,20 @@ class _EditRequestBodyState extends ConsumerState { ), ), Expanded( - child: TextFieldEditor( - key: Key("$activeId-body"), - fieldKey: "$activeId-body-editor", - initialValue: requestModel?.requestBody, - onChanged: (String value) { - ref - .read(collectionStateNotifierProvider.notifier) - .update(activeId, requestBody: value); - }, - ), + child: requestBodyStateWatcher == ContentType.formdata + ? Container( + child: const Text("Vidya"), + ) + : TextFieldEditor( + key: Key("$activeId-body"), + fieldKey: "$activeId-body-editor", + initialValue: requestModel?.requestBody, + onChanged: (String value) { + ref + .read(collectionStateNotifierProvider.notifier) + .update(activeId, requestBody: value); + }, + ), ) ], ), diff --git a/lib/utils/convert_utils.dart b/lib/utils/convert_utils.dart index e12f2dcc..97d8cc4d 100644 --- a/lib/utils/convert_utils.dart +++ b/lib/utils/convert_utils.dart @@ -1,7 +1,10 @@ -import 'dart:typed_data'; import 'dart:convert'; -import '../models/models.dart'; +import 'dart:typed_data'; + +import 'package:apidash/models/form_data_model.dart'; + import '../consts.dart'; +import '../models/models.dart'; String humanizeDuration(Duration? duration) { if (duration == null) { @@ -78,6 +81,17 @@ Map? rowsToMap(List? kvRows, return finalMap; } +Map? rowsToFormDataMap( + List? kvRows, +) { + if (kvRows == null) { + return null; + } + Map finalMap = {}; + for (var row in kvRows) {} + return finalMap; +} + List? mapToRows(Map? kvMap) { if (kvMap == null) { return null; @@ -89,6 +103,21 @@ List? mapToRows(Map? kvMap) { return finalRows; } +List? mapToFormRows(Map? kvMap) { + if (kvMap == null) { + return null; + } + List finalRows = []; + for (var k in kvMap.keys) { + finalRows.add(FormDataModel( + name: k, + value: kvMap[k], + type: FormDataType.text, + )); + } + return finalRows; +} + Uint8List? stringToBytes(String? text) { if (text == null) { return null; diff --git a/lib/widgets/dropdowns.dart b/lib/widgets/dropdowns.dart index e8290714..29f18b0a 100644 --- a/lib/widgets/dropdowns.dart +++ b/lib/widgets/dropdowns.dart @@ -1,6 +1,6 @@ -import 'package:flutter/material.dart'; -import 'package:apidash/utils/utils.dart'; import 'package:apidash/consts.dart'; +import 'package:apidash/utils/utils.dart'; +import 'package:flutter/material.dart'; class DropdownButtonHttpMethod extends StatefulWidget { const DropdownButtonHttpMethod({