Compare commits
3 Commits
0804ad71f7
...
SEND003
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfbf36bf04 | ||
|
|
b16c295094 | ||
|
|
336687eccf |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ untranslated.txt
|
|||||||
generate_protos.sh
|
generate_protos.sh
|
||||||
update_dep.sh
|
update_dep.sh
|
||||||
.vscode/
|
.vscode/
|
||||||
|
GeneratedPluginRegistrant.swift
|
||||||
@@ -27,6 +27,7 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
Resource<Account?> get resource => _resource;
|
Resource<Account?> get resource => _resource;
|
||||||
late LocaleProvider _localeProvider;
|
late LocaleProvider _localeProvider;
|
||||||
PendingLogin? _pendingLogin;
|
PendingLogin? _pendingLogin;
|
||||||
|
Future<void>? _restoreFuture;
|
||||||
|
|
||||||
Account? get account => _resource.data;
|
Account? get account => _resource.data;
|
||||||
PendingLogin? get pendingLogin => _pendingLogin;
|
PendingLogin? get pendingLogin => _pendingLogin;
|
||||||
@@ -34,6 +35,7 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
bool get isLoading => _resource.isLoading;
|
bool get isLoading => _resource.isLoading;
|
||||||
Object? get error => _resource.error;
|
Object? get error => _resource.error;
|
||||||
bool get isReady => (!isLoading) && (account != null);
|
bool get isReady => (!isLoading) && (account != null);
|
||||||
|
Future<void>? get restoreFuture => _restoreFuture;
|
||||||
|
|
||||||
Account? currentUser() {
|
Account? currentUser() {
|
||||||
final acc = account;
|
final acc = account;
|
||||||
@@ -220,4 +222,12 @@ class AccountProvider extends ChangeNotifier {
|
|||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> restoreIfPossible() {
|
||||||
|
return _restoreFuture ??= () async {
|
||||||
|
final hasAuth = await AuthorizationService.isAuthorizationStored();
|
||||||
|
if (!hasAuth) return;
|
||||||
|
await restore();
|
||||||
|
}();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:pshared/models/payment/type.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/widgets/sidebar/destinations.dart';
|
import 'package:pweb/widgets/sidebar/destinations.dart';
|
||||||
|
|
||||||
@@ -19,9 +17,6 @@ class PayoutRoutes {
|
|||||||
static const editWallet = 'payout-edit-wallet';
|
static const editWallet = 'payout-edit-wallet';
|
||||||
static const walletTopUp = 'payout-wallet-top-up';
|
static const walletTopUp = 'payout-wallet-top-up';
|
||||||
|
|
||||||
static const paymentTypeQuery = 'paymentType';
|
|
||||||
static const returnToQuery = 'returnTo';
|
|
||||||
|
|
||||||
static const dashboardPath = '/dashboard';
|
static const dashboardPath = '/dashboard';
|
||||||
static const recipientsPath = '/dashboard/recipients';
|
static const recipientsPath = '/dashboard/recipients';
|
||||||
static const addRecipientPath = '/dashboard/recipients/add';
|
static const addRecipientPath = '/dashboard/recipients/add';
|
||||||
@@ -108,70 +103,10 @@ class PayoutRoutes {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Map<String, String> buildQueryParameters({
|
|
||||||
PaymentType? paymentType,
|
|
||||||
PayoutDestination? returnTo,
|
|
||||||
}) {
|
|
||||||
final params = <String, String>{
|
|
||||||
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 {
|
extension PayoutNavigation on BuildContext {
|
||||||
void goToPayout(PayoutDestination destination) => goNamed(PayoutRoutes.nameFor(destination));
|
void goToPayout(PayoutDestination destination) => goNamed(PayoutRoutes.nameFor(destination));
|
||||||
|
|
||||||
void pushToPayout(PayoutDestination destination) => pushNamed(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),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
@@ -4,13 +4,10 @@ import 'package:go_router/go_router.dart';
|
|||||||
|
|
||||||
import 'package:provider/provider.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:pshared/provider/recipient/provider.dart';
|
||||||
|
|
||||||
import 'package:pweb/app/router/pages.dart';
|
import 'package:pweb/app/router/pages.dart';
|
||||||
import 'package:pweb/app/router/payout_routes.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/form/page.dart';
|
||||||
import 'package:pweb/pages/address_book/page/page.dart';
|
import 'package:pweb/pages/address_book/page/page.dart';
|
||||||
import 'package:pweb/pages/dashboard/dashboard.dart';
|
import 'package:pweb/pages/dashboard/dashboard.dart';
|
||||||
@@ -20,7 +17,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/providers/wallets.dart';
|
import 'package:pweb/providers/page_selector.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';
|
||||||
@@ -39,22 +36,15 @@ RouteBase payoutShellRoute() => ShellRoute(
|
|||||||
path: routerPage(Pages.dashboard),
|
path: routerPage(Pages.dashboard),
|
||||||
pageBuilder: (context, _) => NoTransitionPage(
|
pageBuilder: (context, _) => NoTransitionPage(
|
||||||
child: DashboardPage(
|
child: DashboardPage(
|
||||||
onRecipientSelected: (recipient) => _startPayment(
|
onRecipientSelected: (recipient) => context
|
||||||
context,
|
.read<PageSelectorProvider>()
|
||||||
recipient: recipient,
|
.selectRecipient(context, recipient),
|
||||||
returnTo: PayoutDestination.dashboard,
|
onGoToPaymentWithoutRecipient: (type) => context
|
||||||
),
|
.read<PageSelectorProvider>()
|
||||||
onGoToPaymentWithoutRecipient: (type) => _startPayment(
|
.startPaymentWithoutRecipient(context, type),
|
||||||
context,
|
onTopUp: (wallet) => context
|
||||||
recipient: null,
|
.read<PageSelectorProvider>()
|
||||||
paymentType: type,
|
.openWalletTopUp(context, wallet),
|
||||||
returnTo: PayoutDestination.dashboard,
|
|
||||||
),
|
|
||||||
onTopUp: (wallet) => _openWalletTopUp(
|
|
||||||
context,
|
|
||||||
wallet,
|
|
||||||
returnTo: PayoutDestination.dashboard,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -65,16 +55,15 @@ RouteBase payoutShellRoute() => ShellRoute(
|
|||||||
final loc = AppLocalizations.of(context)!;
|
final loc = AppLocalizations.of(context)!;
|
||||||
return NoTransitionPage(
|
return NoTransitionPage(
|
||||||
child: RecipientAddressBookPage(
|
child: RecipientAddressBookPage(
|
||||||
onRecipientSelected: (recipient) => _startPayment(
|
onRecipientSelected: (recipient) => context
|
||||||
context,
|
.read<PageSelectorProvider>()
|
||||||
recipient: recipient,
|
.selectRecipient(context, recipient, fromList: true),
|
||||||
returnTo: PayoutDestination.recipients,
|
onAddRecipient: () => context
|
||||||
),
|
.read<PageSelectorProvider>()
|
||||||
onAddRecipient: () => _openAddRecipient(context),
|
.goToAddRecipient(context),
|
||||||
onEditRecipient: (recipient) => _openAddRecipient(
|
onEditRecipient: (recipient) => context
|
||||||
context,
|
.read<PageSelectorProvider>()
|
||||||
recipient: recipient,
|
.editRecipient(context, recipient, fromList: true),
|
||||||
),
|
|
||||||
onDeleteRecipient: (recipient) => executeActionWithNotification(
|
onDeleteRecipient: (recipient) => executeActionWithNotification(
|
||||||
context: context,
|
context: context,
|
||||||
action: () async =>
|
action: () async =>
|
||||||
@@ -90,11 +79,15 @@ RouteBase payoutShellRoute() => ShellRoute(
|
|||||||
name: PayoutRoutes.addRecipient,
|
name: PayoutRoutes.addRecipient,
|
||||||
path: PayoutRoutes.addRecipientPath,
|
path: PayoutRoutes.addRecipientPath,
|
||||||
pageBuilder: (context, _) {
|
pageBuilder: (context, _) {
|
||||||
final recipient = context.read<RecipientsProvider>().currentObject;
|
final selector = context.read<PageSelectorProvider>();
|
||||||
|
final recipient = selector.recipientProvider.currentObject;
|
||||||
return NoTransitionPage(
|
return NoTransitionPage(
|
||||||
child: AdressBookRecipientForm(
|
child: AdressBookRecipientForm(
|
||||||
recipient: recipient,
|
recipient: recipient,
|
||||||
onSaved: (_) => context.goToPayout(PayoutDestination.recipients),
|
onSaved: (_) => selector.selectPage(
|
||||||
|
context,
|
||||||
|
PayoutDestination.recipients,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -102,20 +95,13 @@ RouteBase payoutShellRoute() => ShellRoute(
|
|||||||
GoRoute(
|
GoRoute(
|
||||||
name: PayoutRoutes.payment,
|
name: PayoutRoutes.payment,
|
||||||
path: PayoutRoutes.paymentPath,
|
path: PayoutRoutes.paymentPath,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, _) => NoTransitionPage(
|
||||||
final fallbackDestination = PayoutRoutes.fallbackFromState(
|
|
||||||
state,
|
|
||||||
defaultDestination: PayoutDestination.dashboard,
|
|
||||||
);
|
|
||||||
|
|
||||||
return NoTransitionPage(
|
|
||||||
child: PaymentPage(
|
child: PaymentPage(
|
||||||
onBack: (_) => _popOrGo(context, fallbackDestination),
|
onBack: (_) => context
|
||||||
initialPaymentType: PayoutRoutes.paymentTypeFromState(state),
|
.read<PageSelectorProvider>()
|
||||||
fallbackDestination: fallbackDestination,
|
.goBackFromPayment(context),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: PayoutRoutes.settings,
|
name: PayoutRoutes.settings,
|
||||||
@@ -136,30 +122,24 @@ RouteBase payoutShellRoute() => ShellRoute(
|
|||||||
path: PayoutRoutes.methodsPath,
|
path: PayoutRoutes.methodsPath,
|
||||||
pageBuilder: (context, _) => NoTransitionPage(
|
pageBuilder: (context, _) => NoTransitionPage(
|
||||||
child: PaymentConfigPage(
|
child: PaymentConfigPage(
|
||||||
onWalletTap: (wallet) => _openWalletEdit(
|
onWalletTap: (wallet) => context
|
||||||
context,
|
.read<PageSelectorProvider>()
|
||||||
wallet,
|
.selectWallet(context, wallet),
|
||||||
returnTo: PayoutDestination.methods,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: PayoutRoutes.editWallet,
|
name: PayoutRoutes.editWallet,
|
||||||
path: PayoutRoutes.editWalletPath,
|
path: PayoutRoutes.editWalletPath,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, _) {
|
||||||
final walletsProvider = context.read<WalletsProvider>();
|
final provider = context.read<PageSelectorProvider>();
|
||||||
final wallet = walletsProvider.selectedWallet;
|
final wallet = provider.walletsProvider.selectedWallet;
|
||||||
final loc = AppLocalizations.of(context)!;
|
final loc = AppLocalizations.of(context)!;
|
||||||
final fallbackDestination = PayoutRoutes.fallbackFromState(
|
|
||||||
state,
|
|
||||||
defaultDestination: PayoutDestination.methods,
|
|
||||||
);
|
|
||||||
|
|
||||||
return NoTransitionPage(
|
return NoTransitionPage(
|
||||||
child: wallet != null
|
child: wallet != null
|
||||||
? WalletEditPage(
|
? WalletEditPage(
|
||||||
onBack: () => _popOrGo(context, fallbackDestination),
|
onBack: () => provider.goBackFromWalletEdit(context),
|
||||||
)
|
)
|
||||||
: Center(child: Text(loc.noWalletSelected)),
|
: Center(child: Text(loc.noWalletSelected)),
|
||||||
);
|
);
|
||||||
@@ -168,65 +148,13 @@ RouteBase payoutShellRoute() => ShellRoute(
|
|||||||
GoRoute(
|
GoRoute(
|
||||||
name: PayoutRoutes.walletTopUp,
|
name: PayoutRoutes.walletTopUp,
|
||||||
path: PayoutRoutes.walletTopUpPath,
|
path: PayoutRoutes.walletTopUpPath,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, _) => NoTransitionPage(
|
||||||
final fallbackDestination = PayoutRoutes.fallbackFromState(
|
|
||||||
state,
|
|
||||||
defaultDestination: PayoutDestination.dashboard,
|
|
||||||
);
|
|
||||||
|
|
||||||
return NoTransitionPage(
|
|
||||||
child: WalletTopUpPage(
|
child: WalletTopUpPage(
|
||||||
onBack: () => _popOrGo(context, fallbackDestination),
|
onBack: () => context
|
||||||
|
.read<PageSelectorProvider>()
|
||||||
|
.goBackFromWalletTopUp(context),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
void _startPayment(
|
|
||||||
BuildContext context, {
|
|
||||||
Recipient? recipient,
|
|
||||||
PaymentType? paymentType,
|
|
||||||
required PayoutDestination returnTo,
|
|
||||||
}) {
|
|
||||||
context.read<RecipientsProvider>().setCurrentObject(recipient?.id);
|
|
||||||
context.pushToPayment(
|
|
||||||
paymentType: paymentType,
|
|
||||||
returnTo: returnTo,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _openAddRecipient(
|
|
||||||
BuildContext context, {
|
|
||||||
Recipient? recipient,
|
|
||||||
}) {
|
|
||||||
context.read<RecipientsProvider>().setCurrentObject(recipient?.id);
|
|
||||||
context.pushNamed(PayoutRoutes.addRecipient);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _openWalletEdit(
|
|
||||||
BuildContext context,
|
|
||||||
Wallet wallet, {
|
|
||||||
required PayoutDestination returnTo,
|
|
||||||
}) {
|
|
||||||
context.read<WalletsProvider>().selectWallet(wallet);
|
|
||||||
context.pushToEditWallet(returnTo: returnTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _openWalletTopUp(
|
|
||||||
BuildContext context,
|
|
||||||
Wallet wallet, {
|
|
||||||
required PayoutDestination returnTo,
|
|
||||||
}) {
|
|
||||||
context.read<WalletsProvider>().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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import 'package:pweb/app/timeago.dart';
|
|||||||
import 'package:pweb/providers/carousel.dart';
|
import 'package:pweb/providers/carousel.dart';
|
||||||
import 'package:pweb/providers/mock_payment.dart';
|
import 'package:pweb/providers/mock_payment.dart';
|
||||||
import 'package:pweb/providers/operatioins.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/two_factor.dart';
|
||||||
import 'package:pweb/providers/upload_history.dart';
|
import 'package:pweb/providers/upload_history.dart';
|
||||||
import 'package:pweb/providers/wallets.dart';
|
import 'package:pweb/providers/wallets.dart';
|
||||||
@@ -93,6 +94,12 @@ void main() async {
|
|||||||
create: (_) => MockPaymentProvider(),
|
create: (_) => MockPaymentProvider(),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
ChangeNotifierProxyProvider3<RecipientsProvider, WalletsProvider, PaymentMethodsProvider, PageSelectorProvider>(
|
||||||
|
create: (context) => PageSelectorProvider(),
|
||||||
|
update: (context, recipientProv, walletsProv, methodsProv, previous) =>
|
||||||
|
previous ?? PageSelectorProvider()..update(recipientProv, walletsProv, methodsProv),
|
||||||
|
),
|
||||||
|
|
||||||
ChangeNotifierProvider(
|
ChangeNotifierProvider(
|
||||||
create: (_) => OperationProvider(OperationService())..loadOperations(),
|
create: (_) => OperationProvider(OperationService())..loadOperations(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -17,15 +17,34 @@ class AccountLoader extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Consumer<AccountProvider>(builder: (context, provider, _) {
|
Widget build(BuildContext context) => Consumer<AccountProvider>(builder: (context, provider, _) {
|
||||||
if (provider.isLoading) return const Center(child: CircularProgressIndicator());
|
if (provider.account != null) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
});
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
if (provider.error != null) {
|
if (provider.error != null) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
postNotifyUserOfErrorX(
|
postNotifyUserOfErrorX(
|
||||||
context: context,
|
context: context,
|
||||||
errorSituation: AppLocalizations.of(context)!.errorLogin,
|
errorSituation: AppLocalizations.of(context)!.errorLogin,
|
||||||
exception: provider.error!,
|
exception: provider.error!,
|
||||||
);
|
);
|
||||||
navigateAndReplace(context, Pages.login);
|
navigateAndReplace(context, Pages.login);
|
||||||
|
});
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (provider.restoreFuture == null) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
provider.restoreIfPossible().catchError((error, stack) {
|
||||||
|
debugPrint('Account restore failed: $error');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (provider.isLoading) return const Center(child: CircularProgressIndicator());
|
||||||
if (provider.account == null) {
|
if (provider.account == null) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => navigateAndReplace(context, Pages.login));
|
WidgetsBinding.instance.addPostFrameCallback((_) => navigateAndReplace(context, Pages.login));
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
|
Future<bool> showDeleteConfirmationDialog(BuildContext context) async {
|
||||||
|
final l10n = AppLocalizations.of(context)!;
|
||||||
|
return await showDialog<bool>(
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -1,34 +1,19 @@
|
|||||||
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/type.dart';
|
|
||||||
import 'package:pshared/models/payment/type.dart';
|
|
||||||
import 'package:pshared/models/recipient/recipient.dart';
|
import 'package:pshared/models/recipient/recipient.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/models/wallet.dart';
|
import 'package:pweb/providers/payment_flow_provider.dart';
|
||||||
import 'package:pweb/providers/payment_flow.dart';
|
import 'package:pweb/pages/payment_methods/widgets/payment_page_body.dart';
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/body.dart';
|
import 'package:pweb/providers/page_selector.dart';
|
||||||
import 'package:pweb/providers/wallets.dart';
|
|
||||||
import 'package:pweb/widgets/sidebar/destinations.dart';
|
|
||||||
|
|
||||||
|
|
||||||
class PaymentPage extends StatefulWidget {
|
class PaymentPage extends StatefulWidget {
|
||||||
final ValueChanged<Recipient?>? onBack;
|
final ValueChanged<Recipient?>? onBack;
|
||||||
final PaymentType? initialPaymentType;
|
|
||||||
final PayoutDestination fallbackDestination;
|
|
||||||
|
|
||||||
const PaymentPage({
|
const PaymentPage({super.key, this.onBack});
|
||||||
super.key,
|
|
||||||
this.onBack,
|
|
||||||
this.initialPaymentType,
|
|
||||||
this.fallbackDestination = PayoutDestination.dashboard,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PaymentPage> createState() => _PaymentPageState();
|
State<PaymentPage> createState() => _PaymentPageState();
|
||||||
@@ -44,8 +29,9 @@ class _PaymentPageState extends State<PaymentPage> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
_searchController = TextEditingController();
|
_searchController = TextEditingController();
|
||||||
_searchFocusNode = FocusNode();
|
_searchFocusNode = FocusNode();
|
||||||
|
final pageSelector = context.read<PageSelectorProvider>();
|
||||||
_flowProvider = PaymentFlowProvider(
|
_flowProvider = PaymentFlowProvider(
|
||||||
initialType: widget.initialPaymentType ?? PaymentType.bankAccount,
|
initialType: pageSelector.getDefaultPaymentType(),
|
||||||
);
|
);
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => _initializePaymentPage());
|
WidgetsBinding.instance.addPostFrameCallback((_) => _initializePaymentPage());
|
||||||
@@ -60,15 +46,11 @@ class _PaymentPageState extends State<PaymentPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _initializePaymentPage() {
|
void _initializePaymentPage() {
|
||||||
final methodsProvider = context.read<PaymentMethodsProvider>();
|
final pageSelector = context.read<PageSelectorProvider>();
|
||||||
_handleWalletAutoSelection(methodsProvider);
|
|
||||||
|
|
||||||
final recipient = context.read<RecipientsProvider>().currentObject;
|
pageSelector.handleWalletAutoSelection();
|
||||||
_syncFlowProvider(
|
|
||||||
recipient: recipient,
|
_flowProvider.syncWithSelector(pageSelector);
|
||||||
methodsProvider: methodsProvider,
|
|
||||||
preferredType: widget.initialPaymentType,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleSearchChanged(String query) {
|
void _handleSearchChanged(String query) {
|
||||||
@@ -76,28 +58,22 @@ class _PaymentPageState extends State<PaymentPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handleRecipientSelected(Recipient recipient) {
|
void _handleRecipientSelected(Recipient recipient) {
|
||||||
|
final pageSelector = context.read<PageSelectorProvider>();
|
||||||
final recipientProvider = context.read<RecipientsProvider>();
|
final recipientProvider = context.read<RecipientsProvider>();
|
||||||
final methodsProvider = context.read<PaymentMethodsProvider>();
|
|
||||||
|
|
||||||
recipientProvider.setCurrentObject(recipient.id);
|
recipientProvider.setCurrentObject(recipient.id);
|
||||||
_flowProvider.reset(
|
pageSelector.selectRecipient(context, recipient);
|
||||||
recipient: recipient,
|
_flowProvider.reset(pageSelector);
|
||||||
availableTypes: _availablePaymentTypes(recipient, methodsProvider),
|
|
||||||
preferredType: widget.initialPaymentType,
|
|
||||||
);
|
|
||||||
_clearSearchField();
|
_clearSearchField();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleRecipientCleared() {
|
void _handleRecipientCleared() {
|
||||||
|
final pageSelector = context.read<PageSelectorProvider>();
|
||||||
final recipientProvider = context.read<RecipientsProvider>();
|
final recipientProvider = context.read<RecipientsProvider>();
|
||||||
final methodsProvider = context.read<PaymentMethodsProvider>();
|
|
||||||
|
|
||||||
recipientProvider.setCurrentObject(null);
|
recipientProvider.setCurrentObject(null);
|
||||||
_flowProvider.reset(
|
pageSelector.selectRecipient(context, null);
|
||||||
recipient: null,
|
_flowProvider.reset(pageSelector);
|
||||||
availableTypes: _availablePaymentTypes(null, methodsProvider),
|
|
||||||
preferredType: widget.initialPaymentType,
|
|
||||||
);
|
|
||||||
_clearSearchField();
|
_clearSearchField();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,26 +90,13 @@ class _PaymentPageState extends State<PaymentPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final methodsProvider = context.watch<PaymentMethodsProvider>();
|
final pageSelector = context.watch<PageSelectorProvider>();
|
||||||
final recipientProvider = context.watch<RecipientsProvider>();
|
_flowProvider.syncWithSelector(pageSelector);
|
||||||
final recipient = recipientProvider.currentObject;
|
|
||||||
final availableTypes = _availablePaymentTypes(recipient, methodsProvider);
|
|
||||||
|
|
||||||
_syncFlowProvider(
|
|
||||||
recipient: recipient,
|
|
||||||
methodsProvider: methodsProvider,
|
|
||||||
preferredType: recipient != null ? widget.initialPaymentType : null,
|
|
||||||
);
|
|
||||||
|
|
||||||
return ChangeNotifierProvider.value(
|
return ChangeNotifierProvider.value(
|
||||||
value: _flowProvider,
|
value: _flowProvider,
|
||||||
child: PaymentPageBody(
|
child: PaymentPageBody(
|
||||||
onBack: widget.onBack,
|
onBack: widget.onBack,
|
||||||
fallbackDestination: widget.fallbackDestination,
|
|
||||||
recipient: recipient,
|
|
||||||
recipientProvider: recipientProvider,
|
|
||||||
methodsProvider: methodsProvider,
|
|
||||||
availablePaymentTypes: availableTypes,
|
|
||||||
searchController: _searchController,
|
searchController: _searchController,
|
||||||
searchFocusNode: _searchFocusNode,
|
searchFocusNode: _searchFocusNode,
|
||||||
onSearchChanged: _handleSearchChanged,
|
onSearchChanged: _handleSearchChanged,
|
||||||
@@ -143,56 +106,4 @@ class _PaymentPageState extends State<PaymentPage> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleWalletAutoSelection(PaymentMethodsProvider methodsProvider) {
|
|
||||||
final wallet = context.read<WalletsProvider>().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),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
33
frontend/pweb/lib/pages/payment_methods/payment_details.dart
Normal file
33
frontend/pweb/lib/pages/payment_methods/payment_details.dart
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
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<Recipient?>? 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
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<Recipient?>? onBack;
|
|
||||||
final Recipient? recipient;
|
|
||||||
final RecipientsProvider recipientProvider;
|
|
||||||
final PaymentMethodsProvider methodsProvider;
|
|
||||||
final MethodMap availablePaymentTypes;
|
|
||||||
final PayoutDestination fallbackDestination;
|
|
||||||
final TextEditingController searchController;
|
|
||||||
final FocusNode searchFocusNode;
|
|
||||||
final ValueChanged<String> onSearchChanged;
|
|
||||||
final ValueChanged<Recipient> 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
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/dashboard/payouts/payment_form.dart';
|
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/back_button.dart';
|
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/header.dart';
|
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/method_selector.dart';
|
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/send_button.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<Recipient?>? onBack;
|
|
||||||
final Recipient? recipient;
|
|
||||||
final RecipientsProvider recipientProvider;
|
|
||||||
final PaymentMethodsProvider methodsProvider;
|
|
||||||
final MethodMap availablePaymentTypes;
|
|
||||||
final PayoutDestination fallbackDestination;
|
|
||||||
final TextEditingController searchController;
|
|
||||||
final FocusNode searchFocusNode;
|
|
||||||
final ValueChanged<String> onSearchChanged;
|
|
||||||
final ValueChanged<Recipient> 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<PaymentFlowProvider>();
|
|
||||||
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),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,8 @@ import 'package:pshared/models/recipient/recipient.dart';
|
|||||||
|
|
||||||
import 'package:pweb/pages/payment_methods/form.dart';
|
import 'package:pweb/pages/payment_methods/form.dart';
|
||||||
import 'package:pweb/pages/payment_methods/widgets/section_title.dart';
|
import 'package:pweb/pages/payment_methods/widgets/section_title.dart';
|
||||||
import 'package:pweb/providers/payment_flow.dart';
|
import 'package:pweb/providers/page_selector.dart';
|
||||||
|
import 'package:pweb/providers/payment_flow_provider.dart';
|
||||||
import 'package:pweb/utils/dimensions.dart';
|
import 'package:pweb/utils/dimensions.dart';
|
||||||
import 'package:pweb/utils/payment/selector_type.dart';
|
import 'package:pweb/utils/payment/selector_type.dart';
|
||||||
|
|
||||||
@@ -15,14 +16,14 @@ import 'package:pweb/generated/i18n/app_localizations.dart';
|
|||||||
|
|
||||||
class PaymentInfoSection extends StatelessWidget {
|
class PaymentInfoSection extends StatelessWidget {
|
||||||
final AppDimensions dimensions;
|
final AppDimensions dimensions;
|
||||||
final MethodMap availableTypes;
|
final PageSelectorProvider pageSelector;
|
||||||
final PaymentFlowProvider flowProvider;
|
final PaymentFlowProvider flowProvider;
|
||||||
final Recipient? recipient;
|
final Recipient? recipient;
|
||||||
|
|
||||||
const PaymentInfoSection({
|
const PaymentInfoSection({
|
||||||
super.key,
|
super.key,
|
||||||
required this.dimensions,
|
required this.dimensions,
|
||||||
required this.availableTypes,
|
required this.pageSelector,
|
||||||
required this.flowProvider,
|
required this.flowProvider,
|
||||||
required this.recipient,
|
required this.recipient,
|
||||||
});
|
});
|
||||||
@@ -31,11 +32,11 @@ class PaymentInfoSection extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final loc = AppLocalizations.of(context)!;
|
final loc = AppLocalizations.of(context)!;
|
||||||
final hasRecipient = recipient != null;
|
final hasRecipient = recipient != null;
|
||||||
final MethodMap resolvedAvailableTypes = hasRecipient
|
final MethodMap availableTypes = hasRecipient
|
||||||
? availableTypes
|
? pageSelector.getAvailablePaymentTypes()
|
||||||
: {for (final type in PaymentType.values) type: null};
|
: {for (final type in PaymentType.values) type: null};
|
||||||
|
|
||||||
if (hasRecipient && resolvedAvailableTypes.isEmpty) {
|
if (hasRecipient && availableTypes.isEmpty) {
|
||||||
return Text(loc.recipientNoPaymentDetails);
|
return Text(loc.recipientNoPaymentDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ class PaymentInfoSection extends StatelessWidget {
|
|||||||
SectionTitle(loc.paymentInfo),
|
SectionTitle(loc.paymentInfo),
|
||||||
SizedBox(height: dimensions.paddingSmall),
|
SizedBox(height: dimensions.paddingSmall),
|
||||||
PaymentTypeSelector(
|
PaymentTypeSelector(
|
||||||
availableTypes: resolvedAvailableTypes,
|
availableTypes: availableTypes,
|
||||||
selectedType: selectedType,
|
selectedType: selectedType,
|
||||||
onSelected: (type) => flowProvider.selectType(
|
onSelected: (type) => flowProvider.selectType(
|
||||||
type,
|
type,
|
||||||
@@ -62,7 +63,7 @@ class PaymentInfoSection extends StatelessWidget {
|
|||||||
flowProvider.setManualPaymentData(data);
|
flowProvider.setManualPaymentData(data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
initialData: hasRecipient ? resolvedAvailableTypes[selectedType] : flowProvider.manualPaymentData,
|
initialData: hasRecipient ? availableTypes[selectedType] : flowProvider.manualPaymentData,
|
||||||
isEditable: !hasRecipient,
|
isEditable: !hasRecipient,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -2,33 +2,26 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/payment/methods/data.dart';
|
|
||||||
import 'package:pshared/models/recipient/recipient.dart';
|
import 'package:pshared/models/recipient/recipient.dart';
|
||||||
import 'package:pshared/provider/recipient/pmethods.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/payment_methods/payment_page/back_button.dart';
|
import 'package:pweb/pages/payment_methods/header.dart';
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/header.dart';
|
import 'package:pweb/pages/payment_methods/method_selector.dart';
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/method_selector.dart';
|
import 'package:pweb/pages/payment_methods/send_button.dart';
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/send_button.dart';
|
|
||||||
import 'package:pweb/pages/dashboard/payouts/payment_form.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/payment_info_section.dart';
|
||||||
import 'package:pweb/pages/payment_methods/widgets/recipient_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/pages/payment_methods/widgets/section_title.dart';
|
||||||
import 'package:pweb/providers/payment_flow.dart';
|
import 'package:pweb/providers/page_selector.dart';
|
||||||
|
import 'package:pweb/providers/payment_flow_provider.dart';
|
||||||
import 'package:pweb/utils/dimensions.dart';
|
import 'package:pweb/utils/dimensions.dart';
|
||||||
import 'package:pweb/widgets/sidebar/destinations.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
class PaymentPageContent extends StatelessWidget {
|
class PaymentPageBody extends StatelessWidget {
|
||||||
final ValueChanged<Recipient?>? onBack;
|
final ValueChanged<Recipient?>? onBack;
|
||||||
final Recipient? recipient;
|
|
||||||
final RecipientsProvider recipientProvider;
|
|
||||||
final PaymentMethodsProvider methodsProvider;
|
|
||||||
final MethodMap availablePaymentTypes;
|
|
||||||
final PayoutDestination fallbackDestination;
|
|
||||||
final TextEditingController searchController;
|
final TextEditingController searchController;
|
||||||
final FocusNode searchFocusNode;
|
final FocusNode searchFocusNode;
|
||||||
final ValueChanged<String> onSearchChanged;
|
final ValueChanged<String> onSearchChanged;
|
||||||
@@ -36,14 +29,9 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
final VoidCallback onRecipientCleared;
|
final VoidCallback onRecipientCleared;
|
||||||
final VoidCallback onSend;
|
final VoidCallback onSend;
|
||||||
|
|
||||||
const PaymentPageContent({
|
const PaymentPageBody({
|
||||||
super.key,
|
super.key,
|
||||||
required this.onBack,
|
required this.onBack,
|
||||||
required this.recipient,
|
|
||||||
required this.recipientProvider,
|
|
||||||
required this.methodsProvider,
|
|
||||||
required this.availablePaymentTypes,
|
|
||||||
required this.fallbackDestination,
|
|
||||||
required this.searchController,
|
required this.searchController,
|
||||||
required this.searchFocusNode,
|
required this.searchFocusNode,
|
||||||
required this.onSearchChanged,
|
required this.onSearchChanged,
|
||||||
@@ -55,9 +43,21 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final dimensions = AppDimensions();
|
final dimensions = AppDimensions();
|
||||||
|
final pageSelector = context.watch<PageSelectorProvider>();
|
||||||
|
final methodsProvider = context.watch<PaymentMethodsProvider>();
|
||||||
|
final recipientProvider = context.watch<RecipientsProvider>();
|
||||||
final flowProvider = context.watch<PaymentFlowProvider>();
|
final flowProvider = context.watch<PaymentFlowProvider>();
|
||||||
|
final recipient = pageSelector.selectedRecipient;
|
||||||
final loc = AppLocalizations.of(context)!;
|
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(
|
return Align(
|
||||||
alignment: Alignment.topCenter,
|
alignment: Alignment.topCenter,
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
@@ -73,20 +73,18 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
PaymentBackButton(
|
PaymentBackButton(onBack: onBack, pageSelector: pageSelector),
|
||||||
onBack: onBack,
|
|
||||||
recipient: recipient,
|
|
||||||
fallbackDestination: fallbackDestination,
|
|
||||||
),
|
|
||||||
SizedBox(height: dimensions.paddingSmall),
|
SizedBox(height: dimensions.paddingSmall),
|
||||||
PaymentHeader(),
|
PaymentHeader(),
|
||||||
SizedBox(height: dimensions.paddingXXLarge),
|
SizedBox(height: dimensions.paddingXXLarge),
|
||||||
|
|
||||||
SectionTitle(loc.sourceOfFunds),
|
SectionTitle(loc.sourceOfFunds),
|
||||||
SizedBox(height: dimensions.paddingSmall),
|
SizedBox(height: dimensions.paddingSmall),
|
||||||
PaymentMethodSelector(
|
PaymentMethodSelector(
|
||||||
onMethodChanged: (m) => methodsProvider.setCurrentObject(m.id),
|
onMethodChanged: (m) => methodsProvider.setCurrentObject(m.id),
|
||||||
),
|
),
|
||||||
SizedBox(height: dimensions.paddingXLarge),
|
SizedBox(height: dimensions.paddingXLarge),
|
||||||
|
|
||||||
RecipientSection(
|
RecipientSection(
|
||||||
recipient: recipient,
|
recipient: recipient,
|
||||||
dimensions: dimensions,
|
dimensions: dimensions,
|
||||||
@@ -97,15 +95,19 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
onRecipientSelected: onRecipientSelected,
|
onRecipientSelected: onRecipientSelected,
|
||||||
onRecipientCleared: onRecipientCleared,
|
onRecipientCleared: onRecipientCleared,
|
||||||
),
|
),
|
||||||
|
|
||||||
SizedBox(height: dimensions.paddingXLarge),
|
SizedBox(height: dimensions.paddingXLarge),
|
||||||
|
|
||||||
PaymentInfoSection(
|
PaymentInfoSection(
|
||||||
dimensions: dimensions,
|
dimensions: dimensions,
|
||||||
|
pageSelector: pageSelector,
|
||||||
flowProvider: flowProvider,
|
flowProvider: flowProvider,
|
||||||
recipient: recipient,
|
recipient: recipient,
|
||||||
availableTypes: availablePaymentTypes,
|
|
||||||
),
|
),
|
||||||
|
|
||||||
SizedBox(height: dimensions.paddingLarge),
|
SizedBox(height: dimensions.paddingLarge),
|
||||||
const PaymentFormWidget(),
|
const PaymentFormWidget(),
|
||||||
|
|
||||||
SizedBox(height: dimensions.paddingXXXLarge),
|
SizedBox(height: dimensions.paddingXXXLarge),
|
||||||
SendButton(onPressed: onSend),
|
SendButton(onPressed: onSend),
|
||||||
SizedBox(height: dimensions.paddingLarge),
|
SizedBox(height: dimensions.paddingLarge),
|
||||||
@@ -118,3 +120,31 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PaymentBackButton extends StatelessWidget {
|
||||||
|
final ValueChanged<Recipient?>? 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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,11 +2,8 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/payment/type.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/providers/wallets.dart';
|
||||||
import 'package:pweb/widgets/sidebar/destinations.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
@@ -23,14 +20,12 @@ class SendPayoutButton extends StatelessWidget {
|
|||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
final pageSelectorProvider = context.read<PageSelectorProvider>();
|
||||||
final walletsProvider = context.read<WalletsProvider>();
|
final walletsProvider = context.read<WalletsProvider>();
|
||||||
final wallet = walletsProvider.selectedWallet;
|
final wallet = walletsProvider.selectedWallet;
|
||||||
|
|
||||||
if (wallet != null) {
|
if (wallet != null) {
|
||||||
context.pushToPayment(
|
pageSelectorProvider.startPaymentFromWallet(context, wallet);
|
||||||
paymentType: PaymentType.wallet,
|
|
||||||
returnTo: PayoutDestination.editwallet,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Text(loc.payoutNavSendPayout),
|
child: Text(loc.payoutNavSendPayout),
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pweb/app/router/payout_routes.dart';
|
import 'package:pweb/providers/page_selector.dart';
|
||||||
import 'package:pweb/providers/wallets.dart';
|
import 'package:pweb/providers/wallets.dart';
|
||||||
import 'package:pweb/widgets/sidebar/destinations.dart';
|
|
||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
@@ -27,7 +26,7 @@ class TopUpButton extends StatelessWidget{
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
context.pushToWalletTopUp(returnTo: PayoutDestination.editwallet);
|
context.read<PageSelectorProvider>().openWalletTopUp(context, wallet);
|
||||||
},
|
},
|
||||||
child: Text(loc.topUpBalance),
|
child: Text(loc.topUpBalance),
|
||||||
);
|
);
|
||||||
|
|||||||
264
frontend/pweb/lib/providers/page_selector.dart
Normal file
264
frontend/pweb/lib/providers/page_selector.dart
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
@@ -1,110 +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';
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
79
frontend/pweb/lib/providers/payment_flow_provider.dart
Normal file
79
frontend/pweb/lib/providers/payment_flow_provider.dart
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import 'package:pshared/models/resources.dart';
|
|||||||
import 'package:pshared/provider/permissions.dart';
|
import 'package:pshared/provider/permissions.dart';
|
||||||
|
|
||||||
import 'package:pweb/pages/loader.dart';
|
import 'package:pweb/pages/loader.dart';
|
||||||
|
import 'package:pweb/providers/page_selector.dart';
|
||||||
import 'package:pweb/utils/logout.dart';
|
import 'package:pweb/utils/logout.dart';
|
||||||
import 'package:pweb/widgets/appbar/app_bar.dart';
|
import 'package:pweb/widgets/appbar/app_bar.dart';
|
||||||
import 'package:pweb/widgets/sidebar/destinations.dart';
|
import 'package:pweb/widgets/sidebar/destinations.dart';
|
||||||
@@ -31,10 +32,9 @@ class PageSelector extends StatelessWidget {
|
|||||||
final permissions = context.read<PermissionsProvider>();
|
final permissions = context.read<PermissionsProvider>();
|
||||||
if (!permissions.isReady) return Center(child: CircularProgressIndicator());
|
if (!permissions.isReady) return Center(child: CircularProgressIndicator());
|
||||||
|
|
||||||
|
final provider = context.watch<PageSelectorProvider>();
|
||||||
|
|
||||||
final bool restrictedAccess = !permissions.canRead(ResourceType.chainWallets);
|
final bool restrictedAccess = !permissions.canRead(ResourceType.chainWallets);
|
||||||
final fallbackDestination = restrictedAccess
|
|
||||||
? PayoutDestination.settings
|
|
||||||
: PayoutDestination.dashboard;
|
|
||||||
final allowedDestinations = restrictedAccess
|
final allowedDestinations = restrictedAccess
|
||||||
? <PayoutDestination>{
|
? <PayoutDestination>{
|
||||||
PayoutDestination.settings,
|
PayoutDestination.settings,
|
||||||
@@ -44,10 +44,10 @@ class PageSelector extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
: PayoutDestination.values.toSet();
|
: PayoutDestination.values.toSet();
|
||||||
|
|
||||||
final routeDestination = _destinationFromState(routerState);
|
final routeDestination = _destinationFromState(routerState) ?? provider.selected;
|
||||||
final selected = routeDestination != null && allowedDestinations.contains(routeDestination)
|
final selected = allowedDestinations.contains(routeDestination)
|
||||||
? routeDestination
|
? routeDestination
|
||||||
: fallbackDestination;
|
: (restrictedAccess ? PayoutDestination.settings : PayoutDestination.dashboard);
|
||||||
|
|
||||||
if (selected != routeDestination) {
|
if (selected != routeDestination) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
@@ -55,6 +55,10 @@ class PageSelector extends StatelessWidget {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (provider.selected != selected) {
|
||||||
|
provider.syncDestination(selected);
|
||||||
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: PayoutAppBar(
|
appBar: PayoutAppBar(
|
||||||
title: Text(selected.localizedLabel(context)),
|
title: Text(selected.localizedLabel(context)),
|
||||||
|
|||||||
Reference in New Issue
Block a user