redesigned payment page + a lot of fixes
This commit is contained in:
@@ -10,6 +10,8 @@ import 'package:pshared/utils/exception.dart';
|
||||
|
||||
|
||||
class PaymentsProvider with ChangeNotifier {
|
||||
static const Duration _pendingRefreshInterval = Duration(seconds: 10);
|
||||
|
||||
OrganizationsProvider? _organizations;
|
||||
String? _loadedOrganizationRef;
|
||||
|
||||
@@ -17,15 +19,14 @@ class PaymentsProvider with ChangeNotifier {
|
||||
bool _isLoaded = false;
|
||||
bool _isLoadingMore = false;
|
||||
String? _nextCursor;
|
||||
Timer? _autoRefreshTimer;
|
||||
int _autoRefreshRefs = 0;
|
||||
Duration _autoRefreshInterval = const Duration(seconds: 15);
|
||||
int? _limit;
|
||||
String? _sourceRef;
|
||||
String? _destinationRef;
|
||||
List<String>? _states;
|
||||
|
||||
int _opSeq = 0;
|
||||
Timer? _pendingRefreshTimer;
|
||||
bool _isPendingRefreshInFlight = false;
|
||||
|
||||
Resource<List<Payment>> get resource => _resource;
|
||||
List<Payment> get payments => _resource.data ?? [];
|
||||
@@ -37,23 +38,6 @@ class PaymentsProvider with ChangeNotifier {
|
||||
String? get nextCursor => _nextCursor;
|
||||
bool get canLoadMore => _nextCursor != null && _nextCursor!.isNotEmpty;
|
||||
|
||||
void beginAutoRefresh({Duration interval = const Duration(seconds: 15)}) {
|
||||
_autoRefreshRefs += 1;
|
||||
if (interval < _autoRefreshInterval) {
|
||||
_autoRefreshInterval = interval;
|
||||
_restartAutoRefreshTimer();
|
||||
}
|
||||
_ensureAutoRefreshTimer();
|
||||
}
|
||||
|
||||
void endAutoRefresh() {
|
||||
if (_autoRefreshRefs == 0) return;
|
||||
_autoRefreshRefs -= 1;
|
||||
if (_autoRefreshRefs == 0) {
|
||||
_stopAutoRefreshTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void update(OrganizationsProvider organizations) {
|
||||
_organizations = organizations;
|
||||
if (!organizations.isOrganizationSet) {
|
||||
@@ -61,10 +45,6 @@ class PaymentsProvider with ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_autoRefreshRefs > 0) {
|
||||
_ensureAutoRefreshTimer();
|
||||
}
|
||||
|
||||
final orgRef = organizations.current.id;
|
||||
if (_loadedOrganizationRef != orgRef) {
|
||||
_loadedOrganizationRef = orgRef;
|
||||
@@ -104,6 +84,30 @@ class PaymentsProvider with ChangeNotifier {
|
||||
);
|
||||
}
|
||||
|
||||
void mergePayments(List<Payment> incoming) {
|
||||
if (incoming.isEmpty) return;
|
||||
final existing = List<Payment>.from(_resource.data ?? const []);
|
||||
final combined = <Payment>[
|
||||
...incoming,
|
||||
...existing,
|
||||
];
|
||||
final seen = <String>{};
|
||||
final merged = <Payment>[];
|
||||
|
||||
for (final payment in combined) {
|
||||
final key = _paymentKey(payment);
|
||||
if (key == null || key.isEmpty) {
|
||||
merged.add(payment);
|
||||
continue;
|
||||
}
|
||||
if (seen.contains(key)) continue;
|
||||
seen.add(key);
|
||||
merged.add(payment);
|
||||
}
|
||||
|
||||
_applyResource(_resource.copyWith(data: merged), notify: true);
|
||||
}
|
||||
|
||||
Future<void> _refresh({
|
||||
int? limit,
|
||||
String? sourceRef,
|
||||
@@ -224,12 +228,13 @@ class PaymentsProvider with ChangeNotifier {
|
||||
_destinationRef = null;
|
||||
_states = null;
|
||||
_resource = Resource(data: []);
|
||||
_pauseAutoRefreshTimer();
|
||||
_stopPendingRefreshTimer();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _applyResource(Resource<List<Payment>> newResource, {required bool notify}) {
|
||||
_resource = newResource;
|
||||
_syncPendingRefresh();
|
||||
if (notify) notifyListeners();
|
||||
}
|
||||
|
||||
@@ -239,6 +244,12 @@ class PaymentsProvider with ChangeNotifier {
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
String? _paymentKey(Payment payment) {
|
||||
final ref = _normalize(payment.paymentRef);
|
||||
if (ref != null) return ref;
|
||||
return _normalize(payment.idempotencyKey);
|
||||
}
|
||||
|
||||
List<String>? _normalizeStates(List<String>? states) {
|
||||
if (states == null || states.isEmpty) return null;
|
||||
final normalized = states
|
||||
@@ -249,31 +260,70 @@ class PaymentsProvider with ChangeNotifier {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
|
||||
void _ensureAutoRefreshTimer() {
|
||||
if (_autoRefreshTimer != null) return;
|
||||
_autoRefreshTimer = Timer.periodic(_autoRefreshInterval, (_) {
|
||||
if (_resource.isLoading || _isLoadingMore) return;
|
||||
unawaited(refreshSilently());
|
||||
});
|
||||
void _syncPendingRefresh() {
|
||||
final hasPending = payments.any(_isPending);
|
||||
if (!hasPending) {
|
||||
_stopPendingRefreshTimer();
|
||||
return;
|
||||
}
|
||||
_ensurePendingRefreshTimer();
|
||||
}
|
||||
|
||||
void _restartAutoRefreshTimer() {
|
||||
if (_autoRefreshTimer == null) return;
|
||||
_autoRefreshTimer?.cancel();
|
||||
_autoRefreshTimer = null;
|
||||
_ensureAutoRefreshTimer();
|
||||
void _ensurePendingRefreshTimer() {
|
||||
if (_pendingRefreshTimer != null) return;
|
||||
_pendingRefreshTimer = Timer.periodic(
|
||||
_pendingRefreshInterval,
|
||||
(_) => _refreshPending(),
|
||||
);
|
||||
_refreshPending();
|
||||
}
|
||||
|
||||
void _stopAutoRefreshTimer() {
|
||||
_autoRefreshTimer?.cancel();
|
||||
_autoRefreshTimer = null;
|
||||
_autoRefreshRefs = 0;
|
||||
_autoRefreshInterval = const Duration(seconds: 15);
|
||||
Future<void> _refreshPending() async {
|
||||
if (_isPendingRefreshInFlight) return;
|
||||
if (isLoading || isLoadingMore) return;
|
||||
|
||||
_isPendingRefreshInFlight = true;
|
||||
try {
|
||||
await refreshSilently(
|
||||
limit: _limit,
|
||||
sourceRef: _sourceRef,
|
||||
destinationRef: _destinationRef,
|
||||
states: _states,
|
||||
);
|
||||
} finally {
|
||||
_isPendingRefreshInFlight = false;
|
||||
}
|
||||
}
|
||||
|
||||
void _pauseAutoRefreshTimer() {
|
||||
_autoRefreshTimer?.cancel();
|
||||
_autoRefreshTimer = null;
|
||||
void _stopPendingRefreshTimer() {
|
||||
_pendingRefreshTimer?.cancel();
|
||||
_pendingRefreshTimer = null;
|
||||
_isPendingRefreshInFlight = false;
|
||||
}
|
||||
|
||||
bool _isPending(Payment payment) {
|
||||
final raw = payment.state;
|
||||
final trimmed = (raw ?? '').trim().toUpperCase();
|
||||
final normalized = trimmed.startsWith('PAYMENT_STATE_')
|
||||
? trimmed.substring('PAYMENT_STATE_'.length)
|
||||
: trimmed;
|
||||
|
||||
switch (normalized) {
|
||||
case 'SUCCESS':
|
||||
case 'FAILED':
|
||||
case 'CANCELLED':
|
||||
return false;
|
||||
case 'PROCESSING':
|
||||
return true;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_stopPendingRefreshTimer();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user