Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Expense Tracking #1

Merged
merged 3 commits into from
Oct 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class App extends StatelessWidget {
}

void _setSystemUI(BuildContext context, SettingsState state) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
switch (state.themeMode) {
case ThemeMode.system:
final Brightness brightnessValue =
Expand Down
117 changes: 117 additions & 0 deletions lib/cubits/expense_add_update_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kitchenowl/models/expense.dart';
import 'package:kitchenowl/models/user.dart';
import 'package:kitchenowl/services/api/api_service.dart';

class AddUpdateExpenseCubit extends Cubit<AddUpdateExpenseState> {
final Expense expense;
AddUpdateExpenseCubit([this.expense = const Expense()])
: super(AddUpdateExpenseState(
amount: expense.amount,
name: expense.name,
paidBy: expense.paidById,
paidFor: expense.paidFor,
));

Future<void> saveExpense() async {
if (state.isValid()) {
if (expense.id == null) {
await ApiService.getInstance().addExpense(Expense(
amount: state.amount,
name: state.name,
paidById: state.paidBy,
paidFor: state.paidFor,
));
} else {
await ApiService.getInstance().updateExpense(expense.copyWith(
name: state.name,
amount: state.amount,
paidById: state.paidBy,
paidFor: state.paidFor,
));
}
}
}

Future<bool> deleteExpense() async {
if (expense.id != null) {
return ApiService.getInstance().deleteExpense(expense);
}
return false;
}

void setName(String name) {
emit(state.copyWith(name: name));
}

void setAmount(double amount) {
emit(state.copyWith(amount: amount));
}

void setPaidBy(User user) {
emit(state.copyWith(paidBy: user.id));
}

void setPaidById(int userId) {
emit(state.copyWith(paidBy: userId));
}

void addUser(User user) {
if (!containsUser(user)) {
emit(state.copyWith(
paidFor: List.from(state.paidFor)
..add(PaidForModel(
userId: user.id,
factor: 1,
))));
}
}

bool containsUser(User user) {
return state.paidFor.map((e) => e.userId).contains(user.id);
}

void removeUser(User user) {
final l = List<PaidForModel>.from(state.paidFor);
l.removeWhere((e) => e.userId == user.id);
emit(state.copyWith(paidFor: l));
}

void setFactor(User user, int factor) {
final l = List<PaidForModel>.from(state.paidFor);
final i = l.indexWhere((e) => e.userId == user.id);
if (i > 0) {
l[i] = PaidForModel(userId: user.id, factor: factor);
emit(state.copyWith(paidFor: l));
}
}
}

class AddUpdateExpenseState extends Equatable {
final String name;
final double amount;
final int paidBy;
final List<PaidForModel> paidFor;

const AddUpdateExpenseState(
{this.name = "", this.amount, this.paidBy, this.paidFor = const []});

AddUpdateExpenseState copyWith({
String name,
double amount,
int paidBy,
List<PaidForModel> paidFor,
}) =>
AddUpdateExpenseState(
name: name ?? this.name,
amount: amount ?? this.amount,
paidBy: paidBy ?? this.paidBy,
paidFor: paidFor ?? this.paidFor,
);

bool isValid() => name.isNotEmpty && amount != 0;

@override
List<Object> get props => [name, amount, paidBy] + paidFor;
}
61 changes: 61 additions & 0 deletions lib/cubits/expense_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kitchenowl/enums/update_enum.dart';
import 'package:kitchenowl/models/expense.dart';
import 'package:kitchenowl/models/user.dart';
import 'package:kitchenowl/services/transaction_handler.dart';
import 'package:kitchenowl/services/transactions/expense.dart';
import 'package:kitchenowl/services/transactions/user.dart';

class ExpenseCubit extends Cubit<ExpenseCubitState> {
ExpenseCubit(Expense expense, List<User> users)
: super(ExpenseCubitState(
expense: expense,
users: users,
)) {
refresh();
}

void setUpdateState(UpdateEnum updateState) {
emit(state.copyWith(updateState: updateState));
}

void refresh() async {
final expense = await TransactionHandler.getInstance()
.runTransaction(TransactionExpenseGet(expense: state.expense));
final users = await TransactionHandler.getInstance()
.runTransaction(TransactionUserGetAll());
if (expense != null) {
emit(state.copyWith(
expense: expense,
users: users,
));
}
}
}

class ExpenseCubitState extends Equatable {
final Expense expense;
final List<User> users;
final UpdateEnum updateState;

const ExpenseCubitState({
this.expense,
this.users = const [],
this.updateState = UpdateEnum.unchanged,
});

ExpenseCubitState copyWith({
Expense expense,
List<User> users,
UpdateEnum updateState,
}) =>
ExpenseCubitState(
expense: expense ?? this.expense,
users: users ?? this.users,
updateState: updateState ?? this.updateState,
);

@override
List<Object> get props => [updateState, expense, users];
}
55 changes: 55 additions & 0 deletions lib/cubits/expense_list_cubit.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kitchenowl/models/expense.dart';
import 'package:kitchenowl/models/user.dart';
import 'package:kitchenowl/services/transaction_handler.dart';
import 'package:kitchenowl/services/transactions/expense.dart';
import 'package:kitchenowl/services/transactions/user.dart';

class ExpenseListCubit extends Cubit<ExpenseListCubitState> {
ExpenseListCubit() : super(const ExpenseListCubitState()) {
refresh();
}

Future<void> remove(Expense expense) async {
await TransactionHandler.getInstance()
.runTransaction(TransactionExpenseRemove(expense: expense));
await refresh();
}

Future<void> add(Expense expense) async {
await TransactionHandler.getInstance()
.runTransaction(TransactionExpenseAdd(expense: expense));
await refresh();
}

Future<void> update(Expense expense) async {
await TransactionHandler.getInstance()
.runTransaction(TransactionExpenseUpdate(expense: expense));
await refresh();
}

Future<void> refresh() async {
final users = await TransactionHandler.getInstance()
.runTransaction(TransactionUserGetAll()) ??
[];
final expenses = await TransactionHandler.getInstance()
.runTransaction(TransactionExpenseGetAll()) ??
[];

emit(ExpenseListCubitState(users, expenses));
}
}

class ExpenseListCubitState extends Equatable {
final List<User> users;
final List<Expense> expenses;

const ExpenseListCubitState([
this.users = const [],
this.expenses = const [],
]);

@override
List<Object> get props => users.cast<Object>() + expenses;
}
18 changes: 10 additions & 8 deletions lib/cubits/recipe_add_update_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ class AddUpdateRecipeCubit extends Cubit<AddUpdateRecipeState> {
));

Future<void> saveRecipe() async {
if (recipe.id == null) {
if (state.name.isNotEmpty) {
if (state.isValid()) {
if (recipe.id == null) {
await ApiService.getInstance().addRecipe(Recipe(
name: state.name,
description: state.description ?? "",
items: state.items,
));
} else {
await ApiService.getInstance().updateRecipe(recipe.copyWith(
name: state.name,
description: state.description,
items: state.items,
));
}
} else {
await ApiService.getInstance().updateRecipe(recipe.copyWith(
name: state.name,
description: state.description,
items: state.items,
));
}
}

Expand Down Expand Up @@ -95,6 +95,8 @@ class AddUpdateRecipeState extends Equatable {
items: items ?? this.items,
);

bool isValid() => name.isNotEmpty;

@override
List<Object> get props => [name, description, items];
}
39 changes: 38 additions & 1 deletion lib/cubits/settings_cubit.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
import 'dart:convert';

import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kitchenowl/config.dart';
import 'package:kitchenowl/models/server_settings.dart';
import 'package:kitchenowl/services/api/api_service.dart';
import 'package:kitchenowl/services/storage/storage.dart';
import 'package:kitchenowl/services/transaction_handler.dart';
import 'package:package_info_plus/package_info_plus.dart';

class SettingsCubit extends Cubit<SettingsState> {
SettingsCubit() : super(const SettingsState()) {
load();
ApiService.getInstance().addSettingsListener(serverSettingsUpdated);
}

Future<void> serverSettingsUpdated() async {
await PreferenceStorage.getInstance().write(
key: 'serverSettings',
value: jsonEncode(ApiService.getInstance().serverSettings.toJson()),
);
emit(state.copyWith(
serverSettings: ApiService.getInstance().serverSettings));
}

Future<void> load() async {
Expand All @@ -21,8 +35,13 @@ class SettingsCubit extends Cubit<SettingsState> {
themeMode = darkmode ? ThemeMode.dark : ThemeMode.light;
}

final serverSettings = ServerSettings.fromJson(jsonDecode(
(await PreferenceStorage.getInstance().read(key: 'serverSettings')) ??
"{}"));

emit(SettingsState(
themeMode: themeMode,
serverSettings: serverSettings,
));
}

Expand All @@ -38,26 +57,44 @@ class SettingsCubit extends Cubit<SettingsState> {
TransactionHandler.getInstance().runOpenTransactions();
}
}

void setFeaturePlanner(bool featurePlanner) {
PreferenceStorage.getInstance()
.writeBool(key: 'featurePlanner', value: featurePlanner);
ApiService.getInstance()
.setSettings(ServerSettings(featurePlanner: featurePlanner));
}

void setFeatureExpenses(bool featureExpenses) {
PreferenceStorage.getInstance()
.writeBool(key: 'featureExpenses', value: featureExpenses);
ApiService.getInstance()
.setSettings(ServerSettings(featureExpenses: featureExpenses));
}
}

class SettingsState extends Equatable {
final ThemeMode themeMode;
final bool forcedOfflineMode;
final ServerSettings serverSettings;

const SettingsState({
this.themeMode = ThemeMode.system,
this.forcedOfflineMode = false,
this.serverSettings = const ServerSettings(),
});

SettingsState copyWith({
ThemeMode themeMode,
bool forcedOfflineMode,
ServerSettings serverSettings,
}) =>
SettingsState(
themeMode: themeMode ?? this.themeMode,
forcedOfflineMode: forcedOfflineMode ?? this.forcedOfflineMode,
serverSettings: serverSettings ?? this.serverSettings,
);

@override
List<Object> get props => [themeMode, forcedOfflineMode];
List<Object> get props => [themeMode, forcedOfflineMode, serverSettings];
}
24 changes: 24 additions & 0 deletions lib/helpers/currency_text_input_formatter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';

class CurrencyTextInputFormater extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
if (newValue.text.replaceFirst(".", "").length > 9) return oldValue;
final number =
(double.tryParse(newValue.text.replaceFirst(".", "")) ?? 0) / 100;
// final oldNumber = double.tryParse(oldValue.text) ?? 0;
// final addedNumber =
// double.tryParse(newValue.text[newValue.selection.baseOffset - 1]) ??
// 0;
// if (true) number = oldNumber * 10 + addedNumber / 100;
final text = number.toStringAsFixed(2);
return TextEditingValue(
text: text,
selection: TextSelection(
baseOffset: newValue.selection.baseOffset.clamp(0, text.length),
extentOffset: newValue.selection.baseOffset.clamp(0, text.length),
));
}
}
Loading