redesigned payment page + a lot of fixes

This commit is contained in:
Arseni
2026-02-21 21:55:20 +03:00
parent a68aa2abff
commit 0c6fa03aba
208 changed files with 4062 additions and 2217 deletions

View File

@@ -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();
}
}