Refactor entity actions

This commit is contained in:
Hillel Coren 2019-06-16 11:26:49 +03:00
parent ea64a3f1d1
commit c3237fd659
9 changed files with 103 additions and 144 deletions

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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,
void showEntityActionsDialog({
@required BuildContext context,
@required BaseEntity entity,
@required UserEntity user,
@required Function onEntityAction}) async {
@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) {

View File

@ -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),
);
}

View File

@ -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 {

View File

@ -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),
)
],

View File

@ -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,
});
@ -73,37 +71,9 @@ class ProductEditVM {
: 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;
}
});
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;

View File

@ -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),
);
}),
);

View File

@ -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),
);
}