import 'dart:async'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:pshared/models/payment/methods/data.dart'; import 'package:pshared/models/payment/type.dart'; import 'package:pshared/models/recipient/recipient.dart'; import 'package:pshared/models/recipient/status.dart'; import 'package:pshared/models/recipient/type.dart'; import 'package:pshared/provider/recipient/methods_cache.dart'; import 'package:pshared/provider/recipient/provider.dart'; import 'package:pweb/pages/address_book/form/view.dart'; import 'package:pweb/services/posthog.dart'; import 'package:pweb/utils/error/snackbar.dart'; import 'package:pweb/utils/payment/label.dart'; import 'package:pweb/utils/snackbar.dart'; import 'package:pweb/generated/i18n/app_localizations.dart'; class AddressBookRecipientForm extends StatefulWidget { final Recipient? recipient; final ValueChanged? onSaved; const AddressBookRecipientForm({super.key, this.recipient, this.onSaved}); @override State createState() => _AddressBookRecipientFormState(); } class _AddressBookRecipientFormState extends State { final _formKey = GlobalKey(); late TextEditingController _nameCtrl; late TextEditingController _emailCtrl; RecipientType _type = RecipientType.internal; RecipientStatus _status = RecipientStatus.ready; final MethodMap _methods = {}; late RecipientMethodsCacheProvider _methodsCacheProvider; bool _hasInitializedMethods = false; @override void initState() { super.initState(); final r = widget.recipient; _nameCtrl = TextEditingController(text: r?.name ?? ''); _emailCtrl = TextEditingController(text: r?.email ?? ''); _type = r?.type ?? RecipientType.internal; _status = r?.status ?? RecipientStatus.ready; _methodsCacheProvider = context.read() ..addListener(_onProviderChanged); if (r != null) { _methodsCacheProvider.refreshRecipient(r.id); _syncMethodsFromCache(); } } Future _doSave() async { final recipients = context.read(); final recipient = widget.recipient == null ? await recipients.create( name: _nameCtrl.text, email: _emailCtrl.text, ) : widget.recipient!; recipients.setCurrentObject(recipient.id); final methods = {}; final names = {}; for (final entry in _methods.entries) { final data = entry.value; if (data == null) continue; methods[entry.key] = data; names[entry.key] = getPaymentTypeLabel(context, entry.key); } await _methodsCacheProvider.syncRecipientMethods( recipientId: recipient.id, methods: methods, names: names, ); return recipient; } Future _save() async { final l10n = AppLocalizations.of(context)!; if (!_formKey.currentState!.validate()) { notifyUser(context, l10n.recipientFormValidationError); return; } if (_methods.isEmpty) { notifyUser(context, l10n.recipientFormRule); return; } unawaited(PosthogService.recipientAddCompleted( _type, _status, _methods.keys.toSet(), )); final recipient = await executeActionWithNotification( context: context, action: _doSave, errorMessage: l10n.errorSaveRecipient, successMessage: l10n.recipientSavedSuccessfully, ); widget.onSaved?.call(recipient); } @override void dispose() { _methodsCacheProvider.removeListener(_onProviderChanged); super.dispose(); } 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 Widget build(BuildContext context) => FormView( formKey: _formKey, nameCtrl: _nameCtrl, emailCtrl: _emailCtrl, type: _type, status: _status, methods: _methods, onTypeChanged: (t) => setState(() => _type = t), onStatusChanged: (s) => setState(() => _status = s), onMethodsChanged: (type, data) { setState(() { if (data != null) { _methods[type] = data; } else { _methods.remove(type); } }); }, onSave: _save, isEditing: widget.recipient != null, onBack: () { widget.onSaved?.call(null); }, ); }