Compare commits

..

No commits in common. "baf0dfa99dc132e076c99a7d933ef50b99984cf7" and "5dc474407cb16e6e41ae51487f37f1224c16a09f" have entirely different histories.

13 changed files with 92 additions and 182 deletions

View File

@ -26,8 +26,3 @@ linter:
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
analyzer:
exclude:
- "**/*.g.dart"
- build/**
- .dart_tool/**

View File

@ -56,8 +56,9 @@ class Budgets extends Table {
class BudgetsDto {
final Budget budget;
final List<BudgetItemDto> budgetItems;
BudgetsDto({required this.budget});
BudgetsDto({required this.budget, required this.budgetItems});
}
class Loans extends Table {
@ -75,8 +76,13 @@ class LoanChanges extends Table {
class LoanDto {
final Loan loan;
final Beneficiary beneficiary;
final List<LoanChange> changes;
LoanDto({required this.loan, required this.beneficiary});
LoanDto({
required this.loan,
required this.beneficiary,
required this.changes,
});
}
class RecurringTransactions extends Table {
@ -367,12 +373,6 @@ class ExpenseCategoriesDao extends DatabaseAccessor<OkaneDatabase>
return select(expenseCategories).get();
}
Future<ExpenseCategory> upsertCategory(ExpenseCategoriesCompanion category) {
return into(
expenseCategories,
).insertReturning(category, mode: InsertMode.insertOrReplace);
}
}
@DriftAccessor(tables: [Budgets, BudgetItems])
@ -381,12 +381,23 @@ class BudgetsDao extends DatabaseAccessor<OkaneDatabase>
BudgetsDao(OkaneDatabase db) : super(db);
Stream<List<BudgetsDto>> budgetsStream(Account account) {
return (select(budgets)
..where((b) => b.accountId.equals(account.id))).watch().map((rows) {
return rows.map((row) {
return BudgetsDto(budget: row);
}).toList();
});
return (select(budgets)..where((b) => b.accountId.equals(account.id)))
.join([
leftOuterJoin(
budgetItems,
budgetItems.budgetId.equalsExp(budgets.id),
),
])
.watch()
.map((rows) {
return rows.map((row) {
return BudgetsDto(
budget: row.readTable(budgets),
// TODO
budgetItems: List.empty(),
);
}).toList();
});
}
Future<List<BudgetsDto>> getBudgets(Account? account) {
@ -394,28 +405,20 @@ class BudgetsDao extends DatabaseAccessor<OkaneDatabase>
return Future.value(List.empty());
}
return (select(budgets)
..where((b) => b.accountId.equals(account.id))).get().then((rows) {
return rows.map((row) {
return BudgetsDto(budget: row);
}).toList();
});
}
Stream<List<BudgetItemDto>> watchBudgetItems(Budget budget) {
return (select(budgetItems)..where((b) => b.budgetId.equals(budget.id)))
return (select(budgets)..where((b) => b.accountId.equals(account.id)))
.join([
leftOuterJoin(
expenseCategories,
expenseCategories.id.equalsExp(budgetItems.expenseCategoryId),
budgetItems,
budgetItems.budgetId.equalsExp(budgets.id),
),
])
.watch()
.map((rows) {
.get()
.then((rows) {
return rows.map((row) {
return BudgetItemDto(
expenseCategory: row.readTable(expenseCategories),
item: row.readTable(budgetItems),
return BudgetsDto(
budget: row.readTable(budgets),
// TODO
budgetItems: List.empty(),
);
}).toList();
});
@ -449,10 +452,12 @@ class LoansDao extends DatabaseAccessor<OkaneDatabase> with _$LoansDaoMixin {
.watch()
.map((rows) {
return rows.map((row) {
return LoanDto(
loan: row.readTable(loans),
beneficiary: row.readTable(beneficiaries),
);
return (
loan: row.readTable(loans),
beneficiary: row.readTable(beneficiaries),
changes: List.empty(),
)
as LoanDto;
}).toList();
});
}
@ -472,10 +477,12 @@ class LoansDao extends DatabaseAccessor<OkaneDatabase> with _$LoansDaoMixin {
.get()
.then((rows) {
return rows.map((row) {
return LoanDto(
loan: row.readTable(loans),
beneficiary: row.readTable(beneficiaries),
);
return (
loan: row.readTable(loans),
beneficiary: row.readTable(beneficiaries),
changes: List.empty(),
)
as LoanDto;
}).toList();
});
}
@ -499,11 +506,6 @@ class LoansDao extends DatabaseAccessor<OkaneDatabase> with _$LoansDaoMixin {
).insertReturning(loanChange, mode: InsertMode.insertOrReplace);
}
Stream<List<LoanChange>> watchLoanChanges(Loan loan) {
return (select(loanChanges)
..where((c) => c.loanId.equals(loan.id))).watch();
}
Future<void> deleteLoanChange(int id) {
return (delete(loanChanges)..where((c) => c.id.equals(id))).go();
}

View File

@ -44,7 +44,7 @@ class AccountListPageState extends State<AccountListPage> {
width: 150,
height: 100,
child: Card(
color: colorHash(state.accounts[index].name),
color: colorHash(state.accounts[index].name!),
shape:
index == state.activeAccountIndex
? RoundedRectangleBorder(
@ -61,7 +61,7 @@ class AccountListPageState extends State<AccountListPage> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(state.accounts[index].name),
Text(state.accounts[index].name!),
FutureBuilder(
future: GetIt.I
.get<OkaneDatabase>()

View File

@ -80,6 +80,7 @@ class AccountBalanceGraphCard extends StatelessWidget {
child: FutureBuilder(
future: getAccountBalance(),
builder: (context, snapshot) {
print("SNAPSHOT: ${snapshot.data}");
if (!snapshot.hasData) {
return CircularProgressIndicator();
}

View File

@ -9,13 +9,11 @@ import 'package:okane/ui/widgets/add_expense_category.dart';
class AddBudgetItemPopup extends StatefulWidget {
final VoidCallback onDone;
final BudgetsDto budget;
final List<BudgetItemDto> items;
const AddBudgetItemPopup({
super.key,
required this.onDone,
required this.budget,
required this.items,
});
@override
@ -74,7 +72,7 @@ class AddBudgetItemState extends State<AddBudgetItemPopup> {
_expenseCategory == null) {
return;
}
if (widget.items
if (widget.budget.budgetItems
.where(
(i) =>
i.expenseCategory.name == _expenseCategory!.name,
@ -86,11 +84,10 @@ class AddBudgetItemState extends State<AddBudgetItemPopup> {
await GetIt.I.get<OkaneDatabase>().budgetsDao.upsertBudgetItem(
BudgetItemsCompanion(
expenseCategoryId: Value(_expenseCategory!.id),
amount: Value(
double.parse(_budgetItemAmountEditController.text),
),
expenseCategoryId: Value(_expenseCategory!.id),
budgetId: Value(widget.budget.budget.id),
),
);
widget.onDone();

View File

@ -22,7 +22,6 @@ class BudgetDetailsPage extends StatelessWidget {
builder:
(_) => AddBudgetItemPopup(
budget: state.activeBudget!,
items: state.budgetItems,
onDone: () {
Navigator.of(context).pop();
},
@ -56,7 +55,7 @@ class BudgetDetailsPage extends StatelessWidget {
return Text(t.pages.budgets.details.noBudgetSelected);
}
if (state.budgetItems.isEmpty) {
if (state.activeBudget!.budgetItems.isEmpty) {
return Row(
children: [
Text(t.pages.budgets.details.noBudgetItems),
@ -96,9 +95,10 @@ class BudgetDetailsPage extends StatelessWidget {
),
ListView.builder(
shrinkWrap: true,
itemCount: state.budgetItems.length,
itemCount: state.activeBudget!.budgetItems.length,
itemBuilder: (context, index) {
final item = state.budgetItems.elementAt(index);
final item = state.activeBudget!.budgetItems
.elementAt(index);
final amount = formatCurrency(item.item.amount);
return ListTile(
title: Text(
@ -120,7 +120,7 @@ class BudgetDetailsPage extends StatelessWidget {
}
final categories =
state.budgetItems
state.activeBudget!.budgetItems
// TODO
//.map((i) => i.expenseCategory.value!.name)
.map((i) => "lol")
@ -149,7 +149,7 @@ class BudgetDetailsPage extends StatelessWidget {
spending.isEmpty
? 0
: spending.values.reduce((acc, val) => acc + val);
final budgetTotal = state.budgetItems
final budgetTotal = state.activeBudget!.budgetItems
.map((i) => i.item.amount)
.reduce((acc, val) => acc + val);
return Column(
@ -271,7 +271,7 @@ class BudgetDetailsPage extends StatelessWidget {
fallbackText: "",
valueConverter: formatCurrency,
items:
state.budgetItems
state.activeBudget!.budgetItems
.map(
(i) => (
title: i.expenseCategory.name,
@ -342,9 +342,10 @@ class BudgetDetailsPage extends StatelessWidget {
padding: EdgeInsets.all(8),
child: ListView.builder(
shrinkWrap: true,
itemCount: state.budgetItems.length,
itemCount: state.activeBudget!.budgetItems.length,
itemBuilder: (context, index) {
final item = state.budgetItems.elementAt(index);
final item = state.activeBudget!.budgetItems
.elementAt(index);
final amount = formatCurrency(item.item.amount);
final spent = spending[item.expenseCategory.name];
final left =

View File

@ -91,7 +91,6 @@ class AddLoanPopupState extends State<AddLoanChangePopup> {
sign * double.parse(_amountController.text).abs(),
),
date: Value(DateTime.now()),
loanId: Value(widget.loan.loan.id),
),
);
widget.onDone();

View File

@ -43,9 +43,10 @@ class LoanDetailsPage extends StatelessWidget {
return Text("No loan selected");
}
final loanChanges = state.activeLoan!.changes.toList();
final loanSum =
state.loanChanges.isNotEmpty
? state.loanChanges
loanChanges.isNotEmpty
? loanChanges
.map((c) => c.amount)
.reduce((acc, val) => acc + val)
: 0.0;
@ -93,9 +94,9 @@ class LoanDetailsPage extends StatelessWidget {
SliverToBoxAdapter(
child:
state.loanChanges.isNotEmpty
loanChanges.isNotEmpty
? GroupedListView(
elements: state.loanChanges,
elements: loanChanges,
shrinkWrap: true,
reverse: true,
groupBy:

View File

@ -135,7 +135,7 @@ class TransactionDetailsPage extends StatelessWidget {
GetIt.I
.get<CoreCubit>()
.activeAccount!
.name,
.name!,
),
),
],

View File

@ -20,10 +20,8 @@ abstract class CoreState with _$CoreState {
@Default([]) List<Beneficiary> beneficiaries,
@Default([]) List<ExpenseCategory> expenseCategories,
@Default([]) List<BudgetsDto> budgets,
@Default([]) List<BudgetItemDto> budgetItems,
@Default(null) BudgetsDto? activeBudget,
@Default([]) List<LoanDto> loans,
@Default([]) List<LoanChange> loanChanges,
@Default(null) LoanDto? activeLoan,
@Default(false) bool isDeletingAccount,
}) = _CoreState;
@ -39,9 +37,7 @@ class CoreCubit extends Cubit<CoreState> {
StreamSubscription<void>? _beneficiariesStreamSubscription;
StreamSubscription<void>? _expenseCategoryStreamSubscription;
StreamSubscription<void>? _budgetsStreamSubscription;
StreamSubscription<void>? _budgetItemsStreamSubscription;
StreamSubscription<void>? _loanStreamSubscription;
StreamSubscription<void>? _loanChangesSubscription;
void setupAccountStream() {
_accountsStreamSubscription?.cancel();
@ -126,6 +122,17 @@ class CoreCubit extends Cubit<CoreState> {
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),
),
);
@ -172,17 +179,7 @@ class CoreCubit extends Cubit<CoreState> {
}
void setActiveBudget(BudgetsDto? budget) {
emit(state.copyWith(activeBudget: budget, budgetItems: []));
_budgetItemsStreamSubscription?.cancel();
if (budget != null) {
_budgetItemsStreamSubscription = GetIt.I
.get<OkaneDatabase>()
.budgetsDao
.watchBudgetItems(budget.budget)
.listen((items) {
emit(state.copyWith(budgetItems: items));
});
}
emit(state.copyWith(activeBudget: budget));
}
Future<void> deleteAccount(Account account) async {
@ -201,18 +198,8 @@ class CoreCubit extends Cubit<CoreState> {
await init();
}
void setActiveLoan(LoanDto? loan) {
emit(state.copyWith(activeLoan: loan, loanChanges: []));
_loanChangesSubscription?.cancel();
if (loan != null) {
_loanChangesSubscription = GetIt.I
.get<OkaneDatabase>()
.loansDao
.watchLoanChanges(loan.loan)
.listen((changes) {
emit(state.copyWith(loanChanges: changes));
});
}
void setActiveLoan(LoanDto loan) {
emit(state.copyWith(activeLoan: loan));
}
Account? get activeAccount =>

View File

@ -30,10 +30,8 @@ mixin _$CoreState {
List<ExpenseCategory> get expenseCategories =>
throw _privateConstructorUsedError;
List<BudgetsDto> get budgets => throw _privateConstructorUsedError;
List<BudgetItemDto> get budgetItems => throw _privateConstructorUsedError;
BudgetsDto? get activeBudget => throw _privateConstructorUsedError;
List<LoanDto> get loans => throw _privateConstructorUsedError;
List<LoanChange> get loanChanges => throw _privateConstructorUsedError;
LoanDto? get activeLoan => throw _privateConstructorUsedError;
bool get isDeletingAccount => throw _privateConstructorUsedError;
@ -58,10 +56,8 @@ abstract class $CoreStateCopyWith<$Res> {
List<Beneficiary> beneficiaries,
List<ExpenseCategory> expenseCategories,
List<BudgetsDto> budgets,
List<BudgetItemDto> budgetItems,
BudgetsDto? activeBudget,
List<LoanDto> loans,
List<LoanChange> loanChanges,
LoanDto? activeLoan,
bool isDeletingAccount,
});
@ -90,10 +86,8 @@ class _$CoreStateCopyWithImpl<$Res, $Val extends CoreState>
Object? beneficiaries = null,
Object? expenseCategories = null,
Object? budgets = null,
Object? budgetItems = null,
Object? activeBudget = freezed,
Object? loans = null,
Object? loanChanges = null,
Object? activeLoan = freezed,
Object? isDeletingAccount = null,
}) {
@ -149,11 +143,6 @@ class _$CoreStateCopyWithImpl<$Res, $Val extends CoreState>
? _value.budgets
: budgets // ignore: cast_nullable_to_non_nullable
as List<BudgetsDto>,
budgetItems:
null == budgetItems
? _value.budgetItems
: budgetItems // ignore: cast_nullable_to_non_nullable
as List<BudgetItemDto>,
activeBudget:
freezed == activeBudget
? _value.activeBudget
@ -164,11 +153,6 @@ class _$CoreStateCopyWithImpl<$Res, $Val extends CoreState>
? _value.loans
: loans // ignore: cast_nullable_to_non_nullable
as List<LoanDto>,
loanChanges:
null == loanChanges
? _value.loanChanges
: loanChanges // ignore: cast_nullable_to_non_nullable
as List<LoanChange>,
activeLoan:
freezed == activeLoan
? _value.activeLoan
@ -205,10 +189,8 @@ abstract class _$$CoreStateImplCopyWith<$Res>
List<Beneficiary> beneficiaries,
List<ExpenseCategory> expenseCategories,
List<BudgetsDto> budgets,
List<BudgetItemDto> budgetItems,
BudgetsDto? activeBudget,
List<LoanDto> loans,
List<LoanChange> loanChanges,
LoanDto? activeLoan,
bool isDeletingAccount,
});
@ -236,10 +218,8 @@ class __$$CoreStateImplCopyWithImpl<$Res>
Object? beneficiaries = null,
Object? expenseCategories = null,
Object? budgets = null,
Object? budgetItems = null,
Object? activeBudget = freezed,
Object? loans = null,
Object? loanChanges = null,
Object? activeLoan = freezed,
Object? isDeletingAccount = null,
}) {
@ -295,11 +275,6 @@ class __$$CoreStateImplCopyWithImpl<$Res>
? _value._budgets
: budgets // ignore: cast_nullable_to_non_nullable
as List<BudgetsDto>,
budgetItems:
null == budgetItems
? _value._budgetItems
: budgetItems // ignore: cast_nullable_to_non_nullable
as List<BudgetItemDto>,
activeBudget:
freezed == activeBudget
? _value.activeBudget
@ -310,11 +285,6 @@ class __$$CoreStateImplCopyWithImpl<$Res>
? _value._loans
: loans // ignore: cast_nullable_to_non_nullable
as List<LoanDto>,
loanChanges:
null == loanChanges
? _value._loanChanges
: loanChanges // ignore: cast_nullable_to_non_nullable
as List<LoanChange>,
activeLoan:
freezed == activeLoan
? _value.activeLoan
@ -344,10 +314,8 @@ class _$CoreStateImpl implements _CoreState {
final List<Beneficiary> beneficiaries = const [],
final List<ExpenseCategory> expenseCategories = const [],
final List<BudgetsDto> budgets = const [],
final List<BudgetItemDto> budgetItems = const [],
this.activeBudget = null,
final List<LoanDto> loans = const [],
final List<LoanChange> loanChanges = const [],
this.activeLoan = null,
this.isDeletingAccount = false,
}) : _accounts = accounts,
@ -357,9 +325,7 @@ class _$CoreStateImpl implements _CoreState {
_beneficiaries = beneficiaries,
_expenseCategories = expenseCategories,
_budgets = budgets,
_budgetItems = budgetItems,
_loans = loans,
_loanChanges = loanChanges;
_loans = loans;
@override
@JsonKey()
@ -435,15 +401,6 @@ class _$CoreStateImpl implements _CoreState {
return EqualUnmodifiableListView(_budgets);
}
final List<BudgetItemDto> _budgetItems;
@override
@JsonKey()
List<BudgetItemDto> get budgetItems {
if (_budgetItems is EqualUnmodifiableListView) return _budgetItems;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_budgetItems);
}
@override
@JsonKey()
final BudgetsDto? activeBudget;
@ -456,15 +413,6 @@ class _$CoreStateImpl implements _CoreState {
return EqualUnmodifiableListView(_loans);
}
final List<LoanChange> _loanChanges;
@override
@JsonKey()
List<LoanChange> get loanChanges {
if (_loanChanges is EqualUnmodifiableListView) return _loanChanges;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_loanChanges);
}
@override
@JsonKey()
final LoanDto? activeLoan;
@ -474,7 +422,7 @@ class _$CoreStateImpl implements _CoreState {
@override
String toString() {
return 'CoreState(activePage: $activePage, activeAccountIndex: $activeAccountIndex, activeTransaction: $activeTransaction, accounts: $accounts, recurringTransactions: $recurringTransactions, transactions: $transactions, transactionTemplates: $transactionTemplates, beneficiaries: $beneficiaries, expenseCategories: $expenseCategories, budgets: $budgets, budgetItems: $budgetItems, activeBudget: $activeBudget, loans: $loans, loanChanges: $loanChanges, activeLoan: $activeLoan, isDeletingAccount: $isDeletingAccount)';
return 'CoreState(activePage: $activePage, activeAccountIndex: $activeAccountIndex, activeTransaction: $activeTransaction, accounts: $accounts, recurringTransactions: $recurringTransactions, transactions: $transactions, transactionTemplates: $transactionTemplates, beneficiaries: $beneficiaries, expenseCategories: $expenseCategories, budgets: $budgets, activeBudget: $activeBudget, loans: $loans, activeLoan: $activeLoan, isDeletingAccount: $isDeletingAccount)';
}
@override
@ -510,17 +458,9 @@ class _$CoreStateImpl implements _CoreState {
_expenseCategories,
) &&
const DeepCollectionEquality().equals(other._budgets, _budgets) &&
const DeepCollectionEquality().equals(
other._budgetItems,
_budgetItems,
) &&
(identical(other.activeBudget, activeBudget) ||
other.activeBudget == activeBudget) &&
const DeepCollectionEquality().equals(other._loans, _loans) &&
const DeepCollectionEquality().equals(
other._loanChanges,
_loanChanges,
) &&
(identical(other.activeLoan, activeLoan) ||
other.activeLoan == activeLoan) &&
(identical(other.isDeletingAccount, isDeletingAccount) ||
@ -540,10 +480,8 @@ class _$CoreStateImpl implements _CoreState {
const DeepCollectionEquality().hash(_beneficiaries),
const DeepCollectionEquality().hash(_expenseCategories),
const DeepCollectionEquality().hash(_budgets),
const DeepCollectionEquality().hash(_budgetItems),
activeBudget,
const DeepCollectionEquality().hash(_loans),
const DeepCollectionEquality().hash(_loanChanges),
activeLoan,
isDeletingAccount,
);
@ -567,10 +505,8 @@ abstract class _CoreState implements CoreState {
final List<Beneficiary> beneficiaries,
final List<ExpenseCategory> expenseCategories,
final List<BudgetsDto> budgets,
final List<BudgetItemDto> budgetItems,
final BudgetsDto? activeBudget,
final List<LoanDto> loans,
final List<LoanChange> loanChanges,
final LoanDto? activeLoan,
final bool isDeletingAccount,
}) = _$CoreStateImpl;
@ -596,14 +532,10 @@ abstract class _CoreState implements CoreState {
@override
List<BudgetsDto> get budgets;
@override
List<BudgetItemDto> get budgetItems;
@override
BudgetsDto? get activeBudget;
@override
List<LoanDto> get loans;
@override
List<LoanChange> get loanChanges;
@override
LoanDto? get activeLoan;
@override
bool get isDeletingAccount;

View File

@ -25,7 +25,7 @@ class AccountSwitcher extends StatelessWidget {
itemBuilder: (context, index) {
final item = state.accounts[index];
return ListTile(
title: Text(item.name),
title: Text(item.name!),
trailing: IconButton(
icon: Icon(Icons.delete),
color: Colors.red,
@ -65,7 +65,7 @@ class AccountSwitcher extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
Text(
bloc.activeAccount!.name,
bloc.activeAccount!.name!,
style: Theme.of(context).textTheme.titleLarge,
),
Icon(Icons.arrow_drop_down),

View File

@ -1,8 +1,5 @@
import 'package:drift/drift.dart' show Value;
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:get_it/get_it.dart';
import 'package:okane/database/sqlite.dart';
import 'package:okane/i18n/strings.g.dart';
import 'package:okane/ui/state/core.dart';
@ -51,16 +48,14 @@ class AddExpenseCategoryState extends State<AddExpenseCategory> {
Spacer(),
OutlinedButton(
onPressed: () async {
final category = await GetIt.I
.get<OkaneDatabase>()
.expenseCategoriesDao
.upsertCategory(
ExpenseCategoriesCompanion(
name: Value(_categoryNameController.text),
),
);
// TODO
/*
final category =
ExpenseCategory()
..name = _categoryNameController.text;
await upsertExpenseCategory(category);
_categoryNameController.text = "";
Navigator.of(context).pop(category);
Navigator.of(context).pop(category);*/
},
child: Text(t.modals.add),
),