From 32350ac5712d742ce9f4160681ff39e13542c444 Mon Sep 17 00:00:00 2001 From: Arseni Date: Wed, 21 Jan 2026 16:59:38 +0300 Subject: [PATCH] minor fixes --- .../payment/quotation/intent_builder.dart | 9 ++--- .../provider/payment/quotation/quotation.dart | 40 +++++++++++++------ .../payouts/quote_status/quote_status.dart | 2 +- .../pweb/lib/utils/quote_duration_format.dart | 28 +++++++++++-- frontend/pweb/pubspec.yaml | 1 + 5 files changed, 57 insertions(+), 23 deletions(-) diff --git a/frontend/pshared/lib/provider/payment/quotation/intent_builder.dart b/frontend/pshared/lib/provider/payment/quotation/intent_builder.dart index dbbfd584..ec242cd7 100644 --- a/frontend/pshared/lib/provider/payment/quotation/intent_builder.dart +++ b/frontend/pshared/lib/provider/payment/quotation/intent_builder.dart @@ -96,13 +96,12 @@ class QuotationIntentBuilder { if (name != null && name.isNotEmpty) { final parts = name.split(RegExp(r'\s+')); - if (parts.length == 1) { - firstName = parts.first; - } else if (parts.length == 2) { + if (parts.isNotEmpty) { firstName = parts.first; + } + if (parts.length == 2) { lastName = parts.last; - } else { - firstName = parts.first; + } else if (parts.length > 2) { lastName = parts.last; middleName = parts.sublist(1, parts.length - 1).join(' '); } diff --git a/frontend/pshared/lib/provider/payment/quotation/quotation.dart b/frontend/pshared/lib/provider/payment/quotation/quotation.dart index 777e07a6..d3df6712 100644 --- a/frontend/pshared/lib/provider/payment/quotation/quotation.dart +++ b/frontend/pshared/lib/provider/payment/quotation/quotation.dart @@ -1,7 +1,7 @@ import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart'; -import 'package:collection/collection.dart'; +import 'dart:convert'; import 'package:uuid/uuid.dart'; @@ -23,6 +23,7 @@ import 'package:pshared/provider/resource.dart'; import 'package:pshared/provider/payment/quotation/auto_refresh.dart'; import 'package:pshared/provider/payment/quotation/intent_builder.dart'; import 'package:pshared/service/payment/quotation.dart'; +import 'package:pshared/utils/exception.dart'; class QuotationProvider extends ChangeNotifier { @@ -36,6 +37,11 @@ class QuotationProvider extends ChangeNotifier { QuotationAutoRefreshController(); AutoRefreshMode _autoRefreshMode = AutoRefreshMode.on; + QuotationProvider() { + addListener(_handleStateChanged); + _syncAutoRefresh(); + } + void update( OrganizationsProvider venue, PaymentAmountProvider payment, @@ -53,11 +59,10 @@ class QuotationProvider extends ChangeNotifier { methods: methods, ); if (intent == null) return; - final lastIntentDto = _lastIntent?.toDTO().toJson(); - final isSameIntent = lastIntentDto != null && - const DeepCollectionEquality().equals(intent.toDTO().toJson(), lastIntentDto); - if (isSameIntent) return; - getQuotation(intent); + final intentKey = _buildIntentKey(intent); + final lastIntent = _lastIntent; + if (lastIntent != null && intentKey == _buildIntentKey(lastIntent)) return; + getQuotation(intent, idempotencyKey: intentKey); } PaymentQuote? get quotation => _quotation.data; @@ -86,7 +91,7 @@ class QuotationProvider extends ChangeNotifier { return timeLeft <= Duration.zero; } - QuoteStatusType get quoteStatusType { + QuoteStatusType get quoteStatus { if (isLoading) return QuoteStatusType.loading; if (error != null) return QuoteStatusType.error; if (quotation == null) return QuoteStatusType.missing; @@ -103,34 +108,37 @@ class QuotationProvider extends ChangeNotifier { return createAsset(money.currency, money.amount); } + void _handleStateChanged() { + _syncAutoRefresh(); + } + void _setResource(Resource quotation) { _quotation = quotation; - _syncAutoRefresh(); notifyListeners(); } void setAutoRefreshMode(AutoRefreshMode mode) { if (_autoRefreshMode == mode) return; _autoRefreshMode = mode; - _syncAutoRefresh(); notifyListeners(); } Future refreshQuotation() async { final intent = _lastIntent; if (intent == null) return null; - return getQuotation(intent); + return getQuotation(intent, idempotencyKey: _buildIntentKey(intent)); } - Future getQuotation(PaymentIntent intent) async { + Future getQuotation(PaymentIntent intent, {String? idempotencyKey}) async { if (!_organizations.isOrganizationSet) throw StateError('Organization is not set'); _lastIntent = intent; + final intentKey = idempotencyKey ?? _buildIntentKey(intent); try { _setResource(_quotation.copyWith(isLoading: true, error: null)); final response = await QuotationService.getQuotation( _organizations.current.id, QuotePaymentRequest( - idempotencyKey: Uuid().v4(), + idempotencyKey: intentKey, intent: intent.toDTO(), ), ); @@ -140,7 +148,7 @@ class QuotationProvider extends ChangeNotifier { _logger.warning('Failed to get quotation', e, st); _setResource(_quotation.copyWith( data: null, - error: e is Exception ? e : Exception(e.toString()), + error: toException(e), isLoading: false, )); } @@ -169,7 +177,13 @@ class QuotationProvider extends ChangeNotifier { @override void dispose() { + removeListener(_handleStateChanged); _autoRefreshController.dispose(); super.dispose(); } + + String _buildIntentKey(PaymentIntent intent) { + final payload = jsonEncode(intent.toDTO().toJson()); + return Uuid().v5(Uuid.NAMESPACE_URL, 'quote:$payload'); + } } diff --git a/frontend/pweb/lib/pages/dashboard/payouts/quote_status/quote_status.dart b/frontend/pweb/lib/pages/dashboard/payouts/quote_status/quote_status.dart index c7685858..902b6fb6 100644 --- a/frontend/pweb/lib/pages/dashboard/payouts/quote_status/quote_status.dart +++ b/frontend/pweb/lib/pages/dashboard/payouts/quote_status/quote_status.dart @@ -45,7 +45,7 @@ class _QuoteStatusState extends State { final provider = context.watch(); final timeLeft = provider.quoteTimeLeft; final isLoading = provider.isLoading; - final statusType = provider.quoteStatusType; + final statusType = provider.quoteStatus; final autoRefreshMode = provider.autoRefreshMode; String statusText; diff --git a/frontend/pweb/lib/utils/quote_duration_format.dart b/frontend/pweb/lib/utils/quote_duration_format.dart index 1b756a6b..6ba237e0 100644 --- a/frontend/pweb/lib/utils/quote_duration_format.dart +++ b/frontend/pweb/lib/utils/quote_duration_format.dart @@ -1,11 +1,31 @@ +import 'package:duration/duration.dart'; + String formatQuoteDuration(Duration duration) { - final totalSeconds = duration.inSeconds < 0 ? 0 : duration.inSeconds; - final hours = totalSeconds ~/ 3600; - final minutes = (totalSeconds % 3600) ~/ 60; - final seconds = totalSeconds % 60; + final clampedDuration = duration.isNegative ? Duration.zero : duration; + final pretty = prettyDuration( + clampedDuration, + tersity: DurationTersity.second, + upperTersity: DurationTersity.hour, + abbreviated: true, + delimiter: ':', + spacer: '', + ); + final units = _extractHms(pretty); + final hours = units['h'] ?? 0; + final minutes = units['m'] ?? 0; + final seconds = units['s'] ?? 0; if (hours > 0) { return '${hours.toString()}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}'; } return '${minutes.toString()}:${seconds.toString().padLeft(2, '0')}'; } + +Map _extractHms(String pretty) { + final matches = RegExp(r'(\d+)([hms])').allMatches(pretty); + final units = {}; + for (final match in matches) { + units[match.group(2)!] = int.parse(match.group(1)!); + } + return units; +} diff --git a/frontend/pweb/pubspec.yaml b/frontend/pweb/pubspec.yaml index 4705cf16..e4ad4c4a 100644 --- a/frontend/pweb/pubspec.yaml +++ b/frontend/pweb/pubspec.yaml @@ -69,6 +69,7 @@ dependencies: flutter_multi_formatter: ^2.13.7 dotted_border: ^3.1.0 qr_flutter: ^4.1.0 + duration: ^4.0.3