From cad6c97f89ce126d07ac3b3b1a56c70006f46c05 Mon Sep 17 00:00:00 2001 From: DenserMeerkat Date: Fri, 19 Jul 2024 18:11:37 +0530 Subject: [PATCH] wip: history details pane --- lib/consts.dart | 3 + lib/providers/history_providers.dart | 4 +- lib/screens/common_widgets/button_navbar.dart | 4 +- lib/screens/envvar/environment_editor.dart | 17 +++-- .../history/details_pane/url_card.dart | 66 +++++++++++++++++ lib/screens/history/history_details.dart | 36 +++++++++- lib/screens/history/history_pane.dart | 16 ++--- lib/screens/history/history_requests.dart | 28 +++++++- lib/screens/mobile/navbar.dart | 9 --- .../requests_page/request_response_tabs.dart | 72 +++---------------- lib/utils/history_utils.dart | 17 +++++ lib/widgets/card_sidebar_history.dart | 11 ++- lib/widgets/error_message.dart | 66 ++++++++--------- lib/widgets/field_raw.dart | 3 + lib/widgets/splitview_history.dart | 2 +- lib/widgets/tabbar_request_response.dart | 56 +++++++++++++++ lib/widgets/widgets.dart | 1 + 17 files changed, 275 insertions(+), 136 deletions(-) create mode 100644 lib/screens/history/details_pane/url_card.dart create mode 100644 lib/widgets/tabbar_request_response.dart diff --git a/lib/consts.dart b/lib/consts.dart index 63e1946f..4d87cdf7 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -128,6 +128,9 @@ const kPt8 = EdgeInsets.only( const kPt20 = EdgeInsets.only( top: 20, ); +const kPt24 = EdgeInsets.only( + top: 24, +); const kPt28 = EdgeInsets.only( top: 28, ); diff --git a/lib/providers/history_providers.dart b/lib/providers/history_providers.dart index 3f7d7c95..6f5d23df 100644 --- a/lib/providers/history_providers.dart +++ b/lib/providers/history_providers.dart @@ -5,12 +5,12 @@ import '../utils/history_utils.dart'; final selectedHistoryIdStateProvider = StateProvider((ref) => null); -final selectedRequestGroupStateProvider = StateProvider((ref) { +final selectedRequestGroupIdStateProvider = StateProvider((ref) { final selectedHistoryId = ref.watch(selectedHistoryIdStateProvider); + final historyMetaState = ref.read(historyMetaStateNotifier); if (selectedHistoryId == null) { return null; } - final historyMetaState = ref.read(historyMetaStateNotifier); return getHistoryRequestKey(historyMetaState![selectedHistoryId]!); }); diff --git a/lib/screens/common_widgets/button_navbar.dart b/lib/screens/common_widgets/button_navbar.dart index 1daf31cd..2cefc201 100644 --- a/lib/screens/common_widgets/button_navbar.dart +++ b/lib/screens/common_widgets/button_navbar.dart @@ -37,7 +37,7 @@ class NavbarButton extends ConsumerWidget { if (buttonIdx != null) { ref.read(navRailIndexStateProvider.notifier).state = buttonIdx!; - if (railIdx > 1 && buttonIdx! <= 1) { + if (railIdx > 2 && buttonIdx! <= 2) { ref.read(leftDrawerStateProvider.notifier).state = false; } } @@ -62,7 +62,7 @@ class NavbarButton extends ConsumerWidget { if (buttonIdx != null) { ref.read(navRailIndexStateProvider.notifier).state = buttonIdx!; - if (railIdx > 1 && buttonIdx! <= 1) { + if (railIdx > 2 && buttonIdx! <= 2) { ref.read(leftDrawerStateProvider.notifier).state = false; } diff --git a/lib/screens/envvar/environment_editor.dart b/lib/screens/envvar/environment_editor.dart index b4591e35..934d7ad8 100644 --- a/lib/screens/envvar/environment_editor.dart +++ b/lib/screens/envvar/environment_editor.dart @@ -70,13 +70,16 @@ class EnvironmentEditor extends ConsumerWidget { margin: EdgeInsets.zero, color: kColorTransparent, surfaceTintColor: kColorTransparent, - shape: RoundedRectangleBorder( - side: BorderSide( - color: - Theme.of(context).colorScheme.surfaceContainerHighest, - ), - borderRadius: kBorderRadius12, - ), + shape: context.isMediumWindow + ? null + : RoundedRectangleBorder( + side: BorderSide( + color: Theme.of(context) + .colorScheme + .surfaceContainerHighest, + ), + borderRadius: kBorderRadius12, + ), elevation: 0, child: const Padding( padding: kPv6, diff --git a/lib/screens/history/details_pane/url_card.dart b/lib/screens/history/details_pane/url_card.dart new file mode 100644 index 00000000..af5d4a41 --- /dev/null +++ b/lib/screens/history/details_pane/url_card.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:apidash/widgets/widgets.dart'; +import 'package:apidash/utils/utils.dart'; +import 'package:apidash/consts.dart'; + +class HistoryURLCard extends StatelessWidget { + const HistoryURLCard({ + super.key, + required this.method, + required this.url, + }); + + final HTTPVerb method; + final String url; + + @override + Widget build(BuildContext context) { + final fontSize = Theme.of(context).textTheme.titleMedium?.fontSize; + return LayoutBuilder(builder: (context, constraints) { + final isCompact = constraints.maxWidth <= kMinWindowSize.width; + return Card( + color: kColorTransparent, + surfaceTintColor: kColorTransparent, + elevation: 0, + shape: RoundedRectangleBorder( + side: BorderSide( + color: Theme.of(context).colorScheme.surfaceContainerHighest, + ), + borderRadius: kBorderRadius8, + ), + child: Padding( + padding: EdgeInsets.symmetric( + vertical: 12, + horizontal: isCompact ? 10 : 16, + ), + child: Row( + children: [ + isCompact ? const SizedBox.shrink() : kHSpacer10, + Text( + method.name.toUpperCase(), + style: kCodeStyle.copyWith( + fontSize: fontSize, + fontWeight: FontWeight.bold, + color: getHTTPMethodColor( + method, + brightness: Theme.of(context).brightness, + ), + ), + ), + isCompact ? kHSpacer10 : kHSpacer20, + Expanded( + child: RawTextField( + readOnly: true, + controller: TextEditingController(text: url), + style: kCodeStyle.copyWith( + fontSize: fontSize, + ), + ), + ) + ], + ), + ), + ); + }); + } +} diff --git a/lib/screens/history/history_details.dart b/lib/screens/history/history_details.dart index e32ca2db..cee748ba 100644 --- a/lib/screens/history/history_details.dart +++ b/lib/screens/history/history_details.dart @@ -1,10 +1,42 @@ import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:apidash/providers/providers.dart'; +import 'package:apidash/widgets/widgets.dart'; +import 'package:apidash/consts.dart'; +import './details_pane/url_card.dart'; -class HistoryDetails extends StatelessWidget { +class HistoryDetails extends StatefulHookConsumerWidget { const HistoryDetails({super.key}); + @override + ConsumerState createState() => _HistoryDetailsState(); +} + +class _HistoryDetailsState extends ConsumerState + with TickerProviderStateMixin { @override Widget build(BuildContext context) { - return Container(); + final selectedHistoryRequest = + ref.watch(selectedHistoryRequestModelProvider); + final metaData = selectedHistoryRequest?.metaData; + + final TabController controller = + useTabController(initialLength: 2, vsync: this); + + return selectedHistoryRequest != null + ? Column( + children: [ + Padding( + padding: kP4, + child: HistoryURLCard( + method: metaData!.method, url: metaData.url)), + kVSpacer10, + RequestResponseTabbar( + controller: controller, + ), + ], + ) + : const Text("No Request Selected"); } } diff --git a/lib/screens/history/history_pane.dart b/lib/screens/history/history_pane.dart index 7b32149c..00620f92 100644 --- a/lib/screens/history/history_pane.dart +++ b/lib/screens/history/history_pane.dart @@ -15,9 +15,7 @@ class HistoryPane extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return Padding( - padding: (!context.isMediumWindow && kIsMacOS - ? kP24CollectionPane - : kP8CollectionPane) + + padding: (!context.isMediumWindow && kIsMacOS ? kPt24 : kPt8) + (context.isMediumWindow ? kPb70 : EdgeInsets.zero), child: const Column( crossAxisAlignment: CrossAxisAlignment.stretch, @@ -34,6 +32,7 @@ class HistoryList extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final selectedGroupId = ref.watch(selectedRequestGroupIdStateProvider); final historySequence = ref.watch(historySequenceProvider); final alwaysShowHistoryPaneScrollbar = ref.watch(settingsProvider .select((value) => value.alwaysShowCollectionPaneScrollbar)); @@ -45,12 +44,7 @@ class HistoryList extends HookConsumerWidget { thumbVisibility: alwaysShowHistoryPaneScrollbar, radius: const Radius.circular(12), child: ListView( - padding: context.isMediumWindow - ? EdgeInsets.only( - bottom: MediaQuery.paddingOf(context).bottom, - right: 8, - ) - : kPe8, + padding: EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom), controller: scrollController, children: sortedHistoryKeys != null ? sortedHistoryKeys.map((date) { @@ -69,8 +63,8 @@ class HistoryList extends HookConsumerWidget { id: item.first.historyId, models: item, method: item.first.method, - selectedId: - ref.watch(selectedRequestGroupStateProvider), + isSelected: selectedGroupId == + getHistoryRequestKey(item.first), requestGroupSize: item.length, onTap: () { ref diff --git a/lib/screens/history/history_requests.dart b/lib/screens/history/history_requests.dart index a38b09f1..1a80c2be 100644 --- a/lib/screens/history/history_requests.dart +++ b/lib/screens/history/history_requests.dart @@ -1,10 +1,32 @@ import 'package:flutter/material.dart'; +import 'package:apidash/providers/history_providers.dart'; +import 'package:apidash/utils/history_utils.dart'; +import 'package:apidash/widgets/widgets.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; -class HistoryRequests extends StatelessWidget { +class HistoryRequests extends ConsumerWidget { const HistoryRequests({super.key}); @override - Widget build(BuildContext context) { - return Container(); + Widget build(BuildContext context, WidgetRef ref) { + final selectedRequestId = ref.watch(selectedHistoryIdStateProvider); + final selectedRequest = ref.read(selectedHistoryRequestModelProvider); + final historyMetas = ref.read(historyMetaStateNotifier); + final requestGroup = getRequestGroup( + historyMetas?.values.toList(), selectedRequest?.metaData); + return Column( + children: requestGroup + .map((request) => SidebarHistoryCard( + id: request.historyId, + method: request.method, + isSelected: selectedRequestId == request.historyId, + onTap: () { + ref.read(selectedHistoryIdStateProvider.notifier).state = + request.historyId; + }, + models: [request], + )) + .toList(), + ); } } diff --git a/lib/screens/mobile/navbar.dart b/lib/screens/mobile/navbar.dart index 04caf61a..5449f45f 100644 --- a/lib/screens/mobile/navbar.dart +++ b/lib/screens/mobile/navbar.dart @@ -57,15 +57,6 @@ class BottomNavBar extends ConsumerWidget { label: 'History', ), ), - // Expanded( - // child: NavbarButton( - // railIdx: railIdx, - // buttonIdx: 2, - // selectedIcon: Icons.history, - // icon: Icons.history_outlined, - // label: 'History', - // ), - // ), Expanded( child: NavbarButton( railIdx: railIdx, diff --git a/lib/screens/mobile/requests_page/request_response_tabs.dart b/lib/screens/mobile/requests_page/request_response_tabs.dart index 38fab16a..36ad6c11 100644 --- a/lib/screens/mobile/requests_page/request_response_tabs.dart +++ b/lib/screens/mobile/requests_page/request_response_tabs.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:apidash/widgets/widgets.dart'; import 'package:apidash/consts.dart'; import '../../home_page/editor_pane/details_card/response_pane.dart'; import '../../home_page/editor_pane/editor_request.dart'; @@ -30,72 +31,21 @@ class RequestResponseTabs extends StatelessWidget { } } -class RequestResponseTabbar extends StatelessWidget { - const RequestResponseTabbar({ - super.key, - required this.controller, - }); - - final TabController controller; - - @override - Widget build(BuildContext context) { - return Center( - child: Container( - width: kReqResTabWidth, - height: kReqResTabHeight, - decoration: BoxDecoration( - borderRadius: kBorderRadius20, - border: Border.all( - color: Theme.of(context).colorScheme.outlineVariant, - ), - ), - child: ClipRRect( - borderRadius: kBorderRadius20, - child: TabBar( - dividerColor: Colors.transparent, - indicatorWeight: 0.0, - indicatorSize: TabBarIndicatorSize.tab, - unselectedLabelColor: - Theme.of(context).colorScheme.onSurface.withOpacity(0.4), - labelStyle: kTextStyleTab.copyWith( - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.onPrimary, - ), - unselectedLabelStyle: kTextStyleTab, - splashBorderRadius: kBorderRadius20, - indicator: BoxDecoration( - borderRadius: kBorderRadius20, - color: Theme.of(context).colorScheme.primary, - ), - controller: controller, - tabs: const [ - Tab( - text: kLabelRequest, - ), - Tab( - text: kLabelResponse, - ), - ], - ), - ), - ), - ); - } -} - class RequestResponseTabviews extends StatelessWidget { const RequestResponseTabviews({super.key, required this.controller}); final TabController controller; @override Widget build(BuildContext context) { - return TabBarView(controller: controller, children: const [ - RequestEditor(), - Padding( - padding: kPt8, - child: ResponsePane(), - ), - ]); + return TabBarView( + controller: controller, + children: const [ + RequestEditor(), + Padding( + padding: kPt8, + child: ResponsePane(), + ), + ], + ); } } diff --git a/lib/utils/history_utils.dart b/lib/utils/history_utils.dart index b1d62fb5..de04cd24 100644 --- a/lib/utils/history_utils.dart +++ b/lib/utils/history_utils.dart @@ -84,3 +84,20 @@ Map> getRequestGroups( }); return historyGroups; } + +List getRequestGroup( + List? models, HistoryMetaModel? selectedModel) { + List requestGroup = []; + if (selectedModel == null || (models?.isEmpty ?? true)) { + return requestGroup; + } + String selectedModelKey = getHistoryRequestKey(selectedModel); + for (HistoryMetaModel model in models!) { + String key = getHistoryRequestKey(model); + if (key == selectedModelKey) { + requestGroup.add(model); + } + } + requestGroup.sort((a, b) => b.timeStamp.compareTo(a.timeStamp)); + return requestGroup; +} diff --git a/lib/widgets/card_sidebar_history.dart b/lib/widgets/card_sidebar_history.dart index 4a5e638d..be6387b3 100644 --- a/lib/widgets/card_sidebar_history.dart +++ b/lib/widgets/card_sidebar_history.dart @@ -10,7 +10,7 @@ class SidebarHistoryCard extends StatelessWidget { required this.id, required this.models, required this.method, - this.selectedId, + this.isSelected = false, this.requestGroupSize = 1, this.onTap, }); @@ -18,7 +18,7 @@ class SidebarHistoryCard extends StatelessWidget { final String id; final List models; final HTTPVerb method; - final String? selectedId; + final bool isSelected; final int requestGroupSize; final Function()? onTap; @@ -29,7 +29,6 @@ class SidebarHistoryCard extends StatelessWidget { Theme.of(context).colorScheme.surfaceContainerHighest.withOpacity(0.5); final model = models.first; final Color surfaceTint = Theme.of(context).colorScheme.primary; - bool isSelected = selectedId == getHistoryRequestKey(model); final String name = getHistoryRequestName(model); return Tooltip( message: name, @@ -84,9 +83,9 @@ class SidebarHistoryCard extends StatelessWidget { ), child: Center( child: Text( - requestGroupSize == 2 - ? requestGroupSize.toString() - : "9+", + requestGroupSize > 9 + ? "9+" + : requestGroupSize.toString(), style: Theme.of(context) .textTheme .labelSmall diff --git a/lib/widgets/error_message.dart b/lib/widgets/error_message.dart index b6e9cf46..ed8a2cb8 100644 --- a/lib/widgets/error_message.dart +++ b/lib/widgets/error_message.dart @@ -20,38 +20,40 @@ class ErrorMessage extends StatelessWidget { return Padding( padding: kPh20v10, child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - showIcon - ? Icon( - Icons.warning_rounded, - size: 40, - color: color, - ) - : const SizedBox(), - SelectableText( - message ?? 'An error occurred. $kUnexpectedRaiseIssue', - textAlign: TextAlign.center, - style: Theme.of(context) - .textTheme - .titleMedium - ?.copyWith(color: color), - ), - kVSpacer20, - showIssueButton - ? FilledButton.tonalIcon( - onPressed: () { - launchUrl(Uri.parse(kGitUrl)); - }, - icon: const Icon(Icons.arrow_outward_rounded), - label: Text( - 'Raise Issue', - style: Theme.of(context).textTheme.titleMedium, - ), - ) - : const SizedBox(), - ], + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + showIcon + ? Icon( + Icons.warning_rounded, + size: 40, + color: color, + ) + : const SizedBox(), + SelectableText( + message ?? 'An error occurred. $kUnexpectedRaiseIssue', + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith(color: color), + ), + kVSpacer20, + showIssueButton + ? FilledButton.tonalIcon( + onPressed: () { + launchUrl(Uri.parse(kGitUrl)); + }, + icon: const Icon(Icons.arrow_outward_rounded), + label: Text( + 'Raise Issue', + style: Theme.of(context).textTheme.titleMedium, + ), + ) + : const SizedBox(), + ], + ), ), ), ); diff --git a/lib/widgets/field_raw.dart b/lib/widgets/field_raw.dart index 8adf6fd0..1f216b74 100644 --- a/lib/widgets/field_raw.dart +++ b/lib/widgets/field_raw.dart @@ -8,16 +8,19 @@ class RawTextField extends StatelessWidget { this.controller, this.hintText, this.style, + this.readOnly = false, }); final void Function(String)? onChanged; final TextEditingController? controller; final String? hintText; final TextStyle? style; + final bool readOnly; @override Widget build(BuildContext context) { return TextField( + readOnly: readOnly, controller: controller, onChanged: onChanged, style: style, diff --git a/lib/widgets/splitview_history.dart b/lib/widgets/splitview_history.dart index 698a76db..b955eaeb 100644 --- a/lib/widgets/splitview_history.dart +++ b/lib/widgets/splitview_history.dart @@ -19,7 +19,7 @@ class HistorySplitView extends StatefulWidget { class HistorySplitViewState extends State { final MultiSplitViewController _controller = MultiSplitViewController( areas: [ - Area(id: "sidebar", min: 200, size: 220, max: 300), + Area(id: "sidebar", min: 200, size: 250, max: 300), Area(id: "main"), ], ); diff --git a/lib/widgets/tabbar_request_response.dart b/lib/widgets/tabbar_request_response.dart new file mode 100644 index 00000000..5a117120 --- /dev/null +++ b/lib/widgets/tabbar_request_response.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:apidash/consts.dart'; + +class RequestResponseTabbar extends StatelessWidget { + const RequestResponseTabbar({ + super.key, + required this.controller, + }); + + final TabController controller; + + @override + Widget build(BuildContext context) { + return Center( + child: Container( + width: kReqResTabWidth, + height: kReqResTabHeight, + decoration: BoxDecoration( + borderRadius: kBorderRadius20, + border: Border.all( + color: Theme.of(context).colorScheme.outlineVariant, + ), + ), + child: ClipRRect( + borderRadius: kBorderRadius20, + child: TabBar( + dividerColor: Colors.transparent, + indicatorWeight: 0.0, + indicatorSize: TabBarIndicatorSize.tab, + unselectedLabelColor: + Theme.of(context).colorScheme.onSurface.withOpacity(0.4), + labelStyle: kTextStyleTab.copyWith( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onPrimary, + ), + unselectedLabelStyle: kTextStyleTab, + splashBorderRadius: kBorderRadius20, + indicator: BoxDecoration( + borderRadius: kBorderRadius20, + color: Theme.of(context).colorScheme.primary, + ), + controller: controller, + tabs: const [ + Tab( + text: kLabelRequest, + ), + Tab( + text: kLabelResponse, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/widgets.dart b/lib/widgets/widgets.dart index cf8c84fe..81a7018d 100644 --- a/lib/widgets/widgets.dart +++ b/lib/widgets/widgets.dart @@ -45,6 +45,7 @@ export 'splitview_dashboard.dart'; export 'splitview_equal.dart'; export 'splitview_history.dart'; export 'suggestions_menu.dart'; +export 'tabbar_request_response.dart'; export 'tables.dart'; export 'tabs.dart'; export 'texts.dart';