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));
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import 'package:pshared/provider/payment/flow.dart';
|
|||||||
import 'package:pshared/provider/payment/provider.dart';
|
import 'package:pshared/provider/payment/provider.dart';
|
||||||
import 'package:pshared/provider/payment/quotation/quotation.dart';
|
import 'package:pshared/provider/payment/quotation/quotation.dart';
|
||||||
import 'package:pshared/provider/recipient/provider.dart';
|
import 'package:pshared/provider/recipient/provider.dart';
|
||||||
|
import 'package:pshared/provider/recipient/methods_cache.dart';
|
||||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||||
|
|
||||||
import 'package:pweb/app/router/pages.dart';
|
import 'package:pweb/app/router/pages.dart';
|
||||||
@@ -29,6 +30,7 @@ import 'package:pweb/pages/payout_page/wallet/edit/page.dart';
|
|||||||
import 'package:pweb/pages/report/page.dart';
|
import 'package:pweb/pages/report/page.dart';
|
||||||
import 'package:pweb/pages/settings/profile/page.dart';
|
import 'package:pweb/pages/settings/profile/page.dart';
|
||||||
import 'package:pweb/pages/wallet_top_up/page.dart';
|
import 'package:pweb/pages/wallet_top_up/page.dart';
|
||||||
|
import 'package:pweb/widgets/dialogs/confirmation_dialog.dart';
|
||||||
import 'package:pweb/widgets/error/snackbar.dart';
|
import 'package:pweb/widgets/error/snackbar.dart';
|
||||||
import 'package:pweb/widgets/sidebar/destinations.dart';
|
import 'package:pweb/widgets/sidebar/destinations.dart';
|
||||||
import 'package:pweb/widgets/sidebar/page.dart';
|
import 'package:pweb/widgets/sidebar/page.dart';
|
||||||
@@ -43,6 +45,10 @@ RouteBase payoutShellRoute() => ShellRoute(
|
|||||||
create: (_) => PaymentMethodsProvider(),
|
create: (_) => PaymentMethodsProvider(),
|
||||||
update: (context, organizations, recipients, provider) => provider!..updateProviders(organizations, recipients),
|
update: (context, organizations, recipients, provider) => provider!..updateProviders(organizations, recipients),
|
||||||
),
|
),
|
||||||
|
ChangeNotifierProxyProvider2<OrganizationsProvider, RecipientsProvider, RecipientMethodsCacheProvider>(
|
||||||
|
create: (_) => RecipientMethodsCacheProvider(),
|
||||||
|
update: (context, organizations, recipients, provider) => provider!..updateProviders(organizations, recipients),
|
||||||
|
),
|
||||||
ChangeNotifierProxyProvider2<RecipientsProvider, PaymentMethodsProvider, PaymentFlowProvider>(
|
ChangeNotifierProxyProvider2<RecipientsProvider, PaymentMethodsProvider, PaymentFlowProvider>(
|
||||||
create: (_) => PaymentFlowProvider(initialType: PaymentType.bankAccount),
|
create: (_) => PaymentFlowProvider(initialType: PaymentType.bankAccount),
|
||||||
update: (context, recipients, methods, provider) => provider!..update(
|
update: (context, recipients, methods, provider) => provider!..update(
|
||||||
@@ -118,24 +124,13 @@ RouteBase payoutShellRoute() => ShellRoute(
|
|||||||
recipient: recipient,
|
recipient: recipient,
|
||||||
),
|
),
|
||||||
onDeleteRecipient: (recipient) async {
|
onDeleteRecipient: (recipient) async {
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (dialogContext) => AlertDialog(
|
title: loc.delete,
|
||||||
title: Text(loc.delete),
|
message: loc.deleteRecipientConfirmation,
|
||||||
content: Text(loc.deleteRecipientConfirmation),
|
confirmLabel: loc.delete,
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(dialogContext, false),
|
|
||||||
child: Text(loc.cancel),
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () => Navigator.pop(dialogContext, true),
|
|
||||||
child: Text(loc.delete),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
if (confirmed != true) return;
|
if (!confirmed) return;
|
||||||
await executeActionWithNotification(
|
await executeActionWithNotification(
|
||||||
context: context,
|
context: context,
|
||||||
action: () async =>
|
action: () async =>
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import 'package:pshared/models/payment/type.dart';
|
|||||||
|
|
||||||
import 'package:pweb/pages/payment_methods/form.dart';
|
import 'package:pweb/pages/payment_methods/form.dart';
|
||||||
import 'package:pweb/pages/payment_methods/icon.dart';
|
import 'package:pweb/pages/payment_methods/icon.dart';
|
||||||
|
import 'package:pweb/widgets/dialogs/confirmation_dialog.dart';
|
||||||
|
|
||||||
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
class AddressBookPaymentMethodTile extends StatefulWidget {
|
class AddressBookPaymentMethodTile extends StatefulWidget {
|
||||||
@@ -35,6 +38,19 @@ class AddressBookPaymentMethodTile extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _AddressBookPaymentMethodTileState extends State<AddressBookPaymentMethodTile> {
|
class _AddressBookPaymentMethodTileState extends State<AddressBookPaymentMethodTile> {
|
||||||
|
Future<void> _confirmDelete() async {
|
||||||
|
final l10n = AppLocalizations.of(context)!;
|
||||||
|
final confirmed = await showConfirmationDialog(
|
||||||
|
context: context,
|
||||||
|
title: l10n.delete,
|
||||||
|
message: l10n.deletePaymentConfirmation,
|
||||||
|
confirmLabel: l10n.delete,
|
||||||
|
);
|
||||||
|
if (confirmed) {
|
||||||
|
widget.onChanged(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
@@ -67,9 +83,7 @@ class _AddressBookPaymentMethodTileState extends State<AddressBookPaymentMethodT
|
|||||||
if (isAdded)
|
if (isAdded)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.delete, color: theme.colorScheme.error),
|
icon: Icon(Icons.delete, color: theme.colorScheme.error),
|
||||||
onPressed: () {
|
onPressed: _confirmDelete,
|
||||||
widget.onChanged(null);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
Icon(
|
Icon(
|
||||||
isAdded ? Icons.check_circle : Icons.add_circle_outline,
|
isAdded ? Icons.check_circle : Icons.add_circle_outline,
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/payment/methods/data.dart';
|
import 'package:pshared/models/payment/methods/data.dart';
|
||||||
@@ -11,8 +9,7 @@ import 'package:pshared/models/payment/type.dart';
|
|||||||
import 'package:pshared/models/recipient/recipient.dart';
|
import 'package:pshared/models/recipient/recipient.dart';
|
||||||
import 'package:pshared/models/recipient/status.dart';
|
import 'package:pshared/models/recipient/status.dart';
|
||||||
import 'package:pshared/models/recipient/type.dart';
|
import 'package:pshared/models/recipient/type.dart';
|
||||||
import 'package:pshared/provider/organizations.dart';
|
import 'package:pshared/provider/recipient/methods_cache.dart';
|
||||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
|
||||||
import 'package:pshared/provider/recipient/provider.dart';
|
import 'package:pshared/provider/recipient/provider.dart';
|
||||||
|
|
||||||
import 'package:pweb/pages/address_book/form/view.dart';
|
import 'package:pweb/pages/address_book/form/view.dart';
|
||||||
@@ -41,27 +38,8 @@ class _AddressBookRecipientFormState extends State<AddressBookRecipientForm> {
|
|||||||
RecipientType _type = RecipientType.internal;
|
RecipientType _type = RecipientType.internal;
|
||||||
RecipientStatus _status = RecipientStatus.ready;
|
RecipientStatus _status = RecipientStatus.ready;
|
||||||
final MethodMap _methods = {};
|
final MethodMap _methods = {};
|
||||||
late PaymentMethodsProvider _methodsProvider;
|
late RecipientMethodsCacheProvider _methodsCacheProvider;
|
||||||
|
bool _hasInitializedMethods = false;
|
||||||
Future<void> _loadMethods() async {
|
|
||||||
_methodsProvider = PaymentMethodsProvider()..addListener(_onProviderChanged);
|
|
||||||
await _methodsProvider.loadMethods(
|
|
||||||
context.read<OrganizationsProvider>(),
|
|
||||||
widget.recipient?.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
for (final m in _methodsProvider.methods) {
|
|
||||||
_methods[m.type] = switch (m.type) {
|
|
||||||
PaymentType.card => m.cardData,
|
|
||||||
PaymentType.iban => m.ibanData,
|
|
||||||
PaymentType.wallet => m.walletData,
|
|
||||||
PaymentType.bankAccount => m.bankAccountData,
|
|
||||||
PaymentType.externalChain => m.cryptoAddressData,
|
|
||||||
//TODO: support new payment methods
|
|
||||||
_ => throw UnimplementedError('Payment method ${m.type} is not supported yet'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -71,12 +49,16 @@ class _AddressBookRecipientFormState extends State<AddressBookRecipientForm> {
|
|||||||
_emailCtrl = TextEditingController(text: r?.email ?? '');
|
_emailCtrl = TextEditingController(text: r?.email ?? '');
|
||||||
_type = r?.type ?? RecipientType.internal;
|
_type = r?.type ?? RecipientType.internal;
|
||||||
_status = r?.status ?? RecipientStatus.ready;
|
_status = r?.status ?? RecipientStatus.ready;
|
||||||
_loadMethods();
|
_methodsCacheProvider = context.read<RecipientMethodsCacheProvider>()
|
||||||
|
..addListener(_onProviderChanged);
|
||||||
|
if (r != null) {
|
||||||
|
_methodsCacheProvider.refreshRecipient(r.id);
|
||||||
|
_syncMethodsFromCache();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Recipient?> _doSave() async {
|
Future<Recipient?> _doSave() async {
|
||||||
final recipients = context.read<RecipientsProvider>();
|
final recipients = context.read<RecipientsProvider>();
|
||||||
final methods = PaymentMethodsProvider();
|
|
||||||
final recipient = widget.recipient == null
|
final recipient = widget.recipient == null
|
||||||
? await recipients.create(
|
? await recipients.create(
|
||||||
name: _nameCtrl.text,
|
name: _nameCtrl.text,
|
||||||
@@ -84,25 +66,22 @@ class _AddressBookRecipientFormState extends State<AddressBookRecipientForm> {
|
|||||||
)
|
)
|
||||||
: widget.recipient!;
|
: widget.recipient!;
|
||||||
recipients.setCurrentObject(recipient.id);
|
recipients.setCurrentObject(recipient.id);
|
||||||
//TODO : redesign work with recipients / payment methods
|
final methods = <PaymentType, PaymentMethodData>{};
|
||||||
await methods.loadMethods(context.read<OrganizationsProvider>(), recipient.id);
|
final names = <PaymentType, String>{};
|
||||||
for (final type in _methods.keys) {
|
for (final entry in _methods.entries) {
|
||||||
final data = _methods[type]!;
|
final data = entry.value;
|
||||||
final exising = methods.methods.firstWhereOrNull((m) => m.type == type);
|
if (data == null) continue;
|
||||||
if (exising != null) {
|
methods[entry.key] = data;
|
||||||
await methods.updateMethod(exising.copyWith(data: data));
|
names[entry.key] = getPaymentTypeLabel(context, entry.key);
|
||||||
} else {
|
|
||||||
await methods.create(
|
|
||||||
reacipientRef: recipient.id,
|
|
||||||
name: getPaymentTypeLabel(context, type),
|
|
||||||
data: data,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
await _methodsCacheProvider.syncRecipientMethods(
|
||||||
|
recipientId: recipient.id,
|
||||||
|
methods: methods,
|
||||||
|
names: names,
|
||||||
|
);
|
||||||
return recipient;
|
return recipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Change when registration is ready
|
|
||||||
Future<void> _save() async {
|
Future<void> _save() async {
|
||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
@@ -134,12 +113,39 @@ class _AddressBookRecipientFormState extends State<AddressBookRecipientForm> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_methodsProvider.removeListener(_onProviderChanged);
|
_methodsCacheProvider.removeListener(_onProviderChanged);
|
||||||
_methodsProvider.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onProviderChanged() => setState(() {});
|
void _onProviderChanged() => _syncMethodsFromCache();
|
||||||
|
|
||||||
|
void _syncMethodsFromCache() {
|
||||||
|
final recipient = widget.recipient;
|
||||||
|
if (recipient == null || _hasInitializedMethods) return;
|
||||||
|
if (!_methodsCacheProvider.hasMethodsFor(recipient.id)) return;
|
||||||
|
final list = _methodsCacheProvider.methodsForRecipient(recipient.id);
|
||||||
|
if (list.isEmpty) {
|
||||||
|
_hasInitializedMethods = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_methods
|
||||||
|
..clear()
|
||||||
|
..addEntries(list.map((m) {
|
||||||
|
final data = switch (m.type) {
|
||||||
|
PaymentType.card => m.cardData,
|
||||||
|
PaymentType.iban => m.ibanData,
|
||||||
|
PaymentType.wallet => m.walletData,
|
||||||
|
PaymentType.bankAccount => m.bankAccountData,
|
||||||
|
PaymentType.externalChain => m.cryptoAddressData,
|
||||||
|
//TODO: support new payment methods
|
||||||
|
_ => throw UnimplementedError('Payment method ${m.type} is not supported yet'),
|
||||||
|
};
|
||||||
|
return MapEntry(m.type, data);
|
||||||
|
}));
|
||||||
|
_hasInitializedMethods = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => FormView(
|
Widget build(BuildContext context) => FormView(
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class OwnerField extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => DropdownButtonFormField<String?>(
|
Widget build(BuildContext context) => DropdownButtonFormField<String?>(
|
||||||
value: value,
|
initialValue: value,
|
||||||
decoration: getInputDecoration(context, AppLocalizations.of(context)!.assetOwner, true),
|
decoration: getInputDecoration(context, AppLocalizations.of(context)!.assetOwner, true),
|
||||||
items: items,
|
items: items,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:pshared/models/payment/methods/type.dart';
|
|||||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||||
|
|
||||||
import 'package:pweb/pages/payment_methods/add/widget.dart';
|
import 'package:pweb/pages/payment_methods/add/widget.dart';
|
||||||
|
import 'package:pweb/widgets/dialogs/confirmation_dialog.dart';
|
||||||
|
|
||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
@@ -28,25 +29,13 @@ class PaymentConfigController {
|
|||||||
Future<void> deleteMethod(PaymentMethod method) async {
|
Future<void> deleteMethod(PaymentMethod method) async {
|
||||||
final methodsProvider = context.read<PaymentMethodsProvider>();
|
final methodsProvider = context.read<PaymentMethodsProvider>();
|
||||||
final l10n = AppLocalizations.of(context)!;
|
final l10n = AppLocalizations.of(context)!;
|
||||||
final confirmed = await showDialog<bool>(
|
final confirmed = await showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (dialogContext) => AlertDialog(
|
title: l10n.delete,
|
||||||
title: Text(l10n.delete),
|
message: l10n.deletePaymentConfirmation,
|
||||||
content: Text(l10n.deletePaymentConfirmation),
|
confirmLabel: l10n.delete,
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(dialogContext, false),
|
|
||||||
child: Text(l10n.cancel),
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () => Navigator.pop(dialogContext, true),
|
|
||||||
child: Text(l10n.delete),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
if (confirmed) {
|
||||||
if (confirmed == true) {
|
|
||||||
methodsProvider.delete(method.id);
|
methodsProvider.delete(method.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
33
frontend/pweb/lib/widgets/dialogs/confirmation_dialog.dart
Normal file
33
frontend/pweb/lib/widgets/dialogs/confirmation_dialog.dart
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
|
Future<bool> showConfirmationDialog({
|
||||||
|
required BuildContext context,
|
||||||
|
required String title,
|
||||||
|
required String message,
|
||||||
|
String? confirmLabel,
|
||||||
|
String? cancelLabel,
|
||||||
|
}) async {
|
||||||
|
final l10n = AppLocalizations.of(context)!;
|
||||||
|
final confirmed = await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (dialogContext) => AlertDialog(
|
||||||
|
title: Text(title),
|
||||||
|
content: Text(message),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(dialogContext, false),
|
||||||
|
child: Text(cancelLabel ?? l10n.cancel),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => Navigator.pop(dialogContext, true),
|
||||||
|
child: Text(confirmLabel ?? l10n.confirm),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return confirmed == true;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user