Rework filter on the transactions list screen

This commit is contained in:
Serhii 2022-11-10 13:25:03 +02:00
parent d99aac9d98
commit 1f08d87471
8 changed files with 350 additions and 157 deletions

View file

@ -24,6 +24,9 @@ class ExchangeProviderDescription extends EnumerableItem<int>
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<int>
return sideShift;
case 4:
return simpleSwap;
case 5:
return all;
default:
throw Exception('Unexpected token: $raw for ExchangeProviderDescription deserialize');
}

View file

@ -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,
);
}

View file

@ -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: <Widget>[
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<DateTime> 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: <Widget>[
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<DateTime> 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,
);
}
}

View file

@ -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: <Widget>[
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),
),
)
],
),
);
}
}

View file

@ -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<bool> displayXMRTO = Observable(true);
Observable<bool> displayAllTrades = Observable(true);
Observable<bool> displayChangeNow = Observable(true);
Observable<bool> displaySideShift = Observable(true);
Observable<bool> displayMorphToken = Observable(true);
Observable<bool> 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<TradeListItem> filtered({required List<TradeListItem> 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()

View file

@ -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<bool> displayAll = Observable(true);
Observable<bool> displayIncoming = Observable(true);
Observable<bool> 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<TransactionListItem> filtered({required List<TransactionListItem> transactions}) {
var _transactions = <TransactionListItem>[];
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);
}

View file

@ -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 = '',

View file

@ -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<bool> value;
String caption;
Function(bool) onChanged;
Function onChanged;
}