import 'dart:async'; import 'dart:io'; import 'package:csv/csv.dart'; import 'package:equatable/equatable.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:krezus/repositories/metadata/models/category.dart'; import 'package:krezus/repositories/metadata/metadata_repository.dart'; import 'package:krezus/repositories/metadata/models/account.dart'; import 'package:krezus/repositories/transactions/models/transaction.dart'; import 'package:krezus/repositories/transactions/transactions_repository.dart'; import 'package:uuid/uuid.dart'; part 'account_event.dart'; part 'account_state.dart'; final colors = [ 'FF74feff', 'FF64c0ff', 'FF5873fe', 'FF4b4cff', 'FFc0fcfd', 'FFa7caff', 'FF8d7efd', 'FF7f65fe', 'FFe7ffff', 'FFd7dafd', 'FFd8a6ff', 'FFc065fe', 'FFffffff', 'FFffe6fe', 'FFffbdff', 'FFff80fe', ]; class AccountBloc extends Bloc { final TransactionsRepository _transactionsRepository; final MetadataRepository _metadataRepository; AccountBloc({ required MetadataRepository metadataRepository, required TransactionsRepository transactionsRepository, }) : _metadataRepository = metadataRepository, _transactionsRepository = transactionsRepository, super(const AccountState() ) { on(_onAccountImportCSV); on(_onAccountLoad); // on(_onAccountImportJSON); // on(_onAccountImportJSON); on(_onAccountAdd); on(_onAcountRemove); on(_onAccountEditLabel); on(_onAccountEditSaving); on(_onAccountEditColor); on(_onClearData); _metadataRepository .getAccountsStream() .listen((subAccounts) => add(AccountLoad(subAccounts))); } double _universalConvertToDouble(dynamic value) { if (value is String) { return double.parse(value); } else if (value is int) { return value.toDouble(); } else if (value is double) { return value; } else { throw Error(); } } _onAccountImportCSV(AccountImportCSV event, Emitter emit) async { // int colorIndex = 0; FilePickerResult? result = await FilePicker.platform.pickFiles(); final csvPath = result?.files.first.path; if (csvPath != null) { final File csv = File(csvPath); final String csvFileContent = await csv.readAsString(); final List> csvList = const CsvToListConverter(fieldDelimiter: '|', eol: '\n').convert(csvFileContent); final Map categoriesMap = {}; final Map accounts = {}; final transactions = csvList .map((line) { double value = _universalConvertToDouble(line[4]); String? categoryLabel = line[1]; if (categoryLabel == null || categoryLabel == '') { categoryLabel = 'N/A'; } if (categoriesMap[categoryLabel] == null) { // if (categoryLabel == 'N/A') { // categoriesMap[categoryLabel] = Category(label: 'N/A', color: 'FFFF0000' ); // } else { // String color = colorIndex >= colors.length ? 'FF0000FF' : colors[colorIndex]; // colorIndex++; // categoriesMap[categoryLabel] = Category(label: categoryLabel, color: color ); // } categoriesMap[categoryLabel] = Category(label: categoryLabel, color: value > 0 ? 'FF21E297' : 'FFFFB4AB' ); } String accountLabel = line[3]; if (accounts[accountLabel] == null) { accounts[accountLabel] = Account(label: accountLabel, color: 'FF74feff'); } return Transaction( uuid: const Uuid().v8(), date: DateTime.parse(line[0]), category: categoryLabel, description: line[2], account: line[3], value: value ); }) .toList(); _metadataRepository.deleteMetadata(); _transactionsRepository.deleteTransactions(); _metadataRepository.saveAccounts(accounts.values.toList()); _metadataRepository.saveBudgets([]); _metadataRepository.saveCategories(categoriesMap.values.toList()); _transactionsRepository.saveTransactions(transactions); } } _onAccountLoad( AccountLoad event, Emitter emit ) { emit( state.copyWith(event.subAccounts) ); } _onAccountAdd(AccountAdd event, Emitter emit) { String uuid = const Uuid().v8(); Account account = Account( label: 'Account $uuid', color: 'FF74feff', saving: false ); emit( state.copyWith(_saveAccount(account)) ); } _onAcountRemove(AccountRemove event, Emitter emit) { Account accountToRemove = event.account; List accounts = state.accounts; List transactions = _transactionsRepository.getTransactions(); if (transactions.any((transaction) => transaction.account == accountToRemove.label)) { emit(AccountRemoveFail()); emit(AccountState(accounts: accounts)); } else { accounts.removeWhere((account) => account.label == accountToRemove.label); emit(AccountRemoveSucess()); emit( state.copyWith(_metadataRepository.saveAccounts(accounts)) ); } } _onAccountEditLabel(AccountEditLabel event, Emitter emit) { // Account account = event.account; // TODO check for existance, rename every transaction } _onAccountEditSaving(AccountEditSaving event, Emitter emit) { Account account = event.account; account.saving = event.saving; emit( state.copyWith(_saveAccount(account)) ); } _onAccountEditColor(AccountEditColor event, Emitter emit) { Account account = event.account; account.color = event.color; emit( state.copyWith(_saveAccount(account)) ); } List _saveAccount(Account accountToSave) { List accounts = _metadataRepository.getAccounts(); try { Account accountFound = accounts.firstWhere((account) => account.label == accountToSave.label); accountFound.color = accountToSave.color; accountFound.saving = accountToSave.saving; } catch (e) { if (accounts.isEmpty) { accounts = [accountToSave]; } else { accounts.add(accountToSave); } } _metadataRepository.saveAccounts(accounts); return accounts; } FutureOr _onClearData(ClearData event, Emitter emit) { _metadataRepository.deleteMetadata(); _transactionsRepository.deleteTransactions(); } }