From e895d009817d3e882ea4fc608deca9fe80dcb63c Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Mon, 2 Mar 2020 21:41:03 +0200 Subject: [PATCH] Starter... --- lib/redux/credit/credit_state.dart | 4 + lib/redux/credit/credit_state.g.dart | 17 ++- lib/ui/credit/edit/credit_edit_vm.dart | 92 ++++++------ lib/ui/credit/view/credit_view.dart | 51 ------- lib/ui/credit/view/credit_view_vm.dart | 135 +++++++++++++----- lib/ui/invoice/edit/invoice_edit_desktop.dart | 4 +- lib/utils/i18n.dart | 23 +-- starter.sh | 2 +- 8 files changed, 183 insertions(+), 145 deletions(-) delete mode 100644 lib/ui/credit/view/credit_view.dart diff --git a/lib/redux/credit/credit_state.dart b/lib/redux/credit/credit_state.dart index e58c5482d..49d7c25f5 100644 --- a/lib/redux/credit/credit_state.dart +++ b/lib/redux/credit/credit_state.dart @@ -68,6 +68,10 @@ abstract class CreditUIState extends Object @nullable InvoiceEntity get editing; + @nullable + @BuiltValueField(serialize: false) + int get editingItemIndex; + @override bool get isCreatingNew => editing.isNew; diff --git a/lib/redux/credit/credit_state.g.dart b/lib/redux/credit/credit_state.g.dart index 697d891b5..c6e60ff9c 100644 --- a/lib/redux/credit/credit_state.g.dart +++ b/lib/redux/credit/credit_state.g.dart @@ -255,6 +255,8 @@ class _$CreditUIState extends CreditUIState { @override final InvoiceEntity editing; @override + final int editingItemIndex; + @override final ListUIState listUIState; @override final String selectedId; @@ -268,6 +270,7 @@ class _$CreditUIState extends CreditUIState { _$CreditUIState._( {this.editing, + this.editingItemIndex, this.listUIState, this.selectedId, this.saveCompleter, @@ -290,6 +293,7 @@ class _$CreditUIState extends CreditUIState { if (identical(other, this)) return true; return other is CreditUIState && editing == other.editing && + editingItemIndex == other.editingItemIndex && listUIState == other.listUIState && selectedId == other.selectedId && saveCompleter == other.saveCompleter && @@ -300,7 +304,9 @@ class _$CreditUIState extends CreditUIState { int get hashCode { return $jf($jc( $jc( - $jc($jc($jc(0, editing.hashCode), listUIState.hashCode), + $jc( + $jc($jc($jc(0, editing.hashCode), editingItemIndex.hashCode), + listUIState.hashCode), selectedId.hashCode), saveCompleter.hashCode), cancelCompleter.hashCode)); @@ -310,6 +316,7 @@ class _$CreditUIState extends CreditUIState { String toString() { return (newBuiltValueToStringHelper('CreditUIState') ..add('editing', editing) + ..add('editingItemIndex', editingItemIndex) ..add('listUIState', listUIState) ..add('selectedId', selectedId) ..add('saveCompleter', saveCompleter) @@ -327,6 +334,11 @@ class CreditUIStateBuilder _$this._editing ??= new InvoiceEntityBuilder(); set editing(InvoiceEntityBuilder editing) => _$this._editing = editing; + int _editingItemIndex; + int get editingItemIndex => _$this._editingItemIndex; + set editingItemIndex(int editingItemIndex) => + _$this._editingItemIndex = editingItemIndex; + ListUIStateBuilder _listUIState; ListUIStateBuilder get listUIState => _$this._listUIState ??= new ListUIStateBuilder(); @@ -352,6 +364,7 @@ class CreditUIStateBuilder CreditUIStateBuilder get _$this { if (_$v != null) { _editing = _$v.editing?.toBuilder(); + _editingItemIndex = _$v.editingItemIndex; _listUIState = _$v.listUIState?.toBuilder(); _selectedId = _$v.selectedId; _saveCompleter = _$v.saveCompleter; @@ -381,6 +394,7 @@ class CreditUIStateBuilder _$result = _$v ?? new _$CreditUIState._( editing: _editing?.build(), + editingItemIndex: editingItemIndex, listUIState: listUIState.build(), selectedId: selectedId, saveCompleter: saveCompleter, @@ -390,6 +404,7 @@ class CreditUIStateBuilder try { _$failedField = 'editing'; _editing?.build(); + _$failedField = 'listUIState'; listUIState.build(); } catch (e) { diff --git a/lib/ui/credit/edit/credit_edit_vm.dart b/lib/ui/credit/edit/credit_edit_vm.dart index cc157dc1c..474f0682a 100644 --- a/lib/ui/credit/edit/credit_edit_vm.dart +++ b/lib/ui/credit/edit/credit_edit_vm.dart @@ -2,20 +2,21 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; +import 'package:invoiceninja_flutter/redux/app/app_actions.dart'; +import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart'; import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart'; -import 'package:invoiceninja_flutter/ui/credit/credit_screen.dart'; +import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart'; +import 'package:invoiceninja_flutter/ui/credit/edit/credit_edit.dart'; +import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_vm.dart'; +import 'package:invoiceninja_flutter/ui/credit/view/credit_view_vm.dart'; import 'package:invoiceninja_flutter/utils/platforms.dart'; import 'package:redux/redux.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; -import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart'; -import 'package:invoiceninja_flutter/ui/credit/view/credit_view_vm.dart'; -import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart'; -import 'package:invoiceninja_flutter/data/models/credit_model.dart'; -import 'package:invoiceninja_flutter/ui/credit/edit/credit_edit.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; class CreditEditScreen extends StatelessWidget { const CreditEditScreen({Key key}) : super(key: key); + static const String route = '/credit/edit'; @override @@ -27,60 +28,59 @@ class CreditEditScreen extends StatelessWidget { builder: (context, viewModel) { return CreditEdit( viewModel: viewModel, - key: ValueKey(viewModel.credit.id), ); }, ); } } -class CreditEditVM { +class CreditEditVM extends EntityEditVM { CreditEditVM({ - @required this.state, - @required this.credit, - @required this.company, - @required this.onChanged, - @required this.isSaving, - @required this.origCredit, - @required this.onSavePressed, - @required this.onCancelPressed, - @required this.isLoading, - }); + AppState state, + CompanyEntity company, + InvoiceEntity invoice, + int invoiceItemIndex, + InvoiceEntity origInvoice, + Function(BuildContext) onSavePressed, + Function(List, String) onItemsAdded, + bool isSaving, + Function(BuildContext) onCancelPressed, + }) : super( + state: state, + company: company, + invoice: invoice, + invoiceItemIndex: invoiceItemIndex, + origInvoice: origInvoice, + onSavePressed: onSavePressed, + onItemsAdded: onItemsAdded, + isSaving: isSaving, + onCancelPressed: onCancelPressed, + ); factory CreditEditVM.fromStore(Store store) { - final state = store.state; + final AppState state = store.state; final credit = state.creditUIState.editing; return CreditEditVM( state: state, - isLoading: state.isLoading, + company: state.company, isSaving: state.isSaving, - origCredit: state.creditState.map[credit.id], - credit: credit, - company: state.selectedCompany, - onChanged: (InvoiceEntity credit) { - store.dispatch(UpdateCredit(credit)); - }, - onCancelPressed: (BuildContext context) { - store.dispatch( - EditCredit(credit: InvoiceEntity(), context: context, force: true)); - store.dispatch(UpdateCurrentRoute(state.uiState.previousRoute)); - }, + invoice: credit, + invoiceItemIndex: state.creditUIState.editingItemIndex, + origInvoice: store.state.creditState.map[credit.id], onSavePressed: (BuildContext context) { - final Completer completer = new Completer(); + final Completer completer = Completer(); store.dispatch(SaveCreditRequest(completer: completer, credit: credit)); return completer.future.then((savedCredit) { if (isMobile(context)) { store.dispatch(UpdateCurrentRoute(CreditViewScreen.route)); if (credit.isNew) { - Navigator.of(context) - .pushReplacementNamed(CreditViewScreen.route); + Navigator.of(context).pushReplacementNamed(CreditViewScreen.route); } else { Navigator.of(context).pop(savedCredit); } } else { - store.dispatch(ViewCredit( - context: context, creditId: savedCredit.id, force: true)); + viewEntity(context: context, entity: savedCredit, force: true); } }).catchError((Object error) { showDialog( @@ -90,16 +90,16 @@ class CreditEditVM { }); }); }, + onItemsAdded: (items, clientId) { + if (items.length == 1) { + store.dispatch(EditCreditItem(credit.lineItems.length)); + } + store.dispatch(AddCreditItems(items)); + }, + onCancelPressed: (BuildContext context) { + createEntity(context: context, entity: InvoiceEntity(), force: true); + store.dispatch(UpdateCurrentRoute(state.uiState.previousRoute)); + }, ); } - - final InvoiceEntity credit; - final CompanyEntity company; - final Function(InvoiceEntity) onChanged; - final Function(BuildContext) onSavePressed; - final Function(BuildContext) onCancelPressed; - final bool isLoading; - final bool isSaving; - final InvoiceEntity origCredit; - final AppState state; } diff --git a/lib/ui/credit/view/credit_view.dart b/lib/ui/credit/view/credit_view.dart deleted file mode 100644 index 4712dab69..000000000 --- a/lib/ui/credit/view/credit_view.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:invoiceninja_flutter/ui/app/buttons/edit_icon_button.dart'; -import 'package:invoiceninja_flutter/ui/app/actions_menu_button.dart'; -import 'package:invoiceninja_flutter/ui/credit/view/credit_view_vm.dart'; -import 'package:invoiceninja_flutter/ui/app/form_card.dart'; -import 'package:invoiceninja_flutter/ui/app/entities/entity_state_title.dart'; - -class CreditView extends StatefulWidget { - const CreditView({ - Key key, - @required this.viewModel, - }) : super(key: key); - - final CreditViewVM viewModel; - - @override - _CreditViewState createState() => new _CreditViewState(); -} - -class _CreditViewState extends State { - @override - Widget build(BuildContext context) { - final viewModel = widget.viewModel; - final userCompany = viewModel.state.userCompany; - final credit = viewModel.credit; - - return Scaffold( - appBar: AppBar( - title: EntityStateTitle(entity: credit), - actions: [ - userCompany.canEditEntity(credit) - ? EditIconButton( - isVisible: !credit.isDeleted, - onPressed: () => viewModel.onEditPressed(context), - ) - : Container(), - ActionMenuButton( - entityActions: credit.getActions(userCompany: userCompany), - isSaving: viewModel.isSaving, - entity: credit, - onSelected: viewModel.onEntityAction, - ) - ], - ), - body: FormCard(children: [ - // STARTER: widgets - do not remove comment - ]), - ); - } -} diff --git a/lib/ui/credit/view/credit_view_vm.dart b/lib/ui/credit/view/credit_view_vm.dart index 53d39edf3..6243322c4 100644 --- a/lib/ui/credit/view/credit_view_vm.dart +++ b/lib/ui/credit/view/credit_view_vm.dart @@ -1,54 +1,84 @@ import 'dart:async'; -import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart'; +import 'package:flutter_redux/flutter_redux.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/redux/app/app_actions.dart'; +import 'package:invoiceninja_flutter/redux/app/app_state.dart'; +import 'package:invoiceninja_flutter/redux/document/document_actions.dart'; +import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart'; +import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart'; +import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart'; +import 'package:invoiceninja_flutter/ui/app/snackbar_row.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/utils/completers.dart'; -import 'package:invoiceninja_flutter/ui/credit/credit_screen.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/credit/credit_actions.dart'; -import 'package:invoiceninja_flutter/data/models/credit_model.dart'; -import 'package:invoiceninja_flutter/data/models/models.dart'; -import 'package:invoiceninja_flutter/ui/credit/view/credit_view.dart'; -import 'package:invoiceninja_flutter/redux/app/app_state.dart'; class CreditViewScreen extends StatelessWidget { const CreditViewScreen({Key key}) : super(key: key); + static const String route = '/credit/view'; @override Widget build(BuildContext context) { return StoreConnector( + //distinct: true, converter: (Store store) { return CreditViewVM.fromStore(store); }, - builder: (context, vm) { - return CreditView( - viewModel: vm, + builder: (context, viewModel) { + return InvoiceView( + viewModel: viewModel, ); }, ); } } -class CreditViewVM { +class CreditViewVM extends EntityViewVM { CreditViewVM({ - @required this.state, - @required this.credit, - @required this.company, - @required this.onEntityAction, - @required this.onRefreshed, - @required this.isSaving, - @required this.isLoading, - @required this.isDirty, - }); + AppState state, + CompanyEntity company, + InvoiceEntity invoice, + ClientEntity client, + bool isSaving, + bool isDirty, + Function(BuildContext, EntityAction) onEntityAction, + Function(BuildContext, [int]) onEditPressed, + Function(BuildContext, [bool]) onClientPressed, + Function(BuildContext) onPaymentsPressed, + Function(BuildContext, PaymentEntity) onPaymentPressed, + Function(BuildContext) onRefreshed, + Function(BuildContext, String) onUploadDocument, + Function(BuildContext, DocumentEntity) onDeleteDocument, + Function(BuildContext, DocumentEntity) onViewExpense, + }) : super( + state: state, + company: company, + invoice: invoice, + client: client, + isSaving: isSaving, + isDirty: isDirty, + onActionSelected: onEntityAction, + onEditPressed: onEditPressed, + onClientPressed: onClientPressed, + onPaymentsPressed: onPaymentsPressed, + onPaymentPressed: onPaymentPressed, + onRefreshed: onRefreshed, + onUploadDocument: onUploadDocument, + onDeleteDocument: onDeleteDocument, + onViewExpense: onViewExpense, + ); factory CreditViewVM.fromStore(Store store) { final state = store.state; final credit = state.creditState.map[state.creditUIState.selectedId] ?? InvoiceEntity(id: state.creditUIState.selectedId); + final client = store.state.clientState.map[credit.clientId] ?? + ClientEntity(id: credit.clientId); Future _handleRefresh(BuildContext context) { final completer = snackBarCompleter( @@ -59,23 +89,58 @@ class CreditViewVM { return CreditViewVM( state: state, - company: state.selectedCompany, + company: state.company, isSaving: state.isSaving, - isLoading: state.isLoading, isDirty: credit.isNew, - credit: credit, + invoice: credit, + client: client, + onEditPressed: (BuildContext context, [int index]) { + editEntity( + context: context, + entity: credit, + subIndex: index, + completer: snackBarCompleter( + context, AppLocalization.of(context).updatedCredit)); + }, onRefreshed: (context) => _handleRefresh(context), + onClientPressed: (BuildContext context, [bool longPress = false]) { + if (longPress) { + showEntityActionsDialog( + context: context, + entities: [client], + ); + } else { + viewEntity(context: context, entity: client); + } + }, onEntityAction: (BuildContext context, EntityAction action) => - handleCreditAction(context, credit, action), + handleCreditAction(context, [credit], action), + onUploadDocument: (BuildContext context, String path) { + final Completer completer = Completer(); + final document = DocumentEntity().rebuild((b) => b + ..invoiceId = credit.id + ..path = path); + store.dispatch( + SaveDocumentRequest(document: document, completer: completer)); + completer.future.then((client) { + Scaffold.of(context).showSnackBar(SnackBar( + content: SnackBarRow( + message: AppLocalization.of(context).uploadedDocument, + ))); + }).catchError((Object error) { + showDialog( + context: context, + builder: (BuildContext context) { + return ErrorDialog(error); + }); + }); + }, + onDeleteDocument: (BuildContext context, DocumentEntity document) { + store.dispatch(DeleteDocumentRequest( + snackBarCompleter( + context, AppLocalization.of(context).deletedDocument), + [document.id])); + }, ); } - - final AppState state; - final InvoiceEntity credit; - final CompanyEntity company; - final Function(BuildContext, EntityAction) onEntityAction; - final Function(BuildContext) onRefreshed; - final bool isSaving; - final bool isLoading; - final bool isDirty; } diff --git a/lib/ui/invoice/edit/invoice_edit_desktop.dart b/lib/ui/invoice/edit/invoice_edit_desktop.dart index e4c220312..9ce5ae2ba 100644 --- a/lib/ui/invoice/edit/invoice_edit_desktop.dart +++ b/lib/ui/invoice/edit/invoice_edit_desktop.dart @@ -223,8 +223,8 @@ class InvoiceEditDesktopState extends State validator: (String val) => val.trim().isEmpty ? AppLocalization.of(context).pleaseSelectADate : null, - labelText: widget.isQuote - ? localization.quoteDate + labelText: widget.entityType == EntityType.credit ? localization.creditDate : + widget.entityType == EntityType.quote ? localization.quoteDate : localization.invoiceDate, selectedDate: invoice.date, onSelected: (date) { diff --git a/lib/utils/i18n.dart b/lib/utils/i18n.dart index 092e5164b..123a9edfc 100644 --- a/lib/utils/i18n.dart +++ b/lib/utils/i18n.dart @@ -15,6 +15,7 @@ mixin LocalizationsProvider on LocaleCodeAware { static final Map> _localizedValues = { 'en': { // STARTER: lang key - do not remove comment + 'credit_date': 'Credit Date', 'credit': 'Credit', 'credits': 'Credits', 'new_credit': 'New Credit', @@ -32772,15 +32773,19 @@ mixin LocalizationsProvider on LocaleCodeAware { String get appUpdated => _localizedValues[localeCode]['app_updated']; // STARTER: lang field - do not remove comment - String get credit => _localizedValues[localeCode][' credit']; - String get credits => _localizedValues[localeCode][' credits']; - String get newCredit => _localizedValues[localeCode]['new_ credit']; - String get createdCredit => _localizedValues[localeCode]['created_ credit']; - String get updatedCredit => _localizedValues[localeCode]['updated_ credit']; - String get archivedCredit => _localizedValues[localeCode]['archived_ credit']; - String get deletedCredit => _localizedValues[localeCode]['deleted_ credit']; - String get restoredCredit => _localizedValues[localeCode]['restored_ credit']; - String get editCredit => _localizedValues[localeCode]['edit_ credit']; + String get newCredit => _localizedValues[localeCode]['new_credit']; + + String get createdCredit => _localizedValues[localeCode]['created_credit']; + + String get updatedCredit => _localizedValues[localeCode]['updated_credit']; + + String get archivedCredit => _localizedValues[localeCode]['archived_credit']; + + String get deletedCredit => _localizedValues[localeCode]['deleted_credit']; + + String get restoredCredit => _localizedValues[localeCode]['restored_credit']; + + String get creditDate => _localizedValues[localeCode]['credit_date']; String lookup(String key) { final lookupKey = toSnakeCase(key); diff --git a/starter.sh b/starter.sh index d28fac979..8fd7a86de 100644 --- a/starter.sh +++ b/starter.sh @@ -431,7 +431,7 @@ else sed -i -e "s/$comment/$comment${lineBreak}$code/g" ./lib/utils/i18n.dart comment="STARTER: lang field - do not remove comment" - code="String get ${module_camel} => _localizedValues[localeCode][' ${module_snake}']; String get ${module_camel}s => _localizedValues[localeCode][' ${module_snake}s']; String get new${Module} => _localizedValues[localeCode]['new_ ${module_snake}']; String get created${Module} => _localizedValues[localeCode]['created_ ${module_snake}']; String get updated${Module} => _localizedValues[localeCode]['updated_ ${module_snake}']; String get archived${Module} => _localizedValues[localeCode]['archived_ ${module_snake}']; String get deleted${Module} => _localizedValues[localeCode]['deleted_ ${module_snake}']; String get restored${Module} => _localizedValues[localeCode]['restored_ ${module_snake}']; String get edit${Module} => _localizedValues[localeCode]['edit_ ${module_snake}'];${lineBreak}" + code="String get ${module_camel} => _localizedValues[localeCode][' ${module_snake}']; String get ${module_camel}s => _localizedValues[localeCode]['${module_snake}s']; String get new${Module} => _localizedValues[localeCode]['new_${module_snake}']; String get created${Module} => _localizedValues[localeCode]['created_${module_snake}']; String get updated${Module} => _localizedValues[localeCode]['updated_${module_snake}']; String get archived${Module} => _localizedValues[localeCode]['archived_${module_snake}']; String get deleted${Module} => _localizedValues[localeCode]['deleted_${module_snake}']; String get restored${Module} => _localizedValues[localeCode]['restored_${module_snake}']; String get edit${Module} => _localizedValues[localeCode]['edit_${module_snake}'];${lineBreak}" sed -i -e "s/$comment/$comment${lineBreak}$code/g" ./lib/utils/i18n.dart echo "Generating built files.."