From 64ad8c8b38d17848ff9c7960853631e132403d91 Mon Sep 17 00:00:00 2001 From: Arseni Date: Mon, 8 Dec 2025 17:40:25 +0300 Subject: [PATCH] Navigation now flows entirely through go_router --- .../pweb/lib/app/router/payout_routes.dart | 67 ++++- .../pweb/lib/app/router/payout_shell.dart | 164 ++++++++--- frontend/pweb/lib/main.dart | 7 - .../payment_methods/delete_confirmation.dart | 25 -- .../pweb/lib/pages/payment_methods/page.dart | 127 +++++++-- .../payment_methods/payment_details.dart | 33 --- .../payment_page/back_button.dart | 41 +++ .../payment_methods/payment_page/body.dart | 74 +++++ .../content.dart} | 74 ++--- .../{ => payment_page}/header.dart | 0 .../{ => payment_page}/method_selector.dart | 0 .../payment_methods/payment_page/page.dart | 120 ++++++++ .../{ => payment_page}/send_button.dart | 0 .../widgets/payment_info_section.dart | 17 +- .../payment_methods/widgets/state_view.dart | 21 ++ .../payout_page/wallet/edit/buttons/send.dart | 11 +- .../wallet/edit/buttons/top_up.dart | 5 +- .../pweb/lib/providers/page_selector.dart | 264 ------------------ frontend/pweb/lib/providers/payment_flow.dart | 110 ++++++++ .../lib/providers/payment_flow_provider.dart | 79 ------ frontend/pweb/lib/widgets/sidebar/page.dart | 16 +- 21 files changed, 705 insertions(+), 550 deletions(-) delete mode 100644 frontend/pweb/lib/pages/payment_methods/delete_confirmation.dart delete mode 100644 frontend/pweb/lib/pages/payment_methods/payment_details.dart create mode 100644 frontend/pweb/lib/pages/payment_methods/payment_page/back_button.dart create mode 100644 frontend/pweb/lib/pages/payment_methods/payment_page/body.dart rename frontend/pweb/lib/pages/payment_methods/{widgets/payment_page_body.dart => payment_page/content.dart} (72%) rename frontend/pweb/lib/pages/payment_methods/{ => payment_page}/header.dart (100%) rename frontend/pweb/lib/pages/payment_methods/{ => payment_page}/method_selector.dart (100%) create mode 100644 frontend/pweb/lib/pages/payment_methods/payment_page/page.dart rename frontend/pweb/lib/pages/payment_methods/{ => payment_page}/send_button.dart (100%) create mode 100644 frontend/pweb/lib/pages/payment_methods/widgets/state_view.dart delete mode 100644 frontend/pweb/lib/providers/page_selector.dart create mode 100644 frontend/pweb/lib/providers/payment_flow.dart delete mode 100644 frontend/pweb/lib/providers/payment_flow_provider.dart diff --git a/frontend/pweb/lib/app/router/payout_routes.dart b/frontend/pweb/lib/app/router/payout_routes.dart index 43690b1..9334dcf 100644 --- a/frontend/pweb/lib/app/router/payout_routes.dart +++ b/frontend/pweb/lib/app/router/payout_routes.dart @@ -1,6 +1,8 @@ import 'package:flutter/widgets.dart'; +import 'package:collection/collection.dart'; import 'package:go_router/go_router.dart'; +import 'package:pshared/models/payment/type.dart'; import 'package:pweb/widgets/sidebar/destinations.dart'; @@ -17,6 +19,9 @@ class PayoutRoutes { static const editWallet = 'payout-edit-wallet'; static const walletTopUp = 'payout-wallet-top-up'; + static const paymentTypeQuery = 'paymentType'; + static const returnToQuery = 'returnTo'; + static const dashboardPath = '/dashboard'; static const recipientsPath = '/dashboard/recipients'; static const addRecipientPath = '/dashboard/recipients/add'; @@ -103,10 +108,70 @@ class PayoutRoutes { return null; } } + + static Map buildQueryParameters({ + PaymentType? paymentType, + PayoutDestination? returnTo, + }) { + final params = { + if (paymentType != null) paymentTypeQuery: paymentType.name, + if (returnTo != null) returnToQuery: nameFor(returnTo), + }; + return params; + } + + static PaymentType? paymentTypeFromState(GoRouterState state) => + paymentTypeFromRaw(state.uri.queryParameters[paymentTypeQuery]); + + static PaymentType? paymentTypeFromRaw(String? raw) => raw == null + ? null + : PaymentType.values.firstWhereOrNull((type) => type.name == raw); + + static PayoutDestination fallbackFromState( + GoRouterState state, { + PayoutDestination defaultDestination = PayoutDestination.dashboard, + }) { + final raw = state.uri.queryParameters[returnToQuery]; + return destinationFor(raw) ?? defaultDestination; + } } extension PayoutNavigation on BuildContext { void goToPayout(PayoutDestination destination) => goNamed(PayoutRoutes.nameFor(destination)); void pushToPayout(PayoutDestination destination) => pushNamed(PayoutRoutes.nameFor(destination)); -} + + void goToPayment({ + PaymentType? paymentType, + PayoutDestination? returnTo, + }) => + goNamed( + PayoutRoutes.payment, + queryParameters: PayoutRoutes.buildQueryParameters( + paymentType: paymentType, + returnTo: returnTo, + ), + ); + + void pushToPayment({ + PaymentType? paymentType, + PayoutDestination? returnTo, + }) => + pushNamed( + PayoutRoutes.payment, + queryParameters: PayoutRoutes.buildQueryParameters( + paymentType: paymentType, + returnTo: returnTo, + ), + ); + + void pushToWalletTopUp({PayoutDestination? returnTo}) => pushNamed( + PayoutRoutes.walletTopUp, + queryParameters: PayoutRoutes.buildQueryParameters(returnTo: returnTo), + ); + + void pushToEditWallet({PayoutDestination? returnTo}) => pushNamed( + PayoutRoutes.editWallet, + queryParameters: PayoutRoutes.buildQueryParameters(returnTo: returnTo), + ); +} \ No newline at end of file diff --git a/frontend/pweb/lib/app/router/payout_shell.dart b/frontend/pweb/lib/app/router/payout_shell.dart index 96e3f39..a569a56 100644 --- a/frontend/pweb/lib/app/router/payout_shell.dart +++ b/frontend/pweb/lib/app/router/payout_shell.dart @@ -4,10 +4,13 @@ import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; +import 'package:pshared/models/payment/type.dart'; +import 'package:pshared/models/recipient/recipient.dart'; import 'package:pshared/provider/recipient/provider.dart'; import 'package:pweb/app/router/pages.dart'; import 'package:pweb/app/router/payout_routes.dart'; +import 'package:pweb/models/wallet.dart'; import 'package:pweb/pages/address_book/form/page.dart'; import 'package:pweb/pages/address_book/page/page.dart'; import 'package:pweb/pages/dashboard/dashboard.dart'; @@ -17,7 +20,7 @@ import 'package:pweb/pages/payout_page/wallet/edit/page.dart'; import 'package:pweb/pages/report/page.dart'; import 'package:pweb/pages/settings/profile/page.dart'; import 'package:pweb/pages/wallet_top_up/page.dart'; -import 'package:pweb/providers/page_selector.dart'; +import 'package:pweb/providers/wallets.dart'; import 'package:pweb/widgets/error/snackbar.dart'; import 'package:pweb/widgets/sidebar/destinations.dart'; import 'package:pweb/widgets/sidebar/page.dart'; @@ -36,15 +39,22 @@ RouteBase payoutShellRoute() => ShellRoute( path: routerPage(Pages.dashboard), pageBuilder: (context, _) => NoTransitionPage( child: DashboardPage( - onRecipientSelected: (recipient) => context - .read() - .selectRecipient(context, recipient), - onGoToPaymentWithoutRecipient: (type) => context - .read() - .startPaymentWithoutRecipient(context, type), - onTopUp: (wallet) => context - .read() - .openWalletTopUp(context, wallet), + onRecipientSelected: (recipient) => _startPayment( + context, + recipient: recipient, + returnTo: PayoutDestination.dashboard, + ), + onGoToPaymentWithoutRecipient: (type) => _startPayment( + context, + recipient: null, + paymentType: type, + returnTo: PayoutDestination.dashboard, + ), + onTopUp: (wallet) => _openWalletTopUp( + context, + wallet, + returnTo: PayoutDestination.dashboard, + ), ), ), ), @@ -55,15 +65,16 @@ RouteBase payoutShellRoute() => ShellRoute( final loc = AppLocalizations.of(context)!; return NoTransitionPage( child: RecipientAddressBookPage( - onRecipientSelected: (recipient) => context - .read() - .selectRecipient(context, recipient, fromList: true), - onAddRecipient: () => context - .read() - .goToAddRecipient(context), - onEditRecipient: (recipient) => context - .read() - .editRecipient(context, recipient, fromList: true), + onRecipientSelected: (recipient) => _startPayment( + context, + recipient: recipient, + returnTo: PayoutDestination.recipients, + ), + onAddRecipient: () => _openAddRecipient(context), + onEditRecipient: (recipient) => _openAddRecipient( + context, + recipient: recipient, + ), onDeleteRecipient: (recipient) => executeActionWithNotification( context: context, action: () async => @@ -79,15 +90,11 @@ RouteBase payoutShellRoute() => ShellRoute( name: PayoutRoutes.addRecipient, path: PayoutRoutes.addRecipientPath, pageBuilder: (context, _) { - final selector = context.read(); - final recipient = selector.recipientProvider.currentObject; + final recipient = context.read().currentObject; return NoTransitionPage( child: AdressBookRecipientForm( recipient: recipient, - onSaved: (_) => selector.selectPage( - context, - PayoutDestination.recipients, - ), + onSaved: (_) => context.goToPayout(PayoutDestination.recipients), ), ); }, @@ -95,13 +102,20 @@ RouteBase payoutShellRoute() => ShellRoute( GoRoute( name: PayoutRoutes.payment, path: PayoutRoutes.paymentPath, - pageBuilder: (context, _) => NoTransitionPage( - child: PaymentPage( - onBack: (_) => context - .read() - .goBackFromPayment(context), - ), - ), + pageBuilder: (context, state) { + final fallbackDestination = PayoutRoutes.fallbackFromState( + state, + defaultDestination: PayoutDestination.dashboard, + ); + + return NoTransitionPage( + child: PaymentPage( + onBack: (_) => _popOrGo(context, fallbackDestination), + initialPaymentType: PayoutRoutes.paymentTypeFromState(state), + fallbackDestination: fallbackDestination, + ), + ); + }, ), GoRoute( name: PayoutRoutes.settings, @@ -122,24 +136,30 @@ RouteBase payoutShellRoute() => ShellRoute( path: PayoutRoutes.methodsPath, pageBuilder: (context, _) => NoTransitionPage( child: PaymentConfigPage( - onWalletTap: (wallet) => context - .read() - .selectWallet(context, wallet), + onWalletTap: (wallet) => _openWalletEdit( + context, + wallet, + returnTo: PayoutDestination.methods, + ), ), ), ), GoRoute( name: PayoutRoutes.editWallet, path: PayoutRoutes.editWalletPath, - pageBuilder: (context, _) { - final provider = context.read(); - final wallet = provider.walletsProvider.selectedWallet; + pageBuilder: (context, state) { + final walletsProvider = context.read(); + final wallet = walletsProvider.selectedWallet; final loc = AppLocalizations.of(context)!; + final fallbackDestination = PayoutRoutes.fallbackFromState( + state, + defaultDestination: PayoutDestination.methods, + ); return NoTransitionPage( child: wallet != null ? WalletEditPage( - onBack: () => provider.goBackFromWalletEdit(context), + onBack: () => _popOrGo(context, fallbackDestination), ) : Center(child: Text(loc.noWalletSelected)), ); @@ -148,13 +168,65 @@ RouteBase payoutShellRoute() => ShellRoute( GoRoute( name: PayoutRoutes.walletTopUp, path: PayoutRoutes.walletTopUpPath, - pageBuilder: (context, _) => NoTransitionPage( - child: WalletTopUpPage( - onBack: () => context - .read() - .goBackFromWalletTopUp(context), - ), - ), + pageBuilder: (context, state) { + final fallbackDestination = PayoutRoutes.fallbackFromState( + state, + defaultDestination: PayoutDestination.dashboard, + ); + + return NoTransitionPage( + child: WalletTopUpPage( + onBack: () => _popOrGo(context, fallbackDestination), + ), + ); + }, ), ], ); + +void _startPayment( + BuildContext context, { + Recipient? recipient, + PaymentType? paymentType, + required PayoutDestination returnTo, +}) { + context.read().setCurrentObject(recipient?.id); + context.pushToPayment( + paymentType: paymentType, + returnTo: returnTo, + ); +} + +void _openAddRecipient( + BuildContext context, { + Recipient? recipient, +}) { + context.read().setCurrentObject(recipient?.id); + context.pushNamed(PayoutRoutes.addRecipient); +} + +void _openWalletEdit( + BuildContext context, + Wallet wallet, { + required PayoutDestination returnTo, +}) { + context.read().selectWallet(wallet); + context.pushToEditWallet(returnTo: returnTo); +} + +void _openWalletTopUp( + BuildContext context, + Wallet wallet, { + required PayoutDestination returnTo, +}) { + context.read().selectWallet(wallet); + context.pushToWalletTopUp(returnTo: returnTo); +} + +void _popOrGo(BuildContext context, PayoutDestination destination) { + if (Navigator.of(context).canPop()) { + Navigator.of(context).pop(); + } else { + context.goToPayout(destination); + } +} diff --git a/frontend/pweb/lib/main.dart b/frontend/pweb/lib/main.dart index c75b50d..1e2715b 100644 --- a/frontend/pweb/lib/main.dart +++ b/frontend/pweb/lib/main.dart @@ -20,7 +20,6 @@ import 'package:pweb/app/timeago.dart'; import 'package:pweb/providers/carousel.dart'; import 'package:pweb/providers/mock_payment.dart'; import 'package:pweb/providers/operatioins.dart'; -import 'package:pweb/providers/page_selector.dart'; import 'package:pweb/providers/two_factor.dart'; import 'package:pweb/providers/upload_history.dart'; import 'package:pweb/providers/wallets.dart'; @@ -94,12 +93,6 @@ void main() async { create: (_) => MockPaymentProvider(), ), - ChangeNotifierProxyProvider3( - create: (context) => PageSelectorProvider(), - update: (context, recipientProv, walletsProv, methodsProv, previous) => - previous ?? PageSelectorProvider()..update(recipientProv, walletsProv, methodsProv), - ), - ChangeNotifierProvider( create: (_) => OperationProvider(OperationService())..loadOperations(), ), diff --git a/frontend/pweb/lib/pages/payment_methods/delete_confirmation.dart b/frontend/pweb/lib/pages/payment_methods/delete_confirmation.dart deleted file mode 100644 index edf0404..0000000 --- a/frontend/pweb/lib/pages/payment_methods/delete_confirmation.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:pweb/generated/i18n/app_localizations.dart'; - - -Future showDeleteConfirmationDialog(BuildContext context) async { - final l10n = AppLocalizations.of(context)!; - return await showDialog( - context: context, - builder: (_) => AlertDialog( - title: Text(l10n.delete), - content: Text(l10n.deletePaymentConfirmation), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(false), - child: Text(l10n.cancel), - ), - ElevatedButton( - onPressed: () => Navigator.of(context).pop(true), - child: Text(l10n.delete), - ), - ], - ), - ) ?? false; -} diff --git a/frontend/pweb/lib/pages/payment_methods/page.dart b/frontend/pweb/lib/pages/payment_methods/page.dart index ad22b9c..44d3dd9 100644 --- a/frontend/pweb/lib/pages/payment_methods/page.dart +++ b/frontend/pweb/lib/pages/payment_methods/page.dart @@ -1,19 +1,34 @@ 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/recipient/pmethods.dart'; import 'package:pshared/provider/recipient/provider.dart'; -import 'package:pweb/providers/payment_flow_provider.dart'; -import 'package:pweb/pages/payment_methods/widgets/payment_page_body.dart'; -import 'package:pweb/providers/page_selector.dart'; +import 'package:pweb/models/wallet.dart'; +import 'package:pweb/providers/payment_flow.dart'; +import 'package:pweb/pages/payment_methods/payment_page/body.dart'; +import 'package:pweb/providers/wallets.dart'; +import 'package:pweb/widgets/sidebar/destinations.dart'; class PaymentPage extends StatefulWidget { final ValueChanged? onBack; + final PaymentType? initialPaymentType; + final PayoutDestination fallbackDestination; - const PaymentPage({super.key, this.onBack}); + const PaymentPage({ + super.key, + this.onBack, + this.initialPaymentType, + this.fallbackDestination = PayoutDestination.dashboard, + }); @override State createState() => _PaymentPageState(); @@ -29,9 +44,8 @@ class _PaymentPageState extends State { super.initState(); _searchController = TextEditingController(); _searchFocusNode = FocusNode(); - final pageSelector = context.read(); _flowProvider = PaymentFlowProvider( - initialType: pageSelector.getDefaultPaymentType(), + initialType: widget.initialPaymentType ?? PaymentType.bankAccount, ); WidgetsBinding.instance.addPostFrameCallback((_) => _initializePaymentPage()); @@ -46,11 +60,15 @@ class _PaymentPageState extends State { } void _initializePaymentPage() { - final pageSelector = context.read(); + final methodsProvider = context.read(); + _handleWalletAutoSelection(methodsProvider); - pageSelector.handleWalletAutoSelection(); - - _flowProvider.syncWithSelector(pageSelector); + final recipient = context.read().currentObject; + _syncFlowProvider( + recipient: recipient, + methodsProvider: methodsProvider, + preferredType: widget.initialPaymentType, + ); } void _handleSearchChanged(String query) { @@ -58,22 +76,28 @@ class _PaymentPageState extends State { } void _handleRecipientSelected(Recipient recipient) { - final pageSelector = context.read(); final recipientProvider = context.read(); + final methodsProvider = context.read(); recipientProvider.setCurrentObject(recipient.id); - pageSelector.selectRecipient(context, recipient); - _flowProvider.reset(pageSelector); + _flowProvider.reset( + recipient: recipient, + availableTypes: _availablePaymentTypes(recipient, methodsProvider), + preferredType: widget.initialPaymentType, + ); _clearSearchField(); } void _handleRecipientCleared() { - final pageSelector = context.read(); final recipientProvider = context.read(); + final methodsProvider = context.read(); recipientProvider.setCurrentObject(null); - pageSelector.selectRecipient(context, null); - _flowProvider.reset(pageSelector); + _flowProvider.reset( + recipient: null, + availableTypes: _availablePaymentTypes(null, methodsProvider), + preferredType: widget.initialPaymentType, + ); _clearSearchField(); } @@ -90,13 +114,26 @@ class _PaymentPageState extends State { @override Widget build(BuildContext context) { - final pageSelector = context.watch(); - _flowProvider.syncWithSelector(pageSelector); + final methodsProvider = context.watch(); + final recipientProvider = context.watch(); + final recipient = recipientProvider.currentObject; + final availableTypes = _availablePaymentTypes(recipient, methodsProvider); + + _syncFlowProvider( + recipient: recipient, + methodsProvider: methodsProvider, + preferredType: recipient != null ? widget.initialPaymentType : null, + ); return ChangeNotifierProvider.value( value: _flowProvider, child: PaymentPageBody( onBack: widget.onBack, + fallbackDestination: widget.fallbackDestination, + recipient: recipient, + recipientProvider: recipientProvider, + methodsProvider: methodsProvider, + availablePaymentTypes: availableTypes, searchController: _searchController, searchFocusNode: _searchFocusNode, onSearchChanged: _handleSearchChanged, @@ -106,4 +143,56 @@ class _PaymentPageState extends State { ), ); } -} + + 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); + } + } + + void _syncFlowProvider({ + required Recipient? recipient, + required PaymentMethodsProvider methodsProvider, + PaymentType? preferredType, + }) { + _flowProvider.sync( + recipient: recipient, + availableTypes: _availablePaymentTypes(recipient, methodsProvider), + preferredType: preferredType, + ); + } + + 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), + ); + } +} \ No newline at end of file diff --git a/frontend/pweb/lib/pages/payment_methods/payment_details.dart b/frontend/pweb/lib/pages/payment_methods/payment_details.dart deleted file mode 100644 index ab6f355..0000000 --- a/frontend/pweb/lib/pages/payment_methods/payment_details.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:pshared/models/payment/type.dart'; - - -class PaymentDetailsSection extends StatelessWidget { - final bool isFormVisible; - final VoidCallback? onToggle; - final PaymentType selectedType; - final Object? data; - final bool isEditable; - - const PaymentDetailsSection({ - super.key, - required this.isFormVisible, - this.onToggle, - required this.selectedType, - required this.data, - required this.isEditable, - }); - - @override - Widget build(BuildContext context) { - - return PaymentDetailsSection( - isFormVisible: isFormVisible, - onToggle: onToggle, - selectedType: selectedType, - data: data, - isEditable: isEditable, - ); - } -} \ No newline at end of file diff --git a/frontend/pweb/lib/pages/payment_methods/payment_page/back_button.dart b/frontend/pweb/lib/pages/payment_methods/payment_page/back_button.dart new file mode 100644 index 0000000..1fa62b0 --- /dev/null +++ b/frontend/pweb/lib/pages/payment_methods/payment_page/back_button.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; + +import 'package:pshared/models/recipient/recipient.dart'; + +import 'package:pweb/app/router/payout_routes.dart'; +import 'package:pweb/widgets/sidebar/destinations.dart'; + + +class PaymentBackButton extends StatelessWidget { + final ValueChanged? onBack; + final Recipient? recipient; + final PayoutDestination fallbackDestination; + + const PaymentBackButton({ + super.key, + required this.onBack, + required this.recipient, + required this.fallbackDestination, + }); + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.topLeft, + child: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () { + if (onBack != null) { + onBack!(recipient); + } else { + if (Navigator.of(context).canPop()) { + Navigator.of(context).pop(); + } else { + context.goToPayout(fallbackDestination); + } + } + }, + ), + ); + } +} \ No newline at end of file diff --git a/frontend/pweb/lib/pages/payment_methods/payment_page/body.dart b/frontend/pweb/lib/pages/payment_methods/payment_page/body.dart new file mode 100644 index 0000000..2caf9da --- /dev/null +++ b/frontend/pweb/lib/pages/payment_methods/payment_page/body.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; + +import 'package:pshared/models/payment/methods/data.dart'; +import 'package:pshared/models/recipient/recipient.dart'; +import 'package:pshared/provider/recipient/pmethods.dart'; +import 'package:pshared/provider/recipient/provider.dart'; + +import 'package:pweb/widgets/sidebar/destinations.dart'; +import 'package:pweb/pages/payment_methods/widgets/state_view.dart'; +import 'package:pweb/pages/payment_methods/payment_page/page.dart'; + +import 'package:pweb/generated/i18n/app_localizations.dart'; + + +class PaymentPageBody extends StatelessWidget { + final ValueChanged? onBack; + final Recipient? recipient; + final RecipientsProvider recipientProvider; + final PaymentMethodsProvider methodsProvider; + final MethodMap availablePaymentTypes; + final PayoutDestination fallbackDestination; + final TextEditingController searchController; + final FocusNode searchFocusNode; + final ValueChanged onSearchChanged; + final ValueChanged onRecipientSelected; + final VoidCallback onRecipientCleared; + final VoidCallback onSend; + + const PaymentPageBody({ + super.key, + required this.onBack, + required this.recipient, + required this.recipientProvider, + required this.methodsProvider, + required this.availablePaymentTypes, + required this.fallbackDestination, + required this.searchController, + required this.searchFocusNode, + required this.onSearchChanged, + required this.onRecipientSelected, + required this.onRecipientCleared, + required this.onSend, + }); + + @override + Widget build(BuildContext context) { + final loc = AppLocalizations.of(context)!; + + if (methodsProvider.isLoading) { + return const PaymentMethodsLoadingView(); + } + + if (methodsProvider.error != null) { + return PaymentMethodsErrorView( + message: loc.notificationError(methodsProvider.error ?? loc.noErrorInformation), + ); + } + + return PaymentPageContent( + onBack: onBack, + recipient: recipient, + recipientProvider: recipientProvider, + methodsProvider: methodsProvider, + availablePaymentTypes: availablePaymentTypes, + fallbackDestination: fallbackDestination, + searchController: searchController, + searchFocusNode: searchFocusNode, + onSearchChanged: onSearchChanged, + onRecipientSelected: onRecipientSelected, + onRecipientCleared: onRecipientCleared, + onSend: onSend, + ); + } +} diff --git a/frontend/pweb/lib/pages/payment_methods/widgets/payment_page_body.dart b/frontend/pweb/lib/pages/payment_methods/payment_page/content.dart similarity index 72% rename from frontend/pweb/lib/pages/payment_methods/widgets/payment_page_body.dart rename to frontend/pweb/lib/pages/payment_methods/payment_page/content.dart index a49cc49..85003b6 100644 --- a/frontend/pweb/lib/pages/payment_methods/widgets/payment_page_body.dart +++ b/frontend/pweb/lib/pages/payment_methods/payment_page/content.dart @@ -2,10 +2,12 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:pshared/models/payment/methods/data.dart'; import 'package:pshared/models/recipient/recipient.dart'; import 'package:pshared/provider/recipient/pmethods.dart'; import 'package:pshared/provider/recipient/provider.dart'; +import 'package:pweb/pages/payment_methods/back_button.dart'; import 'package:pweb/pages/payment_methods/header.dart'; import 'package:pweb/pages/payment_methods/method_selector.dart'; import 'package:pweb/pages/payment_methods/send_button.dart'; @@ -13,15 +15,20 @@ import 'package:pweb/pages/dashboard/payouts/payment_form.dart'; import 'package:pweb/pages/payment_methods/widgets/payment_info_section.dart'; import 'package:pweb/pages/payment_methods/widgets/recipient_section.dart'; import 'package:pweb/pages/payment_methods/widgets/section_title.dart'; -import 'package:pweb/providers/page_selector.dart'; -import 'package:pweb/providers/payment_flow_provider.dart'; +import 'package:pweb/providers/payment_flow.dart'; import 'package:pweb/utils/dimensions.dart'; +import 'package:pweb/widgets/sidebar/destinations.dart'; import 'package:pweb/generated/i18n/app_localizations.dart'; -class PaymentPageBody extends StatelessWidget { +class PaymentPageContent extends StatelessWidget { final ValueChanged? onBack; + final Recipient? recipient; + final RecipientsProvider recipientProvider; + final PaymentMethodsProvider methodsProvider; + final MethodMap availablePaymentTypes; + final PayoutDestination fallbackDestination; final TextEditingController searchController; final FocusNode searchFocusNode; final ValueChanged onSearchChanged; @@ -29,9 +36,14 @@ class PaymentPageBody extends StatelessWidget { final VoidCallback onRecipientCleared; final VoidCallback onSend; - const PaymentPageBody({ + const PaymentPageContent({ super.key, required this.onBack, + required this.recipient, + required this.recipientProvider, + required this.methodsProvider, + required this.availablePaymentTypes, + required this.fallbackDestination, required this.searchController, required this.searchFocusNode, required this.onSearchChanged, @@ -43,21 +55,9 @@ class PaymentPageBody extends StatelessWidget { @override Widget build(BuildContext context) { final dimensions = AppDimensions(); - final pageSelector = context.watch(); - final methodsProvider = context.watch(); - final recipientProvider = context.watch(); final flowProvider = context.watch(); - final recipient = pageSelector.selectedRecipient; final loc = AppLocalizations.of(context)!; - if (methodsProvider.isLoading) { - return const Center(child: CircularProgressIndicator()); - } - - if (methodsProvider.error != null) { - return Center(child: Text(loc.notificationError(methodsProvider.error ?? loc.noErrorInformation))); - } - return Align( alignment: Alignment.topCenter, child: ConstrainedBox( @@ -73,18 +73,20 @@ class PaymentPageBody extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - PaymentBackButton(onBack: onBack, pageSelector: pageSelector), + PaymentBackButton( + onBack: onBack, + recipient: recipient, + fallbackDestination: fallbackDestination, + ), SizedBox(height: dimensions.paddingSmall), PaymentHeader(), SizedBox(height: dimensions.paddingXXLarge), - SectionTitle(loc.sourceOfFunds), SizedBox(height: dimensions.paddingSmall), PaymentMethodSelector( onMethodChanged: (m) => methodsProvider.setCurrentObject(m.id), ), SizedBox(height: dimensions.paddingXLarge), - RecipientSection( recipient: recipient, dimensions: dimensions, @@ -95,19 +97,15 @@ class PaymentPageBody extends StatelessWidget { onRecipientSelected: onRecipientSelected, onRecipientCleared: onRecipientCleared, ), - SizedBox(height: dimensions.paddingXLarge), - PaymentInfoSection( dimensions: dimensions, - pageSelector: pageSelector, flowProvider: flowProvider, recipient: recipient, + availableTypes: availablePaymentTypes, ), - SizedBox(height: dimensions.paddingLarge), const PaymentFormWidget(), - SizedBox(height: dimensions.paddingXXXLarge), SendButton(onPressed: onSend), SizedBox(height: dimensions.paddingLarge), @@ -120,31 +118,3 @@ class PaymentPageBody extends StatelessWidget { ); } } - -class PaymentBackButton extends StatelessWidget { - final ValueChanged? onBack; - final PageSelectorProvider pageSelector; - - const PaymentBackButton({ - super.key, - required this.onBack, - required this.pageSelector, - }); - - @override - Widget build(BuildContext context) { - return Align( - alignment: Alignment.topLeft, - child: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () { - if (onBack != null) { - onBack!(pageSelector.selectedRecipient); - } else { - pageSelector.goBackFromPayment(context); - } - }, - ), - ); - } -} diff --git a/frontend/pweb/lib/pages/payment_methods/header.dart b/frontend/pweb/lib/pages/payment_methods/payment_page/header.dart similarity index 100% rename from frontend/pweb/lib/pages/payment_methods/header.dart rename to frontend/pweb/lib/pages/payment_methods/payment_page/header.dart diff --git a/frontend/pweb/lib/pages/payment_methods/method_selector.dart b/frontend/pweb/lib/pages/payment_methods/payment_page/method_selector.dart similarity index 100% rename from frontend/pweb/lib/pages/payment_methods/method_selector.dart rename to frontend/pweb/lib/pages/payment_methods/payment_page/method_selector.dart diff --git a/frontend/pweb/lib/pages/payment_methods/payment_page/page.dart b/frontend/pweb/lib/pages/payment_methods/payment_page/page.dart new file mode 100644 index 0000000..85003b6 --- /dev/null +++ b/frontend/pweb/lib/pages/payment_methods/payment_page/page.dart @@ -0,0 +1,120 @@ +import 'package:flutter/material.dart'; + +import 'package:provider/provider.dart'; + +import 'package:pshared/models/payment/methods/data.dart'; +import 'package:pshared/models/recipient/recipient.dart'; +import 'package:pshared/provider/recipient/pmethods.dart'; +import 'package:pshared/provider/recipient/provider.dart'; + +import 'package:pweb/pages/payment_methods/back_button.dart'; +import 'package:pweb/pages/payment_methods/header.dart'; +import 'package:pweb/pages/payment_methods/method_selector.dart'; +import 'package:pweb/pages/payment_methods/send_button.dart'; +import 'package:pweb/pages/dashboard/payouts/payment_form.dart'; +import 'package:pweb/pages/payment_methods/widgets/payment_info_section.dart'; +import 'package:pweb/pages/payment_methods/widgets/recipient_section.dart'; +import 'package:pweb/pages/payment_methods/widgets/section_title.dart'; +import 'package:pweb/providers/payment_flow.dart'; +import 'package:pweb/utils/dimensions.dart'; +import 'package:pweb/widgets/sidebar/destinations.dart'; + +import 'package:pweb/generated/i18n/app_localizations.dart'; + + +class PaymentPageContent extends StatelessWidget { + final ValueChanged? onBack; + final Recipient? recipient; + final RecipientsProvider recipientProvider; + final PaymentMethodsProvider methodsProvider; + final MethodMap availablePaymentTypes; + final PayoutDestination fallbackDestination; + final TextEditingController searchController; + final FocusNode searchFocusNode; + final ValueChanged onSearchChanged; + final ValueChanged onRecipientSelected; + final VoidCallback onRecipientCleared; + final VoidCallback onSend; + + const PaymentPageContent({ + super.key, + required this.onBack, + required this.recipient, + required this.recipientProvider, + required this.methodsProvider, + required this.availablePaymentTypes, + required this.fallbackDestination, + required this.searchController, + required this.searchFocusNode, + required this.onSearchChanged, + required this.onRecipientSelected, + required this.onRecipientCleared, + required this.onSend, + }); + + @override + Widget build(BuildContext context) { + final dimensions = AppDimensions(); + final flowProvider = context.watch(); + final loc = AppLocalizations.of(context)!; + + return Align( + alignment: Alignment.topCenter, + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: dimensions.maxContentWidth), + child: Material( + elevation: dimensions.elevationSmall, + borderRadius: BorderRadius.circular(dimensions.borderRadiusMedium), + color: Theme.of(context).colorScheme.onSecondary, + child: Padding( + padding: EdgeInsets.all(dimensions.paddingLarge), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + PaymentBackButton( + onBack: onBack, + recipient: recipient, + fallbackDestination: fallbackDestination, + ), + SizedBox(height: dimensions.paddingSmall), + PaymentHeader(), + SizedBox(height: dimensions.paddingXXLarge), + SectionTitle(loc.sourceOfFunds), + SizedBox(height: dimensions.paddingSmall), + PaymentMethodSelector( + onMethodChanged: (m) => methodsProvider.setCurrentObject(m.id), + ), + SizedBox(height: dimensions.paddingXLarge), + RecipientSection( + recipient: recipient, + dimensions: dimensions, + recipientProvider: recipientProvider, + searchController: searchController, + searchFocusNode: searchFocusNode, + onSearchChanged: onSearchChanged, + onRecipientSelected: onRecipientSelected, + onRecipientCleared: onRecipientCleared, + ), + SizedBox(height: dimensions.paddingXLarge), + PaymentInfoSection( + dimensions: dimensions, + flowProvider: flowProvider, + recipient: recipient, + availableTypes: availablePaymentTypes, + ), + SizedBox(height: dimensions.paddingLarge), + const PaymentFormWidget(), + SizedBox(height: dimensions.paddingXXXLarge), + SendButton(onPressed: onSend), + SizedBox(height: dimensions.paddingLarge), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/frontend/pweb/lib/pages/payment_methods/send_button.dart b/frontend/pweb/lib/pages/payment_methods/payment_page/send_button.dart similarity index 100% rename from frontend/pweb/lib/pages/payment_methods/send_button.dart rename to frontend/pweb/lib/pages/payment_methods/payment_page/send_button.dart diff --git a/frontend/pweb/lib/pages/payment_methods/widgets/payment_info_section.dart b/frontend/pweb/lib/pages/payment_methods/widgets/payment_info_section.dart index e1af21a..acd3f51 100644 --- a/frontend/pweb/lib/pages/payment_methods/widgets/payment_info_section.dart +++ b/frontend/pweb/lib/pages/payment_methods/widgets/payment_info_section.dart @@ -6,8 +6,7 @@ import 'package:pshared/models/recipient/recipient.dart'; import 'package:pweb/pages/payment_methods/form.dart'; import 'package:pweb/pages/payment_methods/widgets/section_title.dart'; -import 'package:pweb/providers/page_selector.dart'; -import 'package:pweb/providers/payment_flow_provider.dart'; +import 'package:pweb/providers/payment_flow.dart'; import 'package:pweb/utils/dimensions.dart'; import 'package:pweb/utils/payment/selector_type.dart'; @@ -16,14 +15,14 @@ import 'package:pweb/generated/i18n/app_localizations.dart'; class PaymentInfoSection extends StatelessWidget { final AppDimensions dimensions; - final PageSelectorProvider pageSelector; + final MethodMap availableTypes; final PaymentFlowProvider flowProvider; final Recipient? recipient; const PaymentInfoSection({ super.key, required this.dimensions, - required this.pageSelector, + required this.availableTypes, required this.flowProvider, required this.recipient, }); @@ -32,11 +31,11 @@ class PaymentInfoSection extends StatelessWidget { Widget build(BuildContext context) { final loc = AppLocalizations.of(context)!; final hasRecipient = recipient != null; - final MethodMap availableTypes = hasRecipient - ? pageSelector.getAvailablePaymentTypes() + final MethodMap resolvedAvailableTypes = hasRecipient + ? availableTypes : {for (final type in PaymentType.values) type: null}; - if (hasRecipient && availableTypes.isEmpty) { + if (hasRecipient && resolvedAvailableTypes.isEmpty) { return Text(loc.recipientNoPaymentDetails); } @@ -48,7 +47,7 @@ class PaymentInfoSection extends StatelessWidget { SectionTitle(loc.paymentInfo), SizedBox(height: dimensions.paddingSmall), PaymentTypeSelector( - availableTypes: availableTypes, + availableTypes: resolvedAvailableTypes, selectedType: selectedType, onSelected: (type) => flowProvider.selectType( type, @@ -63,7 +62,7 @@ class PaymentInfoSection extends StatelessWidget { flowProvider.setManualPaymentData(data); } }, - initialData: hasRecipient ? availableTypes[selectedType] : flowProvider.manualPaymentData, + initialData: hasRecipient ? resolvedAvailableTypes[selectedType] : flowProvider.manualPaymentData, isEditable: !hasRecipient, ), ], diff --git a/frontend/pweb/lib/pages/payment_methods/widgets/state_view.dart b/frontend/pweb/lib/pages/payment_methods/widgets/state_view.dart new file mode 100644 index 0000000..c7d2356 --- /dev/null +++ b/frontend/pweb/lib/pages/payment_methods/widgets/state_view.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class PaymentMethodsLoadingView extends StatelessWidget { + const PaymentMethodsLoadingView({super.key}); + + @override + Widget build(BuildContext context) { + return const Center(child: CircularProgressIndicator()); + } +} + +class PaymentMethodsErrorView extends StatelessWidget { + final String message; + + const PaymentMethodsErrorView({super.key, required this.message}); + + @override + Widget build(BuildContext context) { + return Center(child: Text(message)); + } +} diff --git a/frontend/pweb/lib/pages/payout_page/wallet/edit/buttons/send.dart b/frontend/pweb/lib/pages/payout_page/wallet/edit/buttons/send.dart index a3c48e3..118494a 100644 --- a/frontend/pweb/lib/pages/payout_page/wallet/edit/buttons/send.dart +++ b/frontend/pweb/lib/pages/payout_page/wallet/edit/buttons/send.dart @@ -2,8 +2,11 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:pweb/providers/page_selector.dart'; +import 'package:pshared/models/payment/type.dart'; + +import 'package:pweb/app/router/payout_routes.dart'; import 'package:pweb/providers/wallets.dart'; +import 'package:pweb/widgets/sidebar/destinations.dart'; import 'package:pweb/generated/i18n/app_localizations.dart'; @@ -20,12 +23,14 @@ class SendPayoutButton extends StatelessWidget { elevation: 0, ), onPressed: () { - final pageSelectorProvider = context.read(); final walletsProvider = context.read(); final wallet = walletsProvider.selectedWallet; if (wallet != null) { - pageSelectorProvider.startPaymentFromWallet(context, wallet); + context.pushToPayment( + paymentType: PaymentType.wallet, + returnTo: PayoutDestination.editwallet, + ); } }, child: Text(loc.payoutNavSendPayout), diff --git a/frontend/pweb/lib/pages/payout_page/wallet/edit/buttons/top_up.dart b/frontend/pweb/lib/pages/payout_page/wallet/edit/buttons/top_up.dart index ace5cdd..5a66846 100644 --- a/frontend/pweb/lib/pages/payout_page/wallet/edit/buttons/top_up.dart +++ b/frontend/pweb/lib/pages/payout_page/wallet/edit/buttons/top_up.dart @@ -2,8 +2,9 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:pweb/providers/page_selector.dart'; +import 'package:pweb/app/router/payout_routes.dart'; import 'package:pweb/providers/wallets.dart'; +import 'package:pweb/widgets/sidebar/destinations.dart'; import 'package:pweb/generated/i18n/app_localizations.dart'; @@ -26,7 +27,7 @@ class TopUpButton extends StatelessWidget{ ); return; } - context.read().openWalletTopUp(context, wallet); + context.pushToWalletTopUp(returnTo: PayoutDestination.editwallet); }, child: Text(loc.topUpBalance), ); diff --git a/frontend/pweb/lib/providers/page_selector.dart b/frontend/pweb/lib/providers/page_selector.dart deleted file mode 100644 index 0a22ce8..0000000 --- a/frontend/pweb/lib/providers/page_selector.dart +++ /dev/null @@ -1,264 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:collection/collection.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/recipient/pmethods.dart'; -import 'package:pshared/provider/recipient/provider.dart'; - -import 'package:pweb/models/wallet.dart'; -import 'package:pweb/providers/wallets.dart'; -//import 'package:pweb/services/amplitude.dart'; -import 'package:pweb/app/router/payout_routes.dart'; -import 'package:pweb/widgets/sidebar/destinations.dart'; - - -class PageSelectorProvider extends ChangeNotifier { - PayoutDestination _selected = PayoutDestination.dashboard; - PaymentType? _type; - bool _cameFromRecipientList = false; - PayoutDestination? _previousDestination; - - late RecipientsProvider recipientProvider; - late WalletsProvider walletsProvider; - late PaymentMethodsProvider methodsProvider; - - PayoutDestination get selected => _selected; - PaymentType? get type => _type; - bool get cameFromRecipientList => _cameFromRecipientList; - - PageSelectorProvider(); - - void update( - RecipientsProvider recipientProv, - WalletsProvider walletsProv, - PaymentMethodsProvider methodsProv, - ) { - recipientProvider = recipientProv; - walletsProvider = walletsProv; - methodsProvider = methodsProv; - } - - void syncDestination(PayoutDestination destination) { - if (_selected == destination) return; - _selected = destination; - notifyListeners(); - } - - void selectPage( - BuildContext context, - PayoutDestination dest, { - bool replace = true, - }) { - _selected = dest; - notifyListeners(); - _navigateTo(context, dest, replace: replace); - } - - void selectRecipient( - BuildContext context, - Recipient? recipient, { - bool fromList = false, - }) { - final previousDestination = _selected; - recipientProvider.setCurrentObject(recipient?.id); - _cameFromRecipientList = fromList; - _setPreviousDestination(); - _selected = PayoutDestination.payment; - notifyListeners(); - if (previousDestination != PayoutDestination.payment) { - _navigateTo(context, PayoutDestination.payment, replace: false); - } - } - - void editRecipient( - BuildContext context, - Recipient? recipient, { - bool fromList = false, - }) { - final previousDestination = _selected; - recipientProvider.setCurrentObject(recipient?.id); - _cameFromRecipientList = fromList; - _selected = PayoutDestination.addrecipient; - notifyListeners(); - if (previousDestination != PayoutDestination.addrecipient) { - _navigateTo(context, PayoutDestination.addrecipient, replace: false); - } - } - - void goToAddRecipient(BuildContext context) { - // AmplitudeService.recipientAddStarted(); - final previousDestination = _selected; - recipientProvider.setCurrentObject(null); - _selected = PayoutDestination.addrecipient; - _cameFromRecipientList = false; - notifyListeners(); - if (previousDestination != PayoutDestination.addrecipient) { - _navigateTo(context, PayoutDestination.addrecipient, replace: false); - } - } - - void startPaymentWithoutRecipient( - BuildContext context, - PaymentType type, - ) { - final previousDestination = _selected; - recipientProvider.setCurrentObject(null); - _type = type; - _cameFromRecipientList = false; - _setPreviousDestination(); - _selected = PayoutDestination.payment; - notifyListeners(); - if (previousDestination != PayoutDestination.payment) { - _navigateTo(context, PayoutDestination.payment, replace: false); - } - } - - void goBackFromPayment(BuildContext context) { - if (Navigator.of(context).canPop()) { - Navigator.of(context).pop(); - } else { - _navigateTo( - context, - _previousDestination ?? - (_cameFromRecipientList - ? PayoutDestination.recipients - : PayoutDestination.dashboard), - ); - } - _selected = _previousDestination ?? - (_cameFromRecipientList - ? PayoutDestination.recipients - : PayoutDestination.dashboard); - _type = null; - _previousDestination = null; - _cameFromRecipientList = false; - notifyListeners(); - } - - void goBackFromWalletEdit(BuildContext context) { - selectPage(context, PayoutDestination.methods); - } - - void selectWallet(BuildContext context, Wallet wallet) { - final previousDestination = _selected; - walletsProvider.selectWallet(wallet); - _selected = PayoutDestination.editwallet; - notifyListeners(); - if (previousDestination != PayoutDestination.editwallet) { - _navigateTo(context, PayoutDestination.editwallet, replace: false); - } - } - - void startPaymentFromWallet(BuildContext context, Wallet wallet) { - final previousDestination = _selected; - _type = PaymentType.wallet; - _cameFromRecipientList = false; - _setPreviousDestination(); - _selected = PayoutDestination.payment; - notifyListeners(); - if (previousDestination != PayoutDestination.payment) { - _navigateTo(context, PayoutDestination.payment, replace: false); - } - } - - void openWalletTopUp(BuildContext context, Wallet wallet) { - final previousDestination = _selected; - _setPreviousDestination(); - walletsProvider.selectWallet(wallet); - _selected = PayoutDestination.walletTopUp; - notifyListeners(); - if (previousDestination != PayoutDestination.walletTopUp) { - _navigateTo(context, PayoutDestination.walletTopUp, replace: false); - } - } - - void goBackFromWalletTopUp(BuildContext context) { - if (Navigator.of(context).canPop()) { - Navigator.of(context).pop(); - } else { - _navigateTo( - context, - _previousDestination ?? PayoutDestination.dashboard, - ); - } - _selected = _previousDestination ?? PayoutDestination.dashboard; - _previousDestination = null; - notifyListeners(); - } - - PaymentMethod? getPaymentMethodForWallet(Wallet wallet) { - if (methodsProvider.methods.isEmpty) { - return null; - } - - return methodsProvider.methods.firstWhereOrNull( - (method) => method.type == PaymentType.wallet && (method.description?.contains(wallet.walletUserID) ?? false), - ); - } - - MethodMap getAvailablePaymentTypes() { - final recipient = selectedRecipient; - 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, - }; - } - - PaymentType getDefaultPaymentType() { - final availableTypes = getAvailablePaymentTypes(); - final currentType = _type ?? PaymentType.bankAccount; - - if (availableTypes.containsKey(currentType)) { - return currentType; - } - if (availableTypes.isNotEmpty) { - return availableTypes.keys.first; - } - return PaymentType.bankAccount; - } - - bool shouldShowPaymentForm() { - return selectedRecipient == null; - } - - void handleWalletAutoSelection() { - if (selectedWallet != null) { - final wallet = selectedWallet!; - final matchingMethod = getPaymentMethodForWallet(wallet); - if (matchingMethod != null) { - methodsProvider.setCurrentObject(matchingMethod.id); - } - } - } - - void _setPreviousDestination() { - if (_selected != PayoutDestination.payment && - _selected != PayoutDestination.walletTopUp) { - _previousDestination = _selected; - } - } - - void _navigateTo( - BuildContext context, - PayoutDestination destination, { - bool replace = true, - }) { - if (replace) { - context.goToPayout(destination); - } else { - context.pushToPayout(destination); - } - } - - Recipient? get selectedRecipient => recipientProvider.currentObject; - Wallet? get selectedWallet => walletsProvider.selectedWallet; -} diff --git a/frontend/pweb/lib/providers/payment_flow.dart b/frontend/pweb/lib/providers/payment_flow.dart new file mode 100644 index 0000000..e4f3f73 --- /dev/null +++ b/frontend/pweb/lib/providers/payment_flow.dart @@ -0,0 +1,110 @@ +import 'package:flutter/foundation.dart'; + +import 'package:pshared/models/payment/methods/data.dart'; +import 'package:pshared/models/payment/type.dart'; +import 'package:pshared/models/recipient/recipient.dart'; + + +class PaymentFlowProvider extends ChangeNotifier { + PaymentType _selectedType; + PaymentMethodData? _manualPaymentData; + + PaymentFlowProvider({ + required PaymentType initialType, + }) : _selectedType = initialType; + + PaymentType get selectedType => _selectedType; + PaymentMethodData? get manualPaymentData => _manualPaymentData; + + void sync({ + required Recipient? recipient, + required MethodMap availableTypes, + PaymentType? preferredType, + }) { + final resolvedType = _resolveSelectedType( + recipient: recipient, + availableTypes: availableTypes, + preferredType: preferredType, + ); + + var hasChanges = false; + if (resolvedType != _selectedType) { + _selectedType = resolvedType; + hasChanges = true; + } + + if (recipient != null && _manualPaymentData != null) { + _manualPaymentData = null; + hasChanges = true; + } + + if (hasChanges) notifyListeners(); + } + + void reset({ + required Recipient? recipient, + required MethodMap availableTypes, + PaymentType? preferredType, + }) { + final resolvedType = _resolveSelectedType( + recipient: recipient, + availableTypes: availableTypes, + preferredType: preferredType, + ); + + var hasChanges = false; + + if (resolvedType != _selectedType) { + _selectedType = resolvedType; + hasChanges = true; + } + + if (_manualPaymentData != null) { + _manualPaymentData = null; + hasChanges = true; + } + + if (hasChanges) notifyListeners(); + } + + void selectType(PaymentType type, {bool resetManualData = false}) { + if (_selectedType == type && (!resetManualData || _manualPaymentData == null)) { + return; + } + + _selectedType = type; + if (resetManualData) { + _manualPaymentData = null; + } + notifyListeners(); + } + + void setManualPaymentData(PaymentMethodData? data) { + _manualPaymentData = data; + notifyListeners(); + } + + PaymentType _resolveSelectedType({ + required Recipient? recipient, + required MethodMap availableTypes, + PaymentType? preferredType, + }) { + if (recipient == null) { + return preferredType ?? _selectedType; + } + + if (availableTypes.isEmpty) { + return preferredType ?? PaymentType.bankAccount; + } + + if (availableTypes.keys.contains(_selectedType)) { + return _selectedType; + } + + if (preferredType != null && availableTypes.keys.contains(preferredType)) { + return preferredType; + } + + return availableTypes.keys.first; + } +} diff --git a/frontend/pweb/lib/providers/payment_flow_provider.dart b/frontend/pweb/lib/providers/payment_flow_provider.dart deleted file mode 100644 index a464f62..0000000 --- a/frontend/pweb/lib/providers/payment_flow_provider.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:flutter/foundation.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:pweb/providers/page_selector.dart'; - - -class PaymentFlowProvider extends ChangeNotifier { - PaymentType _selectedType; - PaymentMethodData? _manualPaymentData; - - PaymentFlowProvider({ - required PaymentType initialType, - }) : _selectedType = initialType; - - PaymentType get selectedType => _selectedType; - PaymentMethodData? get manualPaymentData => _manualPaymentData; - - void syncWithSelector(PageSelectorProvider selector) { - final recipient = selector.selectedRecipient; - final resolvedType = _resolveSelectedType(selector, recipient); - - var hasChanges = false; - if (resolvedType != _selectedType) { - _selectedType = resolvedType; - hasChanges = true; - } - - if (recipient != null && _manualPaymentData != null) { - _manualPaymentData = null; - hasChanges = true; - } - - if (hasChanges) notifyListeners(); - } - - void reset(PageSelectorProvider selector) { - _selectedType = selector.getDefaultPaymentType(); - _manualPaymentData = null; - notifyListeners(); - } - - void selectType(PaymentType type, {bool resetManualData = false}) { - if (_selectedType == type && (!resetManualData || _manualPaymentData == null)) { - return; - } - - _selectedType = type; - if (resetManualData) { - _manualPaymentData = null; - } - notifyListeners(); - } - - void setManualPaymentData(PaymentMethodData? data) { - _manualPaymentData = data; - notifyListeners(); - } - - PaymentType _resolveSelectedType( - PageSelectorProvider selector, - Recipient? recipient, - ) { - final available = selector.getAvailablePaymentTypes(); - final current = _selectedType; - - if (recipient == null) { - return current; - } - - if (available.keys.contains(current)) { - return current; - } - - return selector.getDefaultPaymentType(); - } -} diff --git a/frontend/pweb/lib/widgets/sidebar/page.dart b/frontend/pweb/lib/widgets/sidebar/page.dart index 736e0a1..0850ce2 100644 --- a/frontend/pweb/lib/widgets/sidebar/page.dart +++ b/frontend/pweb/lib/widgets/sidebar/page.dart @@ -8,7 +8,6 @@ import 'package:pshared/models/resources.dart'; import 'package:pshared/provider/permissions.dart'; import 'package:pweb/pages/loader.dart'; -import 'package:pweb/providers/page_selector.dart'; import 'package:pweb/utils/logout.dart'; import 'package:pweb/widgets/appbar/app_bar.dart'; import 'package:pweb/widgets/sidebar/destinations.dart'; @@ -32,9 +31,10 @@ class PageSelector extends StatelessWidget { final permissions = context.read(); if (!permissions.isReady) return Center(child: CircularProgressIndicator()); - final provider = context.watch(); - final bool restrictedAccess = !permissions.canRead(ResourceType.chainWallets); + final fallbackDestination = restrictedAccess + ? PayoutDestination.settings + : PayoutDestination.dashboard; final allowedDestinations = restrictedAccess ? { PayoutDestination.settings, @@ -44,10 +44,10 @@ class PageSelector extends StatelessWidget { } : PayoutDestination.values.toSet(); - final routeDestination = _destinationFromState(routerState) ?? provider.selected; - final selected = allowedDestinations.contains(routeDestination) + final routeDestination = _destinationFromState(routerState); + final selected = routeDestination != null && allowedDestinations.contains(routeDestination) ? routeDestination - : (restrictedAccess ? PayoutDestination.settings : PayoutDestination.dashboard); + : fallbackDestination; if (selected != routeDestination) { WidgetsBinding.instance.addPostFrameCallback((_) { @@ -55,10 +55,6 @@ class PageSelector extends StatelessWidget { }); } - if (provider.selected != selected) { - provider.syncDestination(selected); - } - return Scaffold( appBar: PayoutAppBar( title: Text(selected.localizedLabel(context)),