minor fixes
This commit is contained in:
@@ -96,13 +96,12 @@ class QuotationIntentBuilder {
|
|||||||
|
|
||||||
if (name != null && name.isNotEmpty) {
|
if (name != null && name.isNotEmpty) {
|
||||||
final parts = name.split(RegExp(r'\s+'));
|
final parts = name.split(RegExp(r'\s+'));
|
||||||
if (parts.length == 1) {
|
if (parts.isNotEmpty) {
|
||||||
firstName = parts.first;
|
|
||||||
} else if (parts.length == 2) {
|
|
||||||
firstName = parts.first;
|
firstName = parts.first;
|
||||||
|
}
|
||||||
|
if (parts.length == 2) {
|
||||||
lastName = parts.last;
|
lastName = parts.last;
|
||||||
} else {
|
} else if (parts.length > 2) {
|
||||||
firstName = parts.first;
|
|
||||||
lastName = parts.last;
|
lastName = parts.last;
|
||||||
middleName = parts.sublist(1, parts.length - 1).join(' ');
|
middleName = parts.sublist(1, parts.length - 1).join(' ');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:uuid/uuid.dart';
|
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/auto_refresh.dart';
|
||||||
import 'package:pshared/provider/payment/quotation/intent_builder.dart';
|
import 'package:pshared/provider/payment/quotation/intent_builder.dart';
|
||||||
import 'package:pshared/service/payment/quotation.dart';
|
import 'package:pshared/service/payment/quotation.dart';
|
||||||
|
import 'package:pshared/utils/exception.dart';
|
||||||
|
|
||||||
|
|
||||||
class QuotationProvider extends ChangeNotifier {
|
class QuotationProvider extends ChangeNotifier {
|
||||||
@@ -36,6 +37,11 @@ class QuotationProvider extends ChangeNotifier {
|
|||||||
QuotationAutoRefreshController();
|
QuotationAutoRefreshController();
|
||||||
AutoRefreshMode _autoRefreshMode = AutoRefreshMode.on;
|
AutoRefreshMode _autoRefreshMode = AutoRefreshMode.on;
|
||||||
|
|
||||||
|
QuotationProvider() {
|
||||||
|
addListener(_handleStateChanged);
|
||||||
|
_syncAutoRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
void update(
|
void update(
|
||||||
OrganizationsProvider venue,
|
OrganizationsProvider venue,
|
||||||
PaymentAmountProvider payment,
|
PaymentAmountProvider payment,
|
||||||
@@ -53,11 +59,10 @@ class QuotationProvider extends ChangeNotifier {
|
|||||||
methods: methods,
|
methods: methods,
|
||||||
);
|
);
|
||||||
if (intent == null) return;
|
if (intent == null) return;
|
||||||
final lastIntentDto = _lastIntent?.toDTO().toJson();
|
final intentKey = _buildIntentKey(intent);
|
||||||
final isSameIntent = lastIntentDto != null &&
|
final lastIntent = _lastIntent;
|
||||||
const DeepCollectionEquality().equals(intent.toDTO().toJson(), lastIntentDto);
|
if (lastIntent != null && intentKey == _buildIntentKey(lastIntent)) return;
|
||||||
if (isSameIntent) return;
|
getQuotation(intent, idempotencyKey: intentKey);
|
||||||
getQuotation(intent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PaymentQuote? get quotation => _quotation.data;
|
PaymentQuote? get quotation => _quotation.data;
|
||||||
@@ -86,7 +91,7 @@ class QuotationProvider extends ChangeNotifier {
|
|||||||
return timeLeft <= Duration.zero;
|
return timeLeft <= Duration.zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
QuoteStatusType get quoteStatusType {
|
QuoteStatusType get quoteStatus {
|
||||||
if (isLoading) return QuoteStatusType.loading;
|
if (isLoading) return QuoteStatusType.loading;
|
||||||
if (error != null) return QuoteStatusType.error;
|
if (error != null) return QuoteStatusType.error;
|
||||||
if (quotation == null) return QuoteStatusType.missing;
|
if (quotation == null) return QuoteStatusType.missing;
|
||||||
@@ -103,34 +108,37 @@ class QuotationProvider extends ChangeNotifier {
|
|||||||
return createAsset(money.currency, money.amount);
|
return createAsset(money.currency, money.amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _handleStateChanged() {
|
||||||
|
_syncAutoRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
void _setResource(Resource<PaymentQuote> quotation) {
|
void _setResource(Resource<PaymentQuote> quotation) {
|
||||||
_quotation = quotation;
|
_quotation = quotation;
|
||||||
_syncAutoRefresh();
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAutoRefreshMode(AutoRefreshMode mode) {
|
void setAutoRefreshMode(AutoRefreshMode mode) {
|
||||||
if (_autoRefreshMode == mode) return;
|
if (_autoRefreshMode == mode) return;
|
||||||
_autoRefreshMode = mode;
|
_autoRefreshMode = mode;
|
||||||
_syncAutoRefresh();
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<PaymentQuote?> refreshQuotation() async {
|
Future<PaymentQuote?> refreshQuotation() async {
|
||||||
final intent = _lastIntent;
|
final intent = _lastIntent;
|
||||||
if (intent == null) return null;
|
if (intent == null) return null;
|
||||||
return getQuotation(intent);
|
return getQuotation(intent, idempotencyKey: _buildIntentKey(intent));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<PaymentQuote?> getQuotation(PaymentIntent intent) async {
|
Future<PaymentQuote?> getQuotation(PaymentIntent intent, {String? idempotencyKey}) async {
|
||||||
if (!_organizations.isOrganizationSet) throw StateError('Organization is not set');
|
if (!_organizations.isOrganizationSet) throw StateError('Organization is not set');
|
||||||
_lastIntent = intent;
|
_lastIntent = intent;
|
||||||
|
final intentKey = idempotencyKey ?? _buildIntentKey(intent);
|
||||||
try {
|
try {
|
||||||
_setResource(_quotation.copyWith(isLoading: true, error: null));
|
_setResource(_quotation.copyWith(isLoading: true, error: null));
|
||||||
final response = await QuotationService.getQuotation(
|
final response = await QuotationService.getQuotation(
|
||||||
_organizations.current.id,
|
_organizations.current.id,
|
||||||
QuotePaymentRequest(
|
QuotePaymentRequest(
|
||||||
idempotencyKey: Uuid().v4(),
|
idempotencyKey: intentKey,
|
||||||
intent: intent.toDTO(),
|
intent: intent.toDTO(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -140,7 +148,7 @@ class QuotationProvider extends ChangeNotifier {
|
|||||||
_logger.warning('Failed to get quotation', e, st);
|
_logger.warning('Failed to get quotation', e, st);
|
||||||
_setResource(_quotation.copyWith(
|
_setResource(_quotation.copyWith(
|
||||||
data: null,
|
data: null,
|
||||||
error: e is Exception ? e : Exception(e.toString()),
|
error: toException(e),
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -169,7 +177,13 @@ class QuotationProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
removeListener(_handleStateChanged);
|
||||||
_autoRefreshController.dispose();
|
_autoRefreshController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _buildIntentKey(PaymentIntent intent) {
|
||||||
|
final payload = jsonEncode(intent.toDTO().toJson());
|
||||||
|
return Uuid().v5(Uuid.NAMESPACE_URL, 'quote:$payload');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class _QuoteStatusState extends State<QuoteStatus> {
|
|||||||
final provider = context.watch<QuotationProvider>();
|
final provider = context.watch<QuotationProvider>();
|
||||||
final timeLeft = provider.quoteTimeLeft;
|
final timeLeft = provider.quoteTimeLeft;
|
||||||
final isLoading = provider.isLoading;
|
final isLoading = provider.isLoading;
|
||||||
final statusType = provider.quoteStatusType;
|
final statusType = provider.quoteStatus;
|
||||||
final autoRefreshMode = provider.autoRefreshMode;
|
final autoRefreshMode = provider.autoRefreshMode;
|
||||||
|
|
||||||
String statusText;
|
String statusText;
|
||||||
|
|||||||
@@ -1,11 +1,31 @@
|
|||||||
|
import 'package:duration/duration.dart';
|
||||||
|
|
||||||
String formatQuoteDuration(Duration duration) {
|
String formatQuoteDuration(Duration duration) {
|
||||||
final totalSeconds = duration.inSeconds < 0 ? 0 : duration.inSeconds;
|
final clampedDuration = duration.isNegative ? Duration.zero : duration;
|
||||||
final hours = totalSeconds ~/ 3600;
|
final pretty = prettyDuration(
|
||||||
final minutes = (totalSeconds % 3600) ~/ 60;
|
clampedDuration,
|
||||||
final seconds = totalSeconds % 60;
|
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) {
|
if (hours > 0) {
|
||||||
return '${hours.toString()}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
return '${hours.toString()}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
||||||
}
|
}
|
||||||
return '${minutes.toString()}:${seconds.toString().padLeft(2, '0')}';
|
return '${minutes.toString()}:${seconds.toString().padLeft(2, '0')}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, int> _extractHms(String pretty) {
|
||||||
|
final matches = RegExp(r'(\d+)([hms])').allMatches(pretty);
|
||||||
|
final units = <String, int>{};
|
||||||
|
for (final match in matches) {
|
||||||
|
units[match.group(2)!] = int.parse(match.group(1)!);
|
||||||
|
}
|
||||||
|
return units;
|
||||||
|
}
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ dependencies:
|
|||||||
flutter_multi_formatter: ^2.13.7
|
flutter_multi_formatter: ^2.13.7
|
||||||
dotted_border: ^3.1.0
|
dotted_border: ^3.1.0
|
||||||
qr_flutter: ^4.1.0
|
qr_flutter: ^4.1.0
|
||||||
|
duration: ^4.0.3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user