Compare commits

...

9 Commits

26 changed files with 412 additions and 156 deletions

1
.gitignore vendored
View File

@@ -11,6 +11,7 @@
.svn/ .svn/
.swiftpm/ .swiftpm/
migrate_working_dir/ migrate_working_dir/
.sentry-native/
# IntelliJ related # IntelliJ related
*.iml *.iml

View File

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

View File

@@ -56,9 +56,8 @@ class Budgets extends Table {
class BudgetsDto { class BudgetsDto {
final Budget budget; final Budget budget;
final List<BudgetItemDto> budgetItems;
BudgetsDto({required this.budget, required this.budgetItems}); BudgetsDto({required this.budget});
} }
class Loans extends Table { class Loans extends Table {
@@ -76,13 +75,8 @@ class LoanChanges extends Table {
class LoanDto { class LoanDto {
final Loan loan; final Loan loan;
final Beneficiary beneficiary; final Beneficiary beneficiary;
final List<LoanChange> changes;
LoanDto({ LoanDto({required this.loan, required this.beneficiary});
required this.loan,
required this.beneficiary,
required this.changes,
});
} }
class RecurringTransactions extends Table { class RecurringTransactions extends Table {
@@ -185,10 +179,19 @@ class OkaneDatabase extends _$OkaneDatabase {
} }
} }
@DriftAccessor(tables: [Accounts]) @DriftAccessor(
tables: [
Accounts,
Transactions,
TransactionTemplates,
RecurringTransactions,
Budgets,
Beneficiaries,
],
)
class AccountsDao extends DatabaseAccessor<OkaneDatabase> class AccountsDao extends DatabaseAccessor<OkaneDatabase>
with _$AccountsDaoMixin { with _$AccountsDaoMixin {
AccountsDao(OkaneDatabase db) : super(db); AccountsDao(super.db);
Stream<List<Account>> accountsStream() { Stream<List<Account>> accountsStream() {
return select(accounts).watch(); return select(accounts).watch();
@@ -201,6 +204,22 @@ class AccountsDao extends DatabaseAccessor<OkaneDatabase>
Future<int> upsertAccount(AccountsCompanion account) { Future<int> upsertAccount(AccountsCompanion account) {
return into(accounts).insertOnConflictUpdate(account); return into(accounts).insertOnConflictUpdate(account);
} }
Future<void> removeAccount(Account account) async {
// Delete dependent data
await (delete(transactions)
..where((t) => t.accountId.equals(account.id))).go();
await (delete(recurringTransactions)
..where((r) => r.accountId.equals(account.id))).go();
await (delete(transactionTemplates)
..where((t) => t.accountId.equals(account.id))).go();
await (delete(budgets)..where((b) => b.accountId.equals(account.id))).go();
await (delete(beneficiaries)
..where((b) => b.accountId.equals(account.id))).go();
// Delete the account
await (delete(accounts)..where((a) => a.id.equals(account.id))).go();
}
} }
enum TransactionQueryDateOption { thisMonth } enum TransactionQueryDateOption { thisMonth }
@@ -208,7 +227,7 @@ enum TransactionQueryDateOption { thisMonth }
@DriftAccessor(tables: [Transactions, Beneficiaries, ExpenseCategories]) @DriftAccessor(tables: [Transactions, Beneficiaries, ExpenseCategories])
class TransactionsDao extends DatabaseAccessor<OkaneDatabase> class TransactionsDao extends DatabaseAccessor<OkaneDatabase>
with _$TransactionsDaoMixin { with _$TransactionsDaoMixin {
TransactionsDao(OkaneDatabase db) : super(db); TransactionsDao(super.db);
JoinedSelectStatement _transactionQuery(Account account) { JoinedSelectStatement _transactionQuery(Account account) {
return (select(transactions) return (select(transactions)
@@ -331,7 +350,7 @@ class TransactionsDao extends DatabaseAccessor<OkaneDatabase>
@DriftAccessor(tables: [Beneficiaries]) @DriftAccessor(tables: [Beneficiaries])
class BeneficiariesDao extends DatabaseAccessor<OkaneDatabase> class BeneficiariesDao extends DatabaseAccessor<OkaneDatabase>
with _$BeneficiariesDaoMixin { with _$BeneficiariesDaoMixin {
BeneficiariesDao(OkaneDatabase db) : super(db); BeneficiariesDao(super.db);
Stream<List<Beneficiary>> beneficiariesStream() { Stream<List<Beneficiary>> beneficiariesStream() {
return select(beneficiaries).watch(); return select(beneficiaries).watch();
@@ -360,7 +379,7 @@ class BeneficiariesDao extends DatabaseAccessor<OkaneDatabase>
@DriftAccessor(tables: [ExpenseCategories]) @DriftAccessor(tables: [ExpenseCategories])
class ExpenseCategoriesDao extends DatabaseAccessor<OkaneDatabase> class ExpenseCategoriesDao extends DatabaseAccessor<OkaneDatabase>
with _$ExpenseCategoriesDaoMixin { with _$ExpenseCategoriesDaoMixin {
ExpenseCategoriesDao(OkaneDatabase db) : super(db); ExpenseCategoriesDao(super.db);
Stream<List<ExpenseCategory>> expenseCategoriesStream(Account account) { Stream<List<ExpenseCategory>> expenseCategoriesStream(Account account) {
return select(expenseCategories).watch(); return select(expenseCategories).watch();
@@ -373,31 +392,26 @@ class ExpenseCategoriesDao extends DatabaseAccessor<OkaneDatabase>
return select(expenseCategories).get(); return select(expenseCategories).get();
} }
Future<ExpenseCategory> upsertCategory(ExpenseCategoriesCompanion category) {
return into(
expenseCategories,
).insertReturning(category, mode: InsertMode.insertOrReplace);
}
} }
@DriftAccessor(tables: [Budgets, BudgetItems]) @DriftAccessor(tables: [Budgets, BudgetItems])
class BudgetsDao extends DatabaseAccessor<OkaneDatabase> class BudgetsDao extends DatabaseAccessor<OkaneDatabase>
with _$BudgetsDaoMixin { with _$BudgetsDaoMixin {
BudgetsDao(OkaneDatabase db) : super(db); BudgetsDao(super.db);
Stream<List<BudgetsDto>> budgetsStream(Account account) { Stream<List<BudgetsDto>> budgetsStream(Account account) {
return (select(budgets)..where((b) => b.accountId.equals(account.id))) return (select(budgets)
.join([ ..where((b) => b.accountId.equals(account.id))).watch().map((rows) {
leftOuterJoin( return rows.map((row) {
budgetItems, return BudgetsDto(budget: row);
budgetItems.budgetId.equalsExp(budgets.id), }).toList();
), });
])
.watch()
.map((rows) {
return rows.map((row) {
return BudgetsDto(
budget: row.readTable(budgets),
// TODO
budgetItems: List.empty(),
);
}).toList();
});
} }
Future<List<BudgetsDto>> getBudgets(Account? account) { Future<List<BudgetsDto>> getBudgets(Account? account) {
@@ -405,20 +419,28 @@ class BudgetsDao extends DatabaseAccessor<OkaneDatabase>
return Future.value(List.empty()); return Future.value(List.empty());
} }
return (select(budgets)..where((b) => b.accountId.equals(account.id))) 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)))
.join([ .join([
leftOuterJoin( leftOuterJoin(
budgetItems, expenseCategories,
budgetItems.budgetId.equalsExp(budgets.id), expenseCategories.id.equalsExp(budgetItems.expenseCategoryId),
), ),
]) ])
.get() .watch()
.then((rows) { .map((rows) {
return rows.map((row) { return rows.map((row) {
return BudgetsDto( return BudgetItemDto(
budget: row.readTable(budgets), expenseCategory: row.readTable(expenseCategories),
// TODO item: row.readTable(budgetItems),
budgetItems: List.empty(),
); );
}).toList(); }).toList();
}); });
@@ -439,7 +461,7 @@ class BudgetsDao extends DatabaseAccessor<OkaneDatabase>
@DriftAccessor(tables: [Loans, LoanChanges, Beneficiaries]) @DriftAccessor(tables: [Loans, LoanChanges, Beneficiaries])
class LoansDao extends DatabaseAccessor<OkaneDatabase> with _$LoansDaoMixin { class LoansDao extends DatabaseAccessor<OkaneDatabase> with _$LoansDaoMixin {
LoansDao(OkaneDatabase db) : super(db); LoansDao(super.db);
Stream<List<LoanDto>> loansStream(Account account) { Stream<List<LoanDto>> loansStream(Account account) {
return select(loans) return select(loans)
@@ -452,12 +474,10 @@ class LoansDao extends DatabaseAccessor<OkaneDatabase> with _$LoansDaoMixin {
.watch() .watch()
.map((rows) { .map((rows) {
return rows.map((row) { return rows.map((row) {
return ( return LoanDto(
loan: row.readTable(loans), loan: row.readTable(loans),
beneficiary: row.readTable(beneficiaries), beneficiary: row.readTable(beneficiaries),
changes: List.empty(), );
)
as LoanDto;
}).toList(); }).toList();
}); });
} }
@@ -477,12 +497,10 @@ class LoansDao extends DatabaseAccessor<OkaneDatabase> with _$LoansDaoMixin {
.get() .get()
.then((rows) { .then((rows) {
return rows.map((row) { return rows.map((row) {
return ( return LoanDto(
loan: row.readTable(loans), loan: row.readTable(loans),
beneficiary: row.readTable(beneficiaries), beneficiary: row.readTable(beneficiaries),
changes: List.empty(), );
)
as LoanDto;
}).toList(); }).toList();
}); });
} }
@@ -506,6 +524,11 @@ class LoansDao extends DatabaseAccessor<OkaneDatabase> with _$LoansDaoMixin {
).insertReturning(loanChange, mode: InsertMode.insertOrReplace); ).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) { Future<void> deleteLoanChange(int id) {
return (delete(loanChanges)..where((c) => c.id.equals(id))).go(); return (delete(loanChanges)..where((c) => c.id.equals(id))).go();
} }
@@ -514,7 +537,7 @@ class LoansDao extends DatabaseAccessor<OkaneDatabase> with _$LoansDaoMixin {
@DriftAccessor(tables: [TransactionTemplates, ExpenseCategories, Beneficiaries]) @DriftAccessor(tables: [TransactionTemplates, ExpenseCategories, Beneficiaries])
class TransactionTemplatesDao extends DatabaseAccessor<OkaneDatabase> class TransactionTemplatesDao extends DatabaseAccessor<OkaneDatabase>
with _$TransactionTemplatesDaoMixin { with _$TransactionTemplatesDaoMixin {
TransactionTemplatesDao(OkaneDatabase db) : super(db); TransactionTemplatesDao(super.db);
Stream<List<TransactionTemplateDto>> transactionTemplatesStream( Stream<List<TransactionTemplateDto>> transactionTemplatesStream(
Account account, Account account,
@@ -595,7 +618,7 @@ class TransactionTemplatesDao extends DatabaseAccessor<OkaneDatabase>
@DriftAccessor(tables: [TransactionTemplates, RecurringTransactions]) @DriftAccessor(tables: [TransactionTemplates, RecurringTransactions])
class RecurringTransactionsDao extends DatabaseAccessor<OkaneDatabase> class RecurringTransactionsDao extends DatabaseAccessor<OkaneDatabase>
with _$RecurringTransactionsDaoMixin { with _$RecurringTransactionsDaoMixin {
RecurringTransactionsDao(OkaneDatabase db) : super(db); RecurringTransactionsDao(super.db);
Stream<List<RecurringTransactionDto>> recurringTransactionsStream( Stream<List<RecurringTransactionDto>> recurringTransactionsStream(
Account account, Account account,

View File

@@ -8190,6 +8190,15 @@ class $OkaneDatabaseManager {
mixin _$AccountsDaoMixin on DatabaseAccessor<OkaneDatabase> { mixin _$AccountsDaoMixin on DatabaseAccessor<OkaneDatabase> {
$AccountsTable get accounts => attachedDatabase.accounts; $AccountsTable get accounts => attachedDatabase.accounts;
$ExpenseCategoriesTable get expenseCategories =>
attachedDatabase.expenseCategories;
$BeneficiariesTable get beneficiaries => attachedDatabase.beneficiaries;
$TransactionsTable get transactions => attachedDatabase.transactions;
$TransactionTemplatesTable get transactionTemplates =>
attachedDatabase.transactionTemplates;
$RecurringTransactionsTable get recurringTransactions =>
attachedDatabase.recurringTransactions;
$BudgetsTable get budgets => attachedDatabase.budgets;
} }
mixin _$BeneficiariesDaoMixin on DatabaseAccessor<OkaneDatabase> { mixin _$BeneficiariesDaoMixin on DatabaseAccessor<OkaneDatabase> {
$AccountsTable get accounts => attachedDatabase.accounts; $AccountsTable get accounts => attachedDatabase.accounts;

View File

@@ -12,6 +12,7 @@ import 'package:okane/ui/pages/budgets/budget_details.dart';
import 'package:okane/ui/pages/transaction_details.dart'; import 'package:okane/ui/pages/transaction_details.dart';
import 'package:okane/ui/state/core.dart'; import 'package:okane/ui/state/core.dart';
import 'package:okane/ui/state/settings.dart'; import 'package:okane/ui/state/settings.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'ui/pages/loans/loan_details.dart'; import 'ui/pages/loans/loan_details.dart';
@@ -21,11 +22,20 @@ Future<void> main() async {
final settings = SettingsCubit(); final settings = SettingsCubit();
await settings.loadSettings(); await settings.loadSettings();
GetIt.I.registerSingleton<SettingsCubit>(settings); GetIt.I.registerSingleton<SettingsCubit>(settings);
GetIt.I.registerSingleton<CoreCubit>(CoreCubit()); GetIt.I.registerSingleton<CoreCubit>(CoreCubit());
GetIt.I.registerSingleton<OkaneDatabase>(OkaneDatabase()); GetIt.I.registerSingleton<OkaneDatabase>(OkaneDatabase());
runApp(const MyApp()); appRunner() => runApp(const MyApp());
if (settings.sentryDsn != null) {
print("Setting up Sentry!");
await SentryFlutter.init((options) {
options.dsn = settings.sentryDsn!;
}, appRunner: appRunner);
} else {
appRunner();
}
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {

View File

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

View File

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

View File

@@ -46,7 +46,7 @@ class BreakdownCard extends StatelessWidget {
Map<String, double> expenses = {}; Map<String, double> expenses = {};
Map<String, Color> colors = {}; Map<String, Color> colors = {};
double usableMoney = 0; double usableMoney = 0;
transactions.forEach((t) { for (var t in transactions) {
String category; String category;
if (t.transaction.amount > 0) { if (t.transaction.amount > 0) {
category = CATEGORY_INCOME; category = CATEGORY_INCOME;
@@ -67,7 +67,7 @@ class BreakdownCard extends StatelessWidget {
ifAbsent: () => t.transaction.amount.abs().toDouble(), ifAbsent: () => t.transaction.amount.abs().toDouble(),
); );
usableMoney += t.transaction.amount; usableMoney += t.transaction.amount;
}); }
return (expenses: expenses, colors: colors, usable: usableMoney); return (expenses: expenses, colors: colors, usable: usableMoney);
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -67,6 +67,74 @@ class SettingsPage extends StatelessWidget {
}, },
), ),
), ),
BlocBuilder<SettingsCubit, SettingsWrapper>(
builder:
(context, state) => ListTile(
title: Text("Sentry"),
subtitle: Text(
"When enabled sends errors to the configured Sentry DSN.",
),
trailing: IconButton(
icon: Icon(Icons.clear),
onPressed:
state.settings.sentryDsn == null
? null
: () async {
final result = await confirm(
context,
"Clear Sentry DSN",
"Are you sure you want to clear the Sentry DSN?",
);
if (!result) {
return;
}
await GetIt.I.get<SettingsCubit>().setSettings(
state.settings.copyWith(sentryDsn: null),
);
},
),
onTap: () async {
final controller = TextEditingController(
text: state.settings.sentryDsn ?? '',
);
final result = await showDialogOrModal<String>(
context: context,
builder: (context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: controller,
decoration: InputDecoration(hintText: "Sentry DSN"),
),
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
OutlinedButton(
child: Text("Apply"),
onPressed: () {
Navigator.of(context).pop(controller.text);
},
),
],
),
],
);
},
);
if (result == null) {
return;
}
await GetIt.I.get<SettingsCubit>().setSettings(
state.settings.copyWith(sentryDsn: result),
);
},
),
),
], ],
); );
} }

View File

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

View File

@@ -20,8 +20,10 @@ abstract class CoreState with _$CoreState {
@Default([]) List<Beneficiary> beneficiaries, @Default([]) List<Beneficiary> beneficiaries,
@Default([]) List<ExpenseCategory> expenseCategories, @Default([]) List<ExpenseCategory> expenseCategories,
@Default([]) List<BudgetsDto> budgets, @Default([]) List<BudgetsDto> budgets,
@Default([]) List<BudgetItemDto> budgetItems,
@Default(null) BudgetsDto? activeBudget, @Default(null) BudgetsDto? activeBudget,
@Default([]) List<LoanDto> loans, @Default([]) List<LoanDto> loans,
@Default([]) List<LoanChange> loanChanges,
@Default(null) LoanDto? activeLoan, @Default(null) LoanDto? activeLoan,
@Default(false) bool isDeletingAccount, @Default(false) bool isDeletingAccount,
}) = _CoreState; }) = _CoreState;
@@ -37,7 +39,9 @@ class CoreCubit extends Cubit<CoreState> {
StreamSubscription<void>? _beneficiariesStreamSubscription; StreamSubscription<void>? _beneficiariesStreamSubscription;
StreamSubscription<void>? _expenseCategoryStreamSubscription; StreamSubscription<void>? _expenseCategoryStreamSubscription;
StreamSubscription<void>? _budgetsStreamSubscription; StreamSubscription<void>? _budgetsStreamSubscription;
StreamSubscription<void>? _budgetItemsStreamSubscription;
StreamSubscription<void>? _loanStreamSubscription; StreamSubscription<void>? _loanStreamSubscription;
StreamSubscription<void>? _loanChangesSubscription;
void setupAccountStream() { void setupAccountStream() {
_accountsStreamSubscription?.cancel(); _accountsStreamSubscription?.cancel();
@@ -122,17 +126,6 @@ class CoreCubit extends Cubit<CoreState> {
state.copyWith( state.copyWith(
accounts: accounts, accounts: accounts,
activeAccountIndex: accounts.isEmpty ? null : 0, 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),
), ),
); );
@@ -179,7 +172,17 @@ class CoreCubit extends Cubit<CoreState> {
} }
void setActiveBudget(BudgetsDto? budget) { void setActiveBudget(BudgetsDto? budget) {
emit(state.copyWith(activeBudget: 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));
});
}
} }
Future<void> deleteAccount(Account account) async { Future<void> deleteAccount(Account account) async {
@@ -190,16 +193,25 @@ class CoreCubit extends Cubit<CoreState> {
cancelStreams(); cancelStreams();
try { try {
// TODO await GetIt.I.get<OkaneDatabase>().accountsDao.removeAccount(account);
//await db.deleteAccount(account);
} finally { } finally {
emit(state.copyWith(isDeletingAccount: false)); emit(state.copyWith(isDeletingAccount: false));
} }
await init(); await init();
} }
void setActiveLoan(LoanDto loan) { void setActiveLoan(LoanDto? loan) {
emit(state.copyWith(activeLoan: 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));
});
}
} }
Account? get activeAccount => Account? get activeAccount =>

View File

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

View File

@@ -13,6 +13,7 @@ enum ColorSchemeSettings { light, dark, system }
abstract class Settings with _$Settings { abstract class Settings with _$Settings {
const factory Settings({ const factory Settings({
@Default(ColorSchemeSettings.system) ColorSchemeSettings colorScheme, @Default(ColorSchemeSettings.system) ColorSchemeSettings colorScheme,
@Default(null) String? sentryDsn,
}) = _Settings; }) = _Settings;
factory Settings.fromJson(Map<String, Object?> json) => factory Settings.fromJson(Map<String, Object?> json) =>
@@ -45,4 +46,6 @@ class SettingsCubit extends Cubit<SettingsWrapper> {
await _prefs.setString("settings", jsonEncode(settings.toJson())); await _prefs.setString("settings", jsonEncode(settings.toJson()));
} }
String? get sentryDsn => state.settings.sentryDsn;
} }

View File

@@ -22,6 +22,7 @@ Settings _$SettingsFromJson(Map<String, dynamic> json) {
/// @nodoc /// @nodoc
mixin _$Settings { mixin _$Settings {
ColorSchemeSettings get colorScheme => throw _privateConstructorUsedError; ColorSchemeSettings get colorScheme => throw _privateConstructorUsedError;
String? get sentryDsn => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
@@ -34,7 +35,7 @@ abstract class $SettingsCopyWith<$Res> {
factory $SettingsCopyWith(Settings value, $Res Function(Settings) then) = factory $SettingsCopyWith(Settings value, $Res Function(Settings) then) =
_$SettingsCopyWithImpl<$Res, Settings>; _$SettingsCopyWithImpl<$Res, Settings>;
@useResult @useResult
$Res call({ColorSchemeSettings colorScheme}); $Res call({ColorSchemeSettings colorScheme, String? sentryDsn});
} }
/// @nodoc /// @nodoc
@@ -49,7 +50,7 @@ class _$SettingsCopyWithImpl<$Res, $Val extends Settings>
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({Object? colorScheme = null}) { $Res call({Object? colorScheme = null, Object? sentryDsn = freezed}) {
return _then( return _then(
_value.copyWith( _value.copyWith(
colorScheme: colorScheme:
@@ -57,6 +58,11 @@ class _$SettingsCopyWithImpl<$Res, $Val extends Settings>
? _value.colorScheme ? _value.colorScheme
: colorScheme // ignore: cast_nullable_to_non_nullable : colorScheme // ignore: cast_nullable_to_non_nullable
as ColorSchemeSettings, as ColorSchemeSettings,
sentryDsn:
freezed == sentryDsn
? _value.sentryDsn
: sentryDsn // ignore: cast_nullable_to_non_nullable
as String?,
) )
as $Val, as $Val,
); );
@@ -72,7 +78,7 @@ abstract class _$$SettingsImplCopyWith<$Res>
) = __$$SettingsImplCopyWithImpl<$Res>; ) = __$$SettingsImplCopyWithImpl<$Res>;
@override @override
@useResult @useResult
$Res call({ColorSchemeSettings colorScheme}); $Res call({ColorSchemeSettings colorScheme, String? sentryDsn});
} }
/// @nodoc /// @nodoc
@@ -86,7 +92,7 @@ class __$$SettingsImplCopyWithImpl<$Res>
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({Object? colorScheme = null}) { $Res call({Object? colorScheme = null, Object? sentryDsn = freezed}) {
return _then( return _then(
_$SettingsImpl( _$SettingsImpl(
colorScheme: colorScheme:
@@ -94,6 +100,11 @@ class __$$SettingsImplCopyWithImpl<$Res>
? _value.colorScheme ? _value.colorScheme
: colorScheme // ignore: cast_nullable_to_non_nullable : colorScheme // ignore: cast_nullable_to_non_nullable
as ColorSchemeSettings, as ColorSchemeSettings,
sentryDsn:
freezed == sentryDsn
? _value.sentryDsn
: sentryDsn // ignore: cast_nullable_to_non_nullable
as String?,
), ),
); );
} }
@@ -102,7 +113,10 @@ class __$$SettingsImplCopyWithImpl<$Res>
/// @nodoc /// @nodoc
@JsonSerializable() @JsonSerializable()
class _$SettingsImpl implements _Settings { class _$SettingsImpl implements _Settings {
const _$SettingsImpl({this.colorScheme = ColorSchemeSettings.system}); const _$SettingsImpl({
this.colorScheme = ColorSchemeSettings.system,
this.sentryDsn = null,
});
factory _$SettingsImpl.fromJson(Map<String, dynamic> json) => factory _$SettingsImpl.fromJson(Map<String, dynamic> json) =>
_$$SettingsImplFromJson(json); _$$SettingsImplFromJson(json);
@@ -110,10 +124,13 @@ class _$SettingsImpl implements _Settings {
@override @override
@JsonKey() @JsonKey()
final ColorSchemeSettings colorScheme; final ColorSchemeSettings colorScheme;
@override
@JsonKey()
final String? sentryDsn;
@override @override
String toString() { String toString() {
return 'Settings(colorScheme: $colorScheme)'; return 'Settings(colorScheme: $colorScheme, sentryDsn: $sentryDsn)';
} }
@override @override
@@ -122,12 +139,14 @@ class _$SettingsImpl implements _Settings {
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is _$SettingsImpl && other is _$SettingsImpl &&
(identical(other.colorScheme, colorScheme) || (identical(other.colorScheme, colorScheme) ||
other.colorScheme == colorScheme)); other.colorScheme == colorScheme) &&
(identical(other.sentryDsn, sentryDsn) ||
other.sentryDsn == sentryDsn));
} }
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
int get hashCode => Object.hash(runtimeType, colorScheme); int get hashCode => Object.hash(runtimeType, colorScheme, sentryDsn);
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@@ -142,8 +161,10 @@ class _$SettingsImpl implements _Settings {
} }
abstract class _Settings implements Settings { abstract class _Settings implements Settings {
const factory _Settings({final ColorSchemeSettings colorScheme}) = const factory _Settings({
_$SettingsImpl; final ColorSchemeSettings colorScheme,
final String? sentryDsn,
}) = _$SettingsImpl;
factory _Settings.fromJson(Map<String, dynamic> json) = factory _Settings.fromJson(Map<String, dynamic> json) =
_$SettingsImpl.fromJson; _$SettingsImpl.fromJson;
@@ -151,6 +172,8 @@ abstract class _Settings implements Settings {
@override @override
ColorSchemeSettings get colorScheme; ColorSchemeSettings get colorScheme;
@override @override
String? get sentryDsn;
@override
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$$SettingsImplCopyWith<_$SettingsImpl> get copyWith => _$$SettingsImplCopyWith<_$SettingsImpl> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;

View File

@@ -14,11 +14,13 @@ _$SettingsImpl _$$SettingsImplFromJson(Map<String, dynamic> json) =>
json['colorScheme'], json['colorScheme'],
) ?? ) ??
ColorSchemeSettings.system, ColorSchemeSettings.system,
sentryDsn: json['sentryDsn'] as String? ?? null,
); );
Map<String, dynamic> _$$SettingsImplToJson(_$SettingsImpl instance) => Map<String, dynamic> _$$SettingsImplToJson(_$SettingsImpl instance) =>
<String, dynamic>{ <String, dynamic>{
'colorScheme': _$ColorSchemeSettingsEnumMap[instance.colorScheme]!, 'colorScheme': _$ColorSchemeSettingsEnumMap[instance.colorScheme]!,
'sentryDsn': instance.sentryDsn,
}; };
const _$ColorSchemeSettingsEnumMap = { const _$ColorSchemeSettingsEnumMap = {

View File

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

View File

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

View File

@@ -6,9 +6,13 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <sentry_flutter/sentry_flutter_plugin.h>
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h> #include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) sentry_flutter_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin");
sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar);
g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar = g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin");
sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar); sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar);

View File

@@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
sentry_flutter
sqlite3_flutter_libs sqlite3_flutter_libs
) )

View File

@@ -405,6 +405,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.0" version: "6.0.0"
http:
dependency: transitive
description:
name: http
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
http_multi_server: http_multi_server:
dependency: transitive dependency: transitive
description: description:
@@ -565,6 +573,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.2.0"
package_info_plus:
dependency: transitive
description:
name: package_info_plus
sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191"
url: "https://pub.dev"
source: hosted
version: "8.3.0"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c"
url: "https://pub.dev"
source: hosted
version: "3.2.0"
path: path:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -685,6 +709,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.9" version: "1.2.9"
sentry:
dependency: transitive
description:
name: sentry
sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb"
url: "https://pub.dev"
source: hosted
version: "8.14.2"
sentry_flutter:
dependency: "direct main"
description:
name: sentry_flutter
sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8"
url: "https://pub.dev"
source: hosted
version: "8.14.2"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -810,6 +850,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.1" version: "1.10.1"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
sqlite3: sqlite3:
dependency: transitive dependency: transitive
description: description:
@@ -898,6 +946,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
uuid:
dependency: transitive
description:
name: uuid
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
url: "https://pub.dev"
source: hosted
version: "4.5.1"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:

View File

@@ -2,7 +2,7 @@ name: okane
description: "A cross-platform finance tracker." description: "A cross-platform finance tracker."
publish_to: "none" publish_to: "none"
version: 1.0.0+3 version: 1.0.0+6
environment: environment:
sdk: ^3.7.0 sdk: ^3.7.0
@@ -29,6 +29,9 @@ dependencies:
drift: ^2.26.1 drift: ^2.26.1
drift_flutter: ^0.2.4 drift_flutter: ^0.2.4
# For optional error tracking
sentry_flutter: ^8.14.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter

View File

@@ -1,38 +0,0 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:okane/database/collections/recurrent.dart';
void main() {
group("isDue", () {
test("null value", () {
final t = RecurringTransaction()..lastExecution = null;
expect(t.isDue(DateTime.now()), true);
});
test("Date before", () {
final now = DateTime.now();
final t =
RecurringTransaction()
..lastExecution = now
..days = 30;
expect(t.isDue(now.add(Duration(days: 10))), false);
});
test("Date before warning", () {
final now = DateTime.now();
final t =
RecurringTransaction()
..lastExecution = now
..days = 30;
expect(t.isDue(now.add(Duration(days: 20))), true);
});
test("Expired", () {
final now = DateTime.now();
final t =
RecurringTransaction()
..lastExecution = now
..days = 30;
expect(t.isDue(now.add(Duration(days: 31))), true);
});
});
}