diff --git a/lib/constants.dart b/lib/constants.dart index 63ba142a0..b3385529d 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -18,6 +18,7 @@ const String kSharedPrefEmailPayment = 'email_payment'; const String kSharedPrefAutoStartTasks = 'auto_start_tasks'; const String kSharedPrefAppVersion = 'app_version'; const String kSharedPrefRequireAuthentication = 'require_authentication'; +const String kSharedPrefAddDocumentsToInvoice = 'add_documents_to_invoice'; String getCompanyTokenKey([int companyIndex = 0]) => '${kSharedPrefToken}_$companyIndex'; diff --git a/lib/data/models/entities.dart b/lib/data/models/entities.dart index 95cdb2af5..414cce705 100644 --- a/lib/data/models/entities.dart +++ b/lib/data/models/entities.dart @@ -135,6 +135,8 @@ abstract class BaseEntity implements SelectableEntity { bool get isNew => id == null || id < 0; + bool get isOld => !isNew; + bool get isActive => archivedAt == null || archivedAt == 0; bool get isArchived => archivedAt != null && archivedAt > 0 && !isDeleted; diff --git a/lib/data/models/expense_model.dart b/lib/data/models/expense_model.dart index fbae01d69..52e82ad15 100644 --- a/lib/data/models/expense_model.dart +++ b/lib/data/models/expense_model.dart @@ -4,6 +4,7 @@ import 'package:built_value/serializer.dart'; import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/data/models/entities.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/redux/ui/ui_state.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; part 'expense_model.g.dart'; @@ -67,13 +68,13 @@ abstract class ExpenseEntity extends Object with BaseEntity, SelectableEntity, BelongsToClient implements Built { factory ExpenseEntity( - {CompanyEntity company, VendorEntity vendor, ClientEntity client}) { + {CompanyEntity company, UIState uiState, VendorEntity vendor, ClientEntity client}) { return _$ExpenseEntity._( id: --ExpenseEntity.counter, privateNotes: '', publicNotes: '', shouldBeInvoiced: false, - invoiceDocuments: false, + invoiceDocuments: uiState?.addDocumentsToInvoice ?? false, transactionId: '', transactionReference: '', bankId: 0, diff --git a/lib/redux/app/app_actions.dart b/lib/redux/app/app_actions.dart index 7ee5ba029..ba9bc8507 100644 --- a/lib/redux/app/app_actions.dart +++ b/lib/redux/app/app_actions.dart @@ -30,12 +30,14 @@ class UserSettingsChanged implements PersistUI { {this.enableDarkMode, this.emailPayment, this.requireAuthentication, - this.autoStartTasks}); + this.autoStartTasks, + this.addDocumentsToInvoice}); final bool enableDarkMode; final bool emailPayment; final bool requireAuthentication; final bool autoStartTasks; + final bool addDocumentsToInvoice; } class LoadDataSuccess { diff --git a/lib/redux/auth/auth_middleware.dart b/lib/redux/auth/auth_middleware.dart index 9547891da..7e951743d 100644 --- a/lib/redux/auth/auth_middleware.dart +++ b/lib/redux/auth/auth_middleware.dart @@ -43,17 +43,15 @@ void _loadAuthLocal(Store store, dynamic action) async { final String secret = prefs.getString(kSharedPrefSecret) ?? ''; store.dispatch(UserLoginLoaded(email, url, secret)); - final bool enableDarkMode = prefs.getBool(kSharedPrefEnableDarkMode) ?? false; - final bool emailPayment = prefs.getBool(kSharedPrefEmailPayment) ?? false; - final bool autoStartTasks = prefs.getBool(kSharedPrefAutoStartTasks) ?? false; - final bool requireAuthentication = - prefs.getBool(kSharedPrefRequireAuthentication) ?? false; - store.dispatch(UserSettingsChanged( - enableDarkMode: enableDarkMode, - emailPayment: emailPayment, - requireAuthentication: requireAuthentication, - autoStartTasks: autoStartTasks)); + enableDarkMode: prefs.getBool(kSharedPrefEnableDarkMode) ?? false, + emailPayment: prefs.getBool(kSharedPrefEmailPayment) ?? false, + requireAuthentication: + prefs.getBool(kSharedPrefRequireAuthentication) ?? false, + autoStartTasks: prefs.getBool(kSharedPrefAutoStartTasks) ?? false, + addDocumentsToInvoice: + prefs.getBool(kSharedPrefAddDocumentsToInvoice) ?? false, + )); } Middleware _createLoginInit() { diff --git a/lib/redux/ui/ui_reducer.dart b/lib/redux/ui/ui_reducer.dart index d17be3392..213c42ec9 100644 --- a/lib/redux/ui/ui_reducer.dart +++ b/lib/redux/ui/ui_reducer.dart @@ -32,6 +32,8 @@ UIState uiReducer(UIState state, dynamic action) { ..requireAuthentication = requireAuthenticationReducer(state.requireAuthentication, action) ..emailPayment = emailPaymentReducer(state.emailPayment, action) + ..addDocumentsToInvoice = + addDocumentsToInvoiceReducer(state.addDocumentsToInvoice, action) ..productUIState.replace(productUIReducer(state.productUIState, action)) ..clientUIState.replace(clientUIReducer(state.clientUIState, action)) ..invoiceUIState.replace(invoiceUIReducer(state.invoiceUIState, action)) @@ -79,6 +81,15 @@ bool updateAutoStartTasksReducer( return action.autoStartTasks ?? autoStartTasks; } +Reducer addDocumentsToInvoiceReducer = combineReducers([ + TypedReducer(updateAddDocumentsToInvoiceReducer), +]); + +bool updateAddDocumentsToInvoiceReducer( + bool addDocumentsToInvoice, UserSettingsChanged action) { + return action.addDocumentsToInvoice ?? addDocumentsToInvoice; +} + Reducer requireAuthenticationReducer = combineReducers([ TypedReducer(updateRequireAuthenticationReducer), ]); diff --git a/lib/redux/ui/ui_state.dart b/lib/redux/ui/ui_state.dart index 5a0239c68..31d3b6641 100644 --- a/lib/redux/ui/ui_state.dart +++ b/lib/redux/ui/ui_state.dart @@ -32,6 +32,7 @@ abstract class UIState implements Built { requireAuthentication: requireAuthentication ?? false, emailPayment: false, autoStartTasks: false, + addDocumentsToInvoice: false, dashboardUIState: DashboardUIState(), productUIState: ProductUIState(), clientUIState: ClientUIState(), @@ -60,6 +61,8 @@ abstract class UIState implements Built { bool get autoStartTasks; + bool get addDocumentsToInvoice; + @nullable String get filter; diff --git a/lib/redux/ui/ui_state.g.dart b/lib/redux/ui/ui_state.g.dart index 04ccbc58b..9305e0778 100644 --- a/lib/redux/ui/ui_state.g.dart +++ b/lib/redux/ui/ui_state.g.dart @@ -49,6 +49,9 @@ class _$UIStateSerializer implements StructuredSerializer { 'autoStartTasks', serializers.serialize(object.autoStartTasks, specifiedType: const FullType(bool)), + 'addDocumentsToInvoice', + serializers.serialize(object.addDocumentsToInvoice, + specifiedType: const FullType(bool)), 'dashboardUIState', serializers.serialize(object.dashboardUIState, specifiedType: const FullType(DashboardUIState)), @@ -125,6 +128,10 @@ class _$UIStateSerializer implements StructuredSerializer { result.autoStartTasks = serializers.deserialize(value, specifiedType: const FullType(bool)) as bool; break; + case 'addDocumentsToInvoice': + result.addDocumentsToInvoice = serializers.deserialize(value, + specifiedType: const FullType(bool)) as bool; + break; case 'filter': result.filter = serializers.deserialize(value, specifiedType: const FullType(String)) as String; @@ -191,6 +198,8 @@ class _$UIState extends UIState { @override final bool autoStartTasks; @override + final bool addDocumentsToInvoice; + @override final String filter; @override final DashboardUIState dashboardUIState; @@ -223,6 +232,7 @@ class _$UIState extends UIState { this.requireAuthentication, this.emailPayment, this.autoStartTasks, + this.addDocumentsToInvoice, this.filter, this.dashboardUIState, this.productUIState, @@ -253,6 +263,9 @@ class _$UIState extends UIState { if (autoStartTasks == null) { throw new BuiltValueNullFieldError('UIState', 'autoStartTasks'); } + if (addDocumentsToInvoice == null) { + throw new BuiltValueNullFieldError('UIState', 'addDocumentsToInvoice'); + } if (dashboardUIState == null) { throw new BuiltValueNullFieldError('UIState', 'dashboardUIState'); } @@ -302,6 +315,7 @@ class _$UIState extends UIState { requireAuthentication == other.requireAuthentication && emailPayment == other.emailPayment && autoStartTasks == other.autoStartTasks && + addDocumentsToInvoice == other.addDocumentsToInvoice && filter == other.filter && dashboardUIState == other.dashboardUIState && productUIState == other.productUIState && @@ -334,17 +348,22 @@ class _$UIState extends UIState { $jc( $jc( $jc( - 0, - selectedCompanyIndex + $jc( + 0, + selectedCompanyIndex + .hashCode), + currentRoute .hashCode), - currentRoute + enableDarkMode .hashCode), - enableDarkMode + requireAuthentication .hashCode), - requireAuthentication + emailPayment .hashCode), - emailPayment.hashCode), - autoStartTasks.hashCode), + autoStartTasks + .hashCode), + addDocumentsToInvoice + .hashCode), filter.hashCode), dashboardUIState.hashCode), productUIState.hashCode), @@ -367,6 +386,7 @@ class _$UIState extends UIState { ..add('requireAuthentication', requireAuthentication) ..add('emailPayment', emailPayment) ..add('autoStartTasks', autoStartTasks) + ..add('addDocumentsToInvoice', addDocumentsToInvoice) ..add('filter', filter) ..add('dashboardUIState', dashboardUIState) ..add('productUIState', productUIState) @@ -413,6 +433,11 @@ class UIStateBuilder implements Builder { set autoStartTasks(bool autoStartTasks) => _$this._autoStartTasks = autoStartTasks; + bool _addDocumentsToInvoice; + bool get addDocumentsToInvoice => _$this._addDocumentsToInvoice; + set addDocumentsToInvoice(bool addDocumentsToInvoice) => + _$this._addDocumentsToInvoice = addDocumentsToInvoice; + String _filter; String get filter => _$this._filter; set filter(String filter) => _$this._filter = filter; @@ -487,6 +512,7 @@ class UIStateBuilder implements Builder { _requireAuthentication = _$v.requireAuthentication; _emailPayment = _$v.emailPayment; _autoStartTasks = _$v.autoStartTasks; + _addDocumentsToInvoice = _$v.addDocumentsToInvoice; _filter = _$v.filter; _dashboardUIState = _$v.dashboardUIState?.toBuilder(); _productUIState = _$v.productUIState?.toBuilder(); @@ -528,6 +554,7 @@ class UIStateBuilder implements Builder { requireAuthentication: requireAuthentication, emailPayment: emailPayment, autoStartTasks: autoStartTasks, + addDocumentsToInvoice: addDocumentsToInvoice, filter: filter, dashboardUIState: dashboardUIState.build(), productUIState: productUIState.build(), diff --git a/lib/ui/app/app_drawer.dart b/lib/ui/app/app_drawer.dart index f1856c4f1..f9e35eb3c 100644 --- a/lib/ui/app/app_drawer.dart +++ b/lib/ui/app/app_drawer.dart @@ -264,7 +264,9 @@ class AppDrawer extends StatelessWidget { onCreateTap: () { navigator.pop(); store.dispatch(EditExpense( - expense: ExpenseEntity(company: company), context: context)); + expense: + ExpenseEntity(company: company, uiState: state.uiState), + context: context)); }, ), diff --git a/lib/ui/client/client_list_vm.dart b/lib/ui/client/client_list_vm.dart index 17cdc2fe7..190b2e1d8 100644 --- a/lib/ui/client/client_list_vm.dart +++ b/lib/ui/client/client_list_vm.dart @@ -94,7 +94,10 @@ class ClientListVM { break; case EntityAction.newExpense: store.dispatch(EditExpense( - expense: ExpenseEntity(company: state.selectedCompany, client: client), + expense: ExpenseEntity( + company: state.selectedCompany, + client: client, + uiState: state.uiState), context: context)); break; case EntityAction.enterPayment: diff --git a/lib/ui/client/view/client_view.dart b/lib/ui/client/view/client_view.dart index 02bb240fd..6347b7b46 100644 --- a/lib/ui/client/view/client_view.dart +++ b/lib/ui/client/view/client_view.dart @@ -160,7 +160,9 @@ class _ClientViewState extends State Navigator.of(context).pop(); store.dispatch(EditExpense( expense: ExpenseEntity( - company: company, client: client), + company: company, + client: client, + uiState: store.state.uiState), context: context)); }, ) diff --git a/lib/ui/client/view/client_view_vm.dart b/lib/ui/client/view/client_view_vm.dart index 8d18554ed..2c4303cea 100644 --- a/lib/ui/client/view/client_view_vm.dart +++ b/lib/ui/client/view/client_view_vm.dart @@ -155,7 +155,9 @@ class ClientViewVM { store.dispatch(EditExpense( context: context, expense: ExpenseEntity( - company: state.selectedCompany, client: client))); + company: state.selectedCompany, + client: client, + uiState: state.uiState))); } else { store.dispatch(FilterExpensesByEntity( entityId: client.id, entityType: EntityType.client)); @@ -183,7 +185,9 @@ class ClientViewVM { case EntityAction.newExpense: store.dispatch(EditExpense( expense: ExpenseEntity( - company: state.selectedCompany, client: client), + company: state.selectedCompany, + client: client, + uiState: state.uiState), context: context)); break; case EntityAction.enterPayment: diff --git a/lib/ui/expense/edit/expense_edit_documents.dart b/lib/ui/expense/edit/expense_edit_documents.dart index a308c11f7..07b6e04f3 100644 --- a/lib/ui/expense/edit/expense_edit_documents.dart +++ b/lib/ui/expense/edit/expense_edit_documents.dart @@ -67,8 +67,11 @@ class ExpenseEditDocumentsState extends State { activeColor: Theme.of(context).accentColor, title: Text(localization.addDocumentsToInvoice), value: expense.invoiceDocuments, - onChanged: (value) => viewModel.onChanged( - expense.rebuild((b) => b..invoiceDocuments = value)), + onChanged: (value) { + viewModel.onChanged( + expense.rebuild((b) => b..invoiceDocuments = value)); + viewModel.onAddDocumentsChanged(value); + } ) ], ), diff --git a/lib/ui/expense/edit/expense_edit_settings.dart b/lib/ui/expense/edit/expense_edit_settings.dart index 14e5da265..e6897c0e4 100644 --- a/lib/ui/expense/edit/expense_edit_settings.dart +++ b/lib/ui/expense/edit/expense_edit_settings.dart @@ -138,8 +138,10 @@ class ExpenseEditSettingsState extends State { activeColor: Theme.of(context).accentColor, title: Text(localization.markBillable), value: expense.shouldBeInvoiced, - onChanged: (value) => viewModel.onChanged( - expense.rebuild((b) => b..shouldBeInvoiced = value)), + onChanged: (value) { + viewModel.onChanged( + expense.rebuild((b) => b..shouldBeInvoiced = value)); + }, ), SwitchListTile( activeColor: Theme.of(context).accentColor, diff --git a/lib/ui/expense/edit/expense_edit_vm.dart b/lib/ui/expense/edit/expense_edit_vm.dart index 78c6fe524..369069514 100644 --- a/lib/ui/expense/edit/expense_edit_vm.dart +++ b/lib/ui/expense/edit/expense_edit_vm.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; +import 'package:invoiceninja_flutter/constants.dart'; +import 'package:invoiceninja_flutter/redux/app/app_actions.dart'; import 'package:invoiceninja_flutter/redux/client/client_actions.dart'; import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart'; import 'package:invoiceninja_flutter/redux/vendor/vendor_actions.dart'; @@ -16,6 +18,7 @@ import 'package:invoiceninja_flutter/redux/expense/expense_actions.dart'; import 'package:invoiceninja_flutter/data/models/expense_model.dart'; import 'package:invoiceninja_flutter/ui/expense/edit/expense_edit.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class ExpenseEditScreen extends StatelessWidget { const ExpenseEditScreen({Key key}) : super(key: key); @@ -49,6 +52,7 @@ class ExpenseEditVM { @required this.isLoading, @required this.onAddClientPressed, @required this.onAddVendorPressed, + @required this.onAddDocumentsChanged, }); factory ExpenseEditVM.fromStore(Store store) { @@ -120,6 +124,14 @@ class ExpenseEditVM { }); }); }, + onAddDocumentsChanged: (value) async { + if (expense.isOld) { + return; + } + final SharedPreferences prefs = await SharedPreferences.getInstance(); + prefs.setBool(kSharedPrefEmailPayment, value); + store.dispatch(UserSettingsChanged(addDocumentsToInvoice: value)); + }, ); } @@ -136,4 +148,5 @@ class ExpenseEditVM { onAddClientPressed; final Function(BuildContext context, Completer completer) onAddVendorPressed; + final Function(bool) onAddDocumentsChanged; } diff --git a/lib/ui/expense/expense_screen.dart b/lib/ui/expense/expense_screen.dart index 125da648f..89b689bc1 100644 --- a/lib/ui/expense/expense_screen.dart +++ b/lib/ui/expense/expense_screen.dart @@ -91,7 +91,8 @@ class ExpenseScreen extends StatelessWidget { backgroundColor: Theme.of(context).primaryColorDark, onPressed: () { store.dispatch(EditExpense( - expense: ExpenseEntity(company: company), + expense: ExpenseEntity( + company: company, uiState: store.state.uiState), context: context)); }, child: Icon( diff --git a/lib/ui/payment/edit/payment_edit_vm.dart b/lib/ui/payment/edit/payment_edit_vm.dart index ecc3e2799..1f22a750b 100644 --- a/lib/ui/payment/edit/payment_edit_vm.dart +++ b/lib/ui/payment/edit/payment_edit_vm.dart @@ -78,6 +78,9 @@ class PaymentEditVM { store.dispatch(UpdatePayment(payment)); }, onEmailChanged: (value) async { + if (payment.isOld) { + return; + } final SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.setBool(kSharedPrefEmailPayment, value); store.dispatch(UserSettingsChanged(emailPayment: value));