From 1f08d8747159a6455a637544714bf69bf9d9eb8b Mon Sep 17 00:00:00 2001 From: Serhii Date: Thu, 10 Nov 2022 13:25:03 +0200 Subject: [PATCH] Rework filter on the transactions list screen --- .../exchange_provider_description.dart | 5 + .../dashboard/widgets/filter_tile.dart | 7 +- .../dashboard/widgets/filter_widget.dart | 231 ++++++++++-------- lib/src/widgets/rounded_checkbox.dart | 91 +++++++ lib/store/dashboard/trade_filter_store.dart | 82 ++++--- .../dashboard/transaction_filter_store.dart | 57 +++-- .../dashboard/dashboard_view_model.dart | 28 ++- lib/view_model/dashboard/filter_item.dart | 6 +- 8 files changed, 350 insertions(+), 157 deletions(-) create mode 100644 lib/src/widgets/rounded_checkbox.dart diff --git a/lib/exchange/exchange_provider_description.dart b/lib/exchange/exchange_provider_description.dart index f9e359454..2fd231085 100644 --- a/lib/exchange/exchange_provider_description.dart +++ b/lib/exchange/exchange_provider_description.dart @@ -24,6 +24,9 @@ class ExchangeProviderDescription extends EnumerableItem static const simpleSwap = ExchangeProviderDescription(title: 'SimpleSwap', raw: 4, image: 'assets/images/simpleSwap.png'); + static const all = + ExchangeProviderDescription(title: 'All trades', raw: 5, image:''); + static ExchangeProviderDescription deserialize({required int raw}) { switch (raw) { case 0: @@ -36,6 +39,8 @@ class ExchangeProviderDescription extends EnumerableItem return sideShift; case 4: return simpleSwap; + case 5: + return all; default: throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize'); } diff --git a/lib/src/screens/dashboard/widgets/filter_tile.dart b/lib/src/screens/dashboard/widgets/filter_tile.dart index 9a5646ac9..3be96073a 100644 --- a/lib/src/screens/dashboard/widgets/filter_tile.dart +++ b/lib/src/screens/dashboard/widgets/filter_tile.dart @@ -9,12 +9,7 @@ class FilterTile extends StatelessWidget { Widget build(BuildContext context) { return Container( width: double.infinity, - padding: EdgeInsets.only( - top: 18, - bottom: 18, - left: 24, - right: 24 - ), + padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 24.0), child: child, ); } diff --git a/lib/src/screens/dashboard/widgets/filter_widget.dart b/lib/src/screens/dashboard/widgets/filter_widget.dart index 98d5add81..8719df562 100644 --- a/lib/src/screens/dashboard/widgets/filter_widget.dart +++ b/lib/src/screens/dashboard/widgets/filter_widget.dart @@ -6,20 +6,21 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/src/widgets/alert_background.dart'; import 'package:cake_wallet/src/widgets/alert_close_button.dart'; -import 'package:cake_wallet/src/widgets/checkbox_widget.dart'; +import 'package:cake_wallet/src/widgets/rounded_checkbox.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; //import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker; class FilterWidget extends StatelessWidget { FilterWidget({required this.dashboardViewModel}); final DashboardViewModel dashboardViewModel; - final backVector = Image.asset('assets/images/back_vector.png', - color: Palette.darkBlueCraiola - ); + final closeIcon = + Image.asset('assets/images/close.png', color: Palette.darkBlueCraiola); @override Widget build(BuildContext context) { + const sectionDivider = SectionDivider(); return AlertBackground( child: Stack( alignment: Alignment.center, @@ -38,118 +39,150 @@ class FilterWidget extends StatelessWidget { ), ), Padding( - padding: EdgeInsets.only( - left: 24, - right: 24, - top: 24 - ), + padding: EdgeInsets.only(left: 24, right: 24, top: 24), child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(14)), + borderRadius: BorderRadius.all(Radius.circular(24)), child: Container( - color: Theme.of(context).textTheme!.bodyText1!.decorationColor!, - child: ListView.separated( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: dashboardViewModel.filterItems.length, - separatorBuilder: (context, _) => Container( - height: 1, - color: Theme.of(context).accentTextTheme!.subtitle1!.backgroundColor!, - ), - itemBuilder: (_, index1) { - final title = dashboardViewModel.filterItems.keys.elementAt(index1); - final section = dashboardViewModel.filterItems.values.elementAt(index1); - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only( - top: 20, - left: 24, - right: 24 - ), - child: Text( - title, - style: TextStyle( - color: Theme.of(context).accentTextTheme!.subtitle1!.color!, - fontSize: 16, - fontWeight: FontWeight.w500, - fontFamily: 'Lato', - decoration: TextDecoration.none - ), + color: Theme.of(context) + .textTheme! + .bodyText1! + .decorationColor!, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.all(24.0), + child: Text( + S.of(context).filters, + style: TextStyle( + color: Theme.of(context) + .textTheme! + .bodyText1! + .decorationColor!, + fontSize: 16, + fontFamily: 'Lato', + decoration: TextDecoration.none, ), ), - ListView.separated( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: section.length, - separatorBuilder: (context, _) => Container( - height: 1, - padding: EdgeInsets.only(left: 24), - color: Theme.of(context).textTheme!.bodyText1!.decorationColor!, - child: Container( - height: 1, - color: Theme.of(context).accentTextTheme!.subtitle1!.backgroundColor!, - ), - ), - itemBuilder: (_, index2) { - - final item = section[index2]; - final content = item.onChanged != null - ? CheckboxWidget( - value: item.value(), - caption: item.caption, - onChanged: item.onChanged - ) - : GestureDetector( - onTap: () async { - //final List picked = - //await date_rage_picker.showDatePicker( - // context: context, - // initialFirstDate: DateTime.now() - // .subtract(Duration(days: 1)), - // initialLastDate: (DateTime.now()), - // firstDate: DateTime(2015), - // lastDate: DateTime.now() - // .add(Duration(days: 1))); - - //if (picked != null && picked.length == 2) { - // dashboardViewModel.transactionFilterStore - // .changeStartDate(picked.first); - // dashboardViewModel.transactionFilterStore - // .changeEndDate(picked.last); - //} - }, - child: Padding( - padding: EdgeInsets.only(left: 32), + ), + sectionDivider, + ListView.separated( + padding: EdgeInsets.zero, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: dashboardViewModel.filterItems.length, + separatorBuilder: (context, _) => sectionDivider, + itemBuilder: (_, index1) { + final title = dashboardViewModel.filterItems.keys + .elementAt(index1); + final section = dashboardViewModel + .filterItems.values + .elementAt(index1); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only( + top: 20, left: 24, right: 24), child: Text( - item.caption, + title, style: TextStyle( - color: Theme.of(context).primaryTextTheme!.headline6!.color!, - fontSize: 18, + color: Theme.of(context) + .primaryTextTheme! + .headline6! + .color!, + fontSize: 16, fontFamily: 'Lato', - fontWeight: FontWeight.w500, - decoration: TextDecoration.none - ), + fontWeight: FontWeight.bold, + decoration: TextDecoration.none), ), ), - ); + ListView.builder( + padding: + EdgeInsets.symmetric(vertical: 8.0), + shrinkWrap: true, + physics: + const NeverScrollableScrollPhysics(), + itemCount: section.length, + itemBuilder: (_, index2) { + final item = section[index2]; + final content = item.onChanged != null + ? Observer( + builder: (_) => + RoundedCheckboxWidget( + value: item.value.value, + caption: item.caption, + onChanged: item.onChanged, + currentTheme: + dashboardViewModel + .settingsStore + .currentTheme, + )) + : GestureDetector( + onTap: () async { + //final List picked = + //await date_rage_picker.showDatePicker( + // context: context, + // initialFirstDate: DateTime.now() + // .subtract(Duration(days: 1)), + // initialLastDate: (DateTime.now()), + // firstDate: DateTime(2015), + // lastDate: DateTime.now() + // .add(Duration(days: 1))); - return FilterTile(child: content); - }, - ) - ], - ); - }, - ), + //if (picked != null && picked.length == 2) { + // dashboardViewModel.transactionFilterStore + // .changeStartDate(picked.first); + // dashboardViewModel.transactionFilterStore + // .changeEndDate(picked.last); + //} + }, + child: Padding( + padding: + EdgeInsets.only(left: 32), + child: Text( + item.caption, + style: TextStyle( + color: Colors.red, + //Theme.of(context).primaryTextTheme.title.color,// + fontSize: 18, + fontFamily: 'Lato', + fontWeight: + FontWeight.w500, + decoration: + TextDecoration.none), + ), + ), + ); + + return FilterTile(child: content); + }, + ) + ], + ); + }, + ), + ]), ), ), ), ], ), - AlertCloseButton(image: backVector) + AlertCloseButton(image: closeIcon) ], ), ); } +} + +class SectionDivider extends StatelessWidget { + const SectionDivider(); + + @override + Widget build(BuildContext context) { + return Container( + height: 1, + color: Colors.red,//Fixme Theme.of(context).accentTextTheme.subhead.backgroundColor, + ); + } } \ No newline at end of file diff --git a/lib/src/widgets/rounded_checkbox.dart b/lib/src/widgets/rounded_checkbox.dart new file mode 100644 index 000000000..6e5b08f1e --- /dev/null +++ b/lib/src/widgets/rounded_checkbox.dart @@ -0,0 +1,91 @@ +import 'package:cake_wallet/palette.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; + +class RoundedCheckboxWidget extends StatelessWidget { + RoundedCheckboxWidget( + {required this.value, + required this.caption, + required this.onChanged, + this.currentTheme}); + + final bool value; + final String caption; + final Function onChanged; + final ThemeBase? currentTheme; + + bool get darkTheme => currentTheme!.type == ThemeType.dark; + + @override + Widget build(BuildContext context) { + + final baseGradient = LinearGradient(colors: [ + Colors.red, //Fixme Theme.of(context).primaryTextTheme!.subtitle!.color!, + Colors.red //Fixme Theme.of(context).primaryTextTheme!.subtitle!.decorationColor!, + ], begin: Alignment.centerLeft, end: Alignment.centerRight); + + final darkThemeGradient = LinearGradient(colors: [ + Palette.blueCraiola, + Palette.blueGreyCraiola, + ], begin: Alignment.topLeft, end: Alignment.bottomRight); + + final gradient = darkTheme ? darkThemeGradient : baseGradient; + + final uncheckedColor = darkTheme + ? Colors.red //Fixme Theme.of(context).accentTextTheme.subhead.decorationColor + : Colors.white; + + final borderColor = darkTheme + ? Colors.red //Fixme Theme.of(context).accentTextTheme.subtitle.backgroundColor + : Colors.transparent; + + final checkedOuterBoxDecoration = + BoxDecoration(shape: BoxShape.circle, gradient: gradient); + final outerBoxDecoration = BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context).accentTextTheme.overline!.color!, + border: Border.all(color: borderColor)); + + final checkedInnerBoxDecoration = + BoxDecoration(shape: BoxShape.circle, color: Colors.white); + final innerBoxDecoration = + BoxDecoration(shape: BoxShape.circle, color: uncheckedColor); + + return GestureDetector( + onTap: () => onChanged(), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + height: 24.0, + width: 24.0, + child: DecoratedBox( + decoration: + value ? checkedOuterBoxDecoration : outerBoxDecoration, + child: Padding( + padding: EdgeInsets.all(value ? 4.0 : 1.0), + child: DecoratedBox( + decoration: + value ? checkedInnerBoxDecoration : innerBoxDecoration, + ), + ), + ), + ), + Padding( + padding: EdgeInsets.only(left: 16), + child: Text( + caption, + style: TextStyle( + color: Colors.red, //Fixme Theme.of(context).primaryTextTheme.title.color, + fontSize: 18, + fontFamily: 'Lato', + fontWeight: FontWeight.w500, + decoration: TextDecoration.none), + ), + ) + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/store/dashboard/trade_filter_store.dart b/lib/store/dashboard/trade_filter_store.dart index bee63b21e..4319c3bc7 100644 --- a/lib/store/dashboard/trade_filter_store.dart +++ b/lib/store/dashboard/trade_filter_store.dart @@ -8,39 +8,61 @@ part'trade_filter_store.g.dart'; class TradeFilterStore = TradeFilterStoreBase with _$TradeFilterStore; abstract class TradeFilterStoreBase with Store { - TradeFilterStoreBase( - {this.displayXMRTO = true, - this.displayChangeNow = true, - this.displayMorphToken = true, - this.displaySimpleSwap = true, - }); + TradeFilterStoreBase(); - @observable - bool displayXMRTO; - - @observable - bool displayChangeNow; - - @observable - bool displayMorphToken; - - @observable - bool displaySimpleSwap; + Observable displayXMRTO = Observable(true); + Observable displayAllTrades = Observable(true); + Observable displayChangeNow = Observable(true); + Observable displaySideShift = Observable(true); + Observable displayMorphToken = Observable(true); + Observable displaySimpleSwap = Observable(true); @action void toggleDisplayExchange(ExchangeProviderDescription provider) { switch (provider) { case ExchangeProviderDescription.changeNow: - displayChangeNow = !displayChangeNow; + displayAllTrades.value = false; + displayChangeNow.value = !displayChangeNow.value; + if (displayChangeNow.value && displaySideShift.value && displaySimpleSwap.value) { + displayAllTrades.value = true; + } break; - case ExchangeProviderDescription.xmrto: - displayXMRTO = !displayXMRTO; - break; - case ExchangeProviderDescription.morphToken: - displayMorphToken = !displayMorphToken; + case ExchangeProviderDescription.sideShift: + displayAllTrades.value = false; + displaySideShift.value = !displaySideShift.value; + if (displayChangeNow.value && displaySideShift.value && displaySimpleSwap.value) { + displayAllTrades.value = true; + } break; case ExchangeProviderDescription.simpleSwap: - displaySimpleSwap = !displaySimpleSwap; + displayAllTrades.value = false; + displaySimpleSwap.value = !displaySimpleSwap.value; + if (displayChangeNow.value && displaySideShift.value && displaySimpleSwap.value) { + displayAllTrades.value = true; + } + break; + case ExchangeProviderDescription.xmrto: + displayXMRTO.value = !displayXMRTO.value; + break; + case ExchangeProviderDescription.morphToken: + displayMorphToken.value = !displayMorphToken.value; + break; + case ExchangeProviderDescription.all: + displayAllTrades.value = !displayAllTrades.value; + if (displayAllTrades.value) { + displayChangeNow.value = true; + displaySideShift.value = true; + displayXMRTO.value = true; + displayMorphToken.value = true; + displaySimpleSwap.value = true; + } + if (!displayAllTrades.value) { + displayChangeNow.value = false; + displaySideShift.value = false; + displayXMRTO.value = false; + displayMorphToken.value = false; + displaySimpleSwap.value = false; + } break; } } @@ -48,20 +70,22 @@ abstract class TradeFilterStoreBase with Store { List filtered({required List trades, required WalletBase wallet}) { final _trades = trades.where((item) => item.trade.walletId == wallet.id).toList(); - final needToFilter = !displayChangeNow || !displayXMRTO || !displayMorphToken || !displaySimpleSwap; + final needToFilter = !displayChangeNow.value || !displaySideShift.value + || !displayXMRTO.value || !displayMorphToken.value + || !displaySimpleSwap.value; return needToFilter ? _trades .where((item) => - (displayXMRTO && + (displayXMRTO.value && item.trade.provider == ExchangeProviderDescription.xmrto) || - (displayChangeNow && + (displayChangeNow.value && item.trade.provider == ExchangeProviderDescription.changeNow) || - (displayMorphToken && + (displayMorphToken.value && item.trade.provider == ExchangeProviderDescription.morphToken) - ||(displaySimpleSwap && + ||(displaySimpleSwap.value && item.trade.provider == ExchangeProviderDescription.simpleSwap)) .toList() diff --git a/lib/store/dashboard/transaction_filter_store.dart b/lib/store/dashboard/transaction_filter_store.dart index 4444075b7..4198f7d8c 100644 --- a/lib/store/dashboard/transaction_filter_store.dart +++ b/lib/store/dashboard/transaction_filter_store.dart @@ -1,6 +1,8 @@ import 'package:mobx/mobx.dart'; import 'package:cw_core/transaction_direction.dart'; import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart'; +import 'package:cake_wallet/view_model/dashboard/filter_item.dart'; +import 'package:cake_wallet/generated/i18n.dart'; part 'transaction_filter_store.g.dart'; @@ -8,14 +10,11 @@ class TransactionFilterStore = TransactionFilterStoreBase with _$TransactionFilterStore; abstract class TransactionFilterStoreBase with Store { - TransactionFilterStoreBase( - {this.displayIncoming = true, this.displayOutgoing = true}); + TransactionFilterStoreBase(); - @observable - bool displayIncoming; - - @observable - bool displayOutgoing; + Observable displayAll = Observable(true); + Observable displayIncoming = Observable(true); + Observable displayOutgoing = Observable(true); @observable DateTime? startDate; @@ -24,10 +23,40 @@ abstract class TransactionFilterStoreBase with Store { DateTime? endDate; @action - void toggleIncoming() => displayIncoming = !displayIncoming; + void toggleIAll() { + displayAll.value = (!displayAll.value); + if (displayAll.value) { + displayOutgoing.value = true; + displayIncoming.value = true; + } + if (!displayAll.value) { + displayOutgoing.value = false; + displayIncoming.value = false; + } + } @action - void toggleOutgoing() => displayOutgoing = !displayOutgoing; + void toggleIncoming() { + displayIncoming.value = (!displayIncoming.value); + if (displayIncoming.value && displayOutgoing.value) { + displayAll.value = true; + } + if (!displayIncoming.value || !displayOutgoing.value) { + displayAll.value = false; + } + } + + + @action + void toggleOutgoing() { + displayOutgoing.value = (!displayOutgoing.value); + if (displayIncoming.value && displayOutgoing.value) { + displayAll.value = true; + } + if (!displayIncoming.value || !displayOutgoing.value) { + displayAll.value = false; + } + } @action void changeStartDate(DateTime date) => startDate = date; @@ -37,8 +66,8 @@ abstract class TransactionFilterStoreBase with Store { List filtered({required List transactions}) { var _transactions = []; - final needToFilter = !displayOutgoing || - !displayIncoming || + final needToFilter = !displayOutgoing.value || + !displayIncoming.value || (startDate != null && endDate != null); if (needToFilter) { @@ -50,11 +79,11 @@ abstract class TransactionFilterStoreBase with Store { && (endDate?.isAfter(item.transaction.date) ?? false); } - if (allowed && (!displayOutgoing || !displayIncoming)) { - allowed = (displayOutgoing && + if (allowed && (!displayOutgoing.value || !displayIncoming.value)) { + allowed = (displayOutgoing.value && item.transaction.direction == TransactionDirection.outgoing) || - (displayIncoming && + (displayIncoming.value && item.transaction.direction == TransactionDirection.incoming); } diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 49dd2437a..d736769e2 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -60,13 +60,17 @@ abstract class DashboardViewModelBase with Store { filterItems = { S.current.transactions: [ FilterItem( - value: () => transactionFilterStore.displayIncoming, - caption: S.current.incoming, - onChanged: (value) => transactionFilterStore.toggleIncoming()), + value: transactionFilterStore.displayAll, + caption: 'S.current.all_transactions',//Fixme + onChanged: () => transactionFilterStore.toggleIAll()), FilterItem( - value: () => transactionFilterStore.displayOutgoing, + value: transactionFilterStore.displayIncoming, + caption: S.current.incoming, + onChanged: () => transactionFilterStore.toggleIncoming()), + FilterItem( + value: transactionFilterStore.displayOutgoing, caption: S.current.outgoing, - onChanged: (value) => transactionFilterStore.toggleOutgoing()), + onChanged: () => transactionFilterStore.toggleOutgoing()), // FilterItem( // value: () => false, // caption: S.current.transactions_by_date, @@ -74,10 +78,20 @@ abstract class DashboardViewModelBase with Store { ], S.current.trades: [ FilterItem( - value: () => tradeFilterStore.displayChangeNow, + value: tradeFilterStore.displayAllTrades, + caption: 'S.current.all_trades',//Fixme + onChanged: () => tradeFilterStore + .toggleDisplayExchange(ExchangeProviderDescription.all)), + FilterItem( + value: tradeFilterStore.displayChangeNow, caption: 'Change.NOW', - onChanged: (value) => tradeFilterStore + onChanged: () => tradeFilterStore .toggleDisplayExchange(ExchangeProviderDescription.changeNow)), + FilterItem( + value: tradeFilterStore.displaySideShift, + caption: 'SideShift', + onChanged: () => tradeFilterStore + .toggleDisplayExchange(ExchangeProviderDescription.sideShift)), ] }, subname = '', diff --git a/lib/view_model/dashboard/filter_item.dart b/lib/view_model/dashboard/filter_item.dart index 0230899b4..8bc0f0bf1 100644 --- a/lib/view_model/dashboard/filter_item.dart +++ b/lib/view_model/dashboard/filter_item.dart @@ -1,10 +1,12 @@ +import 'package:mobx/mobx.dart'; + class FilterItem { FilterItem({ required this.value, required this.caption, required this.onChanged}); - bool Function() value; + Observable value; String caption; - Function(bool) onChanged; + Function onChanged; } \ No newline at end of file