Line data Source code
1 : // Copyright 2023 The terCAD team. All rights reserved. 2 : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file. 3 : 4 : import 'package:app_finance/_classes/storage/app_data.dart'; 5 : import 'package:app_finance/_classes/structure/currency_app_data.dart'; 6 : import 'package:app_finance/_ext/double_ext.dart'; 7 : import 'package:flutter/material.dart'; 8 : import 'package:flutter_currency_picker/flutter_currency_picker.dart'; 9 : 10 : typedef ExchangeMap = Map<String, List<TextEditingController>>; 11 : 12 : typedef ExchangeScope = ({ 13 : String from, 14 : String to, 15 : TextEditingController rate, 16 : TextEditingController sum, 17 : }); 18 : 19 : class ExchangeController extends ValueNotifier<ExchangeMap> { 20 : final Map<String, CurrencyAppData> rate = {}; 21 : final List<String> pairs = []; 22 : final AppData store; 23 : final TextEditingController targetController; 24 : List<Currency?> source; 25 : Currency? target; 26 : 27 2 : ExchangeController( 28 : super.value, { 29 : required this.store, 30 : required this.target, 31 : required this.targetController, 32 : required this.source, 33 : }) { 34 6 : restate(source, target); 35 6 : targetController.addListener(_updateAll); 36 : } 37 : 38 2 : void restate(List<Currency?> source, Currency? target) { 39 2 : this.source = source; 40 2 : this.target = target; 41 4 : pairs.clear(); 42 2 : if (source.isEmpty) { 43 : return; 44 : } 45 4 : _add(target?.code, source.first?.code); 46 8 : for (int i = 0; i < source.length - 1; i++) { 47 8 : _add(source[i]?.code, source[i + 1]?.code); 48 : } 49 2 : notifyListeners(); 50 : } 51 : 52 0 : ExchangeScope get(int index) { 53 0 : final uuid = pairs[index]; 54 0 : final keys = uuid.split('-'); 55 : return ( 56 0 : from: keys[0], 57 0 : to: keys[1], 58 0 : rate: value[uuid]![0], 59 0 : sum: value[uuid]![1], 60 : ); 61 : } 62 : 63 0 : void save() { 64 0 : for (var uuid in pairs) { 65 0 : if (rate[uuid] == null || rate[uuid]?.details == 1.0) { 66 : continue; 67 : } 68 0 : store.update(uuid, rate[uuid]!, true); 69 : } 70 : } 71 : 72 2 : @override 73 : void dispose() { 74 6 : for (var scope in value.values) { 75 10 : for (int i = 0; i < source.length - 1; i++) { 76 4 : scope[i].dispose(); 77 : } 78 : } 79 4 : value.clear(); 80 4 : rate.clear(); 81 4 : pairs.clear(); 82 2 : super.dispose(); 83 : } 84 : 85 2 : void _add(String? from, String? to) { 86 10 : final uuid = [from, to].map((v) => v ?? '?').toList().join('-'); 87 4 : pairs.add(uuid); 88 8 : rate[uuid] ??= store.getByUuid(uuid) ?? 89 2 : CurrencyAppData( 90 2 : currency: CurrencyProvider.find(to), 91 2 : currencyFrom: CurrencyProvider.find(from), 92 : ); 93 6 : value[uuid] ??= [ 94 12 : TextEditingController(text: rate[uuid]!.details.toString())..addListener(() => _updateSum(uuid)), 95 6 : TextEditingController(text: _getAmount(uuid)?.toString())..addListener(() => _updateRate(uuid)), 96 : ]; 97 : } 98 : 99 2 : void _updateAll() { 100 4 : for (String uuid in pairs) { 101 2 : _updateSum(uuid); 102 : } 103 : } 104 : 105 2 : void _updateSum(String uuid) { 106 4 : List<TextEditingController> pair = value[uuid]!; 107 10 : if (pair[0].text != '' && rate[uuid] != null) { 108 12 : rate[uuid]!.details = double.tryParse(pair[0].text); 109 : } 110 2 : final amount = _getAmount(uuid)?.toFixed(CurrencyProvider.find(uuid.split('-')[1])?.decimalDigits); 111 6 : final current = double.tryParse(pair[1].text); 112 2 : if (amount != current) { 113 0 : pair[1].text = (amount ?? '').toString(); 114 0 : pair[1].notifyListeners(); 115 : } 116 : } 117 : 118 0 : void _updateRate(String uuid) { 119 0 : List<TextEditingController> pair = value[uuid]!; 120 0 : final sum = _getRate(uuid, double.tryParse(pair[1].text)); 121 0 : final current = double.tryParse(pair[0].text); 122 0 : if (sum != current) { 123 0 : if (sum != null && rate[uuid] != null) { 124 0 : rate[uuid]!.details = sum; 125 : } 126 0 : pair[0].text = (sum ?? '').toString(); 127 0 : pair[0].notifyListeners(); 128 : } 129 : } 130 : 131 0 : double? _getRate(String uuid, double? amount) { 132 : if (amount == null) return null; 133 0 : double targetAmount = double.tryParse(targetController.text) ?? 0; 134 0 : final index = pairs.indexOf(uuid); 135 0 : double prev = index > 0 ? double.tryParse(value[pairs[index - 1]]?[1].text ?? '0') ?? 0 : targetAmount; 136 0 : return prev > 0 ? amount / prev : null; 137 : } 138 : 139 2 : double? _getAmount(String uuid) { 140 6 : double? result = rate[uuid]?.details; 141 6 : double? input = double.tryParse(targetController.text); 142 : if (result != null && input != null) { 143 0 : final index = pairs.indexOf(uuid); 144 0 : double amount = index > 0 ? double.tryParse(value[pairs[index - 1]]?[1].text ?? '0') ?? 0 : input; 145 0 : result *= amount; 146 : } else { 147 : result = null; 148 : } 149 : return result; 150 : } 151 : }