Refactor entity actions
This commit is contained in:
parent
ea64a3f1d1
commit
c3237fd659
|
|
@ -141,11 +141,10 @@ abstract class BaseEntity implements SelectableEntity {
|
|||
|
||||
bool get isArchived => archivedAt != null && archivedAt > 0 && !isDeleted;
|
||||
|
||||
List<EntityAction> getActions({UserEntity user, bool includeEdit = false}) {
|
||||
List<EntityAction> getActions(
|
||||
{UserEntity user, ClientEntity client, bool includeEdit = false}) {
|
||||
final actions = <EntityAction>[];
|
||||
|
||||
|
||||
|
||||
if (user.canEditEntity(this) && (isArchived || isDeleted)) {
|
||||
actions.add(EntityAction.restore);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,13 @@ import 'package:flutter/widgets.dart';
|
|||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:built_collection/built_collection.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/redux/product/product_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
|
||||
class ViewProductList implements PersistUI {
|
||||
ViewProductList(this.context);
|
||||
|
|
@ -171,3 +178,44 @@ class FilterProductDropdown {
|
|||
|
||||
final String filter;
|
||||
}
|
||||
|
||||
|
||||
void handleProductAction(
|
||||
BuildContext context, ProductEntity product, EntityAction action) {
|
||||
final store = StoreProvider.of<AppState>(context);
|
||||
final state = store.state;
|
||||
final localization = AppLocalization.of(context);
|
||||
|
||||
switch (action) {
|
||||
case EntityAction.newInvoice:
|
||||
final item =
|
||||
convertProductToInvoiceItem(context: context, product: product);
|
||||
store.dispatch(EditInvoice(
|
||||
context: context,
|
||||
invoice: InvoiceEntity(company: state.selectedCompany)
|
||||
.rebuild((b) => b..invoiceItems.add(item))));
|
||||
break;
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditProduct(context: context, product: product));
|
||||
break;
|
||||
case EntityAction.clone:
|
||||
store.dispatch(
|
||||
EditProduct(context: context, product: product.clone));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreProductRequest(
|
||||
snackBarCompleter(context, localization.restoredProduct),
|
||||
product.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveProductRequest(
|
||||
snackBarCompleter(context, localization.archivedProduct),
|
||||
product.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteProductRequest(
|
||||
snackBarCompleter(context, localization.deletedProduct),
|
||||
product.id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ import 'package:invoiceninja_flutter/data/models/models.dart';
|
|||
import 'package:invoiceninja_flutter/utils/icons.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
void showEntityActionsDialog(
|
||||
{@required BuildContext context,
|
||||
@required BaseEntity entity,
|
||||
@required UserEntity user,
|
||||
@required Function onEntityAction}) async {
|
||||
void showEntityActionsDialog({
|
||||
@required BuildContext context,
|
||||
@required BaseEntity entity,
|
||||
@required UserEntity user,
|
||||
@required Function(BuildContext, BaseEntity, EntityAction) onEntityAction,
|
||||
ClientEntity client,
|
||||
}) async {
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -18,7 +20,7 @@ void showEntityActionsDialog(
|
|||
builder: (BuildContext dialogContext) {
|
||||
final actions = <Widget>[];
|
||||
actions.addAll(entity
|
||||
.getActions(user: user, includeEdit: true)
|
||||
.getActions(user: user, includeEdit: true, client: client)
|
||||
.map((entityAction) {
|
||||
if (entityAction == null) {
|
||||
return Divider();
|
||||
|
|
@ -43,7 +45,7 @@ class EntityActionListTile extends StatelessWidget {
|
|||
final BaseEntity entity;
|
||||
final EntityAction entityAction;
|
||||
final BuildContext mainContext;
|
||||
final Function(BuildContext, ClientEntity, EntityAction) onEntityAction;
|
||||
final Function(BuildContext, BaseEntity, EntityAction) onEntityAction;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class ClientListVM {
|
|||
},
|
||||
onRefreshed: (context) => _handleRefresh(context),
|
||||
onEntityAction:
|
||||
(BuildContext context, ClientEntity client, EntityAction action) =>
|
||||
(BuildContext context, BaseEntity client, EntityAction action) =>
|
||||
handleClientAction(context, client, action),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ class InvoiceViewVM extends EntityViewVM {
|
|||
user: state.selectedCompany.user,
|
||||
context: context,
|
||||
entity: client,
|
||||
onEntityAction: (BuildContext context, ClientEntity client,
|
||||
onEntityAction: (BuildContext context, BaseEntity client,
|
||||
EntityAction action) =>
|
||||
handleClientAction(context, client, action));
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ class _ProductEditState extends State<ProductEdit> {
|
|||
: ActionMenuButton(
|
||||
user: viewModel.company.user,
|
||||
entity: viewModel.product,
|
||||
onSelected: viewModel.onActionSelected,
|
||||
onSelected: viewModel.onEntityAction,
|
||||
entityActions: product.getActions(user: user),
|
||||
)
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/product/product_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/product/product_screen.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
|
|
@ -41,7 +39,7 @@ class ProductEditVM {
|
|||
@required this.onChanged,
|
||||
@required this.onSavePressed,
|
||||
@required this.onBackPressed,
|
||||
@required this.onActionSelected,
|
||||
@required this.onEntityAction,
|
||||
@required this.isSaving,
|
||||
@required this.isDirty,
|
||||
});
|
||||
|
|
@ -51,59 +49,31 @@ class ProductEditVM {
|
|||
final product = state.productUIState.editing;
|
||||
|
||||
return ProductEditVM(
|
||||
company: state.selectedCompany,
|
||||
isSaving: state.isSaving,
|
||||
isDirty: product.isNew,
|
||||
product: product,
|
||||
origProduct: state.productState.map[product.id],
|
||||
onChanged: (ProductEntity product) {
|
||||
store.dispatch(UpdateProduct(product));
|
||||
},
|
||||
onBackPressed: () {
|
||||
if (state.uiState.currentRoute.contains(ProductScreen.route)) {
|
||||
store.dispatch(UpdateCurrentRoute(ProductScreen.route));
|
||||
}
|
||||
},
|
||||
onSavePressed: (BuildContext context) {
|
||||
store.dispatch(SaveProductRequest(
|
||||
completer: snackBarCompleter(
|
||||
context,
|
||||
product.isNew
|
||||
? AppLocalization.of(context).createdProduct
|
||||
: AppLocalization.of(context).updatedProduct),
|
||||
product: product));
|
||||
},
|
||||
onActionSelected: (BuildContext context, EntityAction action) {
|
||||
final localization = AppLocalization.of(context);
|
||||
switch (action) {
|
||||
case EntityAction.newInvoice:
|
||||
final item = convertProductToInvoiceItem(
|
||||
context: context, product: product);
|
||||
store.dispatch(EditInvoice(
|
||||
context: context,
|
||||
invoice: InvoiceEntity(company: state.selectedCompany)
|
||||
.rebuild((b) => b..invoiceItems.add(item))));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveProductRequest(
|
||||
popCompleter(context, localization.archivedProduct),
|
||||
product.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteProductRequest(
|
||||
popCompleter(context, localization.deletedProduct),
|
||||
product.id));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreProductRequest(
|
||||
snackBarCompleter(context, localization.restoredProduct),
|
||||
product.id));
|
||||
break;
|
||||
case EntityAction.clone:
|
||||
store.dispatch(UpdateProduct(product.clone));
|
||||
break;
|
||||
}
|
||||
});
|
||||
company: state.selectedCompany,
|
||||
isSaving: state.isSaving,
|
||||
isDirty: product.isNew,
|
||||
product: product,
|
||||
origProduct: state.productState.map[product.id],
|
||||
onChanged: (ProductEntity product) {
|
||||
store.dispatch(UpdateProduct(product));
|
||||
},
|
||||
onBackPressed: () {
|
||||
if (state.uiState.currentRoute.contains(ProductScreen.route)) {
|
||||
store.dispatch(UpdateCurrentRoute(ProductScreen.route));
|
||||
}
|
||||
},
|
||||
onSavePressed: (BuildContext context) {
|
||||
store.dispatch(SaveProductRequest(
|
||||
completer: snackBarCompleter(
|
||||
context,
|
||||
product.isNew
|
||||
? AppLocalization.of(context).createdProduct
|
||||
: AppLocalization.of(context).updatedProduct),
|
||||
product: product));
|
||||
},
|
||||
onEntityAction: (BuildContext context, EntityAction action) =>
|
||||
handleProductAction(context, product, action),
|
||||
);
|
||||
}
|
||||
|
||||
final CompanyEntity company;
|
||||
|
|
@ -111,7 +81,7 @@ class ProductEditVM {
|
|||
final ProductEntity origProduct;
|
||||
final Function(ProductEntity) onChanged;
|
||||
final Function(BuildContext) onSavePressed;
|
||||
final Function(BuildContext, EntityAction) onActionSelected;
|
||||
final Function(BuildContext, EntityAction) onEntityAction;
|
||||
final Function onBackPressed;
|
||||
final bool isSaving;
|
||||
final bool isDirty;
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@ import 'package:flutter/material.dart';
|
|||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/loading_indicator.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart';
|
||||
import 'package:invoiceninja_flutter/ui/product/product_list_item.dart';
|
||||
import 'package:invoiceninja_flutter/ui/product/product_list_vm.dart';
|
||||
import 'package:invoiceninja_flutter/utils/icons.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
class ProductList extends StatelessWidget {
|
||||
|
|
@ -38,40 +37,6 @@ class ProductList extends StatelessWidget {
|
|||
return _buildListView(context);
|
||||
}
|
||||
|
||||
void _showMenu(BuildContext context, ProductEntity product) async {
|
||||
if (product == null) {
|
||||
return;
|
||||
}
|
||||
final user = viewModel.user;
|
||||
final message = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) => SimpleDialog(
|
||||
children: product
|
||||
.getActions(user: user, includeEdit: true)
|
||||
.map((entityAction) {
|
||||
if (entityAction == null) {
|
||||
return Divider();
|
||||
} else {
|
||||
return ListTile(
|
||||
leading: Icon(getEntityActionIcon(entityAction)),
|
||||
title: Text(AppLocalization.of(context)
|
||||
.lookup(entityAction.toString())),
|
||||
onTap: () {
|
||||
Navigator.of(dialogContext).pop();
|
||||
viewModel.onEntityAction(context, product, entityAction);
|
||||
},
|
||||
);
|
||||
}
|
||||
}).toList()));
|
||||
|
||||
if (message != null) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: SnackBarRow(
|
||||
message: message,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildListView(BuildContext context) {
|
||||
return RefreshIndicator(
|
||||
onRefresh: () => viewModel.onRefreshed(context),
|
||||
|
|
@ -87,13 +52,21 @@ class ProductList extends StatelessWidget {
|
|||
product: product,
|
||||
onEntityAction: (EntityAction action) {
|
||||
if (action == EntityAction.more) {
|
||||
_showMenu(context, product);
|
||||
showEntityActionsDialog(
|
||||
entity: product,
|
||||
context: context,
|
||||
user: viewModel.user,
|
||||
onEntityAction: viewModel.onEntityAction);
|
||||
} else {
|
||||
viewModel.onEntityAction(context, product, action);
|
||||
}
|
||||
},
|
||||
onTap: () => viewModel.onProductTap(context, product),
|
||||
onLongPress: () => _showMenu(context, product),
|
||||
onLongPress: () => showEntityActionsDialog(
|
||||
entity: product,
|
||||
context: context,
|
||||
user: viewModel.user,
|
||||
onEntityAction: viewModel.onEntityAction),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'package:built_collection/built_collection.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/product/product_selectors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
|
@ -67,41 +66,9 @@ class ProductListVM {
|
|||
onProductTap: (context, product) {
|
||||
store.dispatch(EditProduct(product: product, context: context));
|
||||
},
|
||||
onEntityAction: (context, product, action) {
|
||||
final localization = AppLocalization.of(context);
|
||||
switch (action) {
|
||||
case EntityAction.newInvoice:
|
||||
final item =
|
||||
convertProductToInvoiceItem(context: context, product: product);
|
||||
store.dispatch(EditInvoice(
|
||||
context: context,
|
||||
invoice: InvoiceEntity(company: state.selectedCompany)
|
||||
.rebuild((b) => b..invoiceItems.add(item))));
|
||||
break;
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditProduct(context: context, product: product));
|
||||
break;
|
||||
case EntityAction.clone:
|
||||
store.dispatch(
|
||||
EditProduct(context: context, product: product.clone));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreProductRequest(
|
||||
snackBarCompleter(context, localization.restoredProduct),
|
||||
product.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveProductRequest(
|
||||
snackBarCompleter(context, localization.archivedProduct),
|
||||
product.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteProductRequest(
|
||||
snackBarCompleter(context, localization.deletedProduct),
|
||||
product.id));
|
||||
break;
|
||||
}
|
||||
},
|
||||
onEntityAction:
|
||||
(BuildContext context, BaseEntity product, EntityAction action) =>
|
||||
handleProductAction(context, product, action),
|
||||
onRefreshed: (context) => _handleRefresh(context),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue