import 'package:flutter/material.dart'; import 'package:pshared/controllers/payment/source.dart'; import 'package:pshared/models/payment/settlement_mode.dart'; import 'package:pshared/provider/payment/amount.dart'; import 'package:pshared/utils/currency.dart'; import 'package:pshared/utils/money.dart'; import 'package:pweb/models/payment/amount/mode.dart'; class PaymentAmountFieldController extends ChangeNotifier { static const String _settlementCurrencyCode = 'RUB'; final TextEditingController textController; final FocusNode focusNode = FocusNode(); PaymentAmountProvider? _provider; PaymentSourceController? _source; bool _isSyncingText = false; PaymentAmountMode _mode = PaymentAmountMode.debit; PaymentAmountFieldController({required double initialAmount}) : textController = TextEditingController( text: amountToString(initialAmount), ); PaymentAmountMode get mode => _mode; bool get isReverseModeAvailable { final sourceCurrencyCode = _sourceCurrencyCode; return sourceCurrencyCode != null && sourceCurrencyCode != _settlementCurrencyCode; } String? get activeCurrencyCode => switch (_mode) { PaymentAmountMode.debit => _sourceCurrencyCode, PaymentAmountMode.settlement => _settlementCurrencyCode, }; void update(PaymentAmountProvider provider, PaymentSourceController source) { if (!identical(_provider, provider)) { _provider?.removeListener(_handleProviderChanged); _provider = provider; _provider?.addListener(_handleProviderChanged); _syncModeWithProvider(provider); } if (!identical(_source, source)) { _source?.removeListener(_handleSourceChanged); _source = source; _source?.addListener(_handleSourceChanged); _normalizeModeForSource(); } _syncTextWithAmount(provider.amount); } void handleChanged(String value) { if (_isSyncingText) return; final parsed = _parseAmount(value); if (parsed != null) { _provider?.setAmount(parsed); } } void handleModeChanged(PaymentAmountMode value) { if (value == _mode) return; if (!isReverseModeAvailable && value == PaymentAmountMode.settlement) { return; } _mode = value; _provider?.setSettlementMode(_settlementModeFromMode(value)); notifyListeners(); } void _handleProviderChanged() { final provider = _provider; if (provider == null) return; _syncTextWithAmount(provider.amount); final changed = _syncModeWithProvider(provider); if (changed) { _normalizeModeForSource(); notifyListeners(); } } void _handleSourceChanged() { _normalizeModeForSource(); notifyListeners(); } bool _syncModeWithProvider(PaymentAmountProvider provider) { final nextMode = _modeFromSettlementMode(provider.settlementMode); if (nextMode == _mode) return false; _mode = nextMode; return true; } bool _normalizeModeForSource() { if (isReverseModeAvailable || _mode != PaymentAmountMode.settlement) { return false; } _mode = PaymentAmountMode.debit; _provider?.setSettlementMode(SettlementMode.fixSource); return true; } String? get _sourceCurrencyCode { return _source?.selectedCurrencyCode; } PaymentAmountMode _modeFromSettlementMode(SettlementMode mode) => switch (mode) { SettlementMode.fixReceived => PaymentAmountMode.settlement, SettlementMode.fixSource || SettlementMode.unspecified => PaymentAmountMode.debit, }; SettlementMode _settlementModeFromMode(PaymentAmountMode mode) => switch (mode) { PaymentAmountMode.debit => SettlementMode.fixSource, PaymentAmountMode.settlement => SettlementMode.fixReceived, }; double? _parseAmount(String value) { final parsed = parseMoneyAmount( value.replaceAll(',', '.'), fallback: double.nan, ); return parsed.isNaN ? null : parsed; } void _syncTextWithAmount(double amount) { final parsedText = _parseAmount(textController.text); if (parsedText != null && parsedText == amount) return; final nextText = amountToString(amount); _isSyncingText = true; textController.value = TextEditingValue( text: nextText, selection: TextSelection.collapsed(offset: nextText.length), ); _isSyncingText = false; } @override void dispose() { _provider?.removeListener(_handleProviderChanged); _source?.removeListener(_handleSourceChanged); focusNode.dispose(); textController.dispose(); super.dispose(); } }