This commit is contained in:
Hillel Coren 2019-06-12 20:12:14 +03:00
parent b45ffe6bd3
commit a3014a86b6
17 changed files with 108 additions and 30 deletions

View File

@ -18,6 +18,7 @@ const String kSharedPrefEmailPayment = 'email_payment';
const String kSharedPrefAutoStartTasks = 'auto_start_tasks'; const String kSharedPrefAutoStartTasks = 'auto_start_tasks';
const String kSharedPrefAppVersion = 'app_version'; const String kSharedPrefAppVersion = 'app_version';
const String kSharedPrefRequireAuthentication = 'require_authentication'; const String kSharedPrefRequireAuthentication = 'require_authentication';
const String kSharedPrefAddDocumentsToInvoice = 'add_documents_to_invoice';
String getCompanyTokenKey([int companyIndex = 0]) => String getCompanyTokenKey([int companyIndex = 0]) =>
'${kSharedPrefToken}_$companyIndex'; '${kSharedPrefToken}_$companyIndex';

View File

@ -135,6 +135,8 @@ abstract class BaseEntity implements SelectableEntity {
bool get isNew => id == null || id < 0; bool get isNew => id == null || id < 0;
bool get isOld => !isNew;
bool get isActive => archivedAt == null || archivedAt == 0; bool get isActive => archivedAt == null || archivedAt == 0;
bool get isArchived => archivedAt != null && archivedAt > 0 && !isDeleted; bool get isArchived => archivedAt != null && archivedAt > 0 && !isDeleted;

View File

@ -4,6 +4,7 @@ import 'package:built_value/serializer.dart';
import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/constants.dart';
import 'package:invoiceninja_flutter/data/models/entities.dart'; import 'package:invoiceninja_flutter/data/models/entities.dart';
import 'package:invoiceninja_flutter/data/models/models.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'; import 'package:invoiceninja_flutter/utils/formatting.dart';
part 'expense_model.g.dart'; part 'expense_model.g.dart';
@ -67,13 +68,13 @@ abstract class ExpenseEntity extends Object
with BaseEntity, SelectableEntity, BelongsToClient with BaseEntity, SelectableEntity, BelongsToClient
implements Built<ExpenseEntity, ExpenseEntityBuilder> { implements Built<ExpenseEntity, ExpenseEntityBuilder> {
factory ExpenseEntity( factory ExpenseEntity(
{CompanyEntity company, VendorEntity vendor, ClientEntity client}) { {CompanyEntity company, UIState uiState, VendorEntity vendor, ClientEntity client}) {
return _$ExpenseEntity._( return _$ExpenseEntity._(
id: --ExpenseEntity.counter, id: --ExpenseEntity.counter,
privateNotes: '', privateNotes: '',
publicNotes: '', publicNotes: '',
shouldBeInvoiced: false, shouldBeInvoiced: false,
invoiceDocuments: false, invoiceDocuments: uiState?.addDocumentsToInvoice ?? false,
transactionId: '', transactionId: '',
transactionReference: '', transactionReference: '',
bankId: 0, bankId: 0,

View File

@ -30,12 +30,14 @@ class UserSettingsChanged implements PersistUI {
{this.enableDarkMode, {this.enableDarkMode,
this.emailPayment, this.emailPayment,
this.requireAuthentication, this.requireAuthentication,
this.autoStartTasks}); this.autoStartTasks,
this.addDocumentsToInvoice});
final bool enableDarkMode; final bool enableDarkMode;
final bool emailPayment; final bool emailPayment;
final bool requireAuthentication; final bool requireAuthentication;
final bool autoStartTasks; final bool autoStartTasks;
final bool addDocumentsToInvoice;
} }
class LoadDataSuccess { class LoadDataSuccess {

View File

@ -43,17 +43,15 @@ void _loadAuthLocal(Store<AppState> store, dynamic action) async {
final String secret = prefs.getString(kSharedPrefSecret) ?? ''; final String secret = prefs.getString(kSharedPrefSecret) ?? '';
store.dispatch(UserLoginLoaded(email, url, secret)); 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( store.dispatch(UserSettingsChanged(
enableDarkMode: enableDarkMode, enableDarkMode: prefs.getBool(kSharedPrefEnableDarkMode) ?? false,
emailPayment: emailPayment, emailPayment: prefs.getBool(kSharedPrefEmailPayment) ?? false,
requireAuthentication: requireAuthentication, requireAuthentication:
autoStartTasks: autoStartTasks)); prefs.getBool(kSharedPrefRequireAuthentication) ?? false,
autoStartTasks: prefs.getBool(kSharedPrefAutoStartTasks) ?? false,
addDocumentsToInvoice:
prefs.getBool(kSharedPrefAddDocumentsToInvoice) ?? false,
));
} }
Middleware<AppState> _createLoginInit() { Middleware<AppState> _createLoginInit() {

View File

@ -32,6 +32,8 @@ UIState uiReducer(UIState state, dynamic action) {
..requireAuthentication = ..requireAuthentication =
requireAuthenticationReducer(state.requireAuthentication, action) requireAuthenticationReducer(state.requireAuthentication, action)
..emailPayment = emailPaymentReducer(state.emailPayment, action) ..emailPayment = emailPaymentReducer(state.emailPayment, action)
..addDocumentsToInvoice =
addDocumentsToInvoiceReducer(state.addDocumentsToInvoice, action)
..productUIState.replace(productUIReducer(state.productUIState, action)) ..productUIState.replace(productUIReducer(state.productUIState, action))
..clientUIState.replace(clientUIReducer(state.clientUIState, action)) ..clientUIState.replace(clientUIReducer(state.clientUIState, action))
..invoiceUIState.replace(invoiceUIReducer(state.invoiceUIState, action)) ..invoiceUIState.replace(invoiceUIReducer(state.invoiceUIState, action))
@ -79,6 +81,15 @@ bool updateAutoStartTasksReducer(
return action.autoStartTasks ?? autoStartTasks; return action.autoStartTasks ?? autoStartTasks;
} }
Reducer<bool> addDocumentsToInvoiceReducer = combineReducers([
TypedReducer<bool, UserSettingsChanged>(updateAddDocumentsToInvoiceReducer),
]);
bool updateAddDocumentsToInvoiceReducer(
bool addDocumentsToInvoice, UserSettingsChanged action) {
return action.addDocumentsToInvoice ?? addDocumentsToInvoice;
}
Reducer<bool> requireAuthenticationReducer = combineReducers([ Reducer<bool> requireAuthenticationReducer = combineReducers([
TypedReducer<bool, UserSettingsChanged>(updateRequireAuthenticationReducer), TypedReducer<bool, UserSettingsChanged>(updateRequireAuthenticationReducer),
]); ]);

View File

@ -32,6 +32,7 @@ abstract class UIState implements Built<UIState, UIStateBuilder> {
requireAuthentication: requireAuthentication ?? false, requireAuthentication: requireAuthentication ?? false,
emailPayment: false, emailPayment: false,
autoStartTasks: false, autoStartTasks: false,
addDocumentsToInvoice: false,
dashboardUIState: DashboardUIState(), dashboardUIState: DashboardUIState(),
productUIState: ProductUIState(), productUIState: ProductUIState(),
clientUIState: ClientUIState(), clientUIState: ClientUIState(),
@ -60,6 +61,8 @@ abstract class UIState implements Built<UIState, UIStateBuilder> {
bool get autoStartTasks; bool get autoStartTasks;
bool get addDocumentsToInvoice;
@nullable @nullable
String get filter; String get filter;

View File

@ -49,6 +49,9 @@ class _$UIStateSerializer implements StructuredSerializer<UIState> {
'autoStartTasks', 'autoStartTasks',
serializers.serialize(object.autoStartTasks, serializers.serialize(object.autoStartTasks,
specifiedType: const FullType(bool)), specifiedType: const FullType(bool)),
'addDocumentsToInvoice',
serializers.serialize(object.addDocumentsToInvoice,
specifiedType: const FullType(bool)),
'dashboardUIState', 'dashboardUIState',
serializers.serialize(object.dashboardUIState, serializers.serialize(object.dashboardUIState,
specifiedType: const FullType(DashboardUIState)), specifiedType: const FullType(DashboardUIState)),
@ -125,6 +128,10 @@ class _$UIStateSerializer implements StructuredSerializer<UIState> {
result.autoStartTasks = serializers.deserialize(value, result.autoStartTasks = serializers.deserialize(value,
specifiedType: const FullType(bool)) as bool; specifiedType: const FullType(bool)) as bool;
break; break;
case 'addDocumentsToInvoice':
result.addDocumentsToInvoice = serializers.deserialize(value,
specifiedType: const FullType(bool)) as bool;
break;
case 'filter': case 'filter':
result.filter = serializers.deserialize(value, result.filter = serializers.deserialize(value,
specifiedType: const FullType(String)) as String; specifiedType: const FullType(String)) as String;
@ -191,6 +198,8 @@ class _$UIState extends UIState {
@override @override
final bool autoStartTasks; final bool autoStartTasks;
@override @override
final bool addDocumentsToInvoice;
@override
final String filter; final String filter;
@override @override
final DashboardUIState dashboardUIState; final DashboardUIState dashboardUIState;
@ -223,6 +232,7 @@ class _$UIState extends UIState {
this.requireAuthentication, this.requireAuthentication,
this.emailPayment, this.emailPayment,
this.autoStartTasks, this.autoStartTasks,
this.addDocumentsToInvoice,
this.filter, this.filter,
this.dashboardUIState, this.dashboardUIState,
this.productUIState, this.productUIState,
@ -253,6 +263,9 @@ class _$UIState extends UIState {
if (autoStartTasks == null) { if (autoStartTasks == null) {
throw new BuiltValueNullFieldError('UIState', 'autoStartTasks'); throw new BuiltValueNullFieldError('UIState', 'autoStartTasks');
} }
if (addDocumentsToInvoice == null) {
throw new BuiltValueNullFieldError('UIState', 'addDocumentsToInvoice');
}
if (dashboardUIState == null) { if (dashboardUIState == null) {
throw new BuiltValueNullFieldError('UIState', 'dashboardUIState'); throw new BuiltValueNullFieldError('UIState', 'dashboardUIState');
} }
@ -302,6 +315,7 @@ class _$UIState extends UIState {
requireAuthentication == other.requireAuthentication && requireAuthentication == other.requireAuthentication &&
emailPayment == other.emailPayment && emailPayment == other.emailPayment &&
autoStartTasks == other.autoStartTasks && autoStartTasks == other.autoStartTasks &&
addDocumentsToInvoice == other.addDocumentsToInvoice &&
filter == other.filter && filter == other.filter &&
dashboardUIState == other.dashboardUIState && dashboardUIState == other.dashboardUIState &&
productUIState == other.productUIState && productUIState == other.productUIState &&
@ -334,17 +348,22 @@ class _$UIState extends UIState {
$jc( $jc(
$jc( $jc(
$jc( $jc(
0, $jc(
selectedCompanyIndex 0,
selectedCompanyIndex
.hashCode),
currentRoute
.hashCode), .hashCode),
currentRoute enableDarkMode
.hashCode), .hashCode),
enableDarkMode requireAuthentication
.hashCode), .hashCode),
requireAuthentication emailPayment
.hashCode), .hashCode),
emailPayment.hashCode), autoStartTasks
autoStartTasks.hashCode), .hashCode),
addDocumentsToInvoice
.hashCode),
filter.hashCode), filter.hashCode),
dashboardUIState.hashCode), dashboardUIState.hashCode),
productUIState.hashCode), productUIState.hashCode),
@ -367,6 +386,7 @@ class _$UIState extends UIState {
..add('requireAuthentication', requireAuthentication) ..add('requireAuthentication', requireAuthentication)
..add('emailPayment', emailPayment) ..add('emailPayment', emailPayment)
..add('autoStartTasks', autoStartTasks) ..add('autoStartTasks', autoStartTasks)
..add('addDocumentsToInvoice', addDocumentsToInvoice)
..add('filter', filter) ..add('filter', filter)
..add('dashboardUIState', dashboardUIState) ..add('dashboardUIState', dashboardUIState)
..add('productUIState', productUIState) ..add('productUIState', productUIState)
@ -413,6 +433,11 @@ class UIStateBuilder implements Builder<UIState, UIStateBuilder> {
set autoStartTasks(bool autoStartTasks) => set autoStartTasks(bool autoStartTasks) =>
_$this._autoStartTasks = autoStartTasks; _$this._autoStartTasks = autoStartTasks;
bool _addDocumentsToInvoice;
bool get addDocumentsToInvoice => _$this._addDocumentsToInvoice;
set addDocumentsToInvoice(bool addDocumentsToInvoice) =>
_$this._addDocumentsToInvoice = addDocumentsToInvoice;
String _filter; String _filter;
String get filter => _$this._filter; String get filter => _$this._filter;
set filter(String filter) => _$this._filter = filter; set filter(String filter) => _$this._filter = filter;
@ -487,6 +512,7 @@ class UIStateBuilder implements Builder<UIState, UIStateBuilder> {
_requireAuthentication = _$v.requireAuthentication; _requireAuthentication = _$v.requireAuthentication;
_emailPayment = _$v.emailPayment; _emailPayment = _$v.emailPayment;
_autoStartTasks = _$v.autoStartTasks; _autoStartTasks = _$v.autoStartTasks;
_addDocumentsToInvoice = _$v.addDocumentsToInvoice;
_filter = _$v.filter; _filter = _$v.filter;
_dashboardUIState = _$v.dashboardUIState?.toBuilder(); _dashboardUIState = _$v.dashboardUIState?.toBuilder();
_productUIState = _$v.productUIState?.toBuilder(); _productUIState = _$v.productUIState?.toBuilder();
@ -528,6 +554,7 @@ class UIStateBuilder implements Builder<UIState, UIStateBuilder> {
requireAuthentication: requireAuthentication, requireAuthentication: requireAuthentication,
emailPayment: emailPayment, emailPayment: emailPayment,
autoStartTasks: autoStartTasks, autoStartTasks: autoStartTasks,
addDocumentsToInvoice: addDocumentsToInvoice,
filter: filter, filter: filter,
dashboardUIState: dashboardUIState.build(), dashboardUIState: dashboardUIState.build(),
productUIState: productUIState.build(), productUIState: productUIState.build(),

View File

@ -264,7 +264,9 @@ class AppDrawer extends StatelessWidget {
onCreateTap: () { onCreateTap: () {
navigator.pop(); navigator.pop();
store.dispatch(EditExpense( store.dispatch(EditExpense(
expense: ExpenseEntity(company: company), context: context)); expense:
ExpenseEntity(company: company, uiState: state.uiState),
context: context));
}, },
), ),

View File

@ -94,7 +94,10 @@ class ClientListVM {
break; break;
case EntityAction.newExpense: case EntityAction.newExpense:
store.dispatch(EditExpense( store.dispatch(EditExpense(
expense: ExpenseEntity(company: state.selectedCompany, client: client), expense: ExpenseEntity(
company: state.selectedCompany,
client: client,
uiState: state.uiState),
context: context)); context: context));
break; break;
case EntityAction.enterPayment: case EntityAction.enterPayment:

View File

@ -160,7 +160,9 @@ class _ClientViewState extends State<ClientView>
Navigator.of(context).pop(); Navigator.of(context).pop();
store.dispatch(EditExpense( store.dispatch(EditExpense(
expense: ExpenseEntity( expense: ExpenseEntity(
company: company, client: client), company: company,
client: client,
uiState: store.state.uiState),
context: context)); context: context));
}, },
) )

View File

@ -155,7 +155,9 @@ class ClientViewVM {
store.dispatch(EditExpense( store.dispatch(EditExpense(
context: context, context: context,
expense: ExpenseEntity( expense: ExpenseEntity(
company: state.selectedCompany, client: client))); company: state.selectedCompany,
client: client,
uiState: state.uiState)));
} else { } else {
store.dispatch(FilterExpensesByEntity( store.dispatch(FilterExpensesByEntity(
entityId: client.id, entityType: EntityType.client)); entityId: client.id, entityType: EntityType.client));
@ -183,7 +185,9 @@ class ClientViewVM {
case EntityAction.newExpense: case EntityAction.newExpense:
store.dispatch(EditExpense( store.dispatch(EditExpense(
expense: ExpenseEntity( expense: ExpenseEntity(
company: state.selectedCompany, client: client), company: state.selectedCompany,
client: client,
uiState: state.uiState),
context: context)); context: context));
break; break;
case EntityAction.enterPayment: case EntityAction.enterPayment:

View File

@ -67,8 +67,11 @@ class ExpenseEditDocumentsState extends State<ExpenseEditDocuments> {
activeColor: Theme.of(context).accentColor, activeColor: Theme.of(context).accentColor,
title: Text(localization.addDocumentsToInvoice), title: Text(localization.addDocumentsToInvoice),
value: expense.invoiceDocuments, value: expense.invoiceDocuments,
onChanged: (value) => viewModel.onChanged( onChanged: (value) {
expense.rebuild((b) => b..invoiceDocuments = value)), viewModel.onChanged(
expense.rebuild((b) => b..invoiceDocuments = value));
viewModel.onAddDocumentsChanged(value);
}
) )
], ],
), ),

View File

@ -138,8 +138,10 @@ class ExpenseEditSettingsState extends State<ExpenseEditSettings> {
activeColor: Theme.of(context).accentColor, activeColor: Theme.of(context).accentColor,
title: Text(localization.markBillable), title: Text(localization.markBillable),
value: expense.shouldBeInvoiced, value: expense.shouldBeInvoiced,
onChanged: (value) => viewModel.onChanged( onChanged: (value) {
expense.rebuild((b) => b..shouldBeInvoiced = value)), viewModel.onChanged(
expense.rebuild((b) => b..shouldBeInvoiced = value));
},
), ),
SwitchListTile( SwitchListTile(
activeColor: Theme.of(context).accentColor, activeColor: Theme.of(context).accentColor,

View File

@ -2,6 +2,8 @@ import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.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/client/client_actions.dart';
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart'; import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
import 'package:invoiceninja_flutter/redux/vendor/vendor_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/data/models/expense_model.dart';
import 'package:invoiceninja_flutter/ui/expense/edit/expense_edit.dart'; import 'package:invoiceninja_flutter/ui/expense/edit/expense_edit.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ExpenseEditScreen extends StatelessWidget { class ExpenseEditScreen extends StatelessWidget {
const ExpenseEditScreen({Key key}) : super(key: key); const ExpenseEditScreen({Key key}) : super(key: key);
@ -49,6 +52,7 @@ class ExpenseEditVM {
@required this.isLoading, @required this.isLoading,
@required this.onAddClientPressed, @required this.onAddClientPressed,
@required this.onAddVendorPressed, @required this.onAddVendorPressed,
@required this.onAddDocumentsChanged,
}); });
factory ExpenseEditVM.fromStore(Store<AppState> store) { factory ExpenseEditVM.fromStore(Store<AppState> 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; onAddClientPressed;
final Function(BuildContext context, Completer<SelectableEntity> completer) final Function(BuildContext context, Completer<SelectableEntity> completer)
onAddVendorPressed; onAddVendorPressed;
final Function(bool) onAddDocumentsChanged;
} }

View File

@ -91,7 +91,8 @@ class ExpenseScreen extends StatelessWidget {
backgroundColor: Theme.of(context).primaryColorDark, backgroundColor: Theme.of(context).primaryColorDark,
onPressed: () { onPressed: () {
store.dispatch(EditExpense( store.dispatch(EditExpense(
expense: ExpenseEntity(company: company), expense: ExpenseEntity(
company: company, uiState: store.state.uiState),
context: context)); context: context));
}, },
child: Icon( child: Icon(

View File

@ -78,6 +78,9 @@ class PaymentEditVM {
store.dispatch(UpdatePayment(payment)); store.dispatch(UpdatePayment(payment));
}, },
onEmailChanged: (value) async { onEmailChanged: (value) async {
if (payment.isOld) {
return;
}
final SharedPreferences prefs = await SharedPreferences.getInstance(); final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool(kSharedPrefEmailPayment, value); prefs.setBool(kSharedPrefEmailPayment, value);
store.dispatch(UserSettingsChanged(emailPayment: value)); store.dispatch(UserSettingsChanged(emailPayment: value));