diff --git a/lib/redux/task/task_selectors.dart b/lib/redux/task/task_selectors.dart index b133cae25..cef63d866 100644 --- a/lib/redux/task/task_selectors.dart +++ b/lib/redux/task/task_selectors.dart @@ -3,6 +3,22 @@ import 'package:built_collection/built_collection.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart'; +var memoizedTaskList = memo2( + (BuiltMap taskMap, int clientId) => + taskList(taskMap, clientId)); + +List taskList(BuiltMap taskMap, int clientId) { + final list = taskMap.keys.where((taskId) { + final task = taskMap[taskId]; + return task.isActive && task.clientId == clientId; + }).toList(); + + list.sort((idA, idB) => + taskMap[idA].listDisplayName.compareTo(taskMap[idB].listDisplayName)); + + return list; +} + var memoizedDropdownTaskList = memo2( (BuiltMap taskMap, BuiltList taskList) => dropdownTasksSelector(taskMap, taskList)); @@ -78,16 +94,13 @@ double taskRateSelector({CompanyEntity company, ProjectEntity project}) { } var memoizedTaskStatsForClient = memo4((int clientId, - BuiltMap taskMap, - String activeLabel, - String archivedLabel) => + BuiltMap taskMap, + String activeLabel, + String archivedLabel) => taskStatsForClient(clientId, taskMap, activeLabel, archivedLabel)); -String taskStatsForClient( - int clientId, - BuiltMap taskMap, - String activeLabel, - String archivedLabel) { +String taskStatsForClient(int clientId, BuiltMap taskMap, + String activeLabel, String archivedLabel) { int countActive = 0; int countArchived = 0; taskMap.forEach((taskId, task) { @@ -114,7 +127,6 @@ String taskStatsForClient( return str; } - var memoizedTaskStatsForProject = memo4((int projectId, BuiltMap taskMap, String activeLabel, diff --git a/lib/ui/invoice/edit/invoice_edit.dart b/lib/ui/invoice/edit/invoice_edit.dart index 9d5014a8d..7fa2916d4 100644 --- a/lib/ui/invoice/edit/invoice_edit.dart +++ b/lib/ui/invoice/edit/invoice_edit.dart @@ -140,6 +140,7 @@ class _InvoiceEditState extends State context: context, builder: (BuildContext context) { return InvoiceItemSelector( + clientId: invoice.clientId, onItemsSelected: (items) { viewModel.onItemsAdded(items); _controller.animateTo(kItemScreen); diff --git a/lib/ui/invoice/edit/invoice_item_selector.dart b/lib/ui/invoice/edit/invoice_item_selector.dart index f7d10624e..859b2f176 100644 --- a/lib/ui/invoice/edit/invoice_item_selector.dart +++ b/lib/ui/invoice/edit/invoice_item_selector.dart @@ -3,16 +3,19 @@ import 'package:invoiceninja_flutter/data/models/invoice_model.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/product/product_selectors.dart'; +import 'package:invoiceninja_flutter/redux/task/task_selectors.dart'; import 'package:flutter/material.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; class InvoiceItemSelector extends StatefulWidget { const InvoiceItemSelector({ + @required this.clientId, this.onItemsSelected, }); final Function(List) onItemsSelected; + final int clientId; @override _InvoiceItemSelectorState createState() => new _InvoiceItemSelectorState(); @@ -136,10 +139,10 @@ class _InvoiceItemSelectorState extends State ); } - Widget _entityList(EntityType entityType) { + Widget _productList() { final state = StoreProvider.of(context).state; final matches = - memoizedProductList(state.productState.map).where((entityId) { + memoizedProductList(state.productState.map).where((entityId) { final entity = state.productState.map[entityId]; return entity.isActive && entity.matchesFilter(_filter); }).toList(); @@ -168,7 +171,57 @@ class _InvoiceItemSelectorState extends State ), entity.listDisplayAmount != null ? Text(formatNumber(entity.listDisplayAmount, context, - formatNumberType: entity.listDisplayAmountType)) + formatNumberType: entity.listDisplayAmountType)) + : Container(), + ], + ), + subtitle: subtitle != null ? Text(subtitle, maxLines: 2) : null, + onTap: () { + if (_selected.isNotEmpty) { + _toggleEntity(entity); + } else { + _selected.add(entity); + _onItemsSelected(context); + } + }, + ); + }, + ); + } + + Widget _taskList() { + final state = StoreProvider.of(context).state; + final matches = + memoizedTaskList(state.taskState.map, widget.clientId).where((entityId) { + final entity = state.taskState.map[entityId]; + return entity.isActive && entity.matchesFilter(_filter); + }).toList(); + + //matches.sort((idA, idB) => + //state.productState.map[idA].compareTo(state.productState.map[idB])); + + return ListView.builder( + shrinkWrap: true, + itemCount: matches.length, + itemBuilder: (BuildContext context, int index) { + final int entityId = matches[index]; + final entity = state.taskState.map[entityId]; + final String subtitle = entity.matchesFilterValue(_filter); + return ListTile( + dense: true, + leading: Checkbox( + activeColor: Theme.of(context).accentColor, + value: _selected.contains(entityId), + onChanged: (bool value) => _toggleEntity(entity), + ), + title: Row( + children: [ + Expanded( + child: Text(entity.listDisplayName), + ), + entity.listDisplayAmount != null + ? Text(formatNumber(entity.listDisplayAmount, context, + formatNumberType: entity.listDisplayAmountType)) : Container(), ], ), @@ -212,8 +265,8 @@ class _InvoiceItemSelectorState extends State child: TabBarView( controller: _tabController, children: [ - _entityList(EntityType.product), - _entityList(EntityType.task), + _productList(), + _taskList(), ], ), ),