This commit is contained in:
Hillel Coren 2019-12-15 19:14:57 +02:00
parent 30a028a07d
commit ea16feae8f
18 changed files with 398 additions and 474 deletions

View File

@ -9,6 +9,7 @@ import 'package:invoiceninja_flutter/data/models/static/static_data_model.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/client/client_actions.dart'; import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_actions.dart'; import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_actions.dart';
import 'package:invoiceninja_flutter/redux/document/document_actions.dart';
import 'package:invoiceninja_flutter/redux/expense/expense_actions.dart'; import 'package:invoiceninja_flutter/redux/expense/expense_actions.dart';
import 'package:invoiceninja_flutter/redux/group/group_actions.dart'; import 'package:invoiceninja_flutter/redux/group/group_actions.dart';
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart'; import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
@ -21,6 +22,7 @@ import 'package:invoiceninja_flutter/redux/tax_rate/tax_rate_actions.dart';
import 'package:invoiceninja_flutter/redux/ui/pref_state.dart'; import 'package:invoiceninja_flutter/redux/ui/pref_state.dart';
import 'package:invoiceninja_flutter/redux/user/user_actions.dart'; import 'package:invoiceninja_flutter/redux/user/user_actions.dart';
import 'package:invoiceninja_flutter/redux/vendor/vendor_actions.dart'; import 'package:invoiceninja_flutter/redux/vendor/vendor_actions.dart';
import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/dialogs.dart'; import 'package:invoiceninja_flutter/utils/dialogs.dart';
import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/utils/localization.dart';
@ -662,49 +664,80 @@ void editEntityById(
Completer completer}) { Completer completer}) {
final store = StoreProvider.of<AppState>(context); final store = StoreProvider.of<AppState>(context);
final navigator = Navigator.of(context); final navigator = Navigator.of(context);
final localization = AppLocalization.of(context);
final map = store.state.getEntityMap(entityType); final map = store.state.getEntityMap(entityType);
final entity = map[entityId] as BaseEntity;
switch (entityType) { switch (entityType) {
case EntityType.client: case EntityType.client:
store.dispatch(EditClient( store.dispatch(
EditClient(
client: map[entityId], client: map[entityId],
navigator: navigator, navigator: navigator,
completer: completer, completer: completer ??
)); snackBarCompleter<ClientEntity>(
context,
entity.isNew
? localization.createdClient
: localization.updatedClient)),
);
break; break;
case EntityType.user: case EntityType.user:
store.dispatch(EditUser( store.dispatch(
EditUser(
user: map[entityId], user: map[entityId],
navigator: navigator, navigator: navigator,
completer: completer, completer: completer ??
)); snackBarCompleter<UserEntity>(
context,
entity.isNew
? localization.createdUser
: localization.updatedUser)),
);
break; break;
case EntityType.project: case EntityType.project:
store.dispatch(EditProject( store.dispatch(EditProject(
project: map[entityId], project: map[entityId],
navigator: navigator, navigator: navigator,
completer: completer, completer: completer ??
)); snackBarCompleter<ProjectEntity>(
context,
entity.isNew
? localization.createdProject
: localization.updatedProject)));
break; break;
case EntityType.taxRate: case EntityType.taxRate:
store.dispatch(EditTaxRate( store.dispatch(EditTaxRate(
taxRate: map[entityId], taxRate: map[entityId],
navigator: navigator, navigator: navigator,
completer: completer, completer: completer ??
)); snackBarCompleter<TaxRateEntity>(
context,
entity.isNew
? localization.createdTaxRate
: localization.updatedTaxRate)));
break; break;
case EntityType.companyGateway: case EntityType.companyGateway:
store.dispatch(EditCompanyGateway( store.dispatch(EditCompanyGateway(
companyGateway: map[entityId], companyGateway: map[entityId],
navigator: navigator, navigator: navigator,
completer: completer, completer: completer ??
)); snackBarCompleter<CompanyGatewayEntity>(
context,
entity.isNew
? localization.createdCompanyGateway
: localization.updatedCompanyGateway)));
break; break;
case EntityType.invoice: case EntityType.invoice:
store.dispatch(EditInvoice( store.dispatch(EditInvoice(
invoice: map[entityId], invoice: map[entityId],
navigator: navigator, navigator: navigator,
completer: completer, completer: completer ??
snackBarCompleter<InvoiceEntity>(
context,
entity.isNew
? localization.createdInvoice
: localization.updatedInvoice),
invoiceItemIndex: subIndex, invoiceItemIndex: subIndex,
)); ));
break; break;
@ -715,7 +748,12 @@ void editEntityById(
store.dispatch(EditQuote( store.dispatch(EditQuote(
quote: map[entityId], quote: map[entityId],
navigator: navigator, navigator: navigator,
completer: completer, completer: completer ??
snackBarCompleter<InvoiceEntity>(
context,
entity.isNew
? localization.createdQuote
: localization.updatedQuote),
quoteItemIndex: subIndex, quoteItemIndex: subIndex,
)); ));
break; break;
@ -723,28 +761,47 @@ void editEntityById(
store.dispatch(EditVendor( store.dispatch(EditVendor(
vendor: map[entityId], vendor: map[entityId],
navigator: navigator, navigator: navigator,
completer: completer, completer: completer ??
snackBarCompleter<VendorEntity>(
context,
entity.isNew
? localization.createdVendor
: localization.updatedVendor),
)); ));
break; break;
case EntityType.product: case EntityType.product:
store.dispatch(EditProduct( store.dispatch(EditProduct(
product: map[entityId], product: map[entityId],
navigator: navigator, navigator: navigator,
completer: completer, completer: completer ??
)); snackBarCompleter<ProductEntity>(
context,
entity.isNew
? localization.createdProduct
: localization.updatedProduct)));
break; break;
case EntityType.task: case EntityType.task:
store.dispatch(EditTask( store.dispatch(EditTask(
task: map[entityId], task: map[entityId],
navigator: navigator, navigator: navigator,
completer: completer, completer: completer ??
snackBarCompleter<TaskEntity>(
context,
entity.isNew
? localization.createdTask
: localization.updatedTask),
)); ));
break; break;
case EntityType.expense: case EntityType.expense:
store.dispatch(EditExpense( store.dispatch(EditExpense(
expense: map[entityId], expense: map[entityId],
navigator: navigator, navigator: navigator,
completer: completer, completer: completer ??
snackBarCompleter<ExpenseEntity>(
context,
entity.isNew
? localization.createdExpense
: localization.updatedExpense),
)); ));
break; break;
//case EntityType.expenseCategory: //case EntityType.expenseCategory:
@ -757,14 +814,24 @@ void editEntityById(
store.dispatch(EditPayment( store.dispatch(EditPayment(
payment: map[entityId], payment: map[entityId],
navigator: navigator, navigator: navigator,
completer: completer, completer: completer ??
snackBarCompleter<PaymentEntity>(
context,
entity.isNew
? localization.createdPayment
: localization.updatedPayment),
)); ));
break; break;
case EntityType.group: case EntityType.group:
store.dispatch(EditGroup( store.dispatch(EditGroup(
group: map[entityId], group: map[entityId],
navigator: navigator, navigator: navigator,
completer: completer, completer: completer ??
snackBarCompleter<GroupEntity>(
context,
entity.isNew
? localization.createdGroup
: localization.updatedGroup),
)); ));
break; break;
// TODO Add to starter // TODO Add to starter
@ -782,3 +849,59 @@ void editEntity(
entityType: entity.entityType, entityType: entity.entityType,
subIndex: subIndex, subIndex: subIndex,
completer: completer); completer: completer);
void handleEntityAction(
BuildContext context, BaseEntity entity, dynamic action) {
handleEntitiesActions(context, [entity], action);
}
void handleEntitiesActions(
BuildContext context, List<BaseEntity> entities, dynamic action) {
if (entities.isEmpty) {
return;
}
switch (entities.first.entityType) {
case EntityType.client:
handleClientAction(context, entities, action);
break;
case EntityType.product:
handleProductAction(context, entities, action);
break;
case EntityType.invoice:
handleInvoiceAction(context, entities, action);
break;
case EntityType.payment:
handlePaymentAction(context, entities, action);
break;
case EntityType.quote:
handleQuoteAction(context, entities, action);
break;
case EntityType.task:
handleTaskAction(context, entities, action);
break;
case EntityType.project:
handleProjectAction(context, entities, action);
break;
case EntityType.expense:
handleExpenseAction(context, entities, action);
break;
case EntityType.vendor:
handleVendorAction(context, entities, action);
break;
case EntityType.user:
handleUserAction(context, entities, action);
break;
case EntityType.companyGateway:
handleCompanyGatewayAction(context, entities, action);
break;
case EntityType.taxRate:
handleTaxRateAction(context, entities, action);
break;
case EntityType.group:
handleGroupAction(context, entities, action);
break;
case EntityType.document:
handleDocumentAction(context, entities, action);
}
}

View File

@ -1,7 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/data/models/entities.dart'; import 'package:invoiceninja_flutter/data/models/entities.dart';
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/ui/app/actions_menu_button.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart'; import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'buttons/edit_icon_button.dart';
import 'entities/entity_state_title.dart'; import 'entities/entity_state_title.dart';
class ViewScaffold extends StatelessWidget { class ViewScaffold extends StatelessWidget {
@ -19,6 +23,10 @@ class ViewScaffold extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final userCompany = state.userCompany;
return WillPopScope( return WillPopScope(
onWillPop: () async { onWillPop: () async {
return true; return true;
@ -28,6 +36,24 @@ class ViewScaffold extends StatelessWidget {
automaticallyImplyLeading: isMobile(context), automaticallyImplyLeading: isMobile(context),
title: EntityStateTitle(entity: entity), title: EntityStateTitle(entity: entity),
bottom: appBarBottom, bottom: appBarBottom,
actions: entity.isNew
? []
: [
userCompany.canEditEntity(entity)
? EditIconButton(
isVisible: !entity.isDeleted,
onPressed: () =>
editEntity(context: context, entity: entity),
)
: Container(),
ActionMenuButton(
isSaving: state.isSaving,
entity: entity,
onSelected: (context, action) =>
handleEntityAction(context, entity, action),
entityActions: entity.getActions(userCompany: userCompany),
)
],
), ),
body: body, body: body,
), ),

View File

@ -7,8 +7,6 @@ import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/app/app_actions.dart'; import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.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/ui/client/client_screen.dart';
import 'package:invoiceninja_flutter/ui/client/view/client_view.dart'; import 'package:invoiceninja_flutter/ui/client/view/client_view.dart';
import 'package:invoiceninja_flutter/utils/completers.dart'; import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/utils/localization.dart';

View File

@ -46,7 +46,6 @@ class CompanyGatewayEditVM {
@required this.origCompanyGateway, @required this.origCompanyGateway,
@required this.onSavePressed, @required this.onSavePressed,
@required this.onCancelPressed, @required this.onCancelPressed,
@required this.onBackPressed,
@required this.isLoading, @required this.isLoading,
}); });
@ -64,13 +63,6 @@ class CompanyGatewayEditVM {
onChanged: (CompanyGatewayEntity companyGateway) { onChanged: (CompanyGatewayEntity companyGateway) {
store.dispatch(UpdateCompanyGateway(companyGateway)); store.dispatch(UpdateCompanyGateway(companyGateway));
}, },
onBackPressed: () {
if (state.uiState.currentRoute.contains(CompanyGatewayScreen.route)) {
store.dispatch(UpdateCurrentRoute(companyGateway.isNew
? CompanyGatewayScreen.route
: CompanyGatewayViewScreen.route));
}
},
onCancelPressed: (BuildContext context) { onCancelPressed: (BuildContext context) {
store.dispatch(UpdateCurrentRoute(state.uiState.previousRoute)); store.dispatch(UpdateCurrentRoute(state.uiState.previousRoute));
}, },
@ -111,7 +103,6 @@ class CompanyGatewayEditVM {
final Function(CompanyGatewayEntity) onChanged; final Function(CompanyGatewayEntity) onChanged;
final Function(BuildContext) onSavePressed; final Function(BuildContext) onSavePressed;
final Function(BuildContext) onCancelPressed; final Function(BuildContext) onCancelPressed;
final Function onBackPressed;
final bool isLoading; final bool isLoading;
final bool isSaving; final bool isSaving;
final CompanyGatewayEntity origCompanyGateway; final CompanyGatewayEntity origCompanyGateway;

View File

@ -2,15 +2,12 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:invoiceninja_flutter/redux/document/document_selectors.dart'; import 'package:invoiceninja_flutter/redux/document/document_selectors.dart';
import 'package:invoiceninja_flutter/ui/app/actions_menu_button.dart'; import 'package:invoiceninja_flutter/ui/app/view_scaffold.dart';
import 'package:invoiceninja_flutter/ui/app/buttons/edit_icon_button.dart';
import 'package:invoiceninja_flutter/ui/app/entities/entity_state_title.dart';
import 'package:invoiceninja_flutter/ui/expense/view/expense_view_details.dart'; import 'package:invoiceninja_flutter/ui/expense/view/expense_view_details.dart';
import 'package:invoiceninja_flutter/ui/expense/view/expense_view_documents.dart'; import 'package:invoiceninja_flutter/ui/expense/view/expense_view_documents.dart';
import 'package:invoiceninja_flutter/ui/expense/view/expense_view_vm.dart'; import 'package:invoiceninja_flutter/ui/expense/view/expense_view_vm.dart';
import 'package:invoiceninja_flutter/ui/expense/view/expense_view_overview.dart'; import 'package:invoiceninja_flutter/ui/expense/view/expense_view_overview.dart';
import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart';
class ExpenseView extends StatefulWidget { class ExpenseView extends StatefulWidget {
const ExpenseView({ const ExpenseView({
@ -45,20 +42,46 @@ class _ExpenseViewState extends State<ExpenseView>
final localization = AppLocalization.of(context); final localization = AppLocalization.of(context);
final viewModel = widget.viewModel; final viewModel = widget.viewModel;
final company = viewModel.state.company; final company = viewModel.state.company;
final expense = viewModel.expense;
final documentState = viewModel.state.documentState;
final documents =
memoizedExpenseDocumentsSelector(documentState.map, viewModel.expense);
return WillPopScope( return ViewScaffold(
onWillPop: () async { entity: expense,
viewModel.onBackPressed(); appBarBottom: TabBar(
return true;
},
child: Scaffold(
appBar: _CustomAppBar(
viewModel: viewModel,
controller: _controller, controller: _controller,
tabs: [
Tab(
text: localization.overview,
), ),
body: CustomTabBarView( Tab(
viewModel: viewModel, text: localization.details,
),
Tab(
text: documents.isEmpty
? localization.documents
: '${localization.documents} (${documents.length})',
),
],
),
body: TabBarView(
controller: _controller, controller: _controller,
children: <Widget>[
RefreshIndicator(
onRefresh: () => viewModel.onRefreshed(context),
child: ExpenseOverview(viewModel: viewModel),
),
RefreshIndicator(
onRefresh: () => viewModel.onRefreshed(context),
child: ExpenseViewDetails(expense: viewModel.expense),
),
RefreshIndicator(
onRefresh: () => viewModel.onRefreshed(context),
child: ExpenseViewDocuments(
viewModel: viewModel, expense: viewModel.expense),
),
],
), ),
floatingActionButton: company.isEnterprisePlan floatingActionButton: company.isEnterprisePlan
? Builder(builder: (BuildContext context) { ? Builder(builder: (BuildContext context) {
@ -80,112 +103,6 @@ class _ExpenseViewState extends State<ExpenseView>
); );
}) })
: null, : null,
),
);
}
}
class CustomTabBarView extends StatefulWidget {
const CustomTabBarView({
@required this.viewModel,
@required this.controller,
});
final ExpenseViewVM viewModel;
final TabController controller;
@override
_CustomTabBarViewState createState() => _CustomTabBarViewState();
}
class _CustomTabBarViewState extends State<CustomTabBarView> {
@override
Widget build(BuildContext context) {
final viewModel = widget.viewModel;
return TabBarView(
controller: widget.controller,
children: <Widget>[
RefreshIndicator(
onRefresh: () => viewModel.onRefreshed(context),
child: ExpenseOverview(viewModel: viewModel),
),
RefreshIndicator(
onRefresh: () => viewModel.onRefreshed(context),
child: ExpenseViewDetails(expense: viewModel.expense),
),
RefreshIndicator(
onRefresh: () => viewModel.onRefreshed(context),
child: ExpenseViewDocuments(
viewModel: viewModel, expense: viewModel.expense),
),
],
);
}
}
class _CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
const _CustomAppBar({
@required this.viewModel,
@required this.controller,
});
final ExpenseViewVM viewModel;
final TabController controller;
@override
final Size preferredSize = const Size(double.infinity, kToolbarHeight * 2);
@override
Widget build(BuildContext context) {
final localization = AppLocalization.of(context);
final expense = viewModel.expense;
final userCompany = viewModel.state.userCompany;
final documentState = viewModel.state.documentState;
final documents =
memoizedExpenseDocumentsSelector(documentState.map, viewModel.expense);
return AppBar(
automaticallyImplyLeading: isMobile(context),
title: EntityStateTitle(
entity: expense,
title: expense.publicNotes.isNotEmpty
? expense.publicNotes
: localization.expense,
),
bottom: TabBar(
controller: controller,
tabs: [
Tab(
text: localization.overview,
),
Tab(
text: localization.details,
),
Tab(
text: documents.isEmpty
? localization.documents
: '${localization.documents} (${documents.length})',
),
],
),
actions: expense.isNew
? []
: [
userCompany.canEditEntity(expense)
? EditIconButton(
isVisible: !expense.isDeleted,
onPressed: () => viewModel.onEditPressed(context),
)
: Container(),
ActionMenuButton(
isSaving: viewModel.isSaving,
entity: expense,
onSelected: viewModel.onEntityAction,
entityActions:
viewModel.expense.getActions(userCompany: userCompany),
)
],
); );
} }
} }

View File

@ -46,7 +46,6 @@ class ExpenseViewVM {
@required this.onEntityAction, @required this.onEntityAction,
@required this.onEntityPressed, @required this.onEntityPressed,
@required this.onEditPressed, @required this.onEditPressed,
@required this.onBackPressed,
@required this.onRefreshed, @required this.onRefreshed,
@required this.onUploadDocument, @required this.onUploadDocument,
@required this.onDeleteDocument, @required this.onDeleteDocument,
@ -85,11 +84,6 @@ class ExpenseViewVM {
context, AppLocalization.of(context).updatedExpense)); context, AppLocalization.of(context).updatedExpense));
}, },
onRefreshed: (context) => _handleRefresh(context), onRefreshed: (context) => _handleRefresh(context),
onBackPressed: () {
if (state.uiState.currentRoute.contains(ExpenseScreen.route)) {
store.dispatch(UpdateCurrentRoute(ExpenseScreen.route));
}
},
onEntityPressed: (BuildContext context, EntityType entityType, onEntityPressed: (BuildContext context, EntityType entityType,
[longPress = false]) { [longPress = false]) {
switch (entityType) { switch (entityType) {
@ -157,7 +151,6 @@ class ExpenseViewVM {
final Function(BuildContext, EntityAction) onEntityAction; final Function(BuildContext, EntityAction) onEntityAction;
final Function(BuildContext, EntityType, [bool]) onEntityPressed; final Function(BuildContext, EntityType, [bool]) onEntityPressed;
final Function(BuildContext) onEditPressed; final Function(BuildContext) onEditPressed;
final Function onBackPressed;
final Function(BuildContext) onRefreshed; final Function(BuildContext) onRefreshed;
final Function(BuildContext, String) onUploadDocument; final Function(BuildContext, String) onUploadDocument;
final Function(BuildContext, DocumentEntity) onDeleteDocument; final Function(BuildContext, DocumentEntity) onDeleteDocument;

View File

@ -1,5 +1,6 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/ui/app/edit_scaffold.dart';
import 'package:invoiceninja_flutter/ui/app/form_card.dart'; import 'package:invoiceninja_flutter/ui/app/form_card.dart';
import 'package:invoiceninja_flutter/ui/app/forms/decorated_form_field.dart'; import 'package:invoiceninja_flutter/ui/app/forms/decorated_form_field.dart';
import 'package:invoiceninja_flutter/ui/group/edit/group_edit_vm.dart'; import 'package:invoiceninja_flutter/ui/group/edit/group_edit_vm.dart';
@ -79,44 +80,19 @@ class _GroupEditState extends State<GroupEdit> {
final localization = AppLocalization.of(context); final localization = AppLocalization.of(context);
final group = viewModel.group; final group = viewModel.group;
return WillPopScope( return EditScaffold(
onWillPop: () async { onCancelPressed: (context) => viewModel.onCancelPressed(context),
viewModel.onBackPressed(); title: group.isNew ? localization.newGroup : localization.editGroup,
return true; onSavePressed: (context) {
},
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: isMobile(context),
title: Text(viewModel.group.isNew
? localization.newGroup
: localization.editGroup),
actions: <Widget>[
if (!isMobile(context))
FlatButton(
child: Text(
localization.cancel,
style: TextStyle(color: Colors.white),
),
onPressed: () => viewModel.onCancelPressed(context),
),
ActionFlatButton(
tooltip: localization.save,
isVisible: !(group.isDeleted ?? false),
// TODO remove this
isDirty: group.isNew || group != viewModel.origGroup,
isSaving: viewModel.isSaving,
onPressed: () {
if (!_formKey.currentState.validate()) { if (!_formKey.currentState.validate()) {
return; return;
} }
viewModel.onSavePressed(context); viewModel.onSavePressed(context);
}, },
),
],
),
body: Form( body: Form(
key: _formKey, key: _formKey,
child: Builder(builder: (BuildContext context) { child: Builder(
builder: (BuildContext context) {
return ListView( return ListView(
children: <Widget>[ children: <Widget>[
FormCard( FormCard(
@ -129,7 +105,8 @@ class _GroupEditState extends State<GroupEdit> {
), ),
], ],
); );
})), },
),
), ),
); );
} }

View File

@ -46,7 +46,6 @@ class GroupEditVM {
@required this.origGroup, @required this.origGroup,
@required this.onSavePressed, @required this.onSavePressed,
@required this.onCancelPressed, @required this.onCancelPressed,
@required this.onBackPressed,
@required this.isLoading, @required this.isLoading,
}); });
@ -64,12 +63,6 @@ class GroupEditVM {
onChanged: (GroupEntity group) { onChanged: (GroupEntity group) {
store.dispatch(UpdateGroup(group)); store.dispatch(UpdateGroup(group));
}, },
onBackPressed: () {
if (state.uiState.currentRoute.contains(GroupSettingsScreen.route)) {
store.dispatch(UpdateCurrentRoute(
group.isNew ? GroupSettingsScreen.route : GroupViewScreen.route));
}
},
onCancelPressed: (BuildContext context) { onCancelPressed: (BuildContext context) {
createEntity(context: context, entity: GroupEntity(), force: true); createEntity(context: context, entity: GroupEntity(), force: true);
store.dispatch(UpdateCurrentRoute(state.uiState.previousRoute)); store.dispatch(UpdateCurrentRoute(state.uiState.previousRoute));
@ -104,7 +97,6 @@ class GroupEditVM {
final Function(GroupEntity) onChanged; final Function(GroupEntity) onChanged;
final Function(BuildContext) onSavePressed; final Function(BuildContext) onSavePressed;
final Function(BuildContext) onCancelPressed; final Function(BuildContext) onCancelPressed;
final Function onBackPressed;
final bool isLoading; final bool isLoading;
final bool isSaving; final bool isSaving;
final GroupEntity origGroup; final GroupEntity origGroup;

View File

@ -50,7 +50,6 @@ class EntityViewVM {
@required this.onUploadDocument, @required this.onUploadDocument,
@required this.onDeleteDocument, @required this.onDeleteDocument,
@required this.onEditPressed, @required this.onEditPressed,
@required this.onBackPressed,
@required this.onClientPressed, @required this.onClientPressed,
@required this.onPaymentsPressed, @required this.onPaymentsPressed,
@required this.onPaymentPressed, @required this.onPaymentPressed,
@ -70,7 +69,6 @@ class EntityViewVM {
final Function(BuildContext) onPaymentsPressed; final Function(BuildContext) onPaymentsPressed;
final Function(BuildContext, PaymentEntity, [bool]) onPaymentPressed; final Function(BuildContext, PaymentEntity, [bool]) onPaymentPressed;
final Function(BuildContext) onRefreshed; final Function(BuildContext) onRefreshed;
final Function onBackPressed;
final Function(BuildContext, String) onUploadDocument; final Function(BuildContext, String) onUploadDocument;
final Function(BuildContext, DocumentEntity) onDeleteDocument; final Function(BuildContext, DocumentEntity) onDeleteDocument;
final Function(BuildContext, DocumentEntity) onViewExpense; final Function(BuildContext, DocumentEntity) onViewExpense;
@ -90,7 +88,6 @@ class InvoiceViewVM extends EntityViewVM {
Function(BuildContext, PaymentEntity, [bool]) onPaymentPressed, Function(BuildContext, PaymentEntity, [bool]) onPaymentPressed,
Function(BuildContext) onPaymentsPressed, Function(BuildContext) onPaymentsPressed,
Function(BuildContext) onRefreshed, Function(BuildContext) onRefreshed,
Function onBackPressed,
Function(BuildContext, String) onUploadDocument, Function(BuildContext, String) onUploadDocument,
Function(BuildContext, DocumentEntity) onDeleteDocument, Function(BuildContext, DocumentEntity) onDeleteDocument,
Function(BuildContext, DocumentEntity) onViewExpense, Function(BuildContext, DocumentEntity) onViewExpense,
@ -107,7 +104,6 @@ class InvoiceViewVM extends EntityViewVM {
onPaymentPressed: onPaymentPressed, onPaymentPressed: onPaymentPressed,
onPaymentsPressed: onPaymentsPressed, onPaymentsPressed: onPaymentsPressed,
onRefreshed: onRefreshed, onRefreshed: onRefreshed,
onBackPressed: onBackPressed,
onUploadDocument: onUploadDocument, onUploadDocument: onUploadDocument,
onDeleteDocument: onDeleteDocument, onDeleteDocument: onDeleteDocument,
onViewExpense: onViewExpense); onViewExpense: onViewExpense);
@ -140,11 +136,6 @@ class InvoiceViewVM extends EntityViewVM {
context, AppLocalization.of(context).updatedInvoice)); context, AppLocalization.of(context).updatedInvoice));
}, },
onRefreshed: (context) => _handleRefresh(context), onRefreshed: (context) => _handleRefresh(context),
onBackPressed: () {
if (state.uiState.currentRoute.contains(InvoiceScreen.route)) {
store.dispatch(UpdateCurrentRoute(InvoiceScreen.route));
}
},
onClientPressed: (BuildContext context, [bool longPress = false]) { onClientPressed: (BuildContext context, [bool longPress = false]) {
if (longPress) { if (longPress) {
showEntityActionsDialog( showEntityActionsDialog(

View File

@ -40,7 +40,6 @@ class ProductViewVM {
@required this.company, @required this.company,
@required this.onEntityAction, @required this.onEntityAction,
@required this.onEditPressed, @required this.onEditPressed,
@required this.onBackPressed,
@required this.isSaving, @required this.isSaving,
@required this.isLoading, @required this.isLoading,
@required this.isDirty, @required this.isDirty,
@ -83,11 +82,6 @@ class ProductViewVM {
onRefreshed: (context, loadActivities) => onRefreshed: (context, loadActivities) =>
_handleRefresh(context, loadActivities), _handleRefresh(context, loadActivities),
*/ */
onBackPressed: () {
if (state.uiState.currentRoute.contains(ProductScreen.route)) {
store.dispatch(UpdateCurrentRoute(ProductScreen.route));
}
},
onEntityAction: (BuildContext context, EntityAction action) => onEntityAction: (BuildContext context, EntityAction action) =>
handleProductAction(context, [product], action), handleProductAction(context, [product], action),
); );
@ -98,7 +92,6 @@ class ProductViewVM {
final CompanyEntity company; final CompanyEntity company;
final Function(BuildContext, EntityAction) onEntityAction; final Function(BuildContext, EntityAction) onEntityAction;
final Function(BuildContext) onEditPressed; final Function(BuildContext) onEditPressed;
final Function onBackPressed;
final Function(BuildContext, bool) onRefreshed; final Function(BuildContext, bool) onRefreshed;
final bool isSaving; final bool isSaving;
final bool isLoading; final bool isLoading;

View File

@ -42,7 +42,6 @@ class ProjectViewVM {
@required this.onEntityAction, @required this.onEntityAction,
@required this.onTasksPressed, @required this.onTasksPressed,
@required this.onEditPressed, @required this.onEditPressed,
@required this.onBackPressed,
@required this.onAddTaskPressed, @required this.onAddTaskPressed,
@required this.onClientPressed, @required this.onClientPressed,
@required this.onRefreshed, @required this.onRefreshed,
@ -113,11 +112,6 @@ class ProjectViewVM {
..clientId = project.clientId), ..clientId = project.clientId),
force: true); force: true);
}, },
onBackPressed: () {
if (state.uiState.currentRoute.contains(ProjectScreen.route)) {
store.dispatch(UpdateCurrentRoute(ProjectScreen.route));
}
},
onEntityAction: (BuildContext context, EntityAction action) => onEntityAction: (BuildContext context, EntityAction action) =>
handleProjectAction(context, [project], action), handleProjectAction(context, [project], action),
); );
@ -130,7 +124,6 @@ class ProjectViewVM {
final Function(BuildContext, EntityAction) onEntityAction; final Function(BuildContext, EntityAction) onEntityAction;
final Function(BuildContext) onEditPressed; final Function(BuildContext) onEditPressed;
final Function(BuildContext, [bool]) onClientPressed; final Function(BuildContext, [bool]) onClientPressed;
final Function onBackPressed;
final Function(BuildContext) onAddTaskPressed; final Function(BuildContext) onAddTaskPressed;
final Function(BuildContext, {bool longPress}) onTasksPressed; final Function(BuildContext, {bool longPress}) onTasksPressed;
final Function(BuildContext) onRefreshed; final Function(BuildContext) onRefreshed;

View File

@ -54,7 +54,6 @@ class QuoteViewVM extends EntityViewVM {
Function(BuildContext) onPaymentsPressed, Function(BuildContext) onPaymentsPressed,
Function(BuildContext, PaymentEntity) onPaymentPressed, Function(BuildContext, PaymentEntity) onPaymentPressed,
Function(BuildContext) onRefreshed, Function(BuildContext) onRefreshed,
Function onBackPressed,
Function(BuildContext, String) onUploadDocument, Function(BuildContext, String) onUploadDocument,
Function(BuildContext, DocumentEntity) onDeleteDocument, Function(BuildContext, DocumentEntity) onDeleteDocument,
Function(BuildContext, DocumentEntity) onViewExpense, Function(BuildContext, DocumentEntity) onViewExpense,
@ -71,7 +70,6 @@ class QuoteViewVM extends EntityViewVM {
onPaymentsPressed: onPaymentsPressed, onPaymentsPressed: onPaymentsPressed,
onPaymentPressed: onPaymentPressed, onPaymentPressed: onPaymentPressed,
onRefreshed: onRefreshed, onRefreshed: onRefreshed,
onBackPressed: onBackPressed,
onUploadDocument: onUploadDocument, onUploadDocument: onUploadDocument,
onDeleteDocument: onDeleteDocument, onDeleteDocument: onDeleteDocument,
onViewExpense: onViewExpense, onViewExpense: onViewExpense,
@ -107,11 +105,6 @@ class QuoteViewVM extends EntityViewVM {
context, AppLocalization.of(context).updatedQuote)); context, AppLocalization.of(context).updatedQuote));
}, },
onRefreshed: (context) => _handleRefresh(context), onRefreshed: (context) => _handleRefresh(context),
onBackPressed: () {
if (state.uiState.currentRoute.contains(QuoteScreen.route)) {
store.dispatch(UpdateCurrentRoute(QuoteScreen.route));
}
},
onClientPressed: (BuildContext context, [bool longPress = false]) { onClientPressed: (BuildContext context, [bool longPress = false]) {
if (longPress) { if (longPress) {
showEntityActionsDialog( showEntityActionsDialog(

View File

@ -47,7 +47,6 @@ class TaskViewVM {
@required this.state, @required this.state,
@required this.onEntityAction, @required this.onEntityAction,
@required this.onEditPressed, @required this.onEditPressed,
@required this.onBackPressed,
@required this.onRefreshed, @required this.onRefreshed,
@required this.onClientPressed, @required this.onClientPressed,
@required this.onProjectPressed, @required this.onProjectPressed,
@ -157,11 +156,6 @@ class TaskViewVM {
*/ */
}, },
onRefreshed: (context) => _handleRefresh(context), onRefreshed: (context) => _handleRefresh(context),
onBackPressed: () {
if (state.uiState.currentRoute.contains(TaskScreen.route)) {
store.dispatch(UpdateCurrentRoute(TaskScreen.route));
}
},
onEntityAction: (BuildContext context, EntityAction action) => onEntityAction: (BuildContext context, EntityAction action) =>
handleTaskAction(context, [task], action), handleTaskAction(context, [task], action),
); );
@ -174,7 +168,6 @@ class TaskViewVM {
final CompanyEntity company; final CompanyEntity company;
final Function(BuildContext, EntityAction) onEntityAction; final Function(BuildContext, EntityAction) onEntityAction;
final Function(BuildContext, [TaskTime]) onEditPressed; final Function(BuildContext, [TaskTime]) onEditPressed;
final Function onBackPressed;
final Function(BuildContext) onFabPressed; final Function(BuildContext) onFabPressed;
final Function(BuildContext) onRefreshed; final Function(BuildContext) onRefreshed;
final Function(BuildContext, [bool]) onClientPressed; final Function(BuildContext, [bool]) onClientPressed;

View File

@ -46,7 +46,6 @@ class TaxRateEditVM {
@required this.origTaxRate, @required this.origTaxRate,
@required this.onSavePressed, @required this.onSavePressed,
@required this.onCancelPressed, @required this.onCancelPressed,
@required this.onBackPressed,
@required this.isLoading, @required this.isLoading,
}); });
@ -68,13 +67,6 @@ class TaxRateEditVM {
createEntity(context: context, entity: TaxRateEntity(), force: true); createEntity(context: context, entity: TaxRateEntity(), force: true);
store.dispatch(UpdateCurrentRoute(state.uiState.previousRoute)); store.dispatch(UpdateCurrentRoute(state.uiState.previousRoute));
}, },
onBackPressed: () {
if (state.uiState.currentRoute.contains(TaxRateSettingsScreen.route)) {
store.dispatch(UpdateCurrentRoute(taxRate.isNew
? TaxRateSettingsScreen.route
: TaxRateViewScreen.route));
}
},
onSavePressed: (BuildContext context) { onSavePressed: (BuildContext context) {
final Completer<TaxRateEntity> completer = final Completer<TaxRateEntity> completer =
new Completer<TaxRateEntity>(); new Completer<TaxRateEntity>();

View File

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/constants.dart';
import 'package:invoiceninja_flutter/data/models/company_model.dart'; import 'package:invoiceninja_flutter/data/models/company_model.dart';
import 'package:invoiceninja_flutter/data/models/entities.dart'; import 'package:invoiceninja_flutter/data/models/entities.dart';
import 'package:invoiceninja_flutter/ui/app/edit_scaffold.dart';
import 'package:invoiceninja_flutter/ui/app/form_card.dart'; import 'package:invoiceninja_flutter/ui/app/form_card.dart';
import 'package:invoiceninja_flutter/ui/app/forms/app_form.dart'; import 'package:invoiceninja_flutter/ui/app/forms/app_form.dart';
import 'package:invoiceninja_flutter/ui/app/forms/decorated_form_field.dart'; import 'package:invoiceninja_flutter/ui/app/forms/decorated_form_field.dart';
@ -108,40 +109,16 @@ class _UserEditState extends State<UserEdit> {
final user = viewModel.user; final user = viewModel.user;
final userCompany = user.userCompany; final userCompany = user.userCompany;
return WillPopScope( return EditScaffold(
onWillPop: () async { title:
viewModel.onBackPressed(); viewModel.user.isNew ? localization.newUser : localization.editUser,
return true; onCancelPressed: (context) => viewModel.onCancelPressed(context),
}, onSavePressed: (context) {
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: isMobile(context),
title: Text(viewModel.user.isNew
? localization.newUser
: localization.editUser),
actions: <Widget>[
if (!isMobile(context))
FlatButton(
child: Text(
localization.cancel,
style: TextStyle(color: Colors.white),
),
onPressed: () => viewModel.onCancelPressed(context),
),
ActionFlatButton(
tooltip: localization.save,
isVisible: user.isActive,
isDirty: user.isNew || user != viewModel.origUser,
isSaving: viewModel.isSaving,
onPressed: () {
if (!_formKey.currentState.validate()) { if (!_formKey.currentState.validate()) {
return; return;
} }
viewModel.onSavePressed(context); viewModel.onSavePressed(context);
}, },
),
],
),
body: AppForm( body: AppForm(
focusNode: _focusNode, focusNode: _focusNode,
formKey: _formKey, formKey: _formKey,
@ -219,8 +196,7 @@ class _UserEditState extends State<UserEdit> {
onChanged: (value) => onChanged: (value) =>
_togglePermission(kPermissionCreateAll), _togglePermission(kPermissionCreateAll),
), ),
onTap: () => onTap: () => _togglePermission(kPermissionCreateAll)),
_togglePermission(kPermissionCreateAll)),
DataCell( DataCell(
_PermissionCheckbox( _PermissionCheckbox(
userCompany: userCompany, userCompany: userCompany,
@ -245,8 +221,7 @@ class _UserEditState extends State<UserEdit> {
EntityType.payment, EntityType.payment,
EntityType.quote, EntityType.quote,
].map((EntityType type) { ].map((EntityType type) {
final createPermission = final createPermission = 'create_' + toSnakeCase('$type');
'create_' + toSnakeCase('$type');
final editPermission = 'edit_' + toSnakeCase('$type'); final editPermission = 'edit_' + toSnakeCase('$type');
final viewPermission = 'view_' + toSnakeCase('$type'); final viewPermission = 'view_' + toSnakeCase('$type');
return DataRow(cells: [ return DataRow(cells: [
@ -298,7 +273,6 @@ class _UserEditState extends State<UserEdit> {
) )
], ],
), ),
),
); );
} }
} }

View File

@ -47,7 +47,6 @@ class UserEditVM {
@required this.origUser, @required this.origUser,
@required this.onSavePressed, @required this.onSavePressed,
@required this.onCancelPressed, @required this.onCancelPressed,
@required this.onBackPressed,
@required this.isLoading, @required this.isLoading,
}); });
@ -66,12 +65,6 @@ class UserEditVM {
onUserChanged: (UserEntity user) { onUserChanged: (UserEntity user) {
store.dispatch(UpdateUser(user)); store.dispatch(UpdateUser(user));
}, },
onBackPressed: () {
if (state.uiState.currentRoute.contains(UserScreen.route)) {
store.dispatch(UpdateCurrentRoute(
user.isNew ? UserScreen.route : UserViewScreen.route));
}
},
onCancelPressed: (BuildContext context) { onCancelPressed: (BuildContext context) {
createEntity(context: context, entity: UserEntity(), force: true); createEntity(context: context, entity: UserEntity(), force: true);
store.dispatch(UpdateCurrentRoute(state.uiState.previousRoute)); store.dispatch(UpdateCurrentRoute(state.uiState.previousRoute));
@ -107,7 +100,6 @@ class UserEditVM {
final Function(UserEntity) onUserChanged; final Function(UserEntity) onUserChanged;
final Function(BuildContext) onSavePressed; final Function(BuildContext) onSavePressed;
final Function(BuildContext) onCancelPressed; final Function(BuildContext) onCancelPressed;
final Function onBackPressed;
final bool isLoading; final bool isLoading;
final bool isSaving; final bool isSaving;
final UserEntity origUser; final UserEntity origUser;

View File

@ -43,7 +43,6 @@ class VendorViewVM {
@required this.onEntityAction, @required this.onEntityAction,
@required this.onEntityPressed, @required this.onEntityPressed,
@required this.onEditPressed, @required this.onEditPressed,
@required this.onBackPressed,
@required this.onRefreshed, @required this.onRefreshed,
@required this.isSaving, @required this.isSaving,
@required this.isLoading, @required this.isLoading,
@ -77,11 +76,6 @@ class VendorViewVM {
context, AppLocalization.of(context).updatedVendor)); context, AppLocalization.of(context).updatedVendor));
}, },
onRefreshed: (context) => _handleRefresh(context), onRefreshed: (context) => _handleRefresh(context),
onBackPressed: () {
if (state.uiState.currentRoute.contains(VendorScreen.route)) {
store.dispatch(UpdateCurrentRoute(VendorScreen.route));
}
},
onEntityPressed: (BuildContext context, EntityType entityType, onEntityPressed: (BuildContext context, EntityType entityType,
[longPress = false]) { [longPress = false]) {
switch (entityType) { switch (entityType) {
@ -115,7 +109,6 @@ class VendorViewVM {
final Function(BuildContext, EntityAction) onEntityAction; final Function(BuildContext, EntityAction) onEntityAction;
final Function(BuildContext) onEditPressed; final Function(BuildContext) onEditPressed;
final Function(BuildContext, EntityType, [bool]) onEntityPressed; final Function(BuildContext, EntityType, [bool]) onEntityPressed;
final Function onBackPressed;
final Function(BuildContext) onRefreshed; final Function(BuildContext) onRefreshed;
final Function(BuildContext) onAddExpensePressed; final Function(BuildContext) onAddExpensePressed;
final bool isSaving; final bool isSaving;

View File

@ -41,7 +41,6 @@ class StubViewVM {
@required this.company, @required this.company,
@required this.onEntityAction, @required this.onEntityAction,
@required this.onEditPressed, @required this.onEditPressed,
@required this.onBackPressed,
@required this.onRefreshed, @required this.onRefreshed,
@required this.isSaving, @required this.isSaving,
@required this.isLoading, @required this.isLoading,
@ -75,11 +74,6 @@ class StubViewVM {
context, AppLocalization.of(context).updatedStub)); context, AppLocalization.of(context).updatedStub));
}, },
onRefreshed: (context) => _handleRefresh(context), onRefreshed: (context) => _handleRefresh(context),
onBackPressed: () {
if (state.uiState.currentRoute.contains(StubScreen.route)) {
store.dispatch(UpdateCurrentRoute(StubScreen.route));
}
},
onEntityAction: (BuildContext context, EntityAction action) => onEntityAction: (BuildContext context, EntityAction action) =>
handleStubAction(context, stub, action), handleStubAction(context, stub, action),
); );
@ -90,7 +84,6 @@ class StubViewVM {
final CompanyEntity company; final CompanyEntity company;
final Function(BuildContext, EntityAction) onEntityAction; final Function(BuildContext, EntityAction) onEntityAction;
final Function(BuildContext) onEditPressed; final Function(BuildContext) onEditPressed;
final Function onBackPressed;
final Function(BuildContext) onRefreshed; final Function(BuildContext) onRefreshed;
final bool isSaving; final bool isSaving;
final bool isLoading; final bool isLoading;