wip: history details pane

This commit is contained in:
DenserMeerkat
2024-07-19 18:11:37 +05:30
parent d5feb0b091
commit cad6c97f89
17 changed files with 275 additions and 136 deletions

View File

@ -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,
);

View File

@ -5,12 +5,12 @@ import '../utils/history_utils.dart';
final selectedHistoryIdStateProvider = StateProvider<String?>((ref) => null);
final selectedRequestGroupStateProvider = StateProvider<String?>((ref) {
final selectedRequestGroupIdStateProvider = StateProvider<String?>((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]!);
});

View File

@ -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;
}

View File

@ -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,

View File

@ -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,
),
),
)
],
),
),
);
});
}
}

View File

@ -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<HistoryDetails> createState() => _HistoryDetailsState();
}
class _HistoryDetailsState extends ConsumerState<HistoryDetails>
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");
}
}

View File

@ -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

View File

@ -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(),
);
}
}

View File

@ -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,

View File

@ -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 <Widget>[
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(),
),
],
);
}
}

View File

@ -84,3 +84,20 @@ Map<String, List<HistoryMetaModel>> getRequestGroups(
});
return historyGroups;
}
List<HistoryMetaModel> getRequestGroup(
List<HistoryMetaModel>? models, HistoryMetaModel? selectedModel) {
List<HistoryMetaModel> 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;
}

View File

@ -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<HistoryMetaModel> 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

View File

@ -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(),
],
),
),
),
);

View File

@ -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,

View File

@ -19,7 +19,7 @@ class HistorySplitView extends StatefulWidget {
class HistorySplitViewState extends State<HistorySplitView> {
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"),
],
);

View File

@ -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 <Widget>[
Tab(
text: kLabelRequest,
),
Tab(
text: kLabelResponse,
),
],
),
),
),
);
}
}

View File

@ -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';