import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:get_it/get_it.dart'; import 'package:okane/database/sqlite.dart'; import 'package:okane/ui/navigation.dart'; part 'core.freezed.dart'; @freezed abstract class CoreState with _$CoreState { const factory CoreState({ @Default(OkanePage.accounts) OkanePage activePage, int? activeAccountIndex, @Default(null) TransactionDto? activeTransaction, @Default([]) List accounts, @Default([]) List recurringTransactions, @Default([]) List transactions, @Default([]) List transactionTemplates, @Default([]) List beneficiaries, @Default([]) List expenseCategories, @Default([]) List budgets, @Default([]) List budgetItems, @Default(null) BudgetsDto? activeBudget, @Default([]) List loans, @Default(null) LoanDto? activeLoan, @Default(false) bool isDeletingAccount, }) = _CoreState; } class CoreCubit extends Cubit { CoreCubit() : super(CoreState()); StreamSubscription? _recurringTransactionStreamSubscription; StreamSubscription? _transactionTemplatesStreamSubcription; StreamSubscription? _accountsStreamSubscription; StreamSubscription? _transactionsStreamSubscription; StreamSubscription? _beneficiariesStreamSubscription; StreamSubscription? _expenseCategoryStreamSubscription; StreamSubscription? _budgetsStreamSubscription; StreamSubscription? _budgetItemsStreamSubscription; StreamSubscription? _loanStreamSubscription; void setupAccountStream() { _accountsStreamSubscription?.cancel(); _accountsStreamSubscription = GetIt.I .get() .accountsDao .accountsStream() .listen((accounts) { emit( state.copyWith( accounts: accounts, activeAccountIndex: accounts.isNotEmpty ? state.activeAccountIndex ?? 0 : null, ), ); }); } void setupStreams(Account account) { final db = GetIt.I.get(); setupAccountStream(); _recurringTransactionStreamSubscription?.cancel(); _recurringTransactionStreamSubscription = db.recurringTransactionsDao .recurringTransactionsStream(account) .listen((recurring) async { emit(state.copyWith(recurringTransactions: recurring)); }); _transactionTemplatesStreamSubcription?.cancel(); _transactionTemplatesStreamSubcription = db.transactionTemplatesDao .transactionTemplatesStream(account) .listen((templates) async { emit(state.copyWith(transactionTemplates: templates)); }); _transactionsStreamSubscription?.cancel(); _transactionsStreamSubscription = db.transactionsDao .transactionsStream(activeAccount!) .listen((transactions) async { emit(state.copyWith(transactions: transactions)); }); _beneficiariesStreamSubscription?.cancel(); _beneficiariesStreamSubscription = db.beneficiariesDao .beneficiariesStream() .listen((beneficiaries) async { emit(state.copyWith(beneficiaries: beneficiaries)); }); _expenseCategoryStreamSubscription?.cancel(); _expenseCategoryStreamSubscription = db.expenseCategoriesDao .expenseCategoriesStream(account) .listen((expenseCategories) async { emit(state.copyWith(expenseCategories: expenseCategories)); }); _budgetsStreamSubscription?.cancel(); _budgetsStreamSubscription = db.budgetsDao .budgetsStream(activeAccount!) .listen((budgets) async { print("BUDGETS: $budgets"); emit(state.copyWith(budgets: budgets)); }); _loanStreamSubscription?.cancel(); _loanStreamSubscription = db.loansDao.loansStream(account).listen(( loans, ) async { emit(state.copyWith(loans: loans)); }); } void cancelStreams() { _recurringTransactionStreamSubscription?.cancel(); _accountsStreamSubscription?.cancel(); _beneficiariesStreamSubscription?.cancel(); _budgetsStreamSubscription?.cancel(); _expenseCategoryStreamSubscription?.cancel(); _transactionsStreamSubscription?.cancel(); _transactionTemplatesStreamSubcription?.cancel(); _loanStreamSubscription?.cancel(); } Future init() async { final db = GetIt.I.get(); final accounts = await db.accountsDao.getAccounts(); final account = accounts.isEmpty ? null : accounts[0]; emit( state.copyWith( accounts: accounts, activeAccountIndex: accounts.isEmpty ? null : 0, transactions: await db.transactionsDao.getTransactions(account), beneficiaries: await db.beneficiariesDao.getBeneficiaries(), transactionTemplates: await db.transactionTemplatesDao .getTransactionTemplates(account), recurringTransactions: await db.recurringTransactionsDao .getRecurringTransactions(account), expenseCategories: await db.expenseCategoriesDao.getExpenseCategories( account, ), //budgets: await db.budgetsDao.getBudgets(account), loans: await db.loansDao.getLoans(account), ), ); if (account != null) { setupAccountStream(); setupStreams(account); } else { setupAccountStream(); } print("Core init done"); } void setPage(OkanePage page) { emit(state.copyWith(activePage: page)); } Future setActiveAccountIndex(int index) async { final db = GetIt.I.get(); final account = state.accounts[index]; emit( state.copyWith( activeAccountIndex: index, transactions: await db.transactionsDao.getTransactions(account), beneficiaries: await db.beneficiariesDao.getBeneficiaries(), transactionTemplates: await db.transactionTemplatesDao .getTransactionTemplates(account), recurringTransactions: await db.recurringTransactionsDao .getRecurringTransactions(account), budgets: await db.budgetsDao.getBudgets(account), activeBudget: null, activeTransaction: null, activeLoan: null, ), ); setupStreams(account); } void setActiveTransaction(TransactionDto? item) { emit(state.copyWith(activeTransaction: item)); } void setAccounts(List accounts) { emit(state.copyWith(accounts: accounts)); } void setActiveBudget(BudgetsDto? budget) { emit(state.copyWith(activeBudget: budget, budgetItems: [])); _budgetItemsStreamSubscription?.cancel(); if (budget != null) { _budgetItemsStreamSubscription = GetIt.I .get() .budgetsDao .watchBudgetItems(budget.budget) .listen((items) { emit(state.copyWith(budgetItems: items)); }); } } Future deleteAccount(Account account) async { final l = List.of(state.accounts); l.removeWhere((a) => a.id == account.id); final newIndex = l.isEmpty ? null : 0; emit(state.copyWith(activeAccountIndex: newIndex, isDeletingAccount: true)); cancelStreams(); try { // TODO //await db.deleteAccount(account); } finally { emit(state.copyWith(isDeletingAccount: false)); } await init(); } void setActiveLoan(LoanDto loan) { emit(state.copyWith(activeLoan: loan)); } Account? get activeAccount => state.activeAccountIndex == null ? null : state.accounts[state.activeAccountIndex!]; }