import 'package:flutter/foundation.dart'; import 'package:pshared/controllers/balance_mask/wallets.dart'; import 'package:pshared/models/money.dart'; import 'package:pshared/models/payment/payment.dart'; import 'package:pshared/models/payment/quote/status_type.dart'; import 'package:pshared/models/payment/wallet.dart'; import 'package:pweb/models/payment/multiple_payouts/csv_row.dart'; import 'package:pweb/models/payment/multiple_payouts/state.dart'; import 'package:pweb/providers/multiple_payouts.dart'; import 'package:pweb/services/payments/csv_input.dart'; class MultiplePayoutsController extends ChangeNotifier { final CsvInputService _csvInput; MultiplePayoutsProvider? _provider; WalletsController? _wallets; _PickState _pickState = _PickState.idle; Exception? _uiError; MultiplePayoutsController({ required CsvInputService csvInput, }) : _csvInput = csvInput; void update(MultiplePayoutsProvider provider, WalletsController wallets) { var shouldNotify = false; if (!identical(_provider, provider)) { _provider?.removeListener(_onProviderChanged); _provider = provider; _provider?.addListener(_onProviderChanged); shouldNotify = true; } if (!identical(_wallets, wallets)) { _wallets?.removeListener(_onWalletsChanged); _wallets = wallets; _wallets?.addListener(_onWalletsChanged); shouldNotify = true; } if (shouldNotify) { notifyListeners(); } } MultiplePayoutsState get state => _provider?.state ?? MultiplePayoutsState.idle; String? get selectedFileName => _provider?.selectedFileName; List get rows => _provider?.rows ?? const []; int get sentCount => _provider?.sentCount ?? 0; Exception? get error => _uiError ?? _provider?.error; bool get isQuoting => _provider?.isQuoting ?? false; bool get isSending => _provider?.isSending ?? false; bool get isBusy => _provider?.isBusy ?? false; bool get quoteIsLoading => _provider?.quoteIsLoading ?? false; QuoteStatusType get quoteStatusType => _provider?.quoteStatusType ?? QuoteStatusType.missing; Duration? get quoteTimeLeft => _provider?.quoteTimeLeft; bool get canSend => _provider?.canSend ?? false; Money? get aggregateDebitAmount => _provider?.aggregateDebitAmountFor(_selectedWallet); Money? get requestedSentAmount => _provider?.requestedSentAmount; Money? get aggregateSettlementAmount => _provider?.aggregateSettlementAmountFor(_selectedWallet); Money? get aggregateFeeAmount => _provider?.aggregateFeeAmountFor(_selectedWallet); double? get aggregateFeePercent => _provider?.aggregateFeePercentFor(_selectedWallet); Future pickAndQuote() async { if (_pickState == _PickState.picking) return; final provider = _provider; if (provider == null) { _setUiError(StateError('Multiple payouts provider is not ready')); return; } _clearUiError(); _pickState = _PickState.picking; try { final picked = await _csvInput.pickCsv(); if (picked == null) return; final wallet = _selectedWallet; if (wallet == null) { _setUiError(StateError('Select source wallet first')); return; } await provider.quoteFromCsv( fileName: picked.name, content: picked.content, sourceWallet: wallet, ); } catch (e) { _setUiError(e); } finally { _pickState = _PickState.idle; } } Future> send() async { return _provider?.send() ?? const []; } Future sendAndGetOutcome() async { _clearUiError(); final provider = _provider; if (provider == null) { _setUiError(StateError('Multiple payouts provider is not ready')); return MultiplePayoutSendOutcome.failure; } final payments = await provider.send(); final hasError = provider.error != null; if (hasError || payments.isEmpty) { return MultiplePayoutSendOutcome.failure; } return MultiplePayoutSendOutcome.success; } void removeUploadedFile() { _provider?.removeUploadedFile(); _clearUiError(notify: false); } void _onProviderChanged() { notifyListeners(); } void _onWalletsChanged() { notifyListeners(); } Wallet? get _selectedWallet => _wallets?.selectedWallet; void _setUiError(Object error) { _uiError = error is Exception ? error : Exception(error.toString()); notifyListeners(); } void _clearUiError({bool notify = true}) { if (_uiError == null) return; _uiError = null; if (notify) { notifyListeners(); } } @override void dispose() { _provider?.removeListener(_onProviderChanged); _wallets?.removeListener(_onWalletsChanged); super.dispose(); } } enum _PickState { idle, picking } enum MultiplePayoutSendOutcome { success, failure }