Skip to content

Commit 51cc91f

Browse files
authored
Removed hardcoded 24h order expiration and used expiration_hours for trade messages and new orders (#392)
* 1.updated order and trade message from using hardcoded 24hours to using expiration hours of kind 38383 sent from mostro 2. updated arb files to have {expiration_hours} place holder for "newOrder" and "yourCeatedOfferMessage", keys * corrected the data type used as fallback data in expHrs to int, for consistency
1 parent c743429 commit 51cc91f

File tree

7 files changed

+131
-65
lines changed

7 files changed

+131
-65
lines changed

lib/features/order/screens/order_confirmation_screen.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import 'package:mostro_mobile/core/app_theme.dart';
55
import 'package:mostro_mobile/features/order/widgets/order_app_bar.dart';
66
import 'package:mostro_mobile/generated/l10n.dart';
77
import 'package:mostro_mobile/shared/widgets/custom_card.dart';
8+
import 'package:mostro_mobile/features/mostro/mostro_instance.dart';
9+
import 'package:mostro_mobile/shared/providers/order_repository_provider.dart';
810

911
class OrderConfirmationScreen extends ConsumerWidget {
1012
final String orderId;
@@ -13,6 +15,8 @@ class OrderConfirmationScreen extends ConsumerWidget {
1315

1416
@override
1517
Widget build(BuildContext context, WidgetRef ref) {
18+
final mostroInstance = ref.watch(orderRepositoryProvider).mostroInstance;
19+
final expirationHours = mostroInstance?.expirationHours ?? 24;
1620
return Scaffold(
1721
backgroundColor: AppTheme.dark1,
1822
appBar: OrderAppBar(title: 'Order Confirmed'),
@@ -23,7 +27,7 @@ class OrderConfirmationScreen extends ConsumerWidget {
2327
mainAxisAlignment: MainAxisAlignment.center,
2428
children: [
2529
Text(
26-
S.of(context)!.newOrder('24'),
30+
S.of(context)!.newOrder(expirationHours.toString()),
2731
style: TextStyle(fontSize: 18, color: AppTheme.cream1),
2832
textAlign: TextAlign.center,
2933
),

lib/features/trades/screens/trade_detail_screen.dart

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ class TradeDetailScreen extends ConsumerWidget {
105105
BuildContext context, WidgetRef ref, OrderState tradeState) {
106106
final session = ref.watch(sessionProvider(orderId));
107107
final isPending = tradeState.status == Status.pending;
108+
final mostroInstance = ref.watch(orderRepositoryProvider).mostroInstance;
109+
final expirationHours = mostroInstance?.expirationHours ?? 24;
108110

109111
// Determine if the user is the creator of the order based on role and order type
110112
final isCreator = _isUserCreator(session, tradeState);
@@ -119,20 +121,19 @@ class TradeDetailScreen extends ConsumerWidget {
119121
// If `orderPayload.amount` is 0, the trade is "at market price"
120122
final isZeroAmount = (tradeState.order!.amount == 0);
121123
String priceText = '';
122-
124+
123125
if (isZeroAmount) {
124126
final premium = tradeState.order!.premium;
125127
final premiumValue = premium.toDouble();
126-
128+
127129
if (premiumValue == 0) {
128130
// No premium - show only market price
129131
priceText = S.of(context)!.atMarketPrice;
130132
} else {
131133
// Has premium/discount - show market price with percentage
132134
final isPremiumPositive = premiumValue >= 0;
133-
final premiumDisplay = isPremiumPositive
134-
? '(+$premiumValue%)'
135-
: '($premiumValue%)';
135+
final premiumDisplay =
136+
isPremiumPositive ? '(+$premiumValue%)' : '($premiumValue%)';
136137
priceText = '${S.of(context)!.atMarketPrice} $premiumDisplay';
137138
}
138139
}
@@ -153,7 +154,9 @@ class TradeDetailScreen extends ConsumerWidget {
153154
return Column(
154155
children: [
155156
NotificationMessageCard(
156-
message: S.of(context)!.youCreatedOfferMessage,
157+
message: S
158+
.of(context)!
159+
.youCreatedOfferMessage(expirationHours.toString()),
157160
icon: Icons.info_outline,
158161
),
159162
const SizedBox(height: 16),
@@ -188,23 +191,22 @@ class TradeDetailScreen extends ConsumerWidget {
188191

189192
final hasFixedSatsAmount = tradeState.order!.amount != 0;
190193
final satAmount = hasFixedSatsAmount ? ' ${tradeState.order!.amount}' : '';
191-
194+
192195
// For market price orders, show premium in the same format as order book
193196
String priceText = '';
194-
197+
195198
if (!hasFixedSatsAmount) {
196199
final premium = tradeState.order!.premium;
197200
final premiumValue = premium.toDouble();
198-
201+
199202
if (premiumValue == 0) {
200203
// No premium - show only market price
201204
priceText = S.of(context)!.atMarketPrice;
202205
} else {
203206
// Has premium/discount - show market price with percentage
204207
final isPremiumPositive = premiumValue >= 0;
205-
final premiumDisplay = isPremiumPositive
206-
? '(+$premiumValue%)'
207-
: '($premiumValue%)';
208+
final premiumDisplay =
209+
isPremiumPositive ? '(+$premiumValue%)' : '($premiumValue%)';
208210
priceText = '${S.of(context)!.atMarketPrice} $premiumDisplay';
209211
}
210212
}
@@ -620,7 +622,8 @@ class TradeDetailScreen extends ConsumerWidget {
620622
shape: RoundedRectangleBorder(
621623
borderRadius: BorderRadius.circular(8),
622624
),
623-
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
625+
padding:
626+
const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
624627
),
625628
child: Text(
626629
S.of(context)!.yes,
@@ -717,7 +720,7 @@ class TradeDetailScreen extends ConsumerWidget {
717720
// Create dispute using the repository
718721
final repository = ref.read(disputeRepositoryProvider);
719722
final success = await repository.createDispute(orderId);
720-
723+
721724
if (success && context.mounted) {
722725
ScaffoldMessenger.of(context).showSnackBar(
723726
SnackBar(
@@ -737,7 +740,9 @@ class TradeDetailScreen extends ConsumerWidget {
737740
if (context.mounted) {
738741
ScaffoldMessenger.of(context).showSnackBar(
739742
SnackBar(
740-
content: Text(S.of(context)!.disputeCreationErrorWithMessage(e.toString())),
743+
content: Text(S
744+
.of(context)!
745+
.disputeCreationErrorWithMessage(e.toString())),
741746
backgroundColor: AppTheme.red1,
742747
),
743748
);
@@ -781,17 +786,22 @@ class TradeDetailScreen extends ConsumerWidget {
781786
Widget _buildButtonRow(
782787
BuildContext context, WidgetRef ref, OrderState tradeState) {
783788
final actionButtons = _buildActionButtons(context, ref, tradeState);
784-
789+
785790
// Check for VIEW DISPUTE button - independent of action system
786791
final List<Widget> extraButtons = [];
787792
if ((tradeState.action == actions.Action.disputeInitiatedByYou ||
788-
tradeState.action == actions.Action.disputeInitiatedByPeer ||
789-
tradeState.action == actions.Action.adminTookDispute) &&
790-
tradeState.dispute?.disputeId != null) {
791-
extraButtons.add(_buildViewDisputeButton(context, tradeState.dispute!.disputeId));
793+
tradeState.action == actions.Action.disputeInitiatedByPeer ||
794+
tradeState.action == actions.Action.adminTookDispute) &&
795+
tradeState.dispute?.disputeId != null) {
796+
extraButtons
797+
.add(_buildViewDisputeButton(context, tradeState.dispute!.disputeId));
792798
}
793-
794-
final allButtons = [_buildCloseButton(context), ...actionButtons, ...extraButtons];
799+
800+
final allButtons = [
801+
_buildCloseButton(context),
802+
...actionButtons,
803+
...extraButtons
804+
];
795805

796806
if (allButtons.length == 1) {
797807
// Single button - center it with natural oval shape
@@ -976,7 +986,6 @@ class _CountdownWidget extends ConsumerWidget {
976986
final status = tradeState.status;
977987
final now = DateTime.now();
978988
final mostroInstance = ref.read(orderRepositoryProvider).mostroInstance;
979-
980989
// Show countdown ONLY for these 3 specific statuses
981990
if (status == Status.pending) {
982991
// Pending orders: use exact timestamps from expires_at
@@ -985,8 +994,10 @@ class _CountdownWidget extends ConsumerWidget {
985994
return null;
986995
}
987996

988-
final expiration = DateTime.fromMillisecondsSinceEpoch(expiresAtTimestamp);
989-
final createdAt = DateTime.fromMillisecondsSinceEpoch((tradeState.order?.createdAt ?? 0) * 1000);
997+
final expiration =
998+
DateTime.fromMillisecondsSinceEpoch(expiresAtTimestamp);
999+
final createdAt = DateTime.fromMillisecondsSinceEpoch(
1000+
(tradeState.order?.createdAt ?? 0) * 1000);
9901001

9911002
// Handle edge case: expiration in the past
9921003
if (expiration.isBefore(now.subtract(const Duration(hours: 1)))) {

lib/features/trades/widgets/mostro_message_detail_widget.dart

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_riverpod/flutter_riverpod.dart';
33
import 'package:mostro_mobile/data/enums.dart';
4-
import 'package:mostro_mobile/data/models/nostr_event.dart';
54
import 'package:mostro_mobile/data/models/session.dart';
65
import 'package:mostro_mobile/features/order/models/order_state.dart';
76
import 'package:mostro_mobile/features/order/providers/order_notifier_provider.dart';
@@ -61,9 +60,9 @@ class MostroMessageDetail extends ConsumerWidget {
6160
switch (action) {
6261
case actions.Action.newOrder:
6362
final expHrs =
64-
ref.read(orderRepositoryProvider).mostroInstance?.expiration ??
65-
'24';
66-
return S.of(context)!.newOrder(int.tryParse(expHrs) ?? 24);
63+
ref.read(orderRepositoryProvider).mostroInstance?.expirationHours ??
64+
24;
65+
return S.of(context)!.newOrder(expHrs.toString());
6766
case actions.Action.canceled:
6867
return S.of(context)!.canceled(orderPayload?.id ?? '');
6968
case actions.Action.payInvoice:
@@ -107,14 +106,17 @@ class MostroMessageDetail extends ConsumerWidget {
107106
?.expirationSeconds ??
108107
900;
109108
final expMinutes = (expSecs / 60).round();
110-
109+
111110
// Check if user is the maker (creator) of the order
112111
final session = ref.watch(sessionProvider(orderPayload?.id ?? ''));
113112
final isUserCreator = _isUserCreator(session, tradeState);
114-
113+
115114
if (isUserCreator) {
116115
// Maker scenario: user created a buy order, show waiting for taker message
117-
return S.of(context)!.orderTakenWaitingCounterpart(expMinutes).replaceAll('\\n', '\n');
116+
return S
117+
.of(context)!
118+
.orderTakenWaitingCounterpart(expMinutes)
119+
.replaceAll('\\n', '\n');
118120
} else {
119121
// Taker scenario: user took someone's order, show original message
120122
return S
@@ -128,15 +130,18 @@ class MostroMessageDetail extends ConsumerWidget {
128130
?.expirationSeconds ??
129131
900;
130132
final expMinutes = (expSecs / 60).round();
131-
133+
132134
// Check if user is the maker (creator) of a sell order
133135
final session = ref.watch(sessionProvider(orderPayload?.id ?? ''));
134136
final isUserCreator = _isUserCreator(session, tradeState);
135137
final isSellOrder = tradeState.order?.kind == OrderType.sell;
136-
138+
137139
if (isUserCreator && isSellOrder) {
138140
// Maker scenario: user created a sell order, show waiting for taker message
139-
return S.of(context)!.orderTakenWaitingCounterpart(expMinutes).replaceAll('\\n', '\n');
141+
return S
142+
.of(context)!
143+
.orderTakenWaitingCounterpart(expMinutes)
144+
.replaceAll('\\n', '\n');
140145
} else {
141146
// Taker scenario: user took someone's order, show original message
142147
return S.of(context)!.waitingBuyerInvoice(expMinutes);
@@ -218,7 +223,8 @@ class MostroMessageDetail extends ConsumerWidget {
218223
return S.of(context)!.adminSettledUsers(orderPayload!.id ?? '');
219224
case actions.Action.paymentFailed:
220225
final payload = ref.read(orderNotifierProvider(orderId)).paymentFailed;
221-
final intervalInMinutes = ((payload?.paymentRetriesInterval ?? 0) / 60).round();
226+
final intervalInMinutes =
227+
((payload?.paymentRetriesInterval ?? 0) / 60).round();
222228
return S.of(context)!.paymentFailed(
223229
payload?.paymentAttempts ?? 0,
224230
intervalInMinutes,

lib/l10n/intl_en.arb

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
"@@locale": "en",
33
"backToHome": "Back to Home",
44
"newOrder": "Your offer has been published! Please wait until another user picks your order. It will be available for {expiration_hours} hours. You can cancel this order before another user picks it up by executing: cancel.",
5+
"@newOrder": {
6+
"placeholders": {
7+
"expiration_hours": {
8+
"type": "String",
9+
"description": "The expiration time in hours"
10+
}
11+
}
12+
},
513
"canceled": "You have canceled the order ID: {id}.",
614
"payInvoice": "Please pay this hold invoice of {amount} Sats for {fiat_code} {fiat_amount} to start the operation. If you do not pay it within {expiration_seconds} minutes, the trade will be canceled.",
715
"@payInvoice": {
@@ -263,7 +271,15 @@
263271
"someoneIsBuyingTitle": "Someone is Buying Sats",
264272
"someoneIsSellingFixedTitle": "Someone is Selling {sats} Sats",
265273
"someoneIsBuyingFixedTitle": "Someone is Buying {sats} Sats",
266-
"youCreatedOfferMessage": "You created this offer. Below are the details of your offer. Wait for another user to take it. It will be published for 24 hours. You can cancel it at any time using the 'Cancel' button.",
274+
"youCreatedOfferMessage": "You created this offer. Below are the details of your offer. Wait for another user to take it. It will be published for {expiration_hours} hours. You can cancel it at any time using the 'Cancel' button.",
275+
"@youCreatedOfferMessage": {
276+
"placeholders": {
277+
"expiration_hours": {
278+
"type": "String",
279+
"description": "The expiration time in hours"
280+
}
281+
}
282+
},
267283
"youAreSellingTitle": "You are selling{sats} sats",
268284
"youAreBuyingTitle": "You are buying{sats} sats",
269285
"forAmount": "for {amount}",
@@ -1217,5 +1233,6 @@
12171233

12181234
"@_comment_backup_reminder": "Backup reminder notification",
12191235
"backupAccountReminder": "You must back up your account",
1220-
"backupAccountReminderMessage": "Back up your secret words to recover your account"
1236+
"backupAccountReminderMessage": "Save your secret words to recover your account"
1237+
12211238
}

lib/l10n/intl_es.arb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
"@@locale": "es",
33
"backToHome": "Volver al inicio",
44
"newOrder": "¡Tu oferta ha sido publicada! Por favor espera hasta que otro usuario elija tu orden. Estará disponible durante {expiration_hours} horas. Puedes cancelar esta orden antes de que otro usuario la tome ejecutando: cancel.",
5+
"@newOrder": {
6+
"placeholders": {
7+
"expiration_hours": {
8+
"type": "String"
9+
}
10+
}
11+
},
512
"canceled": "Has cancelado la orden ID: {id}.",
613
"payInvoice": "Por favor paga esta factura retenida de {amount} Sats por {fiat_amount} {fiat_code} para iniciar la operación. Si no la pagas antes de {expiration_seconds} minutos, el intercambio será cancelado.",
714
"@payInvoice": {
@@ -611,7 +618,14 @@
611618
"someoneIsBuyingTitle": "Alguien está Comprando Sats",
612619
"someoneIsSellingFixedTitle": "Alguien está Vendiendo {sats} Sats",
613620
"someoneIsBuyingFixedTitle": "Alguien está Comprando {sats} Sats",
614-
"youCreatedOfferMessage": "Creaste esta oferta. A continuación se muestran los detalles de tu oferta. Espera a que otro usuario la tome. Se publicará durante 24 horas. Puedes cancelarla en cualquier momento usando el botón 'Cancelar'.",
621+
"youCreatedOfferMessage": "Creaste esta oferta. A continuación se muestran los detalles de tu oferta. Espera a que otro usuario la tome. Se publicará durante {expiration_hours} horas. Puedes cancelarla en cualquier momento usando el botón 'Cancelar'.",
622+
"@youCreatedOfferMessage": {
623+
"placeholders": {
624+
"expiration_hours": {
625+
"type": "String"
626+
}
627+
}
628+
},
615629
"youAreSellingTitle": "Estás vendiendo {sats} sats",
616630
"youAreBuyingTitle": "Estás comprando {sats} sats",
617631
"forAmount": "por {amount}",

lib/l10n/intl_it.arb

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
"@@locale": "it",
33
"backToHome": "Torna alla home",
44
"newOrder": "La tua offerta è stata pubblicata! Attendi fino a quando un altro utente accetta il tuo ordine. Sarà disponibile per {expiration_hours} ore. Puoi annullare questo ordine prima che un altro utente lo prenda premendo: Cancella.",
5+
"@newOrder": {
6+
"placeholders": {
7+
"expiration_hours": {
8+
"type": "String"
9+
}
10+
}
11+
},
512
"canceled": "Hai annullato l'ordine ID: {id}!",
613
"payInvoice": "Paga questa Hodl Invoice di {amount} Sats per {fiat_amount} {fiat_code} per iniziare l'operazione. Se non la paghi entro {expiration_seconds} minuti lo scambio sarà annullato.",
714
"@payInvoice": {
@@ -654,7 +661,14 @@
654661
}
655662
}
656663
},
657-
"youCreatedOfferMessage": "Hai creato questa offerta. Di seguito sono riportati i dettagli della tua offerta. Aspetta che un altro utente la prenda. Sarà pubblicata per 24 ore. Puoi cancellarla in qualsiasi momento utilizzando il pulsante 'Annulla'.",
664+
"youCreatedOfferMessage": "Hai creato questa offerta. Di seguito sono riportati i dettagli della tua offerta. Aspetta che un altro utente la prenda. Sarà pubblicata per {expiration_hours} ore. Puoi cancellarla in qualsiasi momento utilizzando il pulsante 'Annulla'.",
665+
"@youCreatedOfferMessage": {
666+
"placeholders": {
667+
"expiration_hours": {
668+
"type": "String"
669+
}
670+
}
671+
},
658672
"youAreSellingTitle": "Stai vendendo {sats} sats",
659673
"@youAreSellingTitle": {
660674
"placeholders": {

0 commit comments

Comments
 (0)