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;
|
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>[];
|
final actions = <EntityAction>[];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (user.canEditEntity(this) && (isArchived || isDeleted)) {
|
if (user.canEditEntity(this) && (isArchived || isDeleted)) {
|
||||||
actions.add(EntityAction.restore);
|
actions.add(EntityAction.restore);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,13 @@ import 'package:flutter/widgets.dart';
|
||||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||||
import 'package:built_collection/built_collection.dart';
|
import 'package:built_collection/built_collection.dart';
|
||||||
import 'package:invoiceninja_flutter/redux/app/app_actions.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 {
|
class ViewProductList implements PersistUI {
|
||||||
ViewProductList(this.context);
|
ViewProductList(this.context);
|
||||||
|
|
@ -171,3 +178,44 @@ class FilterProductDropdown {
|
||||||
|
|
||||||
final String filter;
|
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/icons.dart';
|
||||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||||
|
|
||||||
void showEntityActionsDialog(
|
void showEntityActionsDialog({
|
||||||
{@required BuildContext context,
|
@required BuildContext context,
|
||||||
@required BaseEntity entity,
|
@required BaseEntity entity,
|
||||||
@required UserEntity user,
|
@required UserEntity user,
|
||||||
@required Function onEntityAction}) async {
|
@required Function(BuildContext, BaseEntity, EntityAction) onEntityAction,
|
||||||
|
ClientEntity client,
|
||||||
|
}) async {
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -18,7 +20,7 @@ void showEntityActionsDialog(
|
||||||
builder: (BuildContext dialogContext) {
|
builder: (BuildContext dialogContext) {
|
||||||
final actions = <Widget>[];
|
final actions = <Widget>[];
|
||||||
actions.addAll(entity
|
actions.addAll(entity
|
||||||
.getActions(user: user, includeEdit: true)
|
.getActions(user: user, includeEdit: true, client: client)
|
||||||
.map((entityAction) {
|
.map((entityAction) {
|
||||||
if (entityAction == null) {
|
if (entityAction == null) {
|
||||||
return Divider();
|
return Divider();
|
||||||
|
|
@ -43,7 +45,7 @@ class EntityActionListTile extends StatelessWidget {
|
||||||
final BaseEntity entity;
|
final BaseEntity entity;
|
||||||
final EntityAction entityAction;
|
final EntityAction entityAction;
|
||||||
final BuildContext mainContext;
|
final BuildContext mainContext;
|
||||||
final Function(BuildContext, ClientEntity, EntityAction) onEntityAction;
|
final Function(BuildContext, BaseEntity, EntityAction) onEntityAction;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ class ClientListVM {
|
||||||
},
|
},
|
||||||
onRefreshed: (context) => _handleRefresh(context),
|
onRefreshed: (context) => _handleRefresh(context),
|
||||||
onEntityAction:
|
onEntityAction:
|
||||||
(BuildContext context, ClientEntity client, EntityAction action) =>
|
(BuildContext context, BaseEntity client, EntityAction action) =>
|
||||||
handleClientAction(context, client, action),
|
handleClientAction(context, client, action),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,7 @@ class InvoiceViewVM extends EntityViewVM {
|
||||||
user: state.selectedCompany.user,
|
user: state.selectedCompany.user,
|
||||||
context: context,
|
context: context,
|
||||||
entity: client,
|
entity: client,
|
||||||
onEntityAction: (BuildContext context, ClientEntity client,
|
onEntityAction: (BuildContext context, BaseEntity client,
|
||||||
EntityAction action) =>
|
EntityAction action) =>
|
||||||
handleClientAction(context, client, action));
|
handleClientAction(context, client, action));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ class _ProductEditState extends State<ProductEdit> {
|
||||||
: ActionMenuButton(
|
: ActionMenuButton(
|
||||||
user: viewModel.company.user,
|
user: viewModel.company.user,
|
||||||
entity: viewModel.product,
|
entity: viewModel.product,
|
||||||
onSelected: viewModel.onActionSelected,
|
onSelected: viewModel.onEntityAction,
|
||||||
entityActions: product.getActions(user: user),
|
entityActions: product.getActions(user: user),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
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/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/redux/ui/ui_actions.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/product/product_screen.dart';
|
import 'package:invoiceninja_flutter/ui/product/product_screen.dart';
|
||||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||||
|
|
@ -41,7 +39,7 @@ class ProductEditVM {
|
||||||
@required this.onChanged,
|
@required this.onChanged,
|
||||||
@required this.onSavePressed,
|
@required this.onSavePressed,
|
||||||
@required this.onBackPressed,
|
@required this.onBackPressed,
|
||||||
@required this.onActionSelected,
|
@required this.onEntityAction,
|
||||||
@required this.isSaving,
|
@required this.isSaving,
|
||||||
@required this.isDirty,
|
@required this.isDirty,
|
||||||
});
|
});
|
||||||
|
|
@ -51,59 +49,31 @@ class ProductEditVM {
|
||||||
final product = state.productUIState.editing;
|
final product = state.productUIState.editing;
|
||||||
|
|
||||||
return ProductEditVM(
|
return ProductEditVM(
|
||||||
company: state.selectedCompany,
|
company: state.selectedCompany,
|
||||||
isSaving: state.isSaving,
|
isSaving: state.isSaving,
|
||||||
isDirty: product.isNew,
|
isDirty: product.isNew,
|
||||||
product: product,
|
product: product,
|
||||||
origProduct: state.productState.map[product.id],
|
origProduct: state.productState.map[product.id],
|
||||||
onChanged: (ProductEntity product) {
|
onChanged: (ProductEntity product) {
|
||||||
store.dispatch(UpdateProduct(product));
|
store.dispatch(UpdateProduct(product));
|
||||||
},
|
},
|
||||||
onBackPressed: () {
|
onBackPressed: () {
|
||||||
if (state.uiState.currentRoute.contains(ProductScreen.route)) {
|
if (state.uiState.currentRoute.contains(ProductScreen.route)) {
|
||||||
store.dispatch(UpdateCurrentRoute(ProductScreen.route));
|
store.dispatch(UpdateCurrentRoute(ProductScreen.route));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSavePressed: (BuildContext context) {
|
onSavePressed: (BuildContext context) {
|
||||||
store.dispatch(SaveProductRequest(
|
store.dispatch(SaveProductRequest(
|
||||||
completer: snackBarCompleter(
|
completer: snackBarCompleter(
|
||||||
context,
|
context,
|
||||||
product.isNew
|
product.isNew
|
||||||
? AppLocalization.of(context).createdProduct
|
? AppLocalization.of(context).createdProduct
|
||||||
: AppLocalization.of(context).updatedProduct),
|
: AppLocalization.of(context).updatedProduct),
|
||||||
product: product));
|
product: product));
|
||||||
},
|
},
|
||||||
onActionSelected: (BuildContext context, EntityAction action) {
|
onEntityAction: (BuildContext context, EntityAction action) =>
|
||||||
final localization = AppLocalization.of(context);
|
handleProductAction(context, product, action),
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final CompanyEntity company;
|
final CompanyEntity company;
|
||||||
|
|
@ -111,7 +81,7 @@ class ProductEditVM {
|
||||||
final ProductEntity origProduct;
|
final ProductEntity origProduct;
|
||||||
final Function(ProductEntity) onChanged;
|
final Function(ProductEntity) onChanged;
|
||||||
final Function(BuildContext) onSavePressed;
|
final Function(BuildContext) onSavePressed;
|
||||||
final Function(BuildContext, EntityAction) onActionSelected;
|
final Function(BuildContext, EntityAction) onEntityAction;
|
||||||
final Function onBackPressed;
|
final Function onBackPressed;
|
||||||
final bool isSaving;
|
final bool isSaving;
|
||||||
final bool isDirty;
|
final bool isDirty;
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,9 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:invoiceninja_flutter/data/models/models.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/lists/list_divider.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/loading_indicator.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_item.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/product/product_list_vm.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';
|
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||||
|
|
||||||
class ProductList extends StatelessWidget {
|
class ProductList extends StatelessWidget {
|
||||||
|
|
@ -38,40 +37,6 @@ class ProductList extends StatelessWidget {
|
||||||
return _buildListView(context);
|
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) {
|
Widget _buildListView(BuildContext context) {
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () => viewModel.onRefreshed(context),
|
onRefresh: () => viewModel.onRefreshed(context),
|
||||||
|
|
@ -87,13 +52,21 @@ class ProductList extends StatelessWidget {
|
||||||
product: product,
|
product: product,
|
||||||
onEntityAction: (EntityAction action) {
|
onEntityAction: (EntityAction action) {
|
||||||
if (action == EntityAction.more) {
|
if (action == EntityAction.more) {
|
||||||
_showMenu(context, product);
|
showEntityActionsDialog(
|
||||||
|
entity: product,
|
||||||
|
context: context,
|
||||||
|
user: viewModel.user,
|
||||||
|
onEntityAction: viewModel.onEntityAction);
|
||||||
} else {
|
} else {
|
||||||
viewModel.onEntityAction(context, product, action);
|
viewModel.onEntityAction(context, product, action);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onTap: () => viewModel.onProductTap(context, product),
|
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 'dart:async';
|
||||||
import 'package:built_collection/built_collection.dart';
|
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:invoiceninja_flutter/redux/product/product_selectors.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
@ -67,41 +66,9 @@ class ProductListVM {
|
||||||
onProductTap: (context, product) {
|
onProductTap: (context, product) {
|
||||||
store.dispatch(EditProduct(product: product, context: context));
|
store.dispatch(EditProduct(product: product, context: context));
|
||||||
},
|
},
|
||||||
onEntityAction: (context, product, action) {
|
onEntityAction:
|
||||||
final localization = AppLocalization.of(context);
|
(BuildContext context, BaseEntity product, EntityAction action) =>
|
||||||
switch (action) {
|
handleProductAction(context, product, 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;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onRefreshed: (context) => _handleRefresh(context),
|
onRefreshed: (context) => _handleRefresh(context),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue