Skip to content

Commit dfbaa1b

Browse files
committed
feat: delete button for trades in terminal states
Add a delete action on the trade details view for both mobile (app bar icon) and desktop (button). Deleting a still-active (non-terminal) trade shows a stronger confirmation warning. closes #1264
1 parent 51db6c7 commit dfbaa1b

2 files changed

Lines changed: 181 additions & 0 deletions

File tree

lib/models/exchange/response_objects/trade.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,30 @@ class Trade {
282282
);
283283
}
284284

285+
/// Statuses that indicate a trade has reached an end state and is no longer
286+
/// expected to change. Trades in one of these states can be safely deleted.
287+
static const Set<String> terminalStatuses = {
288+
"Finished",
289+
"finished",
290+
"completed",
291+
"Completed",
292+
"Failed",
293+
"failed",
294+
"Refunded",
295+
"refunded",
296+
"Expired",
297+
"expired",
298+
"Closed",
299+
"closed",
300+
};
301+
302+
/// Whether this trade has reached a terminal (finished/expired/etc) state.
303+
bool get isTerminalStatus => terminalStatuses.contains(status);
304+
305+
/// Whether this trade is still in progress (waiting, new, processing, etc).
306+
/// These trades can still be deleted but only behind a stronger warning.
307+
bool get isInProgress => !isTerminalStatus;
308+
285309
@override
286310
String toString() {
287311
return toMap().toString();

lib/pages/exchange_view/trade_details_view.dart

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import '../../widgets/conditional_parent.dart';
5050
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
5151
import '../../widgets/custom_buttons/blue_text_button.dart';
5252
import '../../widgets/desktop/desktop_dialog.dart';
53+
import '../../widgets/desktop/primary_button.dart';
5354
import '../../widgets/desktop/secondary_button.dart';
5455
import '../../widgets/qr.dart';
5556
import '../../widgets/rounded_container.dart';
@@ -212,6 +213,131 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
212213
trade.status == "wait" ||
213214
trade.status == "Waiting");
214215

216+
final isTerminalStatus = trade.isTerminalStatus;
217+
218+
void deleteTrade() {
219+
if (isDesktop) {
220+
showDialog<void>(
221+
context: context,
222+
builder: (_) => DesktopDialog(
223+
maxWidth: 450,
224+
maxHeight: 300,
225+
child: Padding(
226+
padding: const EdgeInsets.all(32),
227+
child: Column(
228+
children: [
229+
Text(
230+
isTerminalStatus
231+
? "Delete this trade?"
232+
: "Delete an active trade?",
233+
style: STextStyles.desktopH3(context),
234+
),
235+
const SizedBox(height: 16),
236+
Text(
237+
isTerminalStatus
238+
? "Trade will be deleted permanently!"
239+
: "This trade is still active and has not finished. "
240+
"Deleting it will permanently remove it from your "
241+
"device and you will no longer be able to track "
242+
"its status. Proceed only if you know what you "
243+
"are doing.",
244+
style: STextStyles.desktopTextSmall(context),
245+
),
246+
const Spacer(),
247+
Row(
248+
children: [
249+
Expanded(
250+
child: SecondaryButton(
251+
label: "Cancel",
252+
buttonHeight: ButtonHeight.l,
253+
onPressed: Navigator.of(context).pop,
254+
),
255+
),
256+
const SizedBox(width: 16),
257+
Expanded(
258+
child: PrimaryButton(
259+
label: "Delete",
260+
buttonHeight: ButtonHeight.l,
261+
onPressed: () async {
262+
await ref
263+
.read(tradesServiceProvider)
264+
.delete(
265+
trade: trade,
266+
shouldNotifyListeners: true,
267+
);
268+
if (context.mounted) {
269+
Navigator.of(
270+
context,
271+
).pop(); // close confirm dialog
272+
Navigator.of(
273+
context,
274+
rootNavigator: true,
275+
).pop(); // close trade details dialog
276+
}
277+
},
278+
),
279+
),
280+
],
281+
),
282+
],
283+
),
284+
),
285+
),
286+
);
287+
return;
288+
}
289+
showDialog<dynamic>(
290+
context: context,
291+
useSafeArea: true,
292+
barrierDismissible: true,
293+
builder: (_) => StackDialog(
294+
title: isTerminalStatus
295+
? "Delete this trade?"
296+
: "Delete an active trade?",
297+
message: isTerminalStatus
298+
? "Trade will be deleted permanently!"
299+
: "This trade is still active and has not finished. "
300+
"Deleting it will permanently remove it from your "
301+
"device and you will no longer be able to track its "
302+
"status. Proceed only if you know what you are doing.",
303+
leftButton: TextButton(
304+
style: Theme.of(context)
305+
.extension<StackColors>()!
306+
.getSecondaryEnabledButtonStyle(context),
307+
child: Text(
308+
"Cancel",
309+
style: STextStyles.itemSubtitle12(context),
310+
),
311+
onPressed: () {
312+
Navigator.of(context).pop();
313+
},
314+
),
315+
rightButton: TextButton(
316+
style: Theme.of(context)
317+
.extension<StackColors>()!
318+
.getPrimaryEnabledButtonStyle(context),
319+
child: Text("Delete", style: STextStyles.button(context)),
320+
onPressed: () async {
321+
await ref
322+
.read(tradesServiceProvider)
323+
.delete(trade: trade, shouldNotifyListeners: true);
324+
if (context.mounted) {
325+
Navigator.of(context).pop();
326+
Navigator.of(context).pop();
327+
unawaited(
328+
showFloatingFlushBar(
329+
type: FlushBarType.success,
330+
message: "Trade deleted",
331+
context: context,
332+
),
333+
);
334+
}
335+
},
336+
),
337+
),
338+
);
339+
}
340+
215341
return ConditionalParent(
216342
condition: !isDesktop,
217343
builder: (child) => Background(
@@ -232,6 +358,31 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
232358
"Trade details",
233359
style: STextStyles.navBarTitle(context),
234360
),
361+
actions: [
362+
Padding(
363+
padding: const EdgeInsets.only(top: 10, bottom: 10, right: 10),
364+
child: AspectRatio(
365+
aspectRatio: 1,
366+
child: AppBarIconButton(
367+
key: const Key("tradeDetailsViewDeleteTradeButtonKey"),
368+
size: 36,
369+
shadows: const [],
370+
color: Theme.of(
371+
context,
372+
).extension<StackColors>()!.background,
373+
icon: SvgPicture.asset(
374+
Assets.svg.trash,
375+
color: Theme.of(
376+
context,
377+
).extension<StackColors>()!.accentColorDark,
378+
width: 20,
379+
height: 20,
380+
),
381+
onPressed: deleteTrade,
382+
),
383+
),
384+
),
385+
],
235386
),
236387
body: SafeArea(
237388
child: Padding(
@@ -295,6 +446,12 @@ class _TradeDetailsViewState extends ConsumerState<TradeDetailsView> {
295446
);
296447
},
297448
),
449+
const SizedBox(height: 16),
450+
SecondaryButton(
451+
label: "Delete trade",
452+
buttonHeight: ButtonHeight.l,
453+
onPressed: deleteTrade,
454+
),
298455
const SizedBox(height: 32),
299456
],
300457
),

0 commit comments

Comments
 (0)