diff --git a/lib/redux/dashboard/dashboard_sidebar_selectors.dart b/lib/redux/dashboard/dashboard_sidebar_selectors.dart index 1f431d33f..18fb51195 100644 --- a/lib/redux/dashboard/dashboard_sidebar_selectors.dart +++ b/lib/redux/dashboard/dashboard_sidebar_selectors.dart @@ -61,3 +61,36 @@ List _recentPayments( return payments; } + +var memoizedUpcomingQuotes = memo1((BuiltMap quoteMap) => + _upcomingQuotes(quoteMap: quoteMap)); + +List _upcomingQuotes( + {BuiltMap quoteMap}) { + final quotes = []; + quoteMap.forEach((index, quote) { + if (quote.isUpcoming) { + quotes.add(quote); + } + }); + + quotes.sort((quoteA, quoteB) => quoteA.dueDate.compareTo(quoteB.dueDate)); + + return quotes; +} + +var memoizedExpiredQuotes = memo1((BuiltMap quoteMap) => + _expiredQuotes(quoteMap: quoteMap)); + +List _expiredQuotes({BuiltMap quoteMap}) { + final quotes = []; + quoteMap.forEach((index, quote) { + if (quote.isPastDue) { + quotes.add(quote); + } + }); + + quotes.sort((quoteA, quoteB) => quoteA.dueDate.compareTo(quoteB.dueDate)); + + return quotes; +} diff --git a/lib/redux/quote/quote_state.dart b/lib/redux/quote/quote_state.dart index 4a60e541d..a4f548601 100644 --- a/lib/redux/quote/quote_state.dart +++ b/lib/redux/quote/quote_state.dart @@ -29,6 +29,14 @@ abstract class QuoteState implements Built { BuiltList get list; + InvoiceEntity get(String quoteId) { + if (map.containsKey(quoteId)) { + return map[quoteId]; + } else { + return InvoiceEntity(id: quoteId, entityType: EntityType.quote); + } + } + QuoteState loadQuotes(BuiltList quotes) { final map = Map.fromIterable( quotes, diff --git a/lib/ui/app/dismissible_entity.dart b/lib/ui/app/dismissible_entity.dart index 7987508e1..7f6659f3a 100644 --- a/lib/ui/app/dismissible_entity.dart +++ b/lib/ui/app/dismissible_entity.dart @@ -92,8 +92,10 @@ class DismissibleEntity extends StatelessWidget { ), ], child: SelectedIndicator( - isSelected: - isSelected && !isMultiselect && !entity.entityType.isSetting, + isSelected: isSelected && + showCheckbox && + !isMultiselect && + !entity.entityType.isSetting, child: ConstrainedBox( constraints: BoxConstraints( minHeight: 60, diff --git a/lib/ui/dashboard/dashboard_sidebar.dart b/lib/ui/dashboard/dashboard_sidebar.dart index 9d3e19657..bd03e2d24 100644 --- a/lib/ui/dashboard/dashboard_sidebar.dart +++ b/lib/ui/dashboard/dashboard_sidebar.dart @@ -9,6 +9,7 @@ import 'package:invoiceninja_flutter/ui/app/help_text.dart'; import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart'; import 'package:invoiceninja_flutter/ui/invoice/invoice_list_item.dart'; import 'package:invoiceninja_flutter/ui/payment/payment_list_item.dart'; +import 'package:invoiceninja_flutter/ui/quote/quote_list_item.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; class SidebarScaffold extends StatelessWidget { @@ -185,37 +186,37 @@ class _QuoteSidebar extends StatelessWidget { final localization = AppLocalization.of(context); final store = StoreProvider.of(context); final state = store.state; - final upcomingInvoices = memoizedUpcomingInvoices(state.invoiceState.map); - final pastDueInvoices = memoizedPastDueInvoices(state.invoiceState.map); - final selectedIds = state.uiState.selectedEntities[EntityType.invoice]; + final upcomingQuotes = memoizedUpcomingQuotes(state.quoteState.map); + final expriedQuotes = memoizedExpiredQuotes(state.quoteState.map); + final selectedIds = state.uiState.selectedEntities[EntityType.quote]; return _DashboardSidebar( - entityType: EntityType.invoice, - label1: localization.upcomingInvoices + - (upcomingInvoices.isNotEmpty ? ' (${upcomingInvoices.length})' : ''), - list1: upcomingInvoices.isEmpty + entityType: EntityType.quote, + label1: localization.upcomingQuotes + + (upcomingQuotes.isNotEmpty ? ' (${upcomingQuotes.length})' : ''), + list1: upcomingQuotes.isEmpty ? null : ListView.separated( shrinkWrap: true, - itemCount: upcomingInvoices.length, + itemCount: upcomingQuotes.length, itemBuilder: (BuildContext context, int index) { - return InvoiceListItem( - invoice: upcomingInvoices[index], + return QuoteListItem( + quote: upcomingQuotes[index], showCheckbox: false, ); }, separatorBuilder: (context, index) => ListDivider(), ), label2: localization.pastDueInvoices + - (pastDueInvoices.isNotEmpty ? ' (${pastDueInvoices.length})' : ''), - list2: pastDueInvoices.isEmpty + (expriedQuotes.isNotEmpty ? ' (${expriedQuotes.length})' : ''), + list2: expriedQuotes.isEmpty ? null : ListView.separated( shrinkWrap: true, - itemCount: pastDueInvoices.length, + itemCount: expriedQuotes.length, itemBuilder: (BuildContext context, int index) { - return InvoiceListItem( - invoice: pastDueInvoices[index], + return QuoteListItem( + quote: expriedQuotes[index], showCheckbox: false, ); }, @@ -230,8 +231,8 @@ class _QuoteSidebar extends StatelessWidget { shrinkWrap: true, itemCount: selectedIds?.length, itemBuilder: (BuildContext context, int index) { - return InvoiceListItem( - invoice: state.invoiceState.get(selectedIds[index]), + return QuoteListItem( + quote: state.quoteState.get(selectedIds[index]), showCheckbox: false, ); }, diff --git a/lib/ui/invoice/invoice_list_item.dart b/lib/ui/invoice/invoice_list_item.dart index 35421c5bf..ab24e4107 100644 --- a/lib/ui/invoice/invoice_list_item.dart +++ b/lib/ui/invoice/invoice_list_item.dart @@ -59,7 +59,6 @@ class InvoiceListItem extends StatelessWidget { return DismissibleEntity( isSelected: isDesktop(context) && - showCheckbox && invoice.id == (uiState.isEditing ? invoiceUIState.editing.id diff --git a/lib/ui/payment/payment_list_item.dart b/lib/ui/payment/payment_list_item.dart index 2157dba50..bdc3b6685 100644 --- a/lib/ui/payment/payment_list_item.dart +++ b/lib/ui/payment/payment_list_item.dart @@ -55,7 +55,6 @@ class PaymentListItem extends StatelessWidget { return DismissibleEntity( isSelected: isDesktop(context) && - showCheckbox && payment.id == (uiState.isEditing ? paymentUIState.editing.id diff --git a/lib/ui/quote/quote_list_item.dart b/lib/ui/quote/quote_list_item.dart index e5fe38d71..b842c03ba 100644 --- a/lib/ui/quote/quote_list_item.dart +++ b/lib/ui/quote/quote_list_item.dart @@ -14,33 +14,24 @@ import 'package:invoiceninja_flutter/utils/localization.dart'; class QuoteListItem extends StatelessWidget { const QuoteListItem({ - @required this.user, @required this.quote, - @required this.client, - @required this.filter, - this.onTap, - this.onLongPress, - this.onCheckboxChanged, - this.isChecked = false, + this.filter, + this.showCheckbox = true, }); - final UserEntity user; - final GestureTapCallback onTap; - final GestureTapCallback onLongPress; final InvoiceEntity quote; - final ClientEntity client; final String filter; - final Function(bool) onCheckboxChanged; - final bool isChecked; + final bool showCheckbox; @override Widget build(BuildContext context) { final state = StoreProvider.of(context).state; + final client = state.clientState.get(quote.clientId); final uiState = state.uiState; final quoteUIState = uiState.quoteUIState; final listUIState = state.getUIState(quote.entityType).listUIState; - final isInMultiselect = listUIState.isInMultiselect(); - final showCheckbox = onCheckboxChanged != null || isInMultiselect; + final isInMultiselect = showCheckbox && listUIState.isInMultiselect(); + final isChecked = isInMultiselect && listUIState.isSelected(quote.id); final textStyle = TextStyle(fontSize: 16); final localization = AppLocalization.of(context); final textColor = Theme.of(context).textTheme.bodyText1.color; @@ -66,17 +57,14 @@ class QuoteListItem extends StatelessWidget { ? quoteUIState.editing.id : quoteUIState.selectedId), userCompany: state.userCompany, + showCheckbox: showCheckbox, entity: quote, child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return constraints.maxWidth > kTableListWidthCutoff ? InkWell( - onTap: () => onTap != null - ? onTap() - : selectEntity(entity: quote, context: context), - onLongPress: () => onLongPress != null - ? onLongPress() - : selectEntity( + onTap: () => selectEntity(entity: quote, context: context), + onLongPress: () => selectEntity( entity: quote, context: context, longPress: true), child: Padding( padding: const EdgeInsets.only( @@ -96,8 +84,7 @@ class QuoteListItem extends StatelessWidget { value: isChecked, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - onChanged: (value) => - onCheckboxChanged(value), + onChanged: (value) => null, activeColor: Theme.of(context).accentColor, ), @@ -170,12 +157,8 @@ class QuoteListItem extends StatelessWidget { ), ) : ListTile( - onTap: () => onTap != null - ? onTap() - : selectEntity(entity: quote, context: context), - onLongPress: () => onLongPress != null - ? onLongPress() - : selectEntity( + onTap: () => selectEntity(entity: quote, context: context), + onLongPress: () => selectEntity( entity: quote, context: context, longPress: true), leading: showCheckbox ? IgnorePointer( @@ -184,7 +167,7 @@ class QuoteListItem extends StatelessWidget { value: isChecked, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - onChanged: (value) => onCheckboxChanged(value), + onChanged: (value) => null, activeColor: Theme.of(context).accentColor, ), ) diff --git a/lib/ui/quote/quote_list_vm.dart b/lib/ui/quote/quote_list_vm.dart index 3a3174ebd..3d8671530 100644 --- a/lib/ui/quote/quote_list_vm.dart +++ b/lib/ui/quote/quote_list_vm.dart @@ -27,7 +27,6 @@ class QuoteListBuilder extends StatelessWidget { return StoreConnector( converter: QuoteListVM.fromStore, builder: (context, viewModel) { - final state = viewModel.state; return EntityList( entityType: EntityType.quote, presenter: QuotePresenter(), @@ -41,15 +40,10 @@ class QuoteListBuilder extends StatelessWidget { itemBuilder: (BuildContext context, index) { final invoiceId = viewModel.invoiceList[index]; final invoice = viewModel.invoiceMap[invoiceId]; - final listState = state.getListState(EntityType.quote); - final isInMultiselect = listState.isInMultiselect(); return QuoteListItem( - user: state.user, filter: viewModel.filter, quote: invoice, - client: viewModel.clientMap[invoice.clientId] ?? ClientEntity(), - isChecked: isInMultiselect && listState.isSelected(invoice.id), ); }); },