Merge branch 'foss42:main' into resolve-issue-missing-drag-scrolling

This commit is contained in:
Ragul Raj
2024-03-18 01:26:36 +05:30
committed by GitHub
11 changed files with 159 additions and 56 deletions

View File

@ -9,6 +9,7 @@ _Add your description_
### Checklist ### Checklist
- [ ] I have gone through the [contributing guide](https://github.com/foss42/apidash/blob/main/CONTRIBUTING.md) - [ ] I have gone through the [contributing guide](https://github.com/foss42/apidash/blob/main/CONTRIBUTING.md)
- [ ] I have updated my branch and synced it with project `main` branch before making this PR
- [ ] I have run the tests (`flutter test`) and all tests are passing - [ ] I have run the tests (`flutter test`) and all tests are passing
## Added/updated tests? ## Added/updated tests?

View File

@ -58,7 +58,7 @@ class cURLCodeGen {
} else if (requestModel.hasFormData) { } else if (requestModel.hasFormData) {
for (var formData in requestModel.formDataList) { for (var formData in requestModel.formDataList) {
var templateFormData = jj.Template(kTemplateFormData); var templateFormData = jj.Template(kTemplateFormData);
if (formData.name.isNotEmpty && formData.value.isNotEmpty) { if (formData.name.isNotEmpty) {
result += templateFormData.render({ result += templateFormData.render({
"name": formData.name, "name": formData.name,
"value": "value":

View File

@ -80,10 +80,10 @@ class RequestModel {
bool get hasFormData => bool get hasFormData =>
kMethodsWithBody.contains(method) && kMethodsWithBody.contains(method) &&
hasFormDataContentType && hasFormDataContentType &&
(requestFormDataList ?? <FormDataModel>[]).isNotEmpty; formDataMapList.isNotEmpty;
List<FormDataModel> get formDataList => List<FormDataModel> get formDataList =>
requestFormDataList ?? <FormDataModel>[]; requestFormDataList ?? <FormDataModel>[];
List<Map<String, dynamic>> get formDataMapList => List<Map<String, String>> get formDataMapList =>
rowsToFormDataMapList(requestFormDataList) ?? []; rowsToFormDataMapList(requestFormDataList) ?? [];
bool get hasFileInFormData => formDataList bool get hasFileInFormData => formDataList
.map((e) => e.type == FormDataType.file) .map((e) => e.type == FormDataType.file)
@ -94,12 +94,13 @@ class RequestModel {
RequestModel duplicate({ RequestModel duplicate({
required String id, required String id,
String? name,
}) { }) {
return RequestModel( return RequestModel(
id: id, id: id,
method: method, method: method,
url: url, url: url,
name: "$name (copy)", name: name ?? "${this.name} (copy)",
description: description, description: description,
requestHeaders: requestHeaders != null ? [...requestHeaders!] : null, requestHeaders: requestHeaders != null ? [...requestHeaders!] : null,
requestParams: requestParams != null ? [...requestParams!] : null, requestParams: requestParams != null ? [...requestParams!] : null,
@ -137,6 +138,7 @@ class RequestModel {
var params = requestParams ?? this.requestParams; var params = requestParams ?? this.requestParams;
var enabledHeaders = isHeaderEnabledList ?? this.isHeaderEnabledList; var enabledHeaders = isHeaderEnabledList ?? this.isHeaderEnabledList;
var enabledParams = isParamEnabledList ?? this.isParamEnabledList; var enabledParams = isParamEnabledList ?? this.isParamEnabledList;
var formDataList = requestFormDataList ?? this.requestFormDataList;
return RequestModel( return RequestModel(
id: id ?? this.id, id: id ?? this.id,
method: method ?? this.method, method: method ?? this.method,
@ -151,7 +153,7 @@ class RequestModel {
requestBodyContentType: requestBodyContentType:
requestBodyContentType ?? this.requestBodyContentType, requestBodyContentType ?? this.requestBodyContentType,
requestBody: requestBody ?? this.requestBody, requestBody: requestBody ?? this.requestBody,
requestFormDataList: requestFormDataList ?? this.requestFormDataList, requestFormDataList: formDataList != null ? [...formDataList] : null,
responseStatus: responseStatus ?? this.responseStatus, responseStatus: responseStatus ?? this.responseStatus,
message: message ?? this.message, message: message ?? this.message,
responseModel: responseModel ?? this.responseModel, responseModel: responseModel ?? this.responseModel,

View File

@ -96,6 +96,18 @@ class CollectionStateNotifier
state = map; state = map;
} }
void clearResponse(String? id) {
if (id == null || state?[id] == null) return;
var currentModel = state![id]!;
final newModel = currentModel.duplicate(
id: id,
name: currentModel.name,
);
var map = {...state!};
map[id] = newModel;
state = map;
}
void duplicate(String id) { void duplicate(String id) {
final newId = getNewUuid(); final newId = getNewUuid();
@ -134,21 +146,22 @@ class CollectionStateNotifier
ResponseModel? responseModel, ResponseModel? responseModel,
}) { }) {
final newModel = state![id]!.copyWith( final newModel = state![id]!.copyWith(
method: method, method: method,
url: url, url: url,
name: name, name: name,
description: description, description: description,
requestTabIndex: requestTabIndex, requestTabIndex: requestTabIndex,
requestHeaders: requestHeaders, requestHeaders: requestHeaders,
requestParams: requestParams, requestParams: requestParams,
isHeaderEnabledList: isHeaderEnabledList, isHeaderEnabledList: isHeaderEnabledList,
isParamEnabledList: isParamEnabledList, isParamEnabledList: isParamEnabledList,
requestBodyContentType: requestBodyContentType, requestBodyContentType: requestBodyContentType,
requestBody: requestBody, requestBody: requestBody,
requestFormDataList: requestFormDataList, requestFormDataList: requestFormDataList,
responseStatus: responseStatus, responseStatus: responseStatus,
message: message, message: message,
responseModel: responseModel); responseModel: responseModel,
);
//print(newModel); //print(newModel);
var map = {...state!}; var map = {...state!};
map[id] = newModel; map[id] = newModel;

View File

@ -34,6 +34,7 @@ class ResponseDetails extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
var sm = ScaffoldMessenger.of(context);
final responseStatus = ref.watch( final responseStatus = ref.watch(
selectedRequestModelProvider.select((value) => value?.responseStatus)); selectedRequestModelProvider.select((value) => value?.responseStatus));
final message = ref final message = ref
@ -46,6 +47,14 @@ class ResponseDetails extends ConsumerWidget {
responseStatus: responseStatus, responseStatus: responseStatus,
message: message, message: message,
time: responseModel?.time, time: responseModel?.time,
onClearResponse: () {
final selectedRequest = ref.read(selectedRequestModelProvider);
ref
.read(collectionStateNotifierProvider.notifier)
.clearResponse(selectedRequest?.id);
sm.hideCurrentSnackBar();
sm.showSnackBar(getSnackBar('Response cleared'));
},
), ),
const Expanded( const Expanded(
child: ResponseTabs(), child: ResponseTabs(),

View File

@ -66,39 +66,68 @@ class SettingsPage extends ConsumerWidget {
title: const Text('Default URI Scheme'), title: const Text('Default URI Scheme'),
subtitle: Text( subtitle: Text(
'$kDefaultUri${settings.defaultUriScheme}://$kDefaultUri'), '$kDefaultUri${settings.defaultUriScheme}://$kDefaultUri'),
trailing: DropdownMenu( trailing: Container(
onSelected: (value) { decoration: BoxDecoration(
ref border: Border.all(
.read(settingsProvider.notifier) color: Theme.of(context).colorScheme.onSurface,
.update(defaultUriScheme: value); ),
}, borderRadius: kBorderRadius8,
initialSelection: settings.defaultUriScheme, ),
dropdownMenuEntries: kSupportedUriSchemes child: DropdownButtonHideUnderline(
.map<DropdownMenuEntry<String>>((value) { child: DropdownButton<String>(
return DropdownMenuEntry<String>( borderRadius: kBorderRadius8,
value: value, onChanged: (value) {
label: value, ref
); .read(settingsProvider.notifier)
}).toList()), .update(defaultUriScheme: value);
},
value: settings.defaultUriScheme,
items: kSupportedUriSchemes
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Padding(
padding: kP10,
child: Text(value),
),
);
}).toList(),
),
),
),
), ),
ListTile( ListTile(
contentPadding: kPb10, contentPadding: kPb10,
hoverColor: kColorTransparent, hoverColor: kColorTransparent,
title: const Text('Default Code Generator'), title: const Text('Default Code Generator'),
trailing: DropdownMenu( trailing: Container(
onSelected: (value) { decoration: BoxDecoration(
ref border: Border.all(
.read(settingsProvider.notifier) color: Theme.of(context).colorScheme.onSurface,
.update(defaultCodeGenLang: value); ),
}, borderRadius: kBorderRadius8,
initialSelection: settings.defaultCodeGenLang, ),
dropdownMenuEntries: CodegenLanguage.values child: DropdownButtonHideUnderline(
.map<DropdownMenuEntry<CodegenLanguage>>((value) { child: DropdownButton<CodegenLanguage>(
return DropdownMenuEntry<CodegenLanguage>( borderRadius: kBorderRadius8,
value: value, value: settings.defaultCodeGenLang,
label: value.label, onChanged: (value) {
); ref
}).toList()), .read(settingsProvider.notifier)
.update(defaultCodeGenLang: value);
},
items: CodegenLanguage.values.map((value) {
return DropdownMenuItem<CodegenLanguage>(
value: value,
child: Padding(
padding: kP10,
child: Text(value.label),
),
);
}).toList(),
),
),
),
), ),
CheckboxListTile( CheckboxListTile(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,

View File

@ -1,5 +1,6 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:convert'; import 'dart:convert';
import 'package:collection/collection.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;
@ -90,18 +91,22 @@ List<NameValueModel>? mapToRows(Map<String, String>? kvMap) {
return finalRows; return finalRows;
} }
List<Map<String, dynamic>>? rowsToFormDataMapList( List<Map<String, String>>? rowsToFormDataMapList(
List<FormDataModel>? kvRows, List<FormDataModel>? kvRows,
) { ) {
if (kvRows == null) { if (kvRows == null) {
return null; return null;
} }
List<Map<String, dynamic>> finalMap = kvRows List<Map<String, String>> finalMap = kvRows
.map((FormDataModel formData) => { .map((FormDataModel formData) =>
"name": formData.name, (formData.name.trim().isEmpty && formData.value.trim().isEmpty)
"value": formData.value, ? null
"type": formData.type.name, : {
}) "name": formData.name,
"value": formData.value,
"type": formData.type.name,
})
.whereNotNull()
.toList(); .toList();
return finalMap; return finalMap;
} }

View File

@ -50,7 +50,7 @@ String getShortPath(String path) {
String getFilenameFromPath(String path) { String getFilenameFromPath(String path) {
var f = p.split(path); var f = p.split(path);
return f.last; return f.lastOrNull ?? "";
} }
String getTempFileName() { String getTempFileName() {

View File

@ -235,3 +235,27 @@ class SaveButton extends StatelessWidget {
); );
} }
} }
class ClearResponseButton extends StatelessWidget {
const ClearResponseButton({
super.key,
this.onPressed,
});
final VoidCallback? onPressed;
@override
Widget build(BuildContext context) {
return Tooltip(
message: 'Clear response',
child: TextButton(
style: TextButton.styleFrom(minimumSize: const Size(40, 40)),
onPressed: onPressed,
child: const Icon(
Icons.delete,
size: 20,
),
),
);
}
}

View File

@ -55,11 +55,13 @@ class ResponsePaneHeader extends StatelessWidget {
this.responseStatus, this.responseStatus,
this.message, this.message,
this.time, this.time,
this.onClearResponse,
}); });
final int? responseStatus; final int? responseStatus;
final String? message; final String? message;
final Duration? time; final Duration? time;
final VoidCallback? onClearResponse;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -116,6 +118,10 @@ class ResponsePaneHeader extends StatelessWidget {
color: Theme.of(context).colorScheme.secondary, color: Theme.of(context).colorScheme.secondary,
), ),
), ),
kHSpacer10,
ClearResponseButton(
onPressed: onClearResponse,
)
], ],
), ),
), ),

View File

@ -186,4 +186,18 @@ void main() {
expect(find.byIcon(Icons.save), findsOneWidget); expect(find.byIcon(Icons.save), findsOneWidget);
expect(find.text("Save"), findsOneWidget); expect(find.text("Save"), findsOneWidget);
}); });
testWidgets('Testing for ClearResponseButton', (tester) async {
await tester.pumpWidget(
MaterialApp(
title: 'ClearResponseButton',
theme: kThemeDataLight,
home: const Scaffold(
body: ClearResponseButton(),
),
),
);
expect(find.byIcon(Icons.delete), findsOneWidget);
});
} }