Finally Fixed search field in payment page and cleaned up payment flow
This commit is contained in:
@@ -1,73 +1,63 @@
|
|||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/payment/methods/data.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/payment/type.dart';
|
||||||
import 'package:pshared/models/recipient/recipient.dart';
|
import 'package:pshared/models/recipient/recipient.dart';
|
||||||
|
import 'package:pshared/provider/recipient/provider.dart';
|
||||||
|
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||||
|
|
||||||
|
|
||||||
class PaymentFlowProvider extends ChangeNotifier {
|
class PaymentFlowProvider extends ChangeNotifier {
|
||||||
PaymentType _selectedType;
|
PaymentType _selectedType;
|
||||||
|
PaymentType? _preferredType;
|
||||||
PaymentMethodData? _manualPaymentData;
|
PaymentMethodData? _manualPaymentData;
|
||||||
|
List<PaymentMethod> _recipientMethods = [];
|
||||||
|
Recipient? _recipient;
|
||||||
|
|
||||||
PaymentFlowProvider({
|
PaymentFlowProvider({
|
||||||
required PaymentType initialType,
|
required PaymentType initialType,
|
||||||
}) : _selectedType = initialType;
|
PaymentType? preferredType,
|
||||||
|
}) : _selectedType = initialType,
|
||||||
|
_preferredType = preferredType ?? initialType;
|
||||||
|
|
||||||
PaymentType get selectedType => _selectedType;
|
PaymentType get selectedType => _selectedType;
|
||||||
PaymentMethodData? get manualPaymentData => _manualPaymentData;
|
PaymentMethodData? get manualPaymentData => _manualPaymentData;
|
||||||
|
Recipient? get recipient => _recipient;
|
||||||
|
PaymentMethod? get selectedMethod => hasRecipient
|
||||||
|
? _recipientMethods.firstWhereOrNull((method) => method.type == _selectedType)
|
||||||
|
: null;
|
||||||
|
|
||||||
void sync({
|
bool get hasRecipient => _recipient != null;
|
||||||
required Recipient? recipient,
|
|
||||||
required MethodMap availableTypes,
|
MethodMap get availableTypes => hasRecipient
|
||||||
PaymentType? preferredType,
|
? _buildAvailableTypes(_recipientMethods)
|
||||||
}) {
|
: {for (final type in PaymentType.values) type: null};
|
||||||
final resolvedType = _resolveSelectedType(
|
|
||||||
recipient: recipient,
|
PaymentMethodData? get selectedPaymentData =>
|
||||||
availableTypes: availableTypes,
|
hasRecipient ? selectedMethod?.data : _manualPaymentData;
|
||||||
preferredType: preferredType,
|
|
||||||
|
List<PaymentMethod> get methodsForRecipient => hasRecipient
|
||||||
|
? List<PaymentMethod>.unmodifiable(_recipientMethods)
|
||||||
|
: const [];
|
||||||
|
|
||||||
|
void update(
|
||||||
|
RecipientsProvider recipientsProvider,
|
||||||
|
PaymentMethodsProvider methodsProvider,
|
||||||
|
) =>
|
||||||
|
_applyState(
|
||||||
|
recipient: recipientsProvider.currentObject,
|
||||||
|
methods: methodsProvider.methodsForRecipient(recipientsProvider.currentObject),
|
||||||
|
preferredType: _preferredType,
|
||||||
|
forceResetManualData: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
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}) {
|
void selectType(PaymentType type, {bool resetManualData = false}) {
|
||||||
|
if (hasRecipient && !availableTypes.containsKey(type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_selectedType == type && (!resetManualData || _manualPaymentData == null)) {
|
if (_selectedType == type && (!resetManualData || _manualPaymentData == null)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -84,6 +74,20 @@ class PaymentFlowProvider extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setPreferredType(PaymentType? preferredType) {
|
||||||
|
if (_preferredType == preferredType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_preferredType = preferredType;
|
||||||
|
_applyState(
|
||||||
|
recipient: _recipient,
|
||||||
|
methods: _recipientMethods,
|
||||||
|
preferredType: _preferredType,
|
||||||
|
forceResetManualData: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
PaymentType _resolveSelectedType({
|
PaymentType _resolveSelectedType({
|
||||||
required Recipient? recipient,
|
required Recipient? recipient,
|
||||||
required MethodMap availableTypes,
|
required MethodMap availableTypes,
|
||||||
@@ -107,4 +111,56 @@ class PaymentFlowProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
return availableTypes.keys.first;
|
return availableTypes.keys.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _applyState({
|
||||||
|
required Recipient? recipient,
|
||||||
|
required List<PaymentMethod> methods,
|
||||||
|
required PaymentType? preferredType,
|
||||||
|
required bool forceResetManualData,
|
||||||
|
}) {
|
||||||
|
final availableTypes = _buildAvailableTypes(methods);
|
||||||
|
final resolvedType = _resolveSelectedType(
|
||||||
|
recipient: recipient,
|
||||||
|
availableTypes: availableTypes,
|
||||||
|
preferredType: preferredType,
|
||||||
|
);
|
||||||
|
|
||||||
|
var hasChanges = false;
|
||||||
|
|
||||||
|
if (_recipient != recipient) {
|
||||||
|
_recipient = recipient;
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_hasSameMethods(methods)) {
|
||||||
|
_recipientMethods = methods;
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolvedType != _selectedType) {
|
||||||
|
_selectedType = resolvedType;
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((recipient != null || forceResetManualData) && _manualPaymentData != null) {
|
||||||
|
_manualPaymentData = null;
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasChanges) notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodMap _buildAvailableTypes(List<PaymentMethod> methods) => {
|
||||||
|
for (final method in methods) method.type: method.data,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool _hasSameMethods(List<PaymentMethod> methods) {
|
||||||
|
if (_recipientMethods.length != methods.length) return false;
|
||||||
|
for (var i = 0; i < methods.length; i++) {
|
||||||
|
final current = _recipientMethods[i];
|
||||||
|
final next = methods[i];
|
||||||
|
if (current.id != next.id || current.updatedAt != next.updatedAt) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,58 @@ class QuotationProvider extends ChangeNotifier {
|
|||||||
Asset? get total => quotation == null ? null : createAsset(quotation!.debitAmount!.currency, quotation!.debitAmount!.amount);
|
Asset? get total => quotation == null ? null : createAsset(quotation!.debitAmount!.currency, quotation!.debitAmount!.amount);
|
||||||
Asset? get recipientGets => quotation == null ? null : createAsset(quotation!.expectedSettlementAmount!.currency, quotation!.expectedSettlementAmount!.amount);
|
Asset? get recipientGets => quotation == null ? null : createAsset(quotation!.expectedSettlementAmount!.currency, quotation!.expectedSettlementAmount!.amount);
|
||||||
|
|
||||||
|
Customer _buildCustomer({
|
||||||
|
required Recipient? recipient,
|
||||||
|
required PaymentMethod method,
|
||||||
|
}) {
|
||||||
|
final name = _resolveCustomerName(method, recipient);
|
||||||
|
String? firstName;
|
||||||
|
String? middleName;
|
||||||
|
String? lastName;
|
||||||
|
|
||||||
|
if (name != null && name.isNotEmpty) {
|
||||||
|
final parts = name.split(RegExp(r'\s+'));
|
||||||
|
if (parts.length == 1) {
|
||||||
|
firstName = parts.first;
|
||||||
|
} else if (parts.length == 2) {
|
||||||
|
firstName = parts.first;
|
||||||
|
lastName = parts.last;
|
||||||
|
} else {
|
||||||
|
firstName = parts.first;
|
||||||
|
lastName = parts.last;
|
||||||
|
middleName = parts.sublist(1, parts.length - 1).join(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Customer(
|
||||||
|
id: recipient?.id ?? method.recipientRef,
|
||||||
|
firstName: firstName,
|
||||||
|
middleName: middleName,
|
||||||
|
lastName: lastName,
|
||||||
|
country: method.cardData?.country,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _resolveCustomerName(PaymentMethod method, Recipient? recipient) {
|
||||||
|
final card = method.cardData;
|
||||||
|
if (card != null) {
|
||||||
|
return '${card.firstName} ${card.lastName}'.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
final iban = method.ibanData;
|
||||||
|
if (iban != null && iban.accountHolder.trim().isNotEmpty) {
|
||||||
|
return iban.accountHolder.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
final bank = method.bankAccountData;
|
||||||
|
if (bank != null && bank.recipientName.trim().isNotEmpty) {
|
||||||
|
return bank.recipientName.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
final recipientName = recipient?.name.trim();
|
||||||
|
return recipientName?.isNotEmpty == true ? recipientName : null;
|
||||||
|
}
|
||||||
|
|
||||||
void _setResource(Resource<PaymentQuote> quotation) {
|
void _setResource(Resource<PaymentQuote> quotation) {
|
||||||
_quotation = quotation;
|
_quotation = quotation;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@@ -118,42 +170,3 @@ class QuotationProvider extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Customer? _buildCustomer({
|
|
||||||
required Recipient? recipient,
|
|
||||||
required PaymentMethod method,
|
|
||||||
}) {
|
|
||||||
final recipientId = (recipient?.id ?? method.recipientRef).trim();
|
|
||||||
if (recipientId.isEmpty) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var firstName = '';
|
|
||||||
var lastName = '';
|
|
||||||
final cardData = method.cardData;
|
|
||||||
if (cardData != null) {
|
|
||||||
firstName = cardData.firstName.trim();
|
|
||||||
lastName = cardData.lastName.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((firstName.isEmpty || lastName.isEmpty) && recipient != null) {
|
|
||||||
final parts = recipient.name.trim().split(RegExp(r'\s+'));
|
|
||||||
if (parts.isNotEmpty && firstName.isEmpty) {
|
|
||||||
firstName = parts.first;
|
|
||||||
}
|
|
||||||
if (parts.length > 1 && lastName.isEmpty) {
|
|
||||||
lastName = parts.sublist(1).join(' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastName.isEmpty) {
|
|
||||||
lastName = firstName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Customer(
|
|
||||||
id: recipientId,
|
|
||||||
firstName: firstName.isEmpty ? null : firstName,
|
|
||||||
lastName: lastName.isEmpty ? null : lastName,
|
|
||||||
country: cardData?.country,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/payment/wallet.dart';
|
import 'package:pshared/models/payment/wallet.dart';
|
||||||
@@ -38,10 +39,7 @@ class WalletsProvider with ChangeNotifier {
|
|||||||
throw Exception('update wallet is not implemented');
|
throw Exception('update wallet is not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectWallet(Wallet wallet) {
|
void selectWallet(Wallet wallet) => _setSelectedWallet(wallet);
|
||||||
_selectedWallet = wallet;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> loadWalletsWithBalances() async {
|
Future<void> loadWalletsWithBalances() async {
|
||||||
_setResource(_resource.copyWith(isLoading: true, error: null));
|
_setResource(_resource.copyWith(isLoading: true, error: null));
|
||||||
@@ -98,6 +96,25 @@ class WalletsProvider with ChangeNotifier {
|
|||||||
|
|
||||||
void _setResource(Resource<List<Wallet>> newResource) {
|
void _setResource(Resource<List<Wallet>> newResource) {
|
||||||
_resource = newResource;
|
_resource = newResource;
|
||||||
|
_selectedWallet = _resolveSelectedWallet(_selectedWallet, wallets);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
Wallet? _resolveSelectedWallet(Wallet? current, List<Wallet> available) {
|
||||||
|
if (available.isEmpty) return null;
|
||||||
|
final currentId = current?.id;
|
||||||
|
if (currentId != null) {
|
||||||
|
final existing = available.firstWhereOrNull((wallet) => wallet.id == currentId);
|
||||||
|
if (existing != null) return existing;
|
||||||
|
}
|
||||||
|
return available.firstWhereOrNull((wallet) => !wallet.isHidden) ?? available.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setSelectedWallet(Wallet wallet) {
|
||||||
|
if (_selectedWallet?.id == wallet.id && _selectedWallet?.isHidden == wallet.isHidden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_selectedWallet = wallet;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import 'package:pshared/models/describable.dart';
|
|||||||
import 'package:pshared/models/organization/bound.dart';
|
import 'package:pshared/models/organization/bound.dart';
|
||||||
import 'package:pshared/models/payment/methods/data.dart';
|
import 'package:pshared/models/payment/methods/data.dart';
|
||||||
import 'package:pshared/models/payment/methods/type.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/permissions/bound.dart';
|
import 'package:pshared/models/permissions/bound.dart';
|
||||||
import 'package:pshared/models/storable.dart';
|
import 'package:pshared/models/storable.dart';
|
||||||
import 'package:pshared/provider/organizations.dart';
|
import 'package:pshared/provider/organizations.dart';
|
||||||
@@ -20,6 +22,24 @@ class PaymentMethodsProvider extends GenericProvider<PaymentMethod> {
|
|||||||
|
|
||||||
List<PaymentMethod> get methods => List<PaymentMethod>.unmodifiable(items.toList()..sort((a, b) => a.storable.createdAt.compareTo(b.storable.createdAt)));
|
List<PaymentMethod> get methods => List<PaymentMethod>.unmodifiable(items.toList()..sort((a, b) => a.storable.createdAt.compareTo(b.storable.createdAt)));
|
||||||
|
|
||||||
|
List<PaymentMethod> methodsForRecipient(Recipient? recipient) {
|
||||||
|
if (recipient == null || !isReady) return [];
|
||||||
|
|
||||||
|
return methods
|
||||||
|
.where((method) => !method.isArchived && method.recipientRef == recipient.id)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodMap availableTypesForRecipient(Recipient? recipient) => {
|
||||||
|
for (final method in methodsForRecipient(recipient)) method.type: method.data,
|
||||||
|
};
|
||||||
|
|
||||||
|
PaymentMethod? findMethodByType({
|
||||||
|
required PaymentType type,
|
||||||
|
required Recipient? recipient,
|
||||||
|
}) =>
|
||||||
|
methodsForRecipient(recipient).firstWhereOrNull((method) => method.type == type);
|
||||||
|
|
||||||
void updateProviders(OrganizationsProvider organizations, RecipientsProvider recipients) {
|
void updateProviders(OrganizationsProvider organizations, RecipientsProvider recipients) {
|
||||||
if (recipients.currentObject != null) loadMethods(organizations, recipients.currentObject?.id);
|
if (recipients.currentObject != null) loadMethods(organizations, recipients.currentObject?.id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class RecipientsProvider extends GenericProvider<Recipient> {
|
|||||||
|
|
||||||
RecipientFilter _selectedFilter = RecipientFilter.all;
|
RecipientFilter _selectedFilter = RecipientFilter.all;
|
||||||
String _query = '';
|
String _query = '';
|
||||||
|
String? _previousRecipientRef;
|
||||||
|
|
||||||
RecipientFilter get selectedFilter => _selectedFilter;
|
RecipientFilter get selectedFilter => _selectedFilter;
|
||||||
String get query => _query;
|
String get query => _query;
|
||||||
@@ -22,6 +23,10 @@ class RecipientsProvider extends GenericProvider<Recipient> {
|
|||||||
|
|
||||||
RecipientsProvider() : super(service: RecipientService.basicService);
|
RecipientsProvider() : super(service: RecipientService.basicService);
|
||||||
|
|
||||||
|
Recipient? get previousRecipient => _previousRecipientRef == null
|
||||||
|
? null
|
||||||
|
: getItemByRef(_previousRecipientRef!);
|
||||||
|
|
||||||
List<Recipient> get filteredRecipients {
|
List<Recipient> get filteredRecipients {
|
||||||
List<Recipient> filtered = recipients.where((r) {
|
List<Recipient> filtered = recipients.where((r) {
|
||||||
switch (_selectedFilter) {
|
switch (_selectedFilter) {
|
||||||
@@ -53,6 +58,24 @@ class RecipientsProvider extends GenericProvider<Recipient> {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool setCurrentObject(String? objectRef) {
|
||||||
|
final currentRef = currentObject?.id;
|
||||||
|
final didUpdate = super.setCurrentObject(objectRef);
|
||||||
|
|
||||||
|
if (didUpdate && currentRef != null && currentRef != objectRef) {
|
||||||
|
_previousRecipientRef = currentRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
return didUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void restorePreviousRecipient() {
|
||||||
|
if (_previousRecipientRef != null) {
|
||||||
|
setCurrentObject(_previousRecipientRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<Recipient> create({
|
Future<Recipient> create({
|
||||||
required String name,
|
required String name,
|
||||||
required String email,
|
required String email,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
import 'package:pshared/api/requests/payment/initiate.dart';
|
import 'package:pshared/api/requests/payment/initiate.dart';
|
||||||
|
|||||||
@@ -13,15 +13,18 @@ import 'package:pshared/provider/permissions.dart';
|
|||||||
import 'package:pshared/provider/account.dart';
|
import 'package:pshared/provider/account.dart';
|
||||||
import 'package:pshared/provider/organizations.dart';
|
import 'package:pshared/provider/organizations.dart';
|
||||||
import 'package:pshared/provider/payment/amount.dart';
|
import 'package:pshared/provider/payment/amount.dart';
|
||||||
|
import 'package:pshared/provider/payment/flow.dart';
|
||||||
|
import 'package:pshared/provider/payment/provider.dart';
|
||||||
|
import 'package:pshared/provider/payment/quotation.dart';
|
||||||
import 'package:pshared/provider/recipient/provider.dart';
|
import 'package:pshared/provider/recipient/provider.dart';
|
||||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||||
import 'package:pshared/provider/payment/wallets.dart';
|
import 'package:pshared/provider/payment/wallets.dart';
|
||||||
|
import 'package:pshared/models/payment/type.dart';
|
||||||
import 'package:pshared/service/payment/wallets.dart';
|
import 'package:pshared/service/payment/wallets.dart';
|
||||||
|
|
||||||
import 'package:pweb/app/app.dart';
|
import 'package:pweb/app/app.dart';
|
||||||
import 'package:pweb/app/timeago.dart';
|
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/operatioins.dart';
|
import 'package:pweb/providers/operatioins.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';
|
||||||
@@ -89,8 +92,12 @@ void main() async {
|
|||||||
ChangeNotifierProvider(
|
ChangeNotifierProvider(
|
||||||
create: (_) => WalletTransactionsProvider(MockWalletTransactionsService())..load(),
|
create: (_) => WalletTransactionsProvider(MockWalletTransactionsService())..load(),
|
||||||
),
|
),
|
||||||
ChangeNotifierProvider(
|
ChangeNotifierProxyProvider2<RecipientsProvider, PaymentMethodsProvider, PaymentFlowProvider>(
|
||||||
create: (_) => MockPaymentProvider(),
|
create: (_) => PaymentFlowProvider(initialType: PaymentType.bankAccount),
|
||||||
|
update: (context, recipients, methods, provider) => provider!..update(
|
||||||
|
recipients,
|
||||||
|
methods,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
ChangeNotifierProvider(
|
ChangeNotifierProvider(
|
||||||
@@ -99,6 +106,18 @@ void main() async {
|
|||||||
ChangeNotifierProvider(
|
ChangeNotifierProvider(
|
||||||
create: (_) => PaymentAmountProvider(),
|
create: (_) => PaymentAmountProvider(),
|
||||||
),
|
),
|
||||||
|
ChangeNotifierProxyProvider6<OrganizationsProvider, PaymentAmountProvider, WalletsProvider, PaymentFlowProvider, RecipientsProvider, PaymentMethodsProvider, QuotationProvider>(
|
||||||
|
create: (_) => QuotationProvider(),
|
||||||
|
update: (_, organization, payment, wallet, flow, recipients, methods, provider) =>
|
||||||
|
provider!..update(organization, payment, wallet, flow, recipients, methods),
|
||||||
|
),
|
||||||
|
ChangeNotifierProxyProvider2<OrganizationsProvider, QuotationProvider, PaymentProvider>(
|
||||||
|
create: (_) => PaymentProvider(),
|
||||||
|
update: (context, organization, quotation, provider) => provider!..update(
|
||||||
|
organization,
|
||||||
|
quotation,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const PayApp(),
|
child: const PayApp(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,24 +1,16 @@
|
|||||||
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/payment/type.dart';
|
||||||
import 'package:pshared/models/recipient/recipient.dart';
|
import 'package:pshared/models/recipient/recipient.dart';
|
||||||
import 'package:pshared/provider/organizations.dart';
|
|
||||||
import 'package:pshared/provider/payment/amount.dart';
|
|
||||||
import 'package:pshared/provider/payment/flow.dart';
|
import 'package:pshared/provider/payment/flow.dart';
|
||||||
import 'package:pshared/provider/payment/provider.dart';
|
import 'package:pshared/provider/payment/provider.dart';
|
||||||
import 'package:pshared/provider/payment/quotation.dart';
|
|
||||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||||
import 'package:pshared/provider/recipient/provider.dart';
|
import 'package:pshared/provider/recipient/provider.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/payment/wallet.dart';
|
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/body.dart';
|
|
||||||
import 'package:pshared/provider/payment/wallets.dart';
|
import 'package:pshared/provider/payment/wallets.dart';
|
||||||
|
|
||||||
|
import 'package:pweb/pages/payment_methods/payment_page/body.dart';
|
||||||
import 'package:pweb/widgets/sidebar/destinations.dart';
|
import 'package:pweb/widgets/sidebar/destinations.dart';
|
||||||
import 'package:pweb/services/posthog.dart';
|
import 'package:pweb/services/posthog.dart';
|
||||||
|
|
||||||
@@ -60,30 +52,23 @@ class _PaymentPageState extends State<PaymentPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _initializePaymentPage() {
|
void _initializePaymentPage() {
|
||||||
final methodsProvider = context.read<PaymentMethodsProvider>();
|
final flowProvider = context.read<PaymentFlowProvider>();
|
||||||
_handleWalletAutoSelection(methodsProvider);
|
flowProvider.setPreferredType(widget.initialPaymentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleSearchChanged(String query) {
|
void _handleSearchChanged(String query) {
|
||||||
context.read<RecipientsProvider>().setQuery(query);
|
context.read<RecipientsProvider>().setQuery(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleRecipientSelected(BuildContext context, Recipient recipient) {
|
void _handleRecipientSelected(Recipient recipient) {
|
||||||
final recipientProvider = context.read<RecipientsProvider>();
|
final recipientProvider = context.read<RecipientsProvider>();
|
||||||
recipientProvider.setCurrentObject(recipient.id);
|
recipientProvider.setCurrentObject(recipient.id);
|
||||||
_clearSearchField();
|
_clearSearchField();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleRecipientCleared(BuildContext context) {
|
void _handleRecipientCleared() {
|
||||||
final recipientProvider = context.read<RecipientsProvider>();
|
final recipientProvider = context.read<RecipientsProvider>();
|
||||||
final methodsProvider = context.read<PaymentMethodsProvider>();
|
|
||||||
|
|
||||||
recipientProvider.setCurrentObject(null);
|
recipientProvider.setCurrentObject(null);
|
||||||
context.read<PaymentFlowProvider>().reset(
|
|
||||||
recipient: null,
|
|
||||||
availableTypes: _availablePaymentTypes(null, methodsProvider),
|
|
||||||
preferredType: widget.initialPaymentType,
|
|
||||||
);
|
|
||||||
_clearSearchField();
|
_clearSearchField();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,107 +78,42 @@ class _PaymentPageState extends State<PaymentPage> {
|
|||||||
context.read<RecipientsProvider>().setQuery('');
|
context.read<RecipientsProvider>().setQuery('');
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleSendPayment(BuildContext context) {
|
void _handleSendPayment() {
|
||||||
if (context.read<QuotationProvider>().isReady) {
|
final flowProvider = context.read<PaymentFlowProvider>();
|
||||||
context.read<PaymentProvider>().pay();
|
final paymentProvider = context.read<PaymentProvider>();
|
||||||
PosthogService.paymentInitiated(
|
if (paymentProvider.isLoading) return;
|
||||||
method: context.read<PaymentFlowProvider>().selectedType,
|
|
||||||
|
paymentProvider.pay().then((_) {
|
||||||
|
PosthogService.paymentInitiated(method: flowProvider.selectedType);
|
||||||
|
}).catchError((error) {
|
||||||
|
if (!mounted) return;
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text(error.toString())),
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final methodsProvider = context.watch<PaymentMethodsProvider>();
|
final methodsProvider = context.watch<PaymentMethodsProvider>();
|
||||||
final recipientProvider = context.watch<RecipientsProvider>();
|
final recipientProvider = context.read<RecipientsProvider>();
|
||||||
final recipient = recipientProvider.currentObject;
|
final recipient = context.select<RecipientsProvider, Recipient?>(
|
||||||
final availableTypes = _availablePaymentTypes(recipient, methodsProvider);
|
(provider) => provider.currentObject,
|
||||||
|
|
||||||
return MultiProvider(
|
|
||||||
providers: [
|
|
||||||
ChangeNotifierProxyProvider2<RecipientsProvider, PaymentMethodsProvider, PaymentFlowProvider>(
|
|
||||||
create: (_) => PaymentFlowProvider(
|
|
||||||
initialType: widget.initialPaymentType ?? PaymentType.bankAccount,
|
|
||||||
),
|
|
||||||
update: (_, recipients, methods, flow) {
|
|
||||||
final currentRecipient = recipients.currentObject;
|
|
||||||
flow!.sync(
|
|
||||||
recipient: currentRecipient,
|
|
||||||
availableTypes: _availablePaymentTypes(currentRecipient, methods),
|
|
||||||
preferredType: currentRecipient != null ? widget.initialPaymentType : null,
|
|
||||||
);
|
);
|
||||||
return flow;
|
|
||||||
},
|
return PaymentPageBody(
|
||||||
),
|
|
||||||
ChangeNotifierProvider(
|
|
||||||
create: (_) => PaymentAmountProvider(),
|
|
||||||
),
|
|
||||||
ChangeNotifierProxyProvider6<OrganizationsProvider, PaymentAmountProvider, WalletsProvider, PaymentFlowProvider, RecipientsProvider, PaymentMethodsProvider, QuotationProvider>(
|
|
||||||
create: (_) => QuotationProvider(),
|
|
||||||
update: (_, organization, payment, wallet, flow, recipients, methods, provider) =>
|
|
||||||
provider!..update(organization, payment, wallet, flow, recipients, methods),
|
|
||||||
),
|
|
||||||
ChangeNotifierProxyProvider2<OrganizationsProvider, QuotationProvider, PaymentProvider>(
|
|
||||||
create: (_) => PaymentProvider(),
|
|
||||||
update: (_, organization, quotation, provider) => provider!..update(organization, quotation),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: Builder(
|
|
||||||
builder: (innerContext) => PaymentPageBody(
|
|
||||||
onBack: widget.onBack,
|
onBack: widget.onBack,
|
||||||
fallbackDestination: widget.fallbackDestination,
|
fallbackDestination: widget.fallbackDestination,
|
||||||
recipient: recipient,
|
recipient: recipient,
|
||||||
recipientProvider: recipientProvider,
|
recipientProvider: recipientProvider,
|
||||||
methodsProvider: methodsProvider,
|
methodsProvider: methodsProvider,
|
||||||
availablePaymentTypes: availableTypes,
|
onWalletSelected: context.read<WalletsProvider>().selectWallet,
|
||||||
searchController: _searchController,
|
searchController: _searchController,
|
||||||
searchFocusNode: _searchFocusNode,
|
searchFocusNode: _searchFocusNode,
|
||||||
onSearchChanged: _handleSearchChanged,
|
onSearchChanged: _handleSearchChanged,
|
||||||
onRecipientSelected: (selected) => _handleRecipientSelected(innerContext, selected),
|
onRecipientSelected: _handleRecipientSelected,
|
||||||
onRecipientCleared: () => _handleRecipientCleared(innerContext),
|
onRecipientCleared: _handleRecipientCleared,
|
||||||
onSend: () => _handleSendPayment(innerContext),
|
onSend: _handleSendPayment,
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/models/payment/wallet.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';
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ class PaymentPageBody extends StatelessWidget {
|
|||||||
final Recipient? recipient;
|
final Recipient? recipient;
|
||||||
final RecipientsProvider recipientProvider;
|
final RecipientsProvider recipientProvider;
|
||||||
final PaymentMethodsProvider methodsProvider;
|
final PaymentMethodsProvider methodsProvider;
|
||||||
final MethodMap availablePaymentTypes;
|
final ValueChanged<Wallet> onWalletSelected;
|
||||||
final PayoutDestination fallbackDestination;
|
final PayoutDestination fallbackDestination;
|
||||||
final TextEditingController searchController;
|
final TextEditingController searchController;
|
||||||
final FocusNode searchFocusNode;
|
final FocusNode searchFocusNode;
|
||||||
@@ -32,7 +32,7 @@ class PaymentPageBody extends StatelessWidget {
|
|||||||
required this.recipient,
|
required this.recipient,
|
||||||
required this.recipientProvider,
|
required this.recipientProvider,
|
||||||
required this.methodsProvider,
|
required this.methodsProvider,
|
||||||
required this.availablePaymentTypes,
|
required this.onWalletSelected,
|
||||||
required this.fallbackDestination,
|
required this.fallbackDestination,
|
||||||
required this.searchController,
|
required this.searchController,
|
||||||
required this.searchFocusNode,
|
required this.searchFocusNode,
|
||||||
@@ -60,8 +60,7 @@ class PaymentPageBody extends StatelessWidget {
|
|||||||
onBack: onBack,
|
onBack: onBack,
|
||||||
recipient: recipient,
|
recipient: recipient,
|
||||||
recipientProvider: recipientProvider,
|
recipientProvider: recipientProvider,
|
||||||
methodsProvider: methodsProvider,
|
onWalletSelected: onWalletSelected,
|
||||||
availablePaymentTypes: availablePaymentTypes,
|
|
||||||
fallbackDestination: fallbackDestination,
|
fallbackDestination: fallbackDestination,
|
||||||
searchController: searchController,
|
searchController: searchController,
|
||||||
searchFocusNode: searchFocusNode,
|
searchFocusNode: searchFocusNode,
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:pshared/models/payment/wallet.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/provider.dart';
|
import 'package:pshared/provider/recipient/provider.dart';
|
||||||
import 'package:pshared/provider/payment/flow.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/back_button.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/header.dart';
|
||||||
@@ -26,8 +22,7 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
final ValueChanged<Recipient?>? onBack;
|
final ValueChanged<Recipient?>? onBack;
|
||||||
final Recipient? recipient;
|
final Recipient? recipient;
|
||||||
final RecipientsProvider recipientProvider;
|
final RecipientsProvider recipientProvider;
|
||||||
final PaymentMethodsProvider methodsProvider;
|
final ValueChanged<Wallet> onWalletSelected;
|
||||||
final MethodMap availablePaymentTypes;
|
|
||||||
final PayoutDestination fallbackDestination;
|
final PayoutDestination fallbackDestination;
|
||||||
final TextEditingController searchController;
|
final TextEditingController searchController;
|
||||||
final FocusNode searchFocusNode;
|
final FocusNode searchFocusNode;
|
||||||
@@ -41,8 +36,7 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
required this.onBack,
|
required this.onBack,
|
||||||
required this.recipient,
|
required this.recipient,
|
||||||
required this.recipientProvider,
|
required this.recipientProvider,
|
||||||
required this.methodsProvider,
|
required this.onWalletSelected,
|
||||||
required this.availablePaymentTypes,
|
|
||||||
required this.fallbackDestination,
|
required this.fallbackDestination,
|
||||||
required this.searchController,
|
required this.searchController,
|
||||||
required this.searchFocusNode,
|
required this.searchFocusNode,
|
||||||
@@ -55,7 +49,6 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final dimensions = AppDimensions();
|
final dimensions = AppDimensions();
|
||||||
final flowProvider = context.watch<PaymentFlowProvider>();
|
|
||||||
final loc = AppLocalizations.of(context)!;
|
final loc = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
return Align(
|
return Align(
|
||||||
@@ -84,7 +77,7 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
SectionTitle(loc.sourceOfFunds),
|
SectionTitle(loc.sourceOfFunds),
|
||||||
SizedBox(height: dimensions.paddingSmall),
|
SizedBox(height: dimensions.paddingSmall),
|
||||||
PaymentMethodSelector(
|
PaymentMethodSelector(
|
||||||
onMethodChanged: (m) => methodsProvider.setCurrentObject(m.id),
|
onMethodChanged: onWalletSelected,
|
||||||
),
|
),
|
||||||
SizedBox(height: dimensions.paddingXLarge),
|
SizedBox(height: dimensions.paddingXLarge),
|
||||||
RecipientSection(
|
RecipientSection(
|
||||||
@@ -98,12 +91,7 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
onRecipientCleared: onRecipientCleared,
|
onRecipientCleared: onRecipientCleared,
|
||||||
),
|
),
|
||||||
SizedBox(height: dimensions.paddingXLarge),
|
SizedBox(height: dimensions.paddingXLarge),
|
||||||
PaymentInfoSection(
|
PaymentInfoSection(dimensions: dimensions),
|
||||||
dimensions: dimensions,
|
|
||||||
flowProvider: flowProvider,
|
|
||||||
recipient: recipient,
|
|
||||||
availableTypes: availablePaymentTypes,
|
|
||||||
),
|
|
||||||
SizedBox(height: dimensions.paddingLarge),
|
SizedBox(height: dimensions.paddingLarge),
|
||||||
const PaymentFormWidget(),
|
const PaymentFormWidget(),
|
||||||
SizedBox(height: dimensions.paddingXXXLarge),
|
SizedBox(height: dimensions.paddingXXXLarge),
|
||||||
|
|||||||
@@ -17,9 +17,11 @@ class PaymentMethodSelector extends StatelessWidget {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Consumer<WalletsProvider>(builder:(context, provider, _) => PaymentMethodDropdown(
|
Widget build(BuildContext context) => Consumer<WalletsProvider>(
|
||||||
|
builder: (context, provider, _) => PaymentMethodDropdown(
|
||||||
methods: provider.wallets,
|
methods: provider.wallets,
|
||||||
initialValue: provider.selectedWallet,
|
selectedMethod: provider.selectedWallet,
|
||||||
onChanged: onMethodChanged,
|
onChanged: onMethodChanged,
|
||||||
));
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:pshared/models/payment/wallet.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/provider.dart';
|
import 'package:pshared/provider/recipient/provider.dart';
|
||||||
import 'package:pshared/provider/payment/flow.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/pages/dashboard/payouts/form.dart';
|
import 'package:pweb/pages/dashboard/payouts/form.dart';
|
||||||
|
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/back_button.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/header.dart';
|
||||||
import 'package:pweb/pages/payment_methods/payment_page/method_selector.dart';
|
import 'package:pweb/pages/payment_methods/payment_page/method_selector.dart';
|
||||||
@@ -26,8 +22,7 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
final ValueChanged<Recipient?>? onBack;
|
final ValueChanged<Recipient?>? onBack;
|
||||||
final Recipient? recipient;
|
final Recipient? recipient;
|
||||||
final RecipientsProvider recipientProvider;
|
final RecipientsProvider recipientProvider;
|
||||||
final PaymentMethodsProvider methodsProvider;
|
final ValueChanged<Wallet> onWalletSelected;
|
||||||
final MethodMap availablePaymentTypes;
|
|
||||||
final PayoutDestination fallbackDestination;
|
final PayoutDestination fallbackDestination;
|
||||||
final TextEditingController searchController;
|
final TextEditingController searchController;
|
||||||
final FocusNode searchFocusNode;
|
final FocusNode searchFocusNode;
|
||||||
@@ -41,8 +36,7 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
required this.onBack,
|
required this.onBack,
|
||||||
required this.recipient,
|
required this.recipient,
|
||||||
required this.recipientProvider,
|
required this.recipientProvider,
|
||||||
required this.methodsProvider,
|
required this.onWalletSelected,
|
||||||
required this.availablePaymentTypes,
|
|
||||||
required this.fallbackDestination,
|
required this.fallbackDestination,
|
||||||
required this.searchController,
|
required this.searchController,
|
||||||
required this.searchFocusNode,
|
required this.searchFocusNode,
|
||||||
@@ -55,7 +49,6 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final dimensions = AppDimensions();
|
final dimensions = AppDimensions();
|
||||||
final flowProvider = context.watch<PaymentFlowProvider>();
|
|
||||||
final loc = AppLocalizations.of(context)!;
|
final loc = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
return Align(
|
return Align(
|
||||||
@@ -84,7 +77,7 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
SectionTitle(loc.sourceOfFunds),
|
SectionTitle(loc.sourceOfFunds),
|
||||||
SizedBox(height: dimensions.paddingSmall),
|
SizedBox(height: dimensions.paddingSmall),
|
||||||
PaymentMethodSelector(
|
PaymentMethodSelector(
|
||||||
onMethodChanged: (m) => methodsProvider.setCurrentObject(m.id),
|
onMethodChanged: onWalletSelected,
|
||||||
),
|
),
|
||||||
SizedBox(height: dimensions.paddingXLarge),
|
SizedBox(height: dimensions.paddingXLarge),
|
||||||
RecipientSection(
|
RecipientSection(
|
||||||
@@ -98,12 +91,7 @@ class PaymentPageContent extends StatelessWidget {
|
|||||||
onRecipientCleared: onRecipientCleared,
|
onRecipientCleared: onRecipientCleared,
|
||||||
),
|
),
|
||||||
SizedBox(height: dimensions.paddingXLarge),
|
SizedBox(height: dimensions.paddingXLarge),
|
||||||
PaymentInfoSection(
|
PaymentInfoSection(dimensions: dimensions),
|
||||||
dimensions: dimensions,
|
|
||||||
flowProvider: flowProvider,
|
|
||||||
recipient: recipient,
|
|
||||||
availableTypes: availablePaymentTypes,
|
|
||||||
),
|
|
||||||
SizedBox(height: dimensions.paddingLarge),
|
SizedBox(height: dimensions.paddingLarge),
|
||||||
const PaymentFormWidget(),
|
const PaymentFormWidget(),
|
||||||
SizedBox(height: dimensions.paddingXXXLarge),
|
SizedBox(height: dimensions.paddingXXXLarge),
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/payment/methods/data.dart';
|
import 'package:pshared/models/payment/methods/data.dart';
|
||||||
import 'package:pshared/models/payment/type.dart';
|
|
||||||
import 'package:pshared/models/recipient/recipient.dart';
|
|
||||||
import 'package:pshared/provider/payment/flow.dart';
|
import 'package:pshared/provider/payment/flow.dart';
|
||||||
|
|
||||||
import 'package:pweb/pages/payment_methods/form.dart';
|
import 'package:pweb/pages/payment_methods/form.dart';
|
||||||
@@ -15,25 +15,18 @@ 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 PaymentFlowProvider flowProvider;
|
|
||||||
final Recipient? recipient;
|
|
||||||
|
|
||||||
const PaymentInfoSection({
|
const PaymentInfoSection({
|
||||||
super.key,
|
super.key,
|
||||||
required this.dimensions,
|
required this.dimensions,
|
||||||
required this.availableTypes,
|
|
||||||
required this.flowProvider,
|
|
||||||
required this.recipient,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final loc = AppLocalizations.of(context)!;
|
final loc = AppLocalizations.of(context)!;
|
||||||
final hasRecipient = recipient != null;
|
final flowProvider = context.watch<PaymentFlowProvider>();
|
||||||
final MethodMap resolvedAvailableTypes = hasRecipient
|
final hasRecipient = flowProvider.hasRecipient;
|
||||||
? availableTypes
|
final MethodMap resolvedAvailableTypes = flowProvider.availableTypes;
|
||||||
: {for (final type in PaymentType.values) type: null};
|
|
||||||
|
|
||||||
if (hasRecipient && resolvedAvailableTypes.isEmpty) {
|
if (hasRecipient && resolvedAvailableTypes.isEmpty) {
|
||||||
return Text(loc.recipientNoPaymentDetails);
|
return Text(loc.recipientNoPaymentDetails);
|
||||||
@@ -62,7 +55,7 @@ class PaymentInfoSection extends StatelessWidget {
|
|||||||
flowProvider.setManualPaymentData(data);
|
flowProvider.setManualPaymentData(data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
initialData: hasRecipient ? resolvedAvailableTypes[selectedType] : flowProvider.manualPaymentData,
|
initialData: flowProvider.selectedPaymentData,
|
||||||
isEditable: !hasRecipient,
|
isEditable: !hasRecipient,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -45,6 +45,12 @@ class RecipientSection extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return AnimatedBuilder(
|
||||||
|
animation: recipientProvider,
|
||||||
|
builder: (context, _) {
|
||||||
|
final previousRecipient = recipientProvider.previousRecipient;
|
||||||
|
final hasQuery = recipientProvider.query.isNotEmpty;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -55,7 +61,18 @@ class RecipientSection extends StatelessWidget {
|
|||||||
onChanged: onSearchChanged,
|
onChanged: onSearchChanged,
|
||||||
focusNode: searchFocusNode,
|
focusNode: searchFocusNode,
|
||||||
),
|
),
|
||||||
if (recipientProvider.query.isNotEmpty) ...[
|
if (previousRecipient != null) ...[
|
||||||
|
SizedBox(height: dimensions.paddingSmall),
|
||||||
|
ListTile(
|
||||||
|
dense: true,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
leading: const Icon(Icons.undo),
|
||||||
|
title: Text(loc.back),
|
||||||
|
subtitle: Text(previousRecipient.name),
|
||||||
|
onTap: () => onRecipientSelected(previousRecipient),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
if (hasQuery) ...[
|
||||||
SizedBox(height: dimensions.paddingMedium),
|
SizedBox(height: dimensions.paddingMedium),
|
||||||
RecipientSearchResults(
|
RecipientSearchResults(
|
||||||
dimensions: dimensions,
|
dimensions: dimensions,
|
||||||
@@ -65,5 +82,7 @@ class RecipientSection extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,40 +8,27 @@ import 'package:pweb/pages/payment_methods/icon.dart';
|
|||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
class PaymentMethodDropdown extends StatefulWidget {
|
class PaymentMethodDropdown extends StatelessWidget {
|
||||||
final List<Wallet> methods;
|
final List<Wallet> methods;
|
||||||
final ValueChanged<Wallet> onChanged;
|
final ValueChanged<Wallet> onChanged;
|
||||||
final Wallet? initialValue;
|
final Wallet? selectedMethod;
|
||||||
|
|
||||||
const PaymentMethodDropdown({
|
const PaymentMethodDropdown({
|
||||||
super.key,
|
super.key,
|
||||||
required this.methods,
|
required this.methods,
|
||||||
required this.onChanged,
|
required this.onChanged,
|
||||||
this.initialValue,
|
this.selectedMethod,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
|
||||||
State<PaymentMethodDropdown> createState() => _PaymentMethodDropdownState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PaymentMethodDropdownState extends State<PaymentMethodDropdown> {
|
|
||||||
late Wallet _selectedMethod;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_selectedMethod = widget.initialValue ?? widget.methods.first;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => DropdownButtonFormField<Wallet>(
|
Widget build(BuildContext context) => DropdownButtonFormField<Wallet>(
|
||||||
dropdownColor: Theme.of(context).colorScheme.onSecondary,
|
dropdownColor: Theme.of(context).colorScheme.onSecondary,
|
||||||
initialValue: _selectedMethod,
|
value: _getSelectedMethod(),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: AppLocalizations.of(context)!.whereGetMoney,
|
labelText: AppLocalizations.of(context)!.whereGetMoney,
|
||||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
),
|
),
|
||||||
items: widget.methods.map((method) => DropdownMenuItem<Wallet>(
|
items: methods.map((method) => DropdownMenuItem<Wallet>(
|
||||||
value: method,
|
value: method,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -53,9 +40,14 @@ class _PaymentMethodDropdownState extends State<PaymentMethodDropdown> {
|
|||||||
)).toList(),
|
)).toList(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
setState(() => _selectedMethod = value);
|
onChanged(value);
|
||||||
widget.onChanged(value);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Wallet? _getSelectedMethod() {
|
||||||
|
if (selectedMethod != null) return selectedMethod;
|
||||||
|
if (methods.isEmpty) return null;
|
||||||
|
return methods.first;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user