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 1 : ExchangeController( 28 : super.value, { 29 : required this.store, 30 : required this.target, 31 : required this.targetController, 32 : required this.source, 33 : }) { 34 3 : restate(source, target); 35 3 : targetController.addListener(_updateAll); 36 : } 37 : 38 1 : void restate(List<Currency?> source, Currency? target) { 39 1 : this.source = source; 40 1 : this.target = target; 41 2 : pairs.clear(); 42 1 : if (source.isEmpty) { 43 : return; 44 : } 45 2 : _add(target?.code, source.first?.code); 46 4 : for (int i = 0; i < source.length - 1; i++) { 47 4 : _add(source[i]?.code, source[i + 1]?.code); 48 : } 49 1 : notifyListeners(); 50 : } 51 : 52 1 : ExchangeScope get(int index) { 53 2 : final uuid = pairs[index]; 54 1 : final keys = uuid.split('-'); 55 : return ( 56 1 : from: keys[0], 57 1 : to: keys[1], 58 3 : rate: value[uuid]![0], 59 3 : sum: value[uuid]![1], 60 : ); 61 : } 62 : 63 1 : void save() { 64 2 : for (var uuid in pairs) { 65 6 : if (rate[uuid] == null || rate[uuid]?.details == 1.0) { 66 : continue; 67 : } 68 4 : store.update(uuid, rate[uuid]!, true); 69 : } 70 : } 71 : 72 1 : @override 73 : void dispose() { 74 3 : for (var scope in value.values) { 75 5 : for (int i = 0; i < source.length - 1; i++) { 76 2 : scope[i].dispose(); 77 : } 78 : } 79 2 : value.clear(); 80 2 : rate.clear(); 81 2 : pairs.clear(); 82 1 : super.dispose(); 83 : } 84 : 85 1 : void _add(String? from, String? to) { 86 5 : final uuid = [from, to].map((v) => v ?? '?').toList().join('-'); 87 2 : pairs.add(uuid); 88 4 : rate[uuid] ??= store.getByUuid(uuid) ?? 89 1 : CurrencyAppData( 90 1 : currency: CurrencyProvider.find(to), 91 1 : currencyFrom: CurrencyProvider.find(from), 92 : ); 93 3 : value[uuid] ??= [ 94 8 : TextEditingController(text: rate[uuid]!.details.toString())..addListener(() => _updateSum(uuid)), 95 6 : TextEditingController(text: _getAmount(uuid)?.toString())..addListener(() => _updateRate(uuid)), 96 : ]; 97 : } 98 : 99 1 : void _updateAll() { 100 2 : for (String uuid in pairs) { 101 1 : _updateSum(uuid); 102 : } 103 : } 104 : 105 1 : void _updateSum(String uuid) { 106 2 : List<TextEditingController> pair = value[uuid]!; 107 5 : if (pair[0].text != '' && rate[uuid] != null) { 108 6 : rate[uuid]!.details = double.tryParse(pair[0].text); 109 : } 110 5 : final amount = _getAmount(uuid)?.toFixed(CurrencyProvider.find(uuid.split('-')[1])?.decimalDigits); 111 3 : final current = double.tryParse(pair[1].text); 112 1 : if (amount != current) { 113 3 : pair[1].text = (amount ?? '').toString(); 114 2 : pair[1].notifyListeners(); 115 : } 116 : } 117 : 118 1 : void _updateRate(String uuid) { 119 2 : List<TextEditingController> pair = value[uuid]!; 120 4 : final sum = _getRate(uuid, double.tryParse(pair[1].text)); 121 3 : final current = double.tryParse(pair[0].text); 122 1 : 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 1 : double? _getRate(String uuid, double? amount) { 132 : if (amount == null) return null; 133 3 : double targetAmount = double.tryParse(targetController.text) ?? 0; 134 2 : final index = pairs.indexOf(uuid); 135 9 : double prev = index > 0 ? double.tryParse(value[pairs[index - 1]]?[1].text ?? '0') ?? 0 : targetAmount; 136 2 : return prev > 0 ? amount / prev : null; 137 : } 138 : 139 1 : double? _getAmount(String uuid) { 140 3 : double? result = rate[uuid]?.details; 141 3 : double? input = double.tryParse(targetController.text); 142 : if (result != null && input != null) { 143 2 : final index = pairs.indexOf(uuid); 144 9 : double amount = index > 0 ? double.tryParse(value[pairs[index - 1]]?[1].text ?? '0') ?? 0 : input; 145 1 : result *= amount; 146 : } else { 147 : result = null; 148 : } 149 : return result; 150 : } 151 : }