mirror of
https://github.com/foss42/apidash.git
synced 2025-12-04 20:13:56 +08:00
feat: history of requests
This commit is contained in:
@@ -25,7 +25,7 @@ class _HistoryDetailsState extends ConsumerState<HistoryDetails>
|
||||
final codePaneVisible = ref.watch(historyCodePaneVisibleStateProvider);
|
||||
|
||||
final TabController controller =
|
||||
useTabController(initialLength: 2, vsync: this);
|
||||
useTabController(initialLength: 3, vsync: this);
|
||||
|
||||
return selectedHistoryRequest != null
|
||||
? LayoutBuilder(
|
||||
@@ -42,28 +42,45 @@ class _HistoryDetailsState extends ConsumerState<HistoryDetails>
|
||||
)),
|
||||
kVSpacer10,
|
||||
if (isCompact) ...[
|
||||
RequestResponseTabbar(
|
||||
SegmentedTabbar(
|
||||
controller: controller,
|
||||
tabs: const [
|
||||
Tab(text: kLabelRequest),
|
||||
Tab(text: kLabelResponse),
|
||||
Tab(text: kLabelCode),
|
||||
],
|
||||
),
|
||||
kVSpacer10,
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
controller: controller,
|
||||
children: [
|
||||
HistoryRequestPane(
|
||||
isCompact: isCompact,
|
||||
),
|
||||
const HistoryResponsePane(),
|
||||
],
|
||||
))
|
||||
child: TabBarView(
|
||||
controller: controller,
|
||||
children: [
|
||||
HistoryRequestPane(
|
||||
isCompact: isCompact,
|
||||
),
|
||||
const HistoryResponsePane(),
|
||||
const CodePane(
|
||||
isHistoryRequest: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const HistoryPageBottombar()
|
||||
] else ...[
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: kPh4,
|
||||
child: RequestDetailsCard(
|
||||
child: EqualSplitView(
|
||||
leftWidget: HistoryRequestPane(
|
||||
isCompact: isCompact,
|
||||
leftWidget: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: HistoryRequestPane(
|
||||
isCompact: isCompact,
|
||||
),
|
||||
),
|
||||
const HistoryPageBottombar(),
|
||||
],
|
||||
),
|
||||
rightWidget: codePaneVisible
|
||||
? const CodePane(isHistoryRequest: true)
|
||||
|
||||
@@ -6,7 +6,6 @@ import 'package:apidash/providers/providers.dart';
|
||||
import 'package:apidash/utils/utils.dart';
|
||||
import 'history_pane.dart';
|
||||
import 'history_viewer.dart';
|
||||
import 'history_widgets/history_widgets.dart';
|
||||
|
||||
class HistoryPage extends ConsumerWidget {
|
||||
const HistoryPage({
|
||||
@@ -23,14 +22,14 @@ class HistoryPage extends ConsumerWidget {
|
||||
: 'History';
|
||||
if (context.isMediumWindow) {
|
||||
return DrawerSplitView(
|
||||
scaffoldKey: scaffoldKey,
|
||||
mainContent: const HistoryViewer(),
|
||||
title: Text(title),
|
||||
leftDrawerContent: const HistoryPane(),
|
||||
actions: const [SizedBox(width: 16)],
|
||||
onDrawerChanged: (value) =>
|
||||
ref.read(leftDrawerStateProvider.notifier).state = value,
|
||||
bottomNavigationBar: const HistoryPageBottombar());
|
||||
scaffoldKey: scaffoldKey,
|
||||
mainContent: const HistoryViewer(),
|
||||
title: Text(title),
|
||||
leftDrawerContent: const HistoryPane(),
|
||||
actions: const [SizedBox(width: 16)],
|
||||
onDrawerChanged: (value) =>
|
||||
ref.read(leftDrawerStateProvider.notifier).state = value,
|
||||
);
|
||||
}
|
||||
return const Column(
|
||||
children: [
|
||||
|
||||
@@ -65,6 +65,7 @@ class HistoryList extends HookConsumerWidget {
|
||||
child: HistoryExpansionTile(
|
||||
date: sortedHistoryKeys[index],
|
||||
requestGroups: requestGroups,
|
||||
initiallyExpanded: index == 0,
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -78,10 +79,12 @@ class HistoryExpansionTile extends StatefulHookConsumerWidget {
|
||||
super.key,
|
||||
required this.requestGroups,
|
||||
required this.date,
|
||||
this.initiallyExpanded = false,
|
||||
});
|
||||
|
||||
final Map<String, List<HistoryMetaModel>> requestGroups;
|
||||
final DateTime date;
|
||||
final bool initiallyExpanded;
|
||||
|
||||
@override
|
||||
ConsumerState<HistoryExpansionTile> createState() =>
|
||||
@@ -95,8 +98,9 @@ class _HistoryExpansionTileState extends ConsumerState<HistoryExpansionTile>
|
||||
final animationController = useAnimationController(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
vsync: this,
|
||||
initialValue: widget.initiallyExpanded ? 1.0 : 0.0,
|
||||
);
|
||||
final animation = Tween(begin: 0.25, end: 0.0).animate(animationController);
|
||||
final animation = Tween(begin: 0.0, end: 0.25).animate(animationController);
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final selectedGroupId = ref.watch(selectedRequestGroupIdStateProvider);
|
||||
return ExpansionTile(
|
||||
@@ -122,16 +126,16 @@ class _HistoryExpansionTileState extends ConsumerState<HistoryExpansionTile>
|
||||
),
|
||||
onExpansionChanged: (value) {
|
||||
if (value) {
|
||||
animationController.reverse();
|
||||
} else {
|
||||
animationController.forward();
|
||||
} else {
|
||||
animationController.reverse();
|
||||
}
|
||||
},
|
||||
trailing: const SizedBox.shrink(),
|
||||
tilePadding: kPh8,
|
||||
shape: const RoundedRectangleBorder(),
|
||||
collapsedBackgroundColor: colorScheme.surfaceContainerLow,
|
||||
initiallyExpanded: true,
|
||||
initiallyExpanded: widget.initiallyExpanded,
|
||||
childrenPadding: kPv8 + kPe4,
|
||||
children: widget.requestGroups.values.map((item) {
|
||||
return Padding(
|
||||
|
||||
47
lib/screens/history/history_widgets/his_action_buttons.dart
Normal file
47
lib/screens/history/history_widgets/his_action_buttons.dart
Normal file
@@ -0,0 +1,47 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:apidash/providers/providers.dart';
|
||||
import 'package:apidash/widgets/widgets.dart';
|
||||
import 'package:apidash/models/models.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
|
||||
class HistoryActionButtons extends ConsumerWidget {
|
||||
const HistoryActionButtons({super.key, this.historyRequestModel});
|
||||
|
||||
final HistoryRequestModel? historyRequestModel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final collectionStateNotifier = ref.watch(collectionStateNotifierProvider);
|
||||
final isAvailable = collectionStateNotifier?.values.any((element) =>
|
||||
element.id == historyRequestModel?.metaData.requestId) ??
|
||||
false;
|
||||
final requestId = historyRequestModel?.metaData.requestId;
|
||||
return FilledButtonGroup(buttons: [
|
||||
ButtonData(
|
||||
icon: Icons.copy_rounded,
|
||||
label: kLabelDuplicate,
|
||||
onPressed: requestId != null
|
||||
? () {
|
||||
ref
|
||||
.read(collectionStateNotifierProvider.notifier)
|
||||
.duplicateFromHistory(historyRequestModel!);
|
||||
ref.read(navRailIndexStateProvider.notifier).state = 0;
|
||||
}
|
||||
: null,
|
||||
tooltip: "Duplicate Request",
|
||||
),
|
||||
ButtonData(
|
||||
icon: Icons.north_east_rounded,
|
||||
label: kLabelRequest,
|
||||
onPressed: isAvailable && requestId != null
|
||||
? () {
|
||||
ref.read(selectedIdStateProvider.notifier).state = requestId;
|
||||
ref.read(navRailIndexStateProvider.notifier).state = 0;
|
||||
}
|
||||
: null,
|
||||
tooltip: isAvailable ? "Go to Request" : "Couldn't find Request",
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:apidash/extensions/extensions.dart';
|
||||
import 'package:apidash/providers/providers.dart';
|
||||
import 'package:apidash/utils/utils.dart';
|
||||
import 'package:apidash/consts.dart';
|
||||
import '../history_requests.dart';
|
||||
import 'his_action_buttons.dart';
|
||||
|
||||
class HistoryPageBottombar extends ConsumerWidget {
|
||||
const HistoryPageBottombar({
|
||||
@@ -16,59 +19,87 @@ class HistoryPageBottombar extends ConsumerWidget {
|
||||
final requestGroup = getRequestGroup(
|
||||
historyMetas?.values.toList(), selectedRequestModel?.metaData);
|
||||
final requestCount = requestGroup.length;
|
||||
return Padding(
|
||||
padding: MediaQuery.of(context).viewInsets,
|
||||
child: Container(
|
||||
height: 60 + MediaQuery.paddingOf(context).bottom,
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom,
|
||||
left: 16,
|
||||
right: 16,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
width: 1,
|
||||
),
|
||||
|
||||
return Container(
|
||||
height: 60 + MediaQuery.paddingOf(context).bottom,
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom,
|
||||
left: 16,
|
||||
right: 16,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
requestCount > 1
|
||||
? Badge(
|
||||
label: Text(
|
||||
requestCount > 9 ? '9 +' : requestCount.toString(),
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
child: IconButton(
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.secondaryContainer,
|
||||
),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return ConstrainedBox(
|
||||
constraints:
|
||||
const BoxConstraints(maxWidth: 500),
|
||||
child: const HistorRequestsScrollableSheet());
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.keyboard_arrow_up_rounded,
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
child: context.isMediumWindow
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
HistoryActionButtons(historyRequestModel: selectedRequestModel),
|
||||
HistorySheetButton(requestCount: requestCount)
|
||||
],
|
||||
)
|
||||
: Center(
|
||||
child: HistoryActionButtons(
|
||||
historyRequestModel: selectedRequestModel)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HistorySheetButton extends StatelessWidget {
|
||||
const HistorySheetButton({
|
||||
super.key,
|
||||
required this.requestCount,
|
||||
});
|
||||
|
||||
final int requestCount;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isCompact = context.isCompactWindow;
|
||||
const icon = Icon(Icons.keyboard_arrow_up_rounded);
|
||||
return Badge(
|
||||
isLabelVisible: requestCount > 1,
|
||||
label: Text(
|
||||
requestCount > 9 ? '9+' : requestCount.toString(),
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
child: FilledButton.tonal(
|
||||
style: FilledButton.styleFrom(
|
||||
minimumSize: const Size(44, 44),
|
||||
padding: isCompact ? kP4 : const EdgeInsets.fromLTRB(16, 12, 8, 12),
|
||||
),
|
||||
onPressed: requestCount > 1
|
||||
? () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 500),
|
||||
child: const HistorRequestsScrollableSheet());
|
||||
},
|
||||
);
|
||||
}
|
||||
: null,
|
||||
child: isCompact
|
||||
? icon
|
||||
: const Row(
|
||||
children: [
|
||||
Text(
|
||||
"Show All",
|
||||
style: kTextStyleButton,
|
||||
),
|
||||
kHSpacer5,
|
||||
icon,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user