import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; import 'package:provider/provider.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/recipient/recipient.dart'; import 'package:pshared/provider/organizations.dart'; import 'package:pshared/provider/payment/amount.dart'; import 'package:pshared/provider/payment/flow.dart'; import 'package:pshared/provider/payment/provider.dart'; import 'package:pshared/provider/payment/quotation.dart'; import 'package:pshared/provider/recipient/pmethods.dart'; import 'package:pshared/provider/recipient/provider.dart'; import 'package:pshared/models/payment/wallet.dart'; import 'package:pweb/pages/payment_methods/payment_page/body.dart'; import 'package:pshared/provider/payment/wallets.dart'; import 'package:pweb/widgets/sidebar/destinations.dart'; import 'package:pweb/services/posthog.dart'; class PaymentPage extends StatefulWidget { final ValueChanged? onBack; final PaymentType? initialPaymentType; final PayoutDestination fallbackDestination; const PaymentPage({ super.key, this.onBack, this.initialPaymentType, this.fallbackDestination = PayoutDestination.dashboard, }); @override State createState() => _PaymentPageState(); } class _PaymentPageState extends State { late final TextEditingController _searchController; late final FocusNode _searchFocusNode; @override void initState() { super.initState(); _searchController = TextEditingController(); _searchFocusNode = FocusNode(); WidgetsBinding.instance.addPostFrameCallback((_) => _initializePaymentPage()); } @override void dispose() { _searchController.dispose(); _searchFocusNode.dispose(); super.dispose(); } void _initializePaymentPage() { final methodsProvider = context.read(); _handleWalletAutoSelection(methodsProvider); } void _handleSearchChanged(String query) { context.read().setQuery(query); } void _handleRecipientSelected(BuildContext context, Recipient recipient) { final recipientProvider = context.read(); recipientProvider.setCurrentObject(recipient.id); _clearSearchField(); } void _handleRecipientCleared(BuildContext context) { final recipientProvider = context.read(); final methodsProvider = context.read(); recipientProvider.setCurrentObject(null); context.read().reset( recipient: null, availableTypes: _availablePaymentTypes(null, methodsProvider), preferredType: widget.initialPaymentType, ); _clearSearchField(); } void _clearSearchField() { _searchController.clear(); _searchFocusNode.unfocus(); context.read().setQuery(''); } void _handleSendPayment(BuildContext context) { if (context.read().isReady) { context.read().pay(); PosthogService.paymentInitiated( method: context.read().selectedType, ); } } @override Widget build(BuildContext context) { final methodsProvider = context.watch(); final recipientProvider = context.watch(); final recipient = recipientProvider.currentObject; final availableTypes = _availablePaymentTypes(recipient, methodsProvider); return MultiProvider( providers: [ ChangeNotifierProxyProvider2( create: (_) => PaymentFlowProvider( initialType: widget.initialPaymentType ?? PaymentType.bankAccount, ), update: (_, recipients, methods, flow) { final currentRecipient = recipients.currentObject; flow!.sync( recipient: currentRecipient, availableTypes: _availablePaymentTypes(currentRecipient, methods), preferredType: currentRecipient != null ? widget.initialPaymentType : null, ); return flow; }, ), ChangeNotifierProvider( create: (_) => PaymentAmountProvider(), ), ChangeNotifierProxyProvider6( create: (_) => QuotationProvider(), update: (_, organization, payment, wallet, flow, recipients, methods, provider) => provider!..update(organization, payment, wallet, flow, recipients, methods), ), ChangeNotifierProxyProvider2( create: (_) => PaymentProvider(), update: (_, organization, quotation, provider) => provider!..update(organization, quotation), ), ], child: Builder( builder: (innerContext) => PaymentPageBody( onBack: widget.onBack, fallbackDestination: widget.fallbackDestination, recipient: recipient, recipientProvider: recipientProvider, methodsProvider: methodsProvider, availablePaymentTypes: availableTypes, searchController: _searchController, searchFocusNode: _searchFocusNode, onSearchChanged: _handleSearchChanged, onRecipientSelected: (selected) => _handleRecipientSelected(innerContext, selected), onRecipientCleared: () => _handleRecipientCleared(innerContext), onSend: () => _handleSendPayment(innerContext), ), ), ); } void _handleWalletAutoSelection(PaymentMethodsProvider methodsProvider) { final wallet = context.read().selectedWallet; if (wallet == null) return; final matchingMethod = _getPaymentMethodForWallet(wallet, methodsProvider); if (matchingMethod != null) { methodsProvider.setCurrentObject(matchingMethod.id); } } MethodMap _availablePaymentTypes( Recipient? recipient, PaymentMethodsProvider methodsProvider, ) { if (recipient == null || !methodsProvider.isReady) return {}; final methodsForRecipient = methodsProvider.methods.where( (method) => !method.isArchived && method.recipientRef == recipient.id, ); return { for (final method in methodsForRecipient) method.type: method.data, }; } PaymentMethod? _getPaymentMethodForWallet( Wallet wallet, PaymentMethodsProvider methodsProvider, ) { if (methodsProvider.methods.isEmpty) { return null; } return methodsProvider.methods.firstWhereOrNull( (method) => method.type == PaymentType.wallet && (method.description?.contains(wallet.walletUserID) ?? false), ); } }