Refactor entity actions
This commit is contained in:
parent
c3237fd659
commit
ccb07c4373
|
|
@ -106,8 +106,6 @@ class ExpenseStatusColors {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
const List<int> kPaymentTerms = [0, -1, 7, 10, 14, 15, 30, 60, 90];
|
||||
|
||||
const String kDesignCustom1 = 'Custom 1';
|
||||
|
|
|
|||
|
|
@ -68,7 +68,11 @@ abstract class ExpenseEntity extends Object
|
|||
with BaseEntity, SelectableEntity, BelongsToClient
|
||||
implements Built<ExpenseEntity, ExpenseEntityBuilder> {
|
||||
factory ExpenseEntity(
|
||||
{int id, CompanyEntity company, UIState uiState, VendorEntity vendor, ClientEntity client}) {
|
||||
{int id,
|
||||
CompanyEntity company,
|
||||
UIState uiState,
|
||||
VendorEntity vendor,
|
||||
ClientEntity client}) {
|
||||
return _$ExpenseEntity._(
|
||||
id: id ?? --ExpenseEntity.counter,
|
||||
privateNotes: '',
|
||||
|
|
|
|||
|
|
@ -120,8 +120,6 @@ abstract class CurrencyEntity extends Object
|
|||
@override
|
||||
double get listDisplayAmount => null;
|
||||
|
||||
|
||||
|
||||
static Serializer<CurrencyEntity> get serializer =>
|
||||
_$currencyEntitySerializer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -311,7 +311,8 @@ Middleware<AppState> _createAccountLoaded() {
|
|||
};
|
||||
}
|
||||
|
||||
Middleware<AppState> _createPersistStatic(PersistenceRepository staticRepository) {
|
||||
Middleware<AppState> _createPersistStatic(
|
||||
PersistenceRepository staticRepository) {
|
||||
return (Store<AppState> store, dynamic action, NextDispatcher next) {
|
||||
// first process the action so the data is in the state
|
||||
next(action);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ var memoizedDropdownExpenseCategoriesList = memo2(
|
|||
List<int> dropdownExpenseCategoriesSelector(
|
||||
BuiltMap<int, ExpenseCategoryEntity> categoryMap,
|
||||
BuiltList<ExpenseCategoryEntity> categoryList) {
|
||||
|
||||
final list = categoryList
|
||||
//.where((category) => category.isActive)
|
||||
.map((category) => category.id)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,13 @@ import 'package:flutter/widgets.dart';
|
|||
import 'package:built_collection/built_collection.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.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/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/redux/expense/expense_selectors.dart';
|
||||
|
||||
class ViewExpenseList implements PersistUI {
|
||||
ViewExpenseList(this.context);
|
||||
|
|
@ -227,3 +234,47 @@ class FilterExpensesByEntity implements PersistUI {
|
|||
final int entityId;
|
||||
final EntityType entityType;
|
||||
}
|
||||
|
||||
void handleExpenseAction(
|
||||
BuildContext context, ExpenseEntity expense, EntityAction action) {
|
||||
final store = StoreProvider.of<AppState>(context);
|
||||
final state = store.state;
|
||||
final CompanyEntity company = state.selectedCompany;
|
||||
final localization = AppLocalization.of(context);
|
||||
|
||||
switch (action) {
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditExpense(context: context, expense: expense));
|
||||
break;
|
||||
case EntityAction.clone:
|
||||
store.dispatch(EditExpense(context: context, expense: expense.clone));
|
||||
break;
|
||||
case EntityAction.newInvoice:
|
||||
final item = convertExpenseToInvoiceItem(expense: expense);
|
||||
store.dispatch(EditInvoice(
|
||||
invoice: InvoiceEntity(company: company).rebuild((b) => b
|
||||
..hasExpenses = true
|
||||
..clientId = expense.clientId
|
||||
..invoiceItems.add(item)),
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.viewInvoice:
|
||||
store.dispatch(
|
||||
ViewInvoice(invoiceId: expense.invoiceId, context: context));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreExpenseRequest(
|
||||
snackBarCompleter(context, localization.restoredExpense),
|
||||
expense.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveExpenseRequest(
|
||||
snackBarCompleter(context, localization.archivedExpense),
|
||||
expense.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteExpenseRequest(
|
||||
snackBarCompleter(context, localization.deletedExpense), expense.id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,13 +89,13 @@ ListUIState _filterExpensesByState(
|
|||
ListUIState _filterExpensesByStatus(
|
||||
ListUIState expenseListState, FilterExpensesByStatus action) {
|
||||
if (expenseListState.statusFilters.contains(action.status)) {
|
||||
return expenseListState.rebuild((b) => b..statusFilters.remove(action.status));
|
||||
return expenseListState
|
||||
.rebuild((b) => b..statusFilters.remove(action.status));
|
||||
} else {
|
||||
return expenseListState.rebuild((b) => b..statusFilters.add(action.status));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ListUIState _filterExpenses(
|
||||
ListUIState expenseListState, FilterExpenses action) {
|
||||
return expenseListState.rebuild((b) => b..filter = action.filter);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,17 @@
|
|||
import 'dart:async';
|
||||
|
||||
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/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/redux/payment/payment_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/quote/quote_actions.dart';
|
||||
import 'package:invoiceninja_flutter/utils/pdf.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class ViewInvoiceList implements PersistUI {
|
||||
ViewInvoiceList(this.context);
|
||||
|
|
@ -302,3 +310,63 @@ class FilterInvoicesByCustom2 implements PersistUI {
|
|||
|
||||
final String value;
|
||||
}
|
||||
|
||||
void handleInvoiceAction(
|
||||
BuildContext context, InvoiceEntity invoice, EntityAction action) async {
|
||||
final store = StoreProvider.of<AppState>(context);
|
||||
final state = store.state;
|
||||
final CompanyEntity company = state.selectedCompany;
|
||||
final localization = AppLocalization.of(context);
|
||||
|
||||
switch (action) {
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditInvoice(context: context, invoice: invoice));
|
||||
break;
|
||||
case EntityAction.pdf:
|
||||
viewPdf(invoice, context);
|
||||
break;
|
||||
case EntityAction.clientPortal:
|
||||
if (await canLaunch(invoice.invitationSilentLink)) {
|
||||
await launch(invoice.invitationSilentLink,
|
||||
forceSafariVC: false, forceWebView: false);
|
||||
}
|
||||
break;
|
||||
case EntityAction.markSent:
|
||||
store.dispatch(MarkSentInvoiceRequest(
|
||||
snackBarCompleter(context, localization.markedInvoiceAsSent),
|
||||
invoice.id));
|
||||
break;
|
||||
case EntityAction.sendEmail:
|
||||
store.dispatch(ShowEmailInvoice(
|
||||
completer: snackBarCompleter(context, localization.emailedInvoice),
|
||||
invoice: invoice,
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.cloneToInvoice:
|
||||
store.dispatch(
|
||||
EditInvoice(context: context, invoice: invoice.cloneToInvoice));
|
||||
break;
|
||||
case EntityAction.cloneToQuote:
|
||||
store.dispatch(EditQuote(context: context, quote: invoice.cloneToQuote));
|
||||
break;
|
||||
case EntityAction.enterPayment:
|
||||
store.dispatch(EditPayment(
|
||||
context: context,
|
||||
payment: invoice.createPayment(company)));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreInvoiceRequest(
|
||||
snackBarCompleter(context, localization.restoredInvoice),
|
||||
invoice.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveInvoiceRequest(
|
||||
snackBarCompleter(context, localization.archivedInvoice),
|
||||
invoice.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteInvoiceRequest(
|
||||
snackBarCompleter(context, localization.deletedInvoice), invoice.id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,8 @@ InvoiceEntity _addInvoiceItem(InvoiceEntity invoice, AddInvoiceItem action) {
|
|||
InvoiceEntity _addInvoiceItems(InvoiceEntity invoice, AddInvoiceItems action) {
|
||||
return invoice.rebuild((b) => b
|
||||
..hasTasks = action.invoiceItems.where((item) => item.isTask).isNotEmpty
|
||||
..hasExpenses = action.invoiceItems.where((item) => item.isExpense).isNotEmpty
|
||||
..hasExpenses =
|
||||
action.invoiceItems.where((item) => item.isExpense).isNotEmpty
|
||||
..invoiceItems.addAll(action.invoiceItems));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@ import 'package:flutter/widgets.dart';
|
|||
import 'package:built_collection/built_collection.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.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/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
class ViewPaymentList implements PersistUI {
|
||||
ViewPaymentList(this.context);
|
||||
|
|
@ -236,3 +240,33 @@ class FilterPaymentsByEntity implements PersistUI {
|
|||
final int entityId;
|
||||
final EntityType entityType;
|
||||
}
|
||||
|
||||
void handlePaymentAction(
|
||||
BuildContext context, PaymentEntity payment, EntityAction action) {
|
||||
final store = StoreProvider.of<AppState>(context);
|
||||
final localization = AppLocalization.of(context);
|
||||
|
||||
switch (action) {
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditPayment(context: context, payment: payment));
|
||||
break;
|
||||
case EntityAction.sendEmail:
|
||||
store.dispatch(EmailPaymentRequest(
|
||||
snackBarCompleter(context, localization.emailedPayment), payment));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestorePaymentRequest(
|
||||
snackBarCompleter(context, localization.restoredPayment),
|
||||
payment.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchivePaymentRequest(
|
||||
snackBarCompleter(context, localization.archivedPayment),
|
||||
payment.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeletePaymentRequest(
|
||||
snackBarCompleter(context, localization.deletedPayment), payment.id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ 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);
|
||||
|
||||
|
|
@ -179,7 +178,6 @@ class FilterProductDropdown {
|
|||
final String filter;
|
||||
}
|
||||
|
||||
|
||||
void handleProductAction(
|
||||
BuildContext context, ProductEntity product, EntityAction action) {
|
||||
final store = StoreProvider.of<AppState>(context);
|
||||
|
|
@ -189,7 +187,7 @@ void handleProductAction(
|
|||
switch (action) {
|
||||
case EntityAction.newInvoice:
|
||||
final item =
|
||||
convertProductToInvoiceItem(context: context, product: product);
|
||||
convertProductToInvoiceItem(context: context, product: product);
|
||||
store.dispatch(EditInvoice(
|
||||
context: context,
|
||||
invoice: InvoiceEntity(company: state.selectedCompany)
|
||||
|
|
@ -199,8 +197,7 @@ void handleProductAction(
|
|||
store.dispatch(EditProduct(context: context, product: product));
|
||||
break;
|
||||
case EntityAction.clone:
|
||||
store.dispatch(
|
||||
EditProduct(context: context, product: product.clone));
|
||||
store.dispatch(EditProduct(context: context, product: product.clone));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreProductRequest(
|
||||
|
|
@ -214,8 +211,7 @@ void handleProductAction(
|
|||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteProductRequest(
|
||||
snackBarCompleter(context, localization.deletedProduct),
|
||||
product.id));
|
||||
snackBarCompleter(context, localization.deletedProduct), product.id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,13 @@ import 'package:flutter/widgets.dart';
|
|||
import 'package:built_collection/built_collection.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.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/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/redux/project/project_selectors.dart';
|
||||
|
||||
class ViewProjectList implements PersistUI {
|
||||
ViewProjectList(this.context);
|
||||
|
|
@ -221,3 +228,47 @@ class FilterProjectsByEntity implements PersistUI {
|
|||
final int entityId;
|
||||
final EntityType entityType;
|
||||
}
|
||||
|
||||
void handleProjectAction(
|
||||
BuildContext context, ProjectEntity project, EntityAction action) {
|
||||
final store = StoreProvider.of<AppState>(context);
|
||||
final state = store.state;
|
||||
final CompanyEntity company = state.selectedCompany;
|
||||
|
||||
switch (action) {
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditProject(context: context, project: project));
|
||||
break;
|
||||
case EntityAction.newInvoice:
|
||||
final items =
|
||||
convertProjectToInvoiceItem(project: project, context: context);
|
||||
store.dispatch(EditInvoice(
|
||||
invoice: InvoiceEntity(company: company).rebuild((b) => b
|
||||
..hasTasks = true
|
||||
..clientId = project.clientId
|
||||
..invoiceItems.addAll(items)),
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.clone:
|
||||
store.dispatch(EditProject(context: context, project: project.clone));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreProjectRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).restoredProject),
|
||||
project.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveProjectRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).archivedProject),
|
||||
project.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteProjectRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).deletedProject),
|
||||
project.id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
import 'dart:async';
|
||||
|
||||
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/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/pdf.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ViewQuoteList implements PersistUI {
|
||||
ViewQuoteList(this.context);
|
||||
|
|
@ -322,3 +329,65 @@ class ConvertQuoteFailure implements StopSaving {
|
|||
|
||||
final dynamic error;
|
||||
}
|
||||
|
||||
Future handleQuoteAction(
|
||||
BuildContext context, InvoiceEntity quote, EntityAction action) async {
|
||||
final store = StoreProvider.of<AppState>(context);
|
||||
final localization = AppLocalization.of(context);
|
||||
|
||||
switch (action) {
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditQuote(context: context, quote: quote));
|
||||
break;
|
||||
case EntityAction.pdf:
|
||||
viewPdf(quote, context);
|
||||
break;
|
||||
case EntityAction.clientPortal:
|
||||
if (await canLaunch(quote.invitationSilentLink)) {
|
||||
await launch(quote.invitationSilentLink,
|
||||
forceSafariVC: false, forceWebView: false);
|
||||
}
|
||||
break;
|
||||
case EntityAction.viewInvoice:
|
||||
store.dispatch(
|
||||
ViewInvoice(context: context, invoiceId: quote.quoteInvoiceId));
|
||||
break;
|
||||
case EntityAction.convert:
|
||||
final Completer<InvoiceEntity> completer = Completer<InvoiceEntity>();
|
||||
store.dispatch(ConvertQuote(completer, quote.id));
|
||||
completer.future.then((InvoiceEntity invoice) {
|
||||
store.dispatch(ViewInvoice(invoiceId: invoice.id, context: context));
|
||||
});
|
||||
break;
|
||||
case EntityAction.markSent:
|
||||
store.dispatch(MarkSentQuoteRequest(
|
||||
snackBarCompleter(context, localization.markedQuoteAsSent),
|
||||
quote.id));
|
||||
break;
|
||||
case EntityAction.sendEmail:
|
||||
store.dispatch(ShowEmailQuote(
|
||||
completer: snackBarCompleter(context, localization.emailedQuote),
|
||||
quote: quote,
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.cloneToInvoice:
|
||||
store.dispatch(
|
||||
EditInvoice(context: context, invoice: quote.cloneToInvoice));
|
||||
break;
|
||||
case EntityAction.cloneToQuote:
|
||||
store.dispatch(EditQuote(context: context, quote: quote.cloneToQuote));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreQuoteRequest(
|
||||
snackBarCompleter(context, localization.restoredQuote), quote.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveQuoteRequest(
|
||||
snackBarCompleter(context, localization.archivedQuote), quote.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteQuoteRequest(
|
||||
snackBarCompleter(context, localization.deletedQuote), quote.id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,15 @@ import 'package:flutter/widgets.dart';
|
|||
import 'package:built_collection/built_collection.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.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/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/redux/task/task_selectors.dart';
|
||||
|
||||
class ViewTaskList implements PersistUI {
|
||||
ViewTaskList(this.context);
|
||||
|
|
@ -259,3 +268,73 @@ class FilterTasksByEntity implements PersistUI {
|
|||
final int entityId;
|
||||
final EntityType entityType;
|
||||
}
|
||||
|
||||
void handleTaskAction(
|
||||
BuildContext context, TaskEntity task, EntityAction action) {
|
||||
final store = StoreProvider.of<AppState>(context);
|
||||
final state = store.state;
|
||||
final CompanyEntity company = state.selectedCompany;
|
||||
final localization = AppLocalization.of(context);
|
||||
|
||||
switch (action) {
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditTask(context: context, task: task));
|
||||
break;
|
||||
case EntityAction.start:
|
||||
case EntityAction.stop:
|
||||
case EntityAction.resume:
|
||||
final Completer<TaskEntity> completer = new Completer<TaskEntity>();
|
||||
final localization = AppLocalization.of(context);
|
||||
store
|
||||
.dispatch(SaveTaskRequest(completer: completer, task: task.toggle()));
|
||||
completer.future.then((savedTask) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: SnackBarRow(
|
||||
message: savedTask.isRunning
|
||||
? (savedTask.duration > 0
|
||||
? localization.resumedTask
|
||||
: localization.startedTask)
|
||||
: localization.stoppedTask,
|
||||
)));
|
||||
}).catchError((Object error) {
|
||||
showDialog<ErrorDialog>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return ErrorDialog(error);
|
||||
});
|
||||
});
|
||||
|
||||
break;
|
||||
case EntityAction.newInvoice:
|
||||
final item = convertTaskToInvoiceItem(task: task, context: context);
|
||||
store.dispatch(EditInvoice(
|
||||
invoice:
|
||||
InvoiceEntity(company: company).rebuild((b) => b
|
||||
..hasTasks = true
|
||||
..clientId = task.clientId
|
||||
..invoiceItems.add(item)),
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.viewInvoice:
|
||||
store.dispatch(ViewInvoice(invoiceId: task.invoiceId, context: context));
|
||||
break;
|
||||
case EntityAction.clone:
|
||||
store.dispatch(EditTask(context: context, task: task.clone));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreTaskRequest(
|
||||
snackBarCompleter(context, localization.restoredTask),
|
||||
task.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveTaskRequest(
|
||||
snackBarCompleter(context, localization.archivedTask),
|
||||
task.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteTaskRequest(
|
||||
snackBarCompleter(context, localization.deletedTask),
|
||||
task.id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@ import 'package:flutter/widgets.dart';
|
|||
import 'package:built_collection/built_collection.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.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/expense/expense_actions.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
|
||||
class ViewVendorList implements PersistUI {
|
||||
ViewVendorList(this.context);
|
||||
|
|
@ -251,3 +256,34 @@ class FilterVendorsByEntity implements PersistUI {
|
|||
final int entityId;
|
||||
final EntityType entityType;
|
||||
}
|
||||
|
||||
void handleVendorAction(
|
||||
BuildContext context, VendorEntity vendor, EntityAction action) {
|
||||
final store = StoreProvider.of<AppState>(context);
|
||||
final state = store.state;
|
||||
final CompanyEntity company = state.selectedCompany;
|
||||
final localization = AppLocalization.of(context);
|
||||
|
||||
switch (action) {
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditVendor(context: context, vendor: vendor));
|
||||
break;
|
||||
case EntityAction.newExpense:
|
||||
store.dispatch(EditExpense(
|
||||
expense: ExpenseEntity(company: company, vendor: vendor),
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreVendorRequest(
|
||||
snackBarCompleter(context, localization.restoredVendor), vendor.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveVendorRequest(
|
||||
snackBarCompleter(context, localization.archivedVendor), vendor.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteVendorRequest(
|
||||
snackBarCompleter(context, localization.deletedVendor), vendor.id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,27 +47,26 @@ class ClientList extends StatelessWidget {
|
|||
final clientId = viewModel.clientList[index];
|
||||
final client = viewModel.clientMap[clientId];
|
||||
final user = viewModel.user;
|
||||
|
||||
void showDialog() => showEntityActionsDialog(
|
||||
entity: client,
|
||||
context: context,
|
||||
user: user,
|
||||
onEntityAction: viewModel.onEntityAction);
|
||||
|
||||
return ClientListItem(
|
||||
user: viewModel.user,
|
||||
filter: viewModel.filter,
|
||||
client: client,
|
||||
onEntityAction: (EntityAction action) {
|
||||
if (action == EntityAction.more) {
|
||||
showEntityActionsDialog(
|
||||
entity: client,
|
||||
context: context,
|
||||
user: user,
|
||||
onEntityAction: viewModel.onEntityAction);
|
||||
showDialog();
|
||||
} else {
|
||||
viewModel.onEntityAction(context, client, action);
|
||||
}
|
||||
},
|
||||
onTap: () => viewModel.onClientTap(context, client),
|
||||
onLongPress: () => showEntityActionsDialog(
|
||||
entity: client,
|
||||
context: context,
|
||||
user: user,
|
||||
onEntityAction: viewModel.onEntityAction),
|
||||
onLongPress: () => showDialog(),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -59,11 +59,13 @@ class ClientListItem extends StatelessWidget {
|
|||
: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
filterMatch != null ? Text(
|
||||
filterMatch,
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
) : SizedBox(),
|
||||
filterMatch != null
|
||||
? Text(
|
||||
filterMatch,
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)
|
||||
: SizedBox(),
|
||||
EntityStateLabel(client),
|
||||
],
|
||||
),
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ class ClientListVM {
|
|||
onRefreshed: (context) => _handleRefresh(context),
|
||||
onEntityAction:
|
||||
(BuildContext context, BaseEntity client, EntityAction action) =>
|
||||
handleClientAction(context, client, action),
|
||||
handleClientAction(context, client, action),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -290,7 +290,7 @@ class _CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
user: viewModel.company.user,
|
||||
isSaving: viewModel.isSaving,
|
||||
entity: client,
|
||||
onSelected: viewModel.onActionSelected,
|
||||
onSelected: viewModel.onEntityAction,
|
||||
entityActions: viewModel.client.getActions(user: user),
|
||||
)
|
||||
],
|
||||
|
|
|
|||
|
|
@ -145,8 +145,8 @@ class ClientOverview extends StatelessWidget {
|
|||
title: localization.expenses,
|
||||
onTap: () =>
|
||||
viewModel.onEntityPressed(context, EntityType.expense),
|
||||
onLongPress: () =>
|
||||
viewModel.onEntityPressed(context, EntityType.expense, true),
|
||||
onLongPress: () => viewModel.onEntityPressed(
|
||||
context, EntityType.expense, true),
|
||||
subtitle: memoizedExpenseStatsForClient(
|
||||
client.id,
|
||||
state.expenseState.map,
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class ClientViewVM {
|
|||
ClientViewVM({
|
||||
@required this.client,
|
||||
@required this.company,
|
||||
@required this.onActionSelected,
|
||||
@required this.onEntityAction,
|
||||
@required this.onEntityPressed,
|
||||
@required this.onEditPressed,
|
||||
@required this.onBackPressed,
|
||||
|
|
@ -173,14 +173,14 @@ class ClientViewVM {
|
|||
store.dispatch(UpdateCurrentRoute(ClientScreen.route));
|
||||
}
|
||||
},
|
||||
onActionSelected: (BuildContext context, EntityAction action) =>
|
||||
onEntityAction: (BuildContext context, EntityAction action) =>
|
||||
handleClientAction(context, client, action),
|
||||
);
|
||||
}
|
||||
|
||||
final ClientEntity client;
|
||||
final CompanyEntity company;
|
||||
final Function(BuildContext, EntityAction) onActionSelected;
|
||||
final Function(BuildContext, EntityAction) onEntityAction;
|
||||
final Function(BuildContext) onEditPressed;
|
||||
final Function onBackPressed;
|
||||
final Function(BuildContext, EntityType, [bool]) onEntityPressed;
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ class ExpenseEditDocumentsState extends State<ExpenseEditDocuments> {
|
|||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
final List<TextEditingController> _controllers = [
|
||||
];
|
||||
final List<TextEditingController> _controllers = [];
|
||||
|
||||
_controllers
|
||||
.forEach((dynamic controller) => controller.removeListener(_onChanged));
|
||||
|
|
@ -64,15 +63,14 @@ class ExpenseEditDocumentsState extends State<ExpenseEditDocuments> {
|
|||
FormCard(
|
||||
children: <Widget>[
|
||||
SwitchListTile(
|
||||
activeColor: Theme.of(context).accentColor,
|
||||
title: Text(localization.addDocumentsToInvoice),
|
||||
value: expense.invoiceDocuments,
|
||||
onChanged: (value) {
|
||||
viewModel.onChanged(
|
||||
expense.rebuild((b) => b..invoiceDocuments = value));
|
||||
viewModel.onAddDocumentsChanged(value);
|
||||
}
|
||||
)
|
||||
activeColor: Theme.of(context).accentColor,
|
||||
title: Text(localization.addDocumentsToInvoice),
|
||||
value: expense.invoiceDocuments,
|
||||
onChanged: (value) {
|
||||
viewModel.onChanged(
|
||||
expense.rebuild((b) => b..invoiceDocuments = value));
|
||||
viewModel.onAddDocumentsChanged(value);
|
||||
})
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.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';
|
||||
|
|
@ -17,41 +18,6 @@ class ExpenseList extends StatelessWidget {
|
|||
|
||||
final ExpenseListVM viewModel;
|
||||
|
||||
void _showMenu(BuildContext context, ExpenseEntity expense) async {
|
||||
if (expense == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final user = viewModel.user;
|
||||
final message = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) => SimpleDialog(
|
||||
children: expense
|
||||
.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, expense, entityAction);
|
||||
},
|
||||
);
|
||||
}
|
||||
}).toList()));
|
||||
|
||||
if (message != null) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: SnackBarRow(
|
||||
message: message,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
|
|
@ -119,12 +85,21 @@ class ExpenseList extends StatelessWidget {
|
|||
separatorBuilder: (context, index) => ListDivider(),
|
||||
itemCount: viewModel.expenseList.length,
|
||||
itemBuilder: (BuildContext context, index) {
|
||||
final user = viewModel.user;
|
||||
final expenseId = viewModel.expenseList[index];
|
||||
final expense = viewModel.expenseMap[expenseId];
|
||||
final client =
|
||||
viewModel.state.clientState.map[expense.clientId];
|
||||
final vendor =
|
||||
viewModel.state.vendorState.map[expense.vendorId];
|
||||
|
||||
void showDialog() => showEntityActionsDialog(
|
||||
entity: expense,
|
||||
context: context,
|
||||
user: user,
|
||||
client: client,
|
||||
onEntityAction: viewModel.onEntityAction);
|
||||
|
||||
return ExpenseListItem(
|
||||
user: viewModel.user,
|
||||
filter: viewModel.filter,
|
||||
|
|
@ -134,13 +109,13 @@ class ExpenseList extends StatelessWidget {
|
|||
onTap: () => viewModel.onExpenseTap(context, expense),
|
||||
onEntityAction: (EntityAction action) {
|
||||
if (action == EntityAction.more) {
|
||||
_showMenu(context, expense);
|
||||
showDialog();
|
||||
} else {
|
||||
viewModel.onEntityAction(
|
||||
context, expense, action);
|
||||
}
|
||||
},
|
||||
onLongPress: () => _showMenu(context, expense),
|
||||
onLongPress: () => showDialog(),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/vendor/vendor_actions.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -92,49 +91,9 @@ class ExpenseListVM {
|
|||
onExpenseTap: (context, expense) {
|
||||
store.dispatch(ViewExpense(expenseId: expense.id, context: context));
|
||||
},
|
||||
onEntityAction: (context, expense, action) {
|
||||
switch (action) {
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditExpense(context: context, expense: expense));
|
||||
break;
|
||||
case EntityAction.clone:
|
||||
store.dispatch(
|
||||
EditExpense(context: context, expense: expense.clone));
|
||||
break;
|
||||
case EntityAction.newInvoice:
|
||||
final item = convertExpenseToInvoiceItem(expense: expense);
|
||||
store.dispatch(EditInvoice(
|
||||
invoice: InvoiceEntity(company: state.selectedCompany)
|
||||
.rebuild((b) => b
|
||||
..hasExpenses = true
|
||||
..clientId = expense.clientId
|
||||
..invoiceItems.add(item)),
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.viewInvoice:
|
||||
store.dispatch(
|
||||
ViewInvoice(invoiceId: expense.invoiceId, context: context));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreExpenseRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).restoredExpense),
|
||||
expense.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveExpenseRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).archivedExpense),
|
||||
expense.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteExpenseRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).deletedExpense),
|
||||
expense.id));
|
||||
break;
|
||||
}
|
||||
},
|
||||
onEntityAction:
|
||||
(BuildContext context, BaseEntity expense, EntityAction action) =>
|
||||
handleExpenseAction(context, expense, action),
|
||||
onRefreshed: (context) => _handleRefresh(context),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ class _CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
user: viewModel.company.user,
|
||||
isSaving: viewModel.isSaving,
|
||||
entity: expense,
|
||||
onSelected: viewModel.onActionSelected,
|
||||
onSelected: viewModel.onEntityAction,
|
||||
entityActions: viewModel.expense.getActions(user: user),
|
||||
)
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/expense/expense_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/vendor/vendor_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart';
|
||||
|
|
@ -42,7 +41,7 @@ class ExpenseViewVM {
|
|||
@required this.state,
|
||||
@required this.expense,
|
||||
@required this.company,
|
||||
@required this.onActionSelected,
|
||||
@required this.onEntityAction,
|
||||
@required this.onEntityPressed,
|
||||
@required this.onEditPressed,
|
||||
@required this.onBackPressed,
|
||||
|
|
@ -120,50 +119,17 @@ class ExpenseViewVM {
|
|||
break;
|
||||
}
|
||||
},
|
||||
onActionSelected: (BuildContext context, EntityAction action) {
|
||||
final localization = AppLocalization.of(context);
|
||||
switch (action) {
|
||||
case EntityAction.clone:
|
||||
store.dispatch(
|
||||
EditExpense(context: context, expense: expense.clone));
|
||||
break;
|
||||
case EntityAction.newInvoice:
|
||||
final item = convertExpenseToInvoiceItem(expense: expense);
|
||||
store.dispatch(EditInvoice(
|
||||
invoice: InvoiceEntity(company: state.selectedCompany)
|
||||
.rebuild((b) => b
|
||||
..hasExpenses = true
|
||||
..clientId = expense.clientId
|
||||
..invoiceItems.add(item)),
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.viewInvoice:
|
||||
store.dispatch(
|
||||
ViewInvoice(invoiceId: expense.invoiceId, context: context));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveExpenseRequest(
|
||||
popCompleter(context, localization.archivedExpense),
|
||||
expense.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteExpenseRequest(
|
||||
popCompleter(context, localization.deletedExpense),
|
||||
expense.id));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreExpenseRequest(
|
||||
snackBarCompleter(context, localization.restoredExpense),
|
||||
expense.id));
|
||||
break;
|
||||
}
|
||||
});
|
||||
onEntityAction: (BuildContext context, EntityAction action) =>
|
||||
handleExpenseAction(context, expense, action),
|
||||
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
final AppState state;
|
||||
final ExpenseEntity expense;
|
||||
final CompanyEntity company;
|
||||
final Function(BuildContext, EntityAction) onActionSelected;
|
||||
final Function(BuildContext, EntityAction) onEntityAction;
|
||||
final Function(BuildContext, EntityType, [bool]) onEntityPressed;
|
||||
final Function(BuildContext) onEditPressed;
|
||||
final Function onBackPressed;
|
||||
|
|
|
|||
|
|
@ -101,8 +101,8 @@ class _InvoiceItemSelectorState extends State<InvoiceItemSelector>
|
|||
|
||||
void _updateClientId() {
|
||||
final selected = _selected.firstWhere(
|
||||
(entity) =>
|
||||
entity is BelongsToClient &&
|
||||
(entity) =>
|
||||
entity is BelongsToClient &&
|
||||
(((entity as BelongsToClient).clientId ?? 0) > 0),
|
||||
orElse: () => null);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/invoice_model.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.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/invoice/invoice_list_item.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/invoice_list_vm.dart';
|
||||
import 'package:invoiceninja_flutter/utils/icons.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
class InvoiceList extends StatelessWidget {
|
||||
|
|
@ -18,43 +16,6 @@ class InvoiceList extends StatelessWidget {
|
|||
|
||||
final EntityListVM viewModel;
|
||||
|
||||
void _showMenu(
|
||||
BuildContext context, InvoiceEntity invoice, ClientEntity client) async {
|
||||
if (invoice == null || client == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final user = viewModel.user;
|
||||
final message = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) => SimpleDialog(
|
||||
children: invoice
|
||||
.getActions(
|
||||
user: user, client: client, 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, invoice, entityAction);
|
||||
},
|
||||
);
|
||||
}
|
||||
}).toList()));
|
||||
|
||||
if (message != null) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: SnackBarRow(
|
||||
message: message,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
|
|
@ -117,11 +78,20 @@ class InvoiceList extends StatelessWidget {
|
|||
separatorBuilder: (context, index) => ListDivider(),
|
||||
itemCount: viewModel.invoiceList.length,
|
||||
itemBuilder: (BuildContext context, index) {
|
||||
final user = viewModel.user;
|
||||
final invoiceId = viewModel.invoiceList[index];
|
||||
final invoice = viewModel.invoiceMap[invoiceId];
|
||||
final client =
|
||||
viewModel.clientMap[invoice.clientId] ??
|
||||
ClientEntity();
|
||||
|
||||
void showDialog() => showEntityActionsDialog(
|
||||
entity: invoice,
|
||||
context: context,
|
||||
user: user,
|
||||
client: client,
|
||||
onEntityAction: viewModel.onEntityAction);
|
||||
|
||||
return InvoiceListItem(
|
||||
user: viewModel.user,
|
||||
filter: viewModel.filter,
|
||||
|
|
@ -132,14 +102,13 @@ class InvoiceList extends StatelessWidget {
|
|||
viewModel.onInvoiceTap(context, invoice),
|
||||
onEntityAction: (EntityAction action) {
|
||||
if (action == EntityAction.more) {
|
||||
_showMenu(context, invoice, client);
|
||||
showDialog();
|
||||
} else {
|
||||
viewModel.onEntityAction(
|
||||
context, invoice, action);
|
||||
}
|
||||
},
|
||||
onLongPress: () =>
|
||||
_showMenu(context, invoice, client),
|
||||
onLongPress: () => showDialog(),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -6,18 +6,14 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/payment/payment_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/quote/quote_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/pdf.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/invoice_list.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class InvoiceListBuilder extends StatelessWidget {
|
||||
const InvoiceListBuilder({Key key}) : super(key: key);
|
||||
|
|
@ -136,63 +132,9 @@ class InvoiceListVM extends EntityListVM {
|
|||
ViewClient(
|
||||
clientId: state.invoiceListState.filterEntityId,
|
||||
context: context)),
|
||||
onEntityAction: (context, invoice, action) async {
|
||||
final localization = AppLocalization.of(context);
|
||||
switch (action) {
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditInvoice(context: context, invoice: invoice));
|
||||
break;
|
||||
case EntityAction.pdf:
|
||||
viewPdf(invoice, context);
|
||||
break;
|
||||
case EntityAction.clientPortal:
|
||||
if (await canLaunch(invoice.invitationSilentLink)) {
|
||||
await launch(invoice.invitationSilentLink,
|
||||
forceSafariVC: false, forceWebView: false);
|
||||
}
|
||||
break;
|
||||
case EntityAction.markSent:
|
||||
store.dispatch(MarkSentInvoiceRequest(
|
||||
snackBarCompleter(context, localization.markedInvoiceAsSent),
|
||||
invoice.id));
|
||||
break;
|
||||
case EntityAction.sendEmail:
|
||||
store.dispatch(ShowEmailInvoice(
|
||||
completer:
|
||||
snackBarCompleter(context, localization.emailedInvoice),
|
||||
invoice: invoice,
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.cloneToInvoice:
|
||||
store.dispatch(
|
||||
EditInvoice(context: context, invoice: invoice.cloneToInvoice));
|
||||
break;
|
||||
case EntityAction.cloneToQuote:
|
||||
store.dispatch(
|
||||
EditQuote(context: context, quote: invoice.cloneToQuote));
|
||||
break;
|
||||
case EntityAction.enterPayment:
|
||||
store.dispatch(EditPayment(
|
||||
context: context,
|
||||
payment: invoice.createPayment(state.selectedCompany)));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreInvoiceRequest(
|
||||
snackBarCompleter(context, localization.restoredInvoice),
|
||||
invoice.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveInvoiceRequest(
|
||||
snackBarCompleter(context, localization.archivedInvoice),
|
||||
invoice.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteInvoiceRequest(
|
||||
snackBarCompleter(context, localization.deletedInvoice),
|
||||
invoice.id));
|
||||
break;
|
||||
}
|
||||
},
|
||||
onEntityAction:
|
||||
(BuildContext context, BaseEntity invoice, EntityAction action) =>
|
||||
handleInvoiceAction(context, invoice, action),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -309,8 +309,7 @@ class _CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
: Container(),
|
||||
ActionMenuButton(
|
||||
user: user,
|
||||
entityActions:
|
||||
invoice.getActions(client: client, user: user),
|
||||
entityActions: invoice.getActions(client: client, user: user),
|
||||
isSaving: viewModel.isSaving,
|
||||
entity: invoice,
|
||||
onSelected: viewModel.onActionSelected,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'package:invoiceninja_flutter/redux/payment/payment_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/quote/quote_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
|
@ -11,13 +10,11 @@ import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
|
|||
import 'package:invoiceninja_flutter/ui/invoice/invoice_screen.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/pdf.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/view/invoice_view.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class InvoiceViewScreen extends StatelessWidget {
|
||||
const InvoiceViewScreen({Key key}) : super(key: key);
|
||||
|
|
@ -93,7 +90,7 @@ class InvoiceViewVM extends EntityViewVM {
|
|||
ClientEntity client,
|
||||
bool isSaving,
|
||||
bool isDirty,
|
||||
Function(BuildContext, EntityAction) onActionSelected,
|
||||
Function(BuildContext, EntityAction) onEntityAction,
|
||||
Function(BuildContext, [InvoiceItemEntity]) onEditPressed,
|
||||
Function(BuildContext, [bool]) onClientPressed,
|
||||
Function(BuildContext, PaymentEntity, [bool]) onPaymentPressed,
|
||||
|
|
@ -106,7 +103,7 @@ class InvoiceViewVM extends EntityViewVM {
|
|||
client: client,
|
||||
isSaving: isSaving,
|
||||
isDirty: isDirty,
|
||||
onActionSelected: onActionSelected,
|
||||
onActionSelected: onEntityAction,
|
||||
onEditPressed: onEditPressed,
|
||||
onClientPressed: onClientPressed,
|
||||
onPaymentPressed: onPaymentPressed,
|
||||
|
|
@ -130,110 +127,57 @@ class InvoiceViewVM extends EntityViewVM {
|
|||
}
|
||||
|
||||
return InvoiceViewVM(
|
||||
company: state.selectedCompany,
|
||||
isSaving: state.isSaving,
|
||||
isDirty: invoice.isNew,
|
||||
invoice: invoice,
|
||||
client: client,
|
||||
onEditPressed: (BuildContext context, [InvoiceItemEntity invoiceItem]) {
|
||||
final Completer<InvoiceEntity> completer =
|
||||
new Completer<InvoiceEntity>();
|
||||
store.dispatch(EditInvoice(
|
||||
invoice: invoice,
|
||||
context: context,
|
||||
completer: completer,
|
||||
invoiceItem: invoiceItem));
|
||||
completer.future.then((invoice) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: SnackBarRow(
|
||||
message: AppLocalization.of(context).updatedInvoice,
|
||||
)));
|
||||
});
|
||||
},
|
||||
onRefreshed: (context) => _handleRefresh(context),
|
||||
onBackPressed: () {
|
||||
if (state.uiState.currentRoute.contains(InvoiceScreen.route)) {
|
||||
store.dispatch(UpdateCurrentRoute(InvoiceScreen.route));
|
||||
}
|
||||
},
|
||||
onClientPressed: (BuildContext context, [bool longPress = false]) {
|
||||
if (longPress) {
|
||||
showEntityActionsDialog(
|
||||
user: state.selectedCompany.user,
|
||||
context: context,
|
||||
entity: client,
|
||||
onEntityAction: (BuildContext context, BaseEntity client,
|
||||
EntityAction action) =>
|
||||
handleClientAction(context, client, action));
|
||||
} else {
|
||||
store.dispatch(ViewClient(clientId: client.id, context: context));
|
||||
}
|
||||
},
|
||||
onPaymentPressed: (BuildContext context, PaymentEntity payment,
|
||||
[bool longPress = false]) =>
|
||||
store.dispatch(longPress
|
||||
? EditPayment(payment: payment, context: context)
|
||||
: ViewPayment(paymentId: payment.id, context: context)),
|
||||
onPaymentsPressed: (BuildContext context) {
|
||||
store.dispatch(FilterPaymentsByEntity(
|
||||
entityId: invoice.id, entityType: EntityType.invoice));
|
||||
store.dispatch(ViewPaymentList(context));
|
||||
},
|
||||
onActionSelected: (BuildContext context, EntityAction action) async {
|
||||
final localization = AppLocalization.of(context);
|
||||
switch (action) {
|
||||
case EntityAction.pdf:
|
||||
viewPdf(invoice, context);
|
||||
break;
|
||||
case EntityAction.clientPortal:
|
||||
if (await canLaunch(invoice.invitationSilentLink)) {
|
||||
await launch(invoice.invitationSilentLink,
|
||||
forceSafariVC: false, forceWebView: false);
|
||||
}
|
||||
break;
|
||||
case EntityAction.markSent:
|
||||
store.dispatch(MarkSentInvoiceRequest(
|
||||
snackBarCompleter(context, localization.markedInvoiceAsSent),
|
||||
invoice.id));
|
||||
break;
|
||||
case EntityAction.sendEmail:
|
||||
store.dispatch(ShowEmailInvoice(
|
||||
completer:
|
||||
snackBarCompleter(context, localization.emailedInvoice),
|
||||
invoice: invoice,
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveInvoiceRequest(
|
||||
popCompleter(context, localization.archivedInvoice),
|
||||
invoice.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteInvoiceRequest(
|
||||
popCompleter(context, localization.deletedInvoice),
|
||||
invoice.id));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreInvoiceRequest(
|
||||
snackBarCompleter(context, localization.restoredInvoice),
|
||||
invoice.id));
|
||||
break;
|
||||
case EntityAction.cloneToInvoice:
|
||||
Navigator.of(context).pop();
|
||||
store.dispatch(EditInvoice(
|
||||
context: context, invoice: invoice.cloneToInvoice));
|
||||
break;
|
||||
case EntityAction.cloneToQuote:
|
||||
Navigator.of(context).pop();
|
||||
store.dispatch(
|
||||
EditQuote(context: context, quote: invoice.cloneToQuote));
|
||||
break;
|
||||
case EntityAction.enterPayment:
|
||||
store.dispatch(EditPayment(
|
||||
context: context,
|
||||
payment: invoice.createPayment(state.selectedCompany)));
|
||||
break;
|
||||
}
|
||||
company: state.selectedCompany,
|
||||
isSaving: state.isSaving,
|
||||
isDirty: invoice.isNew,
|
||||
invoice: invoice,
|
||||
client: client,
|
||||
onEditPressed: (BuildContext context, [InvoiceItemEntity invoiceItem]) {
|
||||
final Completer<InvoiceEntity> completer =
|
||||
new Completer<InvoiceEntity>();
|
||||
store.dispatch(EditInvoice(
|
||||
invoice: invoice,
|
||||
context: context,
|
||||
completer: completer,
|
||||
invoiceItem: invoiceItem));
|
||||
completer.future.then((invoice) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: SnackBarRow(
|
||||
message: AppLocalization.of(context).updatedInvoice,
|
||||
)));
|
||||
});
|
||||
},
|
||||
onRefreshed: (context) => _handleRefresh(context),
|
||||
onBackPressed: () {
|
||||
if (state.uiState.currentRoute.contains(InvoiceScreen.route)) {
|
||||
store.dispatch(UpdateCurrentRoute(InvoiceScreen.route));
|
||||
}
|
||||
},
|
||||
onClientPressed: (BuildContext context, [bool longPress = false]) {
|
||||
if (longPress) {
|
||||
showEntityActionsDialog(
|
||||
user: state.selectedCompany.user,
|
||||
context: context,
|
||||
entity: client,
|
||||
onEntityAction: (BuildContext context, BaseEntity client,
|
||||
EntityAction action) =>
|
||||
handleClientAction(context, client, action));
|
||||
} else {
|
||||
store.dispatch(ViewClient(clientId: client.id, context: context));
|
||||
}
|
||||
},
|
||||
onPaymentPressed: (BuildContext context, PaymentEntity payment,
|
||||
[bool longPress = false]) =>
|
||||
store.dispatch(longPress
|
||||
? EditPayment(payment: payment, context: context)
|
||||
: ViewPayment(paymentId: payment.id, context: context)),
|
||||
onPaymentsPressed: (BuildContext context) {
|
||||
store.dispatch(FilterPaymentsByEntity(
|
||||
entityId: invoice.id, entityType: EntityType.invoice));
|
||||
store.dispatch(ViewPaymentList(context));
|
||||
},
|
||||
onEntityAction: (BuildContext context, EntityAction action) =>
|
||||
handleInvoiceAction(context, invoice, action),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/payment_model.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/redux/payment/payment_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.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/payment/payment_list_item.dart';
|
||||
import 'package:invoiceninja_flutter/ui/payment/payment_list_vm.dart';
|
||||
import 'package:invoiceninja_flutter/utils/icons.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
class PaymentList extends StatelessWidget {
|
||||
|
|
@ -21,43 +19,6 @@ class PaymentList extends StatelessWidget {
|
|||
|
||||
final PaymentListVM viewModel;
|
||||
|
||||
void _showMenu(
|
||||
BuildContext context, PaymentEntity payment, ClientEntity client) async {
|
||||
if (payment == null || client == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final user = viewModel.user;
|
||||
final message = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) => SimpleDialog(
|
||||
children: payment
|
||||
.getActions(
|
||||
user: user, client: client, 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, payment, entityAction);
|
||||
},
|
||||
);
|
||||
}
|
||||
}).toList()));
|
||||
|
||||
if (message != null) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: SnackBarRow(
|
||||
message: message,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
|
|
@ -134,10 +95,19 @@ class PaymentList extends StatelessWidget {
|
|||
separatorBuilder: (context, index) => ListDivider(),
|
||||
itemCount: viewModel.paymentList.length,
|
||||
itemBuilder: (BuildContext context, index) {
|
||||
final user = viewModel.user;
|
||||
final paymentId = viewModel.paymentList[index];
|
||||
final payment = state.paymentState.map[paymentId];
|
||||
final client =
|
||||
paymentClientSelector(paymentId, state);
|
||||
|
||||
void showDialog() => showEntityActionsDialog(
|
||||
entity: payment,
|
||||
context: context,
|
||||
user: user,
|
||||
client: client,
|
||||
onEntityAction: viewModel.onEntityAction);
|
||||
|
||||
return PaymentListItem(
|
||||
user: viewModel.user,
|
||||
filter: viewModel.filter,
|
||||
|
|
@ -146,14 +116,13 @@ class PaymentList extends StatelessWidget {
|
|||
viewModel.onPaymentTap(context, payment),
|
||||
onEntityAction: (EntityAction action) {
|
||||
if (action == EntityAction.more) {
|
||||
_showMenu(context, payment, client);
|
||||
showDialog();
|
||||
} else {
|
||||
viewModel.onEntityAction(
|
||||
context, payment, action);
|
||||
}
|
||||
},
|
||||
onLongPress: () =>
|
||||
_showMenu(context, payment, client),
|
||||
onLongPress: () => showDialog(),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -79,34 +79,9 @@ class PaymentListVM {
|
|||
onPaymentTap: (context, payment) {
|
||||
store.dispatch(ViewPayment(paymentId: payment.id, context: context));
|
||||
},
|
||||
onEntityAction: (context, payment, action) {
|
||||
final localization = AppLocalization.of(context);
|
||||
switch (action) {
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditPayment(context: context, payment: payment));
|
||||
break;
|
||||
case EntityAction.sendEmail:
|
||||
store.dispatch(EmailPaymentRequest(
|
||||
snackBarCompleter(context, localization.emailedPayment),
|
||||
payment));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestorePaymentRequest(
|
||||
snackBarCompleter(context, localization.restoredPayment),
|
||||
payment.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchivePaymentRequest(
|
||||
snackBarCompleter(context, localization.archivedPayment),
|
||||
payment.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeletePaymentRequest(
|
||||
snackBarCompleter(context, localization.deletedPayment),
|
||||
payment.id));
|
||||
break;
|
||||
}
|
||||
},
|
||||
onEntityAction:
|
||||
(BuildContext context, BaseEntity payment, EntityAction action) =>
|
||||
handlePaymentAction(context, payment, action),
|
||||
onClearEntityFilterPressed: () =>
|
||||
store.dispatch(FilterPaymentsByEntity()),
|
||||
onViewClientFilterPressed: (BuildContext context) {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class _PaymentViewState extends State<PaymentView> {
|
|||
user: viewModel.company.user,
|
||||
isSaving: viewModel.isSaving,
|
||||
entity: payment,
|
||||
onSelected: viewModel.onActionSelected,
|
||||
onSelected: viewModel.onEntityAction,
|
||||
entityActions: viewModel.payment.getActions(
|
||||
user: viewModel.company.user, client: client),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/payment/payment_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/payment/payment_actions.dart';
|
||||
|
|
@ -37,7 +35,7 @@ class PaymentViewVM {
|
|||
PaymentViewVM({
|
||||
@required this.payment,
|
||||
@required this.company,
|
||||
@required this.onActionSelected,
|
||||
@required this.onEntityAction,
|
||||
@required this.onEditPressed,
|
||||
@required this.onClientPressed,
|
||||
@required this.onInvoicePressed,
|
||||
|
|
@ -53,53 +51,32 @@ class PaymentViewVM {
|
|||
final client = paymentClientSelector(payment.id, state) ?? ClientEntity();
|
||||
|
||||
return PaymentViewVM(
|
||||
company: state.selectedCompany,
|
||||
isSaving: state.isSaving,
|
||||
isDirty: payment.isNew,
|
||||
isLoading: state.isLoading,
|
||||
payment: payment,
|
||||
onEditPressed: (BuildContext context) {
|
||||
store.dispatch(EditPayment(payment: payment, context: context));
|
||||
},
|
||||
onClientPressed: (context, [bool longPress = false]) => store.dispatch(
|
||||
longPress
|
||||
? EditClient(client: client, context: context)
|
||||
: ViewClient(clientId: client.id, context: context)),
|
||||
onInvoicePressed: (context, [bool longPress = false]) => store.dispatch(
|
||||
longPress
|
||||
? EditInvoice(
|
||||
invoice: state.invoiceState.map[payment.invoiceId],
|
||||
context: context)
|
||||
: ViewInvoice(invoiceId: payment.invoiceId, context: context)),
|
||||
onActionSelected: (BuildContext context, EntityAction action) {
|
||||
final localization = AppLocalization.of(context);
|
||||
switch (action) {
|
||||
case EntityAction.sendEmail:
|
||||
store.dispatch(EmailPaymentRequest(
|
||||
popCompleter(context, localization.emailedPayment), payment));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchivePaymentRequest(
|
||||
popCompleter(context, localization.archivedClient),
|
||||
payment.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeletePaymentRequest(
|
||||
popCompleter(context, localization.deletedClient),
|
||||
payment.id));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestorePaymentRequest(
|
||||
snackBarCompleter(context, localization.restoredClient),
|
||||
payment.id));
|
||||
break;
|
||||
}
|
||||
});
|
||||
company: state.selectedCompany,
|
||||
isSaving: state.isSaving,
|
||||
isDirty: payment.isNew,
|
||||
isLoading: state.isLoading,
|
||||
payment: payment,
|
||||
onEditPressed: (BuildContext context) {
|
||||
store.dispatch(EditPayment(payment: payment, context: context));
|
||||
},
|
||||
onClientPressed: (context, [bool longPress = false]) => store.dispatch(
|
||||
longPress
|
||||
? EditClient(client: client, context: context)
|
||||
: ViewClient(clientId: client.id, context: context)),
|
||||
onInvoicePressed: (context, [bool longPress = false]) => store.dispatch(
|
||||
longPress
|
||||
? EditInvoice(
|
||||
invoice: state.invoiceState.map[payment.invoiceId],
|
||||
context: context)
|
||||
: ViewInvoice(invoiceId: payment.invoiceId, context: context)),
|
||||
onEntityAction: (BuildContext context, EntityAction action) =>
|
||||
handlePaymentAction(context, payment, action),
|
||||
);
|
||||
}
|
||||
|
||||
final PaymentEntity payment;
|
||||
final CompanyEntity company;
|
||||
final Function(BuildContext, EntityAction) onActionSelected;
|
||||
final Function(BuildContext, EntityAction) onEntityAction;
|
||||
final Function(BuildContext) onEditPressed;
|
||||
final Function(BuildContext, [bool]) onInvoicePressed;
|
||||
final Function(BuildContext, [bool]) onClientPressed;
|
||||
|
|
|
|||
|
|
@ -71,8 +71,18 @@ class ProductEditVM {
|
|||
: AppLocalization.of(context).updatedProduct),
|
||||
product: product));
|
||||
},
|
||||
onEntityAction: (BuildContext context, EntityAction action) =>
|
||||
handleProductAction(context, product, action),
|
||||
onEntityAction: (BuildContext context, EntityAction action) {
|
||||
// TODO Add view page for products
|
||||
// Prevent duplicate global key error
|
||||
if (action == EntityAction.clone) {
|
||||
Navigator.pop(context);
|
||||
WidgetsBinding.instance.addPostFrameCallback((duration) {
|
||||
handleProductAction(context, product, action);
|
||||
});
|
||||
} else {
|
||||
handleProductAction(context, product, action);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,27 +46,26 @@ class ProductList extends StatelessWidget {
|
|||
itemBuilder: (BuildContext context, index) {
|
||||
final productId = viewModel.productList[index];
|
||||
final product = viewModel.productMap[productId];
|
||||
|
||||
void showDialog() => showEntityActionsDialog(
|
||||
entity: product,
|
||||
context: context,
|
||||
user: viewModel.user,
|
||||
onEntityAction: viewModel.onEntityAction);
|
||||
|
||||
return ProductListItem(
|
||||
user: viewModel.user,
|
||||
filter: viewModel.filter,
|
||||
product: product,
|
||||
onEntityAction: (EntityAction action) {
|
||||
if (action == EntityAction.more) {
|
||||
showEntityActionsDialog(
|
||||
entity: product,
|
||||
context: context,
|
||||
user: viewModel.user,
|
||||
onEntityAction: viewModel.onEntityAction);
|
||||
showDialog();
|
||||
} else {
|
||||
viewModel.onEntityAction(context, product, action);
|
||||
}
|
||||
},
|
||||
onTap: () => viewModel.onProductTap(context, product),
|
||||
onLongPress: () => showEntityActionsDialog(
|
||||
entity: product,
|
||||
context: context,
|
||||
user: viewModel.user,
|
||||
onEntityAction: viewModel.onEntityAction),
|
||||
onLongPress: () => showDialog(),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.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';
|
||||
|
|
@ -17,43 +18,6 @@ class ProjectList extends StatelessWidget {
|
|||
|
||||
final ProjectListVM viewModel;
|
||||
|
||||
void _showMenu(
|
||||
BuildContext context, ProjectEntity project, ClientEntity client) async {
|
||||
if (project == null || client == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final user = viewModel.user;
|
||||
final message = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) => SimpleDialog(
|
||||
children: project
|
||||
.getActions(
|
||||
user: user, client: client, 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, project, entityAction);
|
||||
},
|
||||
);
|
||||
}
|
||||
}).toList()));
|
||||
|
||||
if (message != null) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: SnackBarRow(
|
||||
message: message,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
|
|
@ -116,30 +80,37 @@ class ProjectList extends StatelessWidget {
|
|||
separatorBuilder: (context, index) => ListDivider(),
|
||||
itemCount: viewModel.projectList.length,
|
||||
itemBuilder: (BuildContext context, index) {
|
||||
final user = viewModel.user;
|
||||
final projectId = viewModel.projectList[index];
|
||||
final project = viewModel.projectMap[projectId];
|
||||
final client =
|
||||
viewModel.clientMap[project.clientId] ??
|
||||
ClientEntity(id: project.clientId);
|
||||
|
||||
void showDialog() => showEntityActionsDialog(
|
||||
entity: project,
|
||||
context: context,
|
||||
user: user,
|
||||
client: client,
|
||||
onEntityAction: viewModel.onEntityAction);
|
||||
|
||||
return ProjectListItem(
|
||||
user: viewModel.user,
|
||||
filter: viewModel.filter,
|
||||
project: project,
|
||||
client:
|
||||
viewModel.clientMap[project.clientId] ??
|
||||
client: viewModel.clientMap[project.clientId] ??
|
||||
ClientEntity(),
|
||||
onTap: () =>
|
||||
viewModel.onProjectTap(context, project),
|
||||
onEntityAction: (EntityAction action) {
|
||||
if (action == EntityAction.more) {
|
||||
_showMenu(context, project, client);
|
||||
showDialog();
|
||||
} else {
|
||||
viewModel.onEntityAction(
|
||||
context, project, action);
|
||||
}
|
||||
},
|
||||
onLongPress: () =>
|
||||
_showMenu(context, project, client),
|
||||
onLongPress: () => showDialog(),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -84,46 +83,9 @@ class ProjectListVM {
|
|||
onProjectTap: (context, project) {
|
||||
store.dispatch(ViewProject(projectId: project.id, context: context));
|
||||
},
|
||||
onEntityAction: (context, project, action) {
|
||||
switch (action) {
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditProject(context: context, project: project));
|
||||
break;
|
||||
case EntityAction.newInvoice:
|
||||
final items =
|
||||
convertProjectToInvoiceItem(project: project, context: context);
|
||||
store.dispatch(EditInvoice(
|
||||
invoice: InvoiceEntity(company: state.selectedCompany)
|
||||
.rebuild((b) => b
|
||||
..hasTasks = true
|
||||
..clientId = project.clientId
|
||||
..invoiceItems.addAll(items)),
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.clone:
|
||||
store.dispatch(
|
||||
EditProject(context: context, project: project.clone));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreProjectRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).restoredProject),
|
||||
project.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveProjectRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).archivedProject),
|
||||
project.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteProjectRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).deletedProject),
|
||||
project.id));
|
||||
break;
|
||||
}
|
||||
},
|
||||
onEntityAction:
|
||||
(BuildContext context, BaseEntity project, EntityAction action) =>
|
||||
handleProjectAction(context, project, action),
|
||||
onRefreshed: (context) => _handleRefresh(context),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,11 +187,11 @@ class _CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
: Container(),
|
||||
ActionMenuButton(
|
||||
user: user,
|
||||
entityActions: project.getActions(
|
||||
client: viewModel.client, user: user),
|
||||
entityActions:
|
||||
project.getActions(client: viewModel.client, user: user),
|
||||
isSaving: viewModel.isSaving,
|
||||
entity: project,
|
||||
onSelected: viewModel.onActionSelected,
|
||||
onSelected: viewModel.onEntityAction,
|
||||
)
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/project/project_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/redux/task/task_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/project/project_screen.dart';
|
||||
|
|
@ -41,7 +39,7 @@ class ProjectViewVM {
|
|||
@required this.project,
|
||||
@required this.client,
|
||||
@required this.company,
|
||||
@required this.onActionSelected,
|
||||
@required this.onEntityAction,
|
||||
@required this.onTasksPressed,
|
||||
@required this.onEditPressed,
|
||||
@required this.onBackPressed,
|
||||
|
|
@ -99,48 +97,17 @@ class ProjectViewVM {
|
|||
store.dispatch(UpdateCurrentRoute(ProjectScreen.route));
|
||||
}
|
||||
},
|
||||
onActionSelected: (BuildContext context, EntityAction action) {
|
||||
final localization = AppLocalization.of(context);
|
||||
switch (action) {
|
||||
case EntityAction.newInvoice:
|
||||
final items = convertProjectToInvoiceItem(
|
||||
project: project, context: context);
|
||||
store.dispatch(EditInvoice(
|
||||
invoice: InvoiceEntity(company: state.selectedCompany)
|
||||
.rebuild((b) => b
|
||||
..hasTasks = true
|
||||
..clientId = project.clientId
|
||||
..invoiceItems.addAll(items)),
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.clone:
|
||||
store.dispatch(
|
||||
EditProject(context: context, project: project.clone));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveProjectRequest(
|
||||
popCompleter(context, localization.archivedProject),
|
||||
project.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteProjectRequest(
|
||||
popCompleter(context, localization.deletedProject),
|
||||
project.id));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreProjectRequest(
|
||||
snackBarCompleter(context, localization.restoredProject),
|
||||
project.id));
|
||||
break;
|
||||
}
|
||||
});
|
||||
onEntityAction: (BuildContext context, EntityAction action) =>
|
||||
handleProjectAction(context, project, action),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
final AppState state;
|
||||
final ProjectEntity project;
|
||||
final ClientEntity client;
|
||||
final CompanyEntity company;
|
||||
final Function(BuildContext, EntityAction) onActionSelected;
|
||||
final Function(BuildContext, EntityAction) onEntityAction;
|
||||
final Function(BuildContext) onEditPressed;
|
||||
final Function(BuildContext, [bool]) onClientPressed;
|
||||
final Function onBackPressed;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'package:built_collection/built_collection.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/quote/quote_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/quote/quote_selectors.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -13,11 +12,9 @@ import 'package:invoiceninja_flutter/ui/invoice/invoice_list.dart';
|
|||
import 'package:invoiceninja_flutter/ui/invoice/invoice_list_vm.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/pdf.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class QuoteListBuilder extends StatelessWidget {
|
||||
const QuoteListBuilder({Key key}) : super(key: key);
|
||||
|
|
@ -99,71 +96,9 @@ class QuoteListVM extends EntityListVM {
|
|||
onViewEntityFilterPressed: (BuildContext context) => store.dispatch(
|
||||
ViewClient(
|
||||
clientId: state.quoteListState.filterEntityId, context: context)),
|
||||
onEntityAction: (context, quote, action) async {
|
||||
final localization = AppLocalization.of(context);
|
||||
switch (action) {
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditQuote(context: context, quote: quote));
|
||||
break;
|
||||
case EntityAction.pdf:
|
||||
viewPdf(quote, context);
|
||||
break;
|
||||
case EntityAction.clientPortal:
|
||||
if (await canLaunch(quote.invitationSilentLink)) {
|
||||
await launch(quote.invitationSilentLink,
|
||||
forceSafariVC: false, forceWebView: false);
|
||||
}
|
||||
break;
|
||||
case EntityAction.viewInvoice:
|
||||
store.dispatch(
|
||||
ViewInvoice(context: context, invoiceId: quote.quoteInvoiceId));
|
||||
break;
|
||||
case EntityAction.convert:
|
||||
final Completer<InvoiceEntity> completer =
|
||||
Completer<InvoiceEntity>();
|
||||
store.dispatch(ConvertQuote(completer, quote.id));
|
||||
completer.future.then((InvoiceEntity invoice) {
|
||||
store.dispatch(
|
||||
ViewInvoice(invoiceId: invoice.id, context: context));
|
||||
});
|
||||
break;
|
||||
case EntityAction.markSent:
|
||||
store.dispatch(MarkSentQuoteRequest(
|
||||
snackBarCompleter(context, localization.markedQuoteAsSent),
|
||||
quote.id));
|
||||
break;
|
||||
case EntityAction.sendEmail:
|
||||
store.dispatch(ShowEmailQuote(
|
||||
completer:
|
||||
snackBarCompleter(context, localization.emailedQuote),
|
||||
quote: quote,
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.cloneToInvoice:
|
||||
store.dispatch(
|
||||
EditInvoice(context: context, invoice: quote.cloneToInvoice));
|
||||
break;
|
||||
case EntityAction.cloneToQuote:
|
||||
store.dispatch(
|
||||
EditQuote(context: context, quote: quote.cloneToQuote));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreQuoteRequest(
|
||||
snackBarCompleter(context, localization.restoredQuote),
|
||||
quote.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveQuoteRequest(
|
||||
snackBarCompleter(context, localization.archivedQuote),
|
||||
quote.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteQuoteRequest(
|
||||
snackBarCompleter(context, localization.deletedQuote),
|
||||
quote.id));
|
||||
break;
|
||||
}
|
||||
},
|
||||
onEntityAction:
|
||||
(BuildContext context, BaseEntity quote, EntityAction action) =>
|
||||
handleQuoteAction(context, quote, action),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,20 +3,17 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/view/invoice_view.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/view/invoice_view_vm.dart';
|
||||
import 'package:invoiceninja_flutter/ui/quote/quote_screen.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/pdf.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/quote/quote_actions.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class QuoteViewScreen extends StatelessWidget {
|
||||
const QuoteViewScreen({Key key}) : super(key: key);
|
||||
|
|
@ -46,7 +43,7 @@ class QuoteViewVM extends EntityViewVM {
|
|||
ClientEntity client,
|
||||
bool isSaving,
|
||||
bool isDirty,
|
||||
Function(BuildContext, EntityAction) onActionSelected,
|
||||
Function(BuildContext, EntityAction) onEntityAction,
|
||||
Function(BuildContext, [InvoiceItemEntity]) onEditPressed,
|
||||
Function(BuildContext, [bool]) onClientPressed,
|
||||
Function(BuildContext) onPaymentsPressed,
|
||||
|
|
@ -59,7 +56,7 @@ class QuoteViewVM extends EntityViewVM {
|
|||
client: client,
|
||||
isSaving: isSaving,
|
||||
isDirty: isDirty,
|
||||
onActionSelected: onActionSelected,
|
||||
onActionSelected: onEntityAction,
|
||||
onEditPressed: onEditPressed,
|
||||
onClientPressed: onClientPressed,
|
||||
onPaymentsPressed: onPaymentsPressed,
|
||||
|
|
@ -113,67 +110,9 @@ class QuoteViewVM extends EntityViewVM {
|
|||
store.dispatch(longPress
|
||||
? EditClient(client: client, context: context)
|
||||
: ViewClient(clientId: client.id, context: context)),
|
||||
onActionSelected: (BuildContext context, EntityAction action) async {
|
||||
final localization = AppLocalization.of(context);
|
||||
switch (action) {
|
||||
case EntityAction.pdf:
|
||||
viewPdf(quote, context);
|
||||
break;
|
||||
case EntityAction.clientPortal:
|
||||
if (await canLaunch(quote.invitationSilentLink)) {
|
||||
await launch(quote.invitationSilentLink,
|
||||
forceSafariVC: false, forceWebView: false);
|
||||
}
|
||||
break;
|
||||
case EntityAction.viewInvoice:
|
||||
store.dispatch(ViewInvoice(
|
||||
context: context, invoiceId: quote.quoteInvoiceId));
|
||||
break;
|
||||
case EntityAction.convert:
|
||||
final Completer<InvoiceEntity> completer =
|
||||
Completer<InvoiceEntity>();
|
||||
store.dispatch(ConvertQuote(completer, quote.id));
|
||||
completer.future.then((InvoiceEntity invoice) {
|
||||
store.dispatch(
|
||||
ViewInvoice(invoiceId: invoice.id, context: context));
|
||||
});
|
||||
break;
|
||||
case EntityAction.markSent:
|
||||
store.dispatch(MarkSentQuoteRequest(
|
||||
snackBarCompleter(context, localization.markedQuoteAsSent),
|
||||
quote.id));
|
||||
break;
|
||||
case EntityAction.sendEmail:
|
||||
store.dispatch(ShowEmailQuote(
|
||||
completer:
|
||||
snackBarCompleter(context, localization.emailedQuote),
|
||||
quote: quote,
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveQuoteRequest(
|
||||
popCompleter(context, localization.archivedQuote), quote.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteQuoteRequest(
|
||||
popCompleter(context, localization.deletedQuote), quote.id));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreQuoteRequest(
|
||||
snackBarCompleter(context, localization.restoredQuote),
|
||||
quote.id));
|
||||
break;
|
||||
case EntityAction.cloneToInvoice:
|
||||
Navigator.of(context).pop();
|
||||
store.dispatch(
|
||||
EditInvoice(context: context, invoice: quote.cloneToInvoice));
|
||||
break;
|
||||
case EntityAction.cloneToQuote:
|
||||
Navigator.of(context).pop();
|
||||
store.dispatch(
|
||||
EditQuote(context: context, quote: quote.cloneToQuote));
|
||||
break;
|
||||
}
|
||||
});
|
||||
onEntityAction: (BuildContext context, EntityAction action) =>
|
||||
handleQuoteAction(context, quote, action),
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.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/task/task_list_item.dart';
|
||||
import 'package:invoiceninja_flutter/ui/task/task_list_vm.dart';
|
||||
import 'package:invoiceninja_flutter/utils/icons.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
class TaskList extends StatelessWidget {
|
||||
|
|
@ -17,43 +16,6 @@ class TaskList extends StatelessWidget {
|
|||
|
||||
final TaskListVM viewModel;
|
||||
|
||||
void _showMenu(
|
||||
BuildContext context, TaskEntity task, ClientEntity client) async {
|
||||
if (task == null || client == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final user = viewModel.user;
|
||||
final message = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) => SimpleDialog(
|
||||
children: task
|
||||
.getActions(
|
||||
user: user, client: client, 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, task, entityAction);
|
||||
},
|
||||
);
|
||||
}
|
||||
}).toList()));
|
||||
|
||||
if (message != null) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: SnackBarRow(
|
||||
message: message,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
|
|
@ -127,10 +89,19 @@ class TaskList extends StatelessWidget {
|
|||
separatorBuilder: (context, index) => ListDivider(),
|
||||
itemCount: viewModel.taskList.length,
|
||||
itemBuilder: (BuildContext context, index) {
|
||||
final user = viewModel.user;
|
||||
final taskId = viewModel.taskList[index];
|
||||
final task = viewModel.taskMap[taskId];
|
||||
final client = viewModel.clientMap[task.clientId] ??
|
||||
ClientEntity();
|
||||
|
||||
void showDialog() => showEntityActionsDialog(
|
||||
entity: task,
|
||||
context: context,
|
||||
user: user,
|
||||
client: client,
|
||||
onEntityAction: viewModel.onEntityAction);
|
||||
|
||||
return TaskListItem(
|
||||
user: viewModel.user,
|
||||
filter: viewModel.filter,
|
||||
|
|
@ -139,18 +110,16 @@ class TaskList extends StatelessWidget {
|
|||
ClientEntity(),
|
||||
project: viewModel
|
||||
.state.projectState.map[task.projectId],
|
||||
onTap: () =>
|
||||
viewModel.onTaskTap(context, task),
|
||||
onTap: () => viewModel.onTaskTap(context, task),
|
||||
onEntityAction: (EntityAction action) {
|
||||
if (action == EntityAction.more) {
|
||||
_showMenu(context, task, client);
|
||||
showDialog();
|
||||
} else {
|
||||
viewModel.onEntityAction(
|
||||
context, task, action);
|
||||
}
|
||||
},
|
||||
onLongPress: () =>
|
||||
_showMenu(context, task, client),
|
||||
onLongPress: () => showDialog(),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/project/project_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
|
@ -99,73 +96,9 @@ class TaskListVM {
|
|||
onTaskTap: (context, task) {
|
||||
store.dispatch(ViewTask(taskId: task.id, context: context));
|
||||
},
|
||||
onEntityAction: (context, task, action) {
|
||||
switch (action) {
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditTask(context: context, task: task));
|
||||
break;
|
||||
case EntityAction.start:
|
||||
case EntityAction.stop:
|
||||
case EntityAction.resume:
|
||||
final Completer<TaskEntity> completer = new Completer<TaskEntity>();
|
||||
final localization = AppLocalization.of(context);
|
||||
store.dispatch(
|
||||
SaveTaskRequest(completer: completer, task: task.toggle()));
|
||||
completer.future.then((savedTask) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: SnackBarRow(
|
||||
message: savedTask.isRunning
|
||||
? (savedTask.duration > 0
|
||||
? localization.resumedTask
|
||||
: localization.startedTask)
|
||||
: localization.stoppedTask,
|
||||
)));
|
||||
}).catchError((Object error) {
|
||||
showDialog<ErrorDialog>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return ErrorDialog(error);
|
||||
});
|
||||
});
|
||||
|
||||
break;
|
||||
case EntityAction.newInvoice:
|
||||
final item = convertTaskToInvoiceItem(task: task, context: context);
|
||||
store.dispatch(EditInvoice(
|
||||
invoice: InvoiceEntity(company: state.selectedCompany)
|
||||
.rebuild((b) => b
|
||||
..hasTasks = true
|
||||
..clientId = task.clientId
|
||||
..invoiceItems.add(item)),
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.viewInvoice:
|
||||
store.dispatch(
|
||||
ViewInvoice(invoiceId: task.invoiceId, context: context));
|
||||
break;
|
||||
case EntityAction.clone:
|
||||
store.dispatch(EditTask(context: context, task: task.clone));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreTaskRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).restoredTask),
|
||||
task.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveTaskRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).archivedTask),
|
||||
task.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteTaskRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).deletedTask),
|
||||
task.id));
|
||||
break;
|
||||
}
|
||||
},
|
||||
onEntityAction:
|
||||
(BuildContext context, BaseEntity task, EntityAction action) =>
|
||||
handleTaskAction(context, task, action),
|
||||
onRefreshed: (context) => _handleRefresh(context),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ class _CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
task.getActions(client: viewModel.client, user: user),
|
||||
isSaving: viewModel.isSaving,
|
||||
entity: task,
|
||||
onSelected: viewModel.onActionSelected,
|
||||
onSelected: viewModel.onEntityAction,
|
||||
)
|
||||
],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/project/project_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/task/task_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
|
|
@ -47,7 +45,7 @@ class TaskViewVM {
|
|||
@required this.project,
|
||||
@required this.company,
|
||||
@required this.state,
|
||||
@required this.onActionSelected,
|
||||
@required this.onEntityAction,
|
||||
@required this.onEditPressed,
|
||||
@required this.onBackPressed,
|
||||
@required this.onRefreshed,
|
||||
|
|
@ -151,47 +149,11 @@ class TaskViewVM {
|
|||
store.dispatch(UpdateCurrentRoute(TaskScreen.route));
|
||||
}
|
||||
},
|
||||
onActionSelected: (BuildContext context, EntityAction action) {
|
||||
final localization = AppLocalization.of(context);
|
||||
switch (action) {
|
||||
case EntityAction.resume:
|
||||
case EntityAction.start:
|
||||
case EntityAction.stop:
|
||||
_toggleTask(context);
|
||||
break;
|
||||
case EntityAction.newInvoice:
|
||||
final item =
|
||||
convertTaskToInvoiceItem(task: task, context: context);
|
||||
store.dispatch(EditInvoice(
|
||||
invoice: InvoiceEntity(company: state.selectedCompany)
|
||||
.rebuild((b) => b
|
||||
..hasTasks = true
|
||||
..clientId = task.clientId
|
||||
..invoiceItems.add(item)),
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.clone:
|
||||
store.dispatch(EditTask(context: context, task: task.clone));
|
||||
break;
|
||||
case EntityAction.viewInvoice:
|
||||
store.dispatch(
|
||||
ViewInvoice(invoiceId: task.invoiceId, context: context));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveTaskRequest(
|
||||
popCompleter(context, localization.archivedTask), task.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteTaskRequest(
|
||||
popCompleter(context, localization.deletedTask), task.id));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreTaskRequest(
|
||||
snackBarCompleter(context, localization.restoredTask),
|
||||
task.id));
|
||||
break;
|
||||
}
|
||||
});
|
||||
onEntityAction: (BuildContext context, EntityAction action) =>
|
||||
handleTaskAction(context, task, action),
|
||||
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
final AppState state;
|
||||
|
|
@ -199,7 +161,7 @@ class TaskViewVM {
|
|||
final ClientEntity client;
|
||||
final ProjectEntity project;
|
||||
final CompanyEntity company;
|
||||
final Function(BuildContext, EntityAction) onActionSelected;
|
||||
final Function(BuildContext, EntityAction) onEntityAction;
|
||||
final Function(BuildContext, [TaskTime]) onEditPressed;
|
||||
final Function onBackPressed;
|
||||
final Function(BuildContext) onFabPressed;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.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';
|
||||
|
|
@ -17,41 +18,6 @@ class VendorList extends StatelessWidget {
|
|||
|
||||
final VendorListVM viewModel;
|
||||
|
||||
void _showMenu(BuildContext context, VendorEntity vendor) async {
|
||||
if (vendor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final user = viewModel.user;
|
||||
final message = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) => SimpleDialog(
|
||||
children: vendor
|
||||
.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, vendor, entityAction);
|
||||
},
|
||||
);
|
||||
}
|
||||
}).toList()));
|
||||
|
||||
if (message != null) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: SnackBarRow(
|
||||
message: message,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
|
|
@ -78,8 +44,16 @@ class VendorList extends StatelessWidget {
|
|||
separatorBuilder: (context, index) => ListDivider(),
|
||||
itemCount: viewModel.vendorList.length,
|
||||
itemBuilder: (BuildContext context, index) {
|
||||
final user = viewModel.user;
|
||||
final vendorId = viewModel.vendorList[index];
|
||||
final vendor = viewModel.vendorMap[vendorId];
|
||||
|
||||
void showDialog() => showEntityActionsDialog(
|
||||
entity: vendor,
|
||||
context: context,
|
||||
user: user,
|
||||
onEntityAction: viewModel.onEntityAction);
|
||||
|
||||
return VendorListItem(
|
||||
user: viewModel.user,
|
||||
filter: viewModel.filter,
|
||||
|
|
@ -88,13 +62,13 @@ class VendorList extends StatelessWidget {
|
|||
viewModel.onVendorTap(context, vendor),
|
||||
onEntityAction: (EntityAction action) {
|
||||
if (action == EntityAction.more) {
|
||||
_showMenu(context, vendor);
|
||||
showDialog();
|
||||
} else {
|
||||
viewModel.onEntityAction(
|
||||
context, vendor, action);
|
||||
}
|
||||
},
|
||||
onLongPress: () => _showMenu(context, vendor),
|
||||
onLongPress: () => showDialog(),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:built_collection/built_collection.dart';
|
||||
import 'package:invoiceninja_flutter/redux/expense/expense_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
|
|
@ -78,37 +77,9 @@ class VendorListVM {
|
|||
onVendorTap: (context, vendor) {
|
||||
store.dispatch(ViewVendor(vendorId: vendor.id, context: context));
|
||||
},
|
||||
onEntityAction: (context, vendor, action) {
|
||||
switch (action) {
|
||||
case EntityAction.edit:
|
||||
store.dispatch(EditVendor(context: context, vendor: vendor));
|
||||
break;
|
||||
case EntityAction.newExpense:
|
||||
store.dispatch(EditExpense(
|
||||
expense: ExpenseEntity(
|
||||
company: state.selectedCompany, vendor: vendor),
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreVendorRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).restoredVendor),
|
||||
vendor.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveVendorRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).archivedVendor),
|
||||
vendor.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteVendorRequest(
|
||||
snackBarCompleter(
|
||||
context, AppLocalization.of(context).deletedVendor),
|
||||
vendor.id));
|
||||
break;
|
||||
}
|
||||
},
|
||||
onEntityAction:
|
||||
(BuildContext context, BaseEntity vendor, EntityAction action) =>
|
||||
handleVendorAction(context, vendor, action),
|
||||
onRefreshed: (context) => _handleRefresh(context),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ class _CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
user: viewModel.company.user,
|
||||
isSaving: viewModel.isSaving,
|
||||
entity: vendor,
|
||||
onSelected: viewModel.onActionSelected,
|
||||
onSelected: viewModel.onEntityAction,
|
||||
entityActions: viewModel.vendor.getActions(user: user),
|
||||
)
|
||||
],
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class VendorViewVM {
|
|||
@required this.vendor,
|
||||
@required this.company,
|
||||
@required this.onAddExpensePressed,
|
||||
@required this.onActionSelected,
|
||||
@required this.onEntityAction,
|
||||
@required this.onEntityPressed,
|
||||
@required this.onEditPressed,
|
||||
@required this.onBackPressed,
|
||||
|
|
@ -107,38 +107,16 @@ class VendorViewVM {
|
|||
onAddExpensePressed: (context) => store.dispatch(EditExpense(
|
||||
expense: ExpenseEntity(company: company, vendor: vendor),
|
||||
context: context)),
|
||||
onActionSelected: (BuildContext context, EntityAction action) {
|
||||
final localization = AppLocalization.of(context);
|
||||
switch (action) {
|
||||
case EntityAction.newExpense:
|
||||
store.dispatch(EditExpense(
|
||||
expense: ExpenseEntity(
|
||||
company: state.selectedCompany, vendor: vendor),
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveVendorRequest(
|
||||
popCompleter(context, localization.archivedVendor),
|
||||
vendor.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteVendorRequest(
|
||||
popCompleter(context, localization.deletedVendor),
|
||||
vendor.id));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreVendorRequest(
|
||||
snackBarCompleter(context, localization.restoredVendor),
|
||||
vendor.id));
|
||||
break;
|
||||
}
|
||||
});
|
||||
onEntityAction: (BuildContext context, EntityAction action) =>
|
||||
handleVendorAction(context, vendor, action),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
final AppState state;
|
||||
final VendorEntity vendor;
|
||||
final CompanyEntity company;
|
||||
final Function(BuildContext, EntityAction) onActionSelected;
|
||||
final Function(BuildContext, EntityAction) onEntityAction;
|
||||
final Function(BuildContext) onEditPressed;
|
||||
final Function(BuildContext, EntityType, [bool]) onEntityPressed;
|
||||
final Function onBackPressed;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -31,4 +31,4 @@ class ProductKeys {
|
|||
|
||||
class SettingsKeys {
|
||||
static const String drawer = 'settingsDrawer';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import 'package:invoiceninja_flutter/constants.dart';
|
|||
import 'i18n.dart';
|
||||
|
||||
class AppLocalization extends LocaleCodeAware with LocalizationsProvider {
|
||||
AppLocalization(this.locale): super(locale.toString());
|
||||
AppLocalization(this.locale) : super(locale.toString());
|
||||
|
||||
final Locale locale;
|
||||
|
||||
|
|
@ -18,8 +18,6 @@ class AppLocalization extends LocaleCodeAware with LocalizationsProvider {
|
|||
static AppLocalization of(BuildContext context) {
|
||||
return Localizations.of<AppLocalization>(context, AppLocalization);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalization> {
|
||||
|
|
|
|||
Loading…
Reference in New Issue