Fixed payment method deletion in recipient settings with better flow
This commit is contained in:
146
frontend/pshared/lib/provider/recipient/methods_cache.dart
Normal file
146
frontend/pshared/lib/provider/recipient/methods_cache.dart
Normal file
@@ -0,0 +1,146 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:pshared/models/describable.dart';
|
||||
import 'package:pshared/models/organization/bound.dart';
|
||||
import 'package:pshared/models/payment/methods/data.dart';
|
||||
import 'package:pshared/models/payment/methods/type.dart';
|
||||
import 'package:pshared/models/payment/type.dart';
|
||||
import 'package:pshared/models/permissions/bound.dart';
|
||||
import 'package:pshared/models/storable.dart';
|
||||
import 'package:pshared/provider/organizations.dart';
|
||||
import 'package:pshared/provider/recipient/provider.dart';
|
||||
import 'package:pshared/service/recipient/pmethods.dart';
|
||||
import 'package:pshared/utils/exception.dart';
|
||||
|
||||
|
||||
class RecipientMethodsCacheProvider extends ChangeNotifier {
|
||||
late OrganizationsProvider _organizations;
|
||||
|
||||
final Map<String, List<PaymentMethod>> _methodsByRecipient = {};
|
||||
final Map<String, bool> _loadingByRecipient = {};
|
||||
final Map<String, Object?> _errorByRecipient = {};
|
||||
Set<String> _trackedRecipients = {};
|
||||
|
||||
List<PaymentMethod> methodsForRecipient(String recipientId) =>
|
||||
List<PaymentMethod>.unmodifiable(_methodsByRecipient[recipientId] ?? const []);
|
||||
|
||||
bool isLoadingFor(String recipientId) => _loadingByRecipient[recipientId] == true;
|
||||
Object? errorFor(String recipientId) => _errorByRecipient[recipientId];
|
||||
bool hasMethodsFor(String recipientId) => _methodsByRecipient.containsKey(recipientId);
|
||||
bool isTrackingRecipient(String recipientId) => _trackedRecipients.contains(recipientId);
|
||||
|
||||
void updateProviders(OrganizationsProvider organizations, RecipientsProvider recipients) {
|
||||
_organizations = organizations;
|
||||
if (!_organizations.isOrganizationSet) return;
|
||||
|
||||
final nextRecipients = recipients.items.map((r) => r.id).toSet();
|
||||
if (setEquals(_trackedRecipients, nextRecipients)) return;
|
||||
|
||||
final removed = _trackedRecipients.difference(nextRecipients);
|
||||
for (final recipientId in removed) {
|
||||
_methodsByRecipient.remove(recipientId);
|
||||
_loadingByRecipient.remove(recipientId);
|
||||
_errorByRecipient.remove(recipientId);
|
||||
}
|
||||
|
||||
final added = nextRecipients.difference(_trackedRecipients);
|
||||
_trackedRecipients = nextRecipients;
|
||||
|
||||
for (final recipientId in added) {
|
||||
unawaited(_loadRecipientMethods(recipientId));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> refreshRecipient(String recipientId) => _loadRecipientMethods(recipientId, force: true);
|
||||
|
||||
Future<void> syncRecipientMethods({
|
||||
required String recipientId,
|
||||
required Map<PaymentType, PaymentMethodData> methods,
|
||||
required Map<PaymentType, String> names,
|
||||
}) async {
|
||||
await _ensureLoaded(recipientId);
|
||||
final current = List<PaymentMethod>.from(_methodsByRecipient[recipientId] ?? const []);
|
||||
final currentByType = {for (final method in current) method.type: method};
|
||||
|
||||
for (final entry in currentByType.entries) {
|
||||
if (!methods.containsKey(entry.key)) {
|
||||
await PaymentMethodService.delete(entry.value);
|
||||
current.removeWhere((method) => method.id == entry.value.id);
|
||||
}
|
||||
}
|
||||
|
||||
for (final entry in methods.entries) {
|
||||
final type = entry.key;
|
||||
final data = entry.value;
|
||||
final existing = currentByType[type];
|
||||
if (existing != null) {
|
||||
final updated = existing.copyWith(data: data);
|
||||
final updatedList = await PaymentMethodService.update(updated);
|
||||
final updatedMethod = updatedList.firstWhereOrNull((m) => m.id == updated.id) ?? updated;
|
||||
final index = current.indexWhere((m) => m.id == updatedMethod.id);
|
||||
if (index != -1) {
|
||||
current[index] = updatedMethod;
|
||||
}
|
||||
} else {
|
||||
final created = await _createMethod(
|
||||
recipientId: recipientId,
|
||||
data: data,
|
||||
name: names[type] ?? type.name,
|
||||
);
|
||||
current.add(created);
|
||||
}
|
||||
}
|
||||
|
||||
_methodsByRecipient[recipientId] = _sortedMethods(current);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> _ensureLoaded(String recipientId) async {
|
||||
if (_methodsByRecipient.containsKey(recipientId)) return;
|
||||
await _loadRecipientMethods(recipientId);
|
||||
}
|
||||
|
||||
Future<void> _loadRecipientMethods(String recipientId, {bool force = false}) async {
|
||||
if (!force && _loadingByRecipient[recipientId] == true) return;
|
||||
_setLoading(recipientId, true);
|
||||
try {
|
||||
final list = await PaymentMethodService.list(_organizations.current.id, recipientId);
|
||||
_methodsByRecipient[recipientId] = _sortedMethods(list);
|
||||
_errorByRecipient.remove(recipientId);
|
||||
} catch (e) {
|
||||
_errorByRecipient[recipientId] = toException(e);
|
||||
} finally {
|
||||
_setLoading(recipientId, false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<PaymentMethod> _createMethod({
|
||||
required String recipientId,
|
||||
required PaymentMethodData data,
|
||||
required String name,
|
||||
}) async {
|
||||
final organizationRef = _organizations.current.id;
|
||||
final method = PaymentMethod(
|
||||
storable: newStorable(),
|
||||
permissionBound: newPermissionBound(
|
||||
organizationBound: newOrganizationBound(organizationRef: organizationRef),
|
||||
),
|
||||
recipientRef: recipientId,
|
||||
data: data,
|
||||
describable: newDescribable(name: name),
|
||||
);
|
||||
final created = await PaymentMethodService.create(organizationRef, method);
|
||||
return created.first;
|
||||
}
|
||||
|
||||
void _setLoading(String recipientId, bool value) {
|
||||
_loadingByRecipient[recipientId] = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<PaymentMethod> _sortedMethods(List<PaymentMethod> methods) =>
|
||||
methods.toList()..sort((a, b) => a.storable.createdAt.compareTo(b.storable.createdAt));
|
||||
}
|
||||
Reference in New Issue
Block a user