Multiple Wallet support, history of each wallet and updated payment page
This commit is contained in:
@@ -1,32 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pweb/services/balance.dart';
|
||||
|
||||
|
||||
class BalanceProvider with ChangeNotifier {
|
||||
final BalanceService _service;
|
||||
|
||||
BalanceProvider(this._service);
|
||||
|
||||
double? _balance;
|
||||
String? _walletName;
|
||||
String? _walletId;
|
||||
bool _isHidden = true;
|
||||
|
||||
double? get balance => _balance;
|
||||
String? get walletName => _walletName;
|
||||
String? get walletId => _walletId;
|
||||
bool get isHidden => _isHidden;
|
||||
|
||||
Future<void> loadData() async {
|
||||
_balance = await _service.getBalance();
|
||||
_walletName = await _service.getWalletName();
|
||||
_walletId = await _service.getWalletId();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void toggleVisibility() {
|
||||
_isHidden = !_isHidden;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
82
frontend/pweb/lib/providers/operatioins.dart
Normal file
82
frontend/pweb/lib/providers/operatioins.dart
Normal file
@@ -0,0 +1,82 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pshared/models/payment/operation.dart';
|
||||
import 'package:pshared/models/payment/status.dart';
|
||||
|
||||
import 'package:pweb/services/operations.dart';
|
||||
|
||||
|
||||
class OperationProvider extends ChangeNotifier {
|
||||
final OperationService _service;
|
||||
|
||||
OperationProvider(this._service);
|
||||
|
||||
List<OperationItem> _allOperations = [];
|
||||
List<OperationItem> _filteredOperations = [];
|
||||
DateTimeRange? _dateRange;
|
||||
final Set<String> _selectedStatuses = {};
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
|
||||
// Getters
|
||||
List<OperationItem> get allOperations => _allOperations;
|
||||
List<OperationItem> get filteredOperations => _filteredOperations;
|
||||
DateTimeRange? get dateRange => _dateRange;
|
||||
Set<String> get selectedStatuses => _selectedStatuses;
|
||||
bool get isLoading => _isLoading;
|
||||
String? get error => _error;
|
||||
bool get hasFileName => _allOperations.any((op) => op.fileName != null);
|
||||
|
||||
Future<void> loadOperations() async {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
_allOperations = await _service.fetchOperations();
|
||||
_filteredOperations = List.from(_allOperations);
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void setDateRange(DateTimeRange? range) {
|
||||
_dateRange = range;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void toggleStatus(String status) {
|
||||
if (_selectedStatuses.contains(status)) {
|
||||
_selectedStatuses.remove(status);
|
||||
} else {
|
||||
_selectedStatuses.add(status);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void applyFilters(BuildContext context) {
|
||||
_filteredOperations = _allOperations.where((op) {
|
||||
final statusMatch = _selectedStatuses.isEmpty ||
|
||||
_selectedStatuses.contains(op.status.localized(context));
|
||||
|
||||
final dateMatch = _dateRange == null ||
|
||||
(op.date.isAfter(_dateRange!.start.subtract(const Duration(seconds: 1))) &&
|
||||
op.date.isBefore(_dateRange!.end.add(const Duration(seconds: 1))));
|
||||
|
||||
return statusMatch && dateMatch;
|
||||
}).toList();
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void resetFilters() {
|
||||
_dateRange = null;
|
||||
_selectedStatuses.clear();
|
||||
_filteredOperations = List.from(_allOperations);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import 'package:flutter/material.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:pweb/models/wallet.dart';
|
||||
import 'package:pweb/providers/payment_methods.dart';
|
||||
import 'package:pweb/providers/wallets.dart';
|
||||
import 'package:pweb/widgets/sidebar/destinations.dart';
|
||||
import 'package:pweb/services/amplitude.dart';
|
||||
@@ -15,13 +19,29 @@ class PageSelectorProvider extends ChangeNotifier {
|
||||
PaymentType? _type;
|
||||
bool _cameFromRecipientList = false;
|
||||
|
||||
final RecipientProvider? recipientProvider;
|
||||
final WalletsProvider? walletsProvider;
|
||||
RecipientProvider? recipientProvider;
|
||||
WalletsProvider? walletsProvider;
|
||||
PaymentMethodsProvider? methodsProvider;
|
||||
|
||||
PayoutDestination get selected => _selected;
|
||||
PaymentType? get type => _type;
|
||||
bool get cameFromRecipientList => _cameFromRecipientList;
|
||||
|
||||
PageSelectorProvider({this.recipientProvider, this.walletsProvider});
|
||||
PageSelectorProvider({
|
||||
this.recipientProvider,
|
||||
this.walletsProvider,
|
||||
this.methodsProvider,
|
||||
});
|
||||
|
||||
void update(
|
||||
RecipientProvider recipientProv,
|
||||
WalletsProvider walletsProv,
|
||||
PaymentMethodsProvider methodsProv,
|
||||
) {
|
||||
recipientProvider = recipientProv;
|
||||
walletsProvider = walletsProv;
|
||||
methodsProvider = methodsProv;
|
||||
}
|
||||
|
||||
void selectPage(PayoutDestination dest) {
|
||||
_selected = dest;
|
||||
@@ -90,7 +110,64 @@ class PageSelectorProvider extends ChangeNotifier {
|
||||
_selected = PayoutDestination.editwallet;
|
||||
notifyListeners();
|
||||
} else {
|
||||
debugPrint("RecipientProvider is null — cannot select wallet");
|
||||
debugPrint("WalletsProvider is null — cannot select wallet");
|
||||
}
|
||||
}
|
||||
|
||||
void startPaymentFromWallet(Wallet wallet) {
|
||||
_type = PaymentType.wallet;
|
||||
_cameFromRecipientList = true;
|
||||
_selected = PayoutDestination.payment;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
PaymentMethod? getPaymentMethodForWallet(Wallet wallet) {
|
||||
if (methodsProvider == null || methodsProvider!.methods.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return methodsProvider!.methods.firstWhereOrNull(
|
||||
(method) => method.type == PaymentType.wallet &&
|
||||
method.details.contains(wallet.walletUserID)
|
||||
);
|
||||
}
|
||||
|
||||
Map<PaymentType, Object> getAvailablePaymentTypes() {
|
||||
final recipient = selectedRecipient;
|
||||
if (recipient == null) return {};
|
||||
|
||||
return {
|
||||
if (recipient.card != null) PaymentType.card: recipient.card!,
|
||||
if (recipient.iban != null) PaymentType.iban: recipient.iban!,
|
||||
if (recipient.wallet != null) PaymentType.wallet: recipient.wallet!,
|
||||
if (recipient.bank != null) PaymentType.bankAccount: recipient.bank!,
|
||||
};
|
||||
}
|
||||
|
||||
PaymentType getDefaultPaymentType() {
|
||||
final availableTypes = getAvailablePaymentTypes();
|
||||
final currentType = _type ?? PaymentType.bankAccount;
|
||||
|
||||
if (availableTypes.containsKey(currentType)) {
|
||||
return currentType;
|
||||
} else if (availableTypes.isNotEmpty) {
|
||||
return availableTypes.keys.first;
|
||||
} else {
|
||||
return PaymentType.bankAccount;
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldShowPaymentForm() {
|
||||
return selectedRecipient == null;
|
||||
}
|
||||
|
||||
void handleWalletAutoSelection() {
|
||||
if (selectedWallet != null && methodsProvider != null) {
|
||||
final wallet = selectedWallet!;
|
||||
final matchingMethod = getPaymentMethodForWallet(wallet);
|
||||
if (matchingMethod != null) {
|
||||
methodsProvider!.selectMethod(matchingMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
78
frontend/pweb/lib/providers/payment_flow_provider.dart
Normal file
78
frontend/pweb/lib/providers/payment_flow_provider.dart
Normal file
@@ -0,0 +1,78 @@
|
||||
import 'package:flutter/foundation.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;
|
||||
Object? _manualPaymentData;
|
||||
|
||||
PaymentFlowProvider({
|
||||
required PaymentType initialType,
|
||||
}) : _selectedType = initialType;
|
||||
|
||||
PaymentType get selectedType => _selectedType;
|
||||
Object? 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(Object? 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();
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,9 @@ class PaymentMethodsProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
void makeMain(PaymentMethod method) {
|
||||
for (final m in _methods) m.isMain = false;
|
||||
for (final m in _methods) {
|
||||
m.isMain = false;
|
||||
}
|
||||
method.isMain = true;
|
||||
selectMethod(method);
|
||||
}
|
||||
|
||||
104
frontend/pweb/lib/providers/wallet_transactions.dart
Normal file
104
frontend/pweb/lib/providers/wallet_transactions.dart
Normal file
@@ -0,0 +1,104 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pshared/models/payment/status.dart';
|
||||
|
||||
import 'package:pweb/models/wallet_transaction.dart';
|
||||
import 'package:pweb/services/wallet_transactions.dart';
|
||||
|
||||
|
||||
class WalletTransactionsProvider extends ChangeNotifier {
|
||||
final WalletTransactionsService _service;
|
||||
|
||||
WalletTransactionsProvider(this._service);
|
||||
|
||||
List<WalletTransaction> _transactions = [];
|
||||
List<WalletTransaction> _filteredTransactions = [];
|
||||
DateTimeRange? _dateRange;
|
||||
final Set<OperationStatus> _selectedStatuses = {};
|
||||
final Set<WalletTransactionType> _selectedTypes = {};
|
||||
String? _walletId;
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
|
||||
List<WalletTransaction> get transactions => _transactions;
|
||||
List<WalletTransaction> get filteredTransactions => _filteredTransactions;
|
||||
DateTimeRange? get dateRange => _dateRange;
|
||||
Set<OperationStatus> get selectedStatuses => _selectedStatuses;
|
||||
Set<WalletTransactionType> get selectedTypes => _selectedTypes;
|
||||
String? get walletId => _walletId;
|
||||
bool get isLoading => _isLoading;
|
||||
String? get error => _error;
|
||||
bool get hasFilters =>
|
||||
_dateRange != null ||
|
||||
_selectedStatuses.isNotEmpty ||
|
||||
_selectedTypes.isNotEmpty;
|
||||
|
||||
Future<void> load({String? walletId}) async {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
_walletId = walletId ?? _walletId;
|
||||
_transactions = await _service.fetchHistory(walletId: _walletId);
|
||||
_applyFilters(notify: false);
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
_error = e.toString();
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void setWallet(String walletId) {
|
||||
_walletId = walletId;
|
||||
_applyFilters();
|
||||
}
|
||||
|
||||
void setDateRange(DateTimeRange? range) {
|
||||
_dateRange = range;
|
||||
_applyFilters();
|
||||
}
|
||||
|
||||
void toggleStatus(OperationStatus status) {
|
||||
if (_selectedStatuses.contains(status)) {
|
||||
_selectedStatuses.remove(status);
|
||||
} else {
|
||||
_selectedStatuses.add(status);
|
||||
}
|
||||
_applyFilters();
|
||||
}
|
||||
|
||||
void toggleType(WalletTransactionType type) {
|
||||
if (_selectedTypes.contains(type)) {
|
||||
_selectedTypes.remove(type);
|
||||
} else {
|
||||
_selectedTypes.add(type);
|
||||
}
|
||||
_applyFilters();
|
||||
}
|
||||
|
||||
void resetFilters() {
|
||||
_dateRange = null;
|
||||
_selectedStatuses.clear();
|
||||
_selectedTypes.clear();
|
||||
_applyFilters();
|
||||
}
|
||||
|
||||
void _applyFilters({bool notify = true}) {
|
||||
_filteredTransactions = _transactions.where((tx) {
|
||||
final walletMatch = _walletId == null || tx.walletId == _walletId;
|
||||
final statusMatch =
|
||||
_selectedStatuses.isEmpty || _selectedStatuses.contains(tx.status);
|
||||
final typeMatch =
|
||||
_selectedTypes.isEmpty || _selectedTypes.contains(tx.type);
|
||||
final dateMatch = _dateRange == null ||
|
||||
(tx.date.isAfter(_dateRange!.start.subtract(const Duration(seconds: 1))) &&
|
||||
tx.date.isBefore(_dateRange!.end.add(const Duration(seconds: 1))));
|
||||
|
||||
return walletMatch && statusMatch && typeMatch && dateMatch;
|
||||
}).toList();
|
||||
|
||||
if (notify) notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ class WalletsProvider with ChangeNotifier {
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
Wallet? _selectedWallet;
|
||||
bool _isHidden = true;
|
||||
final bool _isHidden = true;
|
||||
|
||||
List<Wallet>? get wallets => _wallets;
|
||||
bool get isLoading => _isLoading;
|
||||
@@ -120,6 +120,11 @@ class WalletsProvider with ChangeNotifier {
|
||||
if (index != null && index >= 0) {
|
||||
final wallet = _wallets![index];
|
||||
_wallets![index] = wallet.copyWith(isHidden: !wallet.isHidden);
|
||||
|
||||
if (_selectedWallet?.id == walletId) {
|
||||
_selectedWallet = _wallets![index];
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user