CW-512 wallet sort (#1195)

* add shared preferences key and make list re-orderable

* macos sonoma build fix

* everything (almost) works

* add translations

* more translations

* everything works

* fixes

* translation merge file fix

* fix for mobile platforms

* fix filter icon being invisible on bright theme

* first pass of adding ascending/descending to filter widget

* small fix

* update ascending switch

* fixes

* fix

* reverse creation sort

* review fixes part 1

* refactor into function rather than tuple + color changes

* more fixes

* remove unrelated file

* updated wording

* cleaner callback

* undo sonoma fix

---------

Co-authored-by: fossephate <fosse@book.local>
Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
Matthew Fosse
2023-12-13 23:58:43 +02:00
committed by GitHub
co-authored by fossephate Omar Hatem
parent 049e4def27
commit 2ff81df9e7
37 changed files with 624 additions and 36 deletions
@@ -0,0 +1,158 @@
import 'package:cake_wallet/entities/list_order_mode.dart';
import 'package:cake_wallet/entities/wallet_list_order_types.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:cake_wallet/themes/extensions/menu_theme.dart';
import 'package:cake_wallet/view_model/settings/choices_list_item.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/picker_wrapper_widget.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
class FilterListWidget extends StatefulWidget {
FilterListWidget({
required this.initalType,
required this.initalAscending,
required this.onClose,
});
final WalletListOrderType? initalType;
final bool initalAscending;
final Function(bool, WalletListOrderType) onClose;
@override
FilterListWidgetState createState() => FilterListWidgetState();
}
class FilterListWidgetState extends State<FilterListWidget> {
late bool ascending;
late WalletListOrderType? type;
@override
void initState() {
super.initState();
ascending = widget.initalAscending;
type = widget.initalType;
}
void setSelectedOrderType(WalletListOrderType? orderType) {
setState(() {
type = orderType;
});
}
@override
Widget build(BuildContext context) {
const sectionDivider = const HorizontalSectionDivider();
return PickerWrapperWidget(
onClose: () {
widget.onClose(ascending, type!);
Navigator.of(context).pop();
},
children: [
Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(24)),
child: Container(
color: Theme.of(context).extension<CakeMenuTheme>()!.backgroundColor,
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Padding(
padding: EdgeInsets.all(24.0),
child: Text(
S.of(context).order_by,
style: TextStyle(
color:
Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
fontSize: 16,
fontFamily: 'Lato',
decoration: TextDecoration.none,
),
),
),
if (type != WalletListOrderType.Custom) ...[
sectionDivider,
SettingsChoicesCell(
ChoicesListItem<ListOrderMode>(
title: "",
items: ListOrderMode.all,
selectedItem: ascending ? ListOrderMode.ascending : ListOrderMode.descending,
onItemSelected: (ListOrderMode listOrderMode) {
setState(() {
ascending = listOrderMode == ListOrderMode.ascending;
});
},
),
),
],
sectionDivider,
RadioListTile(
value: WalletListOrderType.CreationDate,
groupValue: type,
title: Text(
WalletListOrderType.CreationDate.toString(),
style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none),
),
onChanged: setSelectedOrderType,
activeColor: Theme.of(context).primaryColor,
),
RadioListTile(
value: WalletListOrderType.Alphabetical,
groupValue: type,
title: Text(
WalletListOrderType.Alphabetical.toString(),
style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none),
),
onChanged: setSelectedOrderType,
activeColor: Theme.of(context).primaryColor,
),
RadioListTile(
value: WalletListOrderType.GroupByType,
groupValue: type,
title: Text(
WalletListOrderType.GroupByType.toString(),
style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none),
),
onChanged: setSelectedOrderType,
activeColor: Theme.of(context).primaryColor,
),
RadioListTile(
value: WalletListOrderType.Custom,
groupValue: type,
title: Text(
WalletListOrderType.Custom.toString(),
style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none),
),
onChanged: setSelectedOrderType,
activeColor: Theme.of(context).primaryColor,
),
]),
),
),
)
],
);
}
}
@@ -17,19 +17,21 @@ class SettingsChoicesCell extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
choicesListItem.title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
if (choicesListItem.title.isNotEmpty) ...[
Row(
children: [
Text(
choicesListItem.title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
),
),
),
],
),
const SizedBox(height: 24),
],
),
const SizedBox(height: 24),
],
Center(
child: Container(
decoration: BoxDecoration(
@@ -49,9 +51,7 @@ class SettingsChoicesCell extends StatelessWidget {
padding: EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: isSelected
? Theme.of(context).primaryColor
: null,
color: isSelected ? Theme.of(context).primaryColor : null,
),
child: Center(
child: Text(
@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
class FilteredList extends StatefulWidget {
FilteredList({
required this.list,
required this.itemBuilder,
required this.updateFunction,
});
final ObservableList<dynamic> list;
final Widget Function(BuildContext, int) itemBuilder;
final Function updateFunction;
@override
FilteredListState createState() => FilteredListState();
}
class FilteredListState extends State<FilteredList> {
@override
Widget build(BuildContext context) {
return Observer(
builder: (_) => ReorderableListView.builder(
physics: const BouncingScrollPhysics(),
itemBuilder: widget.itemBuilder,
itemCount: widget.list.length,
onReorder: (int oldIndex, int newIndex) {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final dynamic item = widget.list.removeAt(oldIndex);
widget.list.insert(newIndex, item);
widget.updateFunction();
},
),
);
}
}
@@ -1,8 +1,15 @@
import 'package:cake_wallet/entities/wallet_list_order_types.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/filter_list_widget.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/filter_widget.dart';
import 'package:cake_wallet/src/screens/wallet_list/filtered_list.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/themes/extensions/filter_theme.dart';
import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:another_flushbar/flushbar.dart';
import 'package:flutter/material.dart';
@@ -28,6 +35,53 @@ class WalletListPage extends BasePage {
@override
Widget body(BuildContext context) =>
WalletListBody(walletListViewModel: walletListViewModel, authService: authService);
@override
Widget trailing(BuildContext context) {
final filterIcon = Image.asset('assets/images/filter_icon.png',
color: Theme.of(context).extension<FilterTheme>()!.iconColor);
return MergeSemantics(
child: SizedBox(
height: 37,
width: 37,
child: ButtonTheme(
minWidth: double.minPositive,
child: Semantics(
container: true,
child: GestureDetector(
onTap: () async {
await showPopUp<void>(
context: context,
builder: (context) => FilterListWidget(
initalType: walletListViewModel.orderType,
initalAscending: walletListViewModel.ascending,
onClose: (bool ascending, WalletListOrderType type) async {
walletListViewModel.setAscending(ascending);
await walletListViewModel.setOrderType(type);
},
),
);
},
child: Semantics(
label: 'Transaction Filter',
button: true,
enabled: true,
child: Container(
height: 36,
width: 36,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).extension<FilterTheme>()!.buttonColor,
),
child: filterIcon,
),
),
),
),
),
),
);
}
}
class WalletListBody extends StatefulWidget {
@@ -70,11 +124,9 @@ class WalletListBodyState extends State<WalletListBody> {
Expanded(
child: Container(
child: Observer(
builder: (_) => ListView.separated(
physics: const BouncingScrollPhysics(),
separatorBuilder: (_, index) =>
Divider(color: Theme.of(context).colorScheme.background, height: 32),
itemCount: widget.walletListViewModel.wallets.length,
builder: (_) => FilteredList(
list: widget.walletListViewModel.wallets,
updateFunction: widget.walletListViewModel.reorderAccordingToWalletList,
itemBuilder: (__, index) {
final wallet = widget.walletListViewModel.wallets[index];
final currentColor = wallet.isCurrent
@@ -83,6 +135,7 @@ class WalletListBodyState extends State<WalletListBody> {
.createNewWalletButtonBackgroundColor
: Theme.of(context).colorScheme.background;
final row = GestureDetector(
key: ValueKey(wallet.name),
onTap: () => wallet.isCurrent ? null : _loadWallet(wallet),
child: Container(
height: tileHeight,
@@ -117,7 +170,7 @@ class WalletListBodyState extends State<WalletListBody> {
maxLines: null,
softWrap: true,
style: TextStyle(
fontSize: 22,
fontSize: 20,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.extension<CakeTextTheme>()!
@@ -137,13 +190,15 @@ class WalletListBodyState extends State<WalletListBody> {
return wallet.isCurrent
? row
: Row(
key: ValueKey(wallet.name),
children: [
Expanded(child: row),
GestureDetector(
onTap: () => Navigator.of(context).pushNamed(Routes.walletEdit,
arguments: [widget.walletListViewModel, wallet]),
child: Container(
padding: EdgeInsets.only(right: 20),
padding: EdgeInsets.only(
right: DeviceInfo.instance.isMobile ? 20 : 40),
child: Center(
child: Container(
height: 40,
+3 -2
View File
@@ -4,10 +4,11 @@ import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
class PickerWrapperWidget extends StatelessWidget {
PickerWrapperWidget({required this.children, this.hasTitle = false});
PickerWrapperWidget({required this.children, this.hasTitle = false, this.onClose});
final List<Widget> children;
final bool hasTitle;
final Function()? onClose;
@override
Widget build(BuildContext context) {
@@ -45,7 +46,7 @@ class PickerWrapperWidget extends StatelessWidget {
children: children,
),
SizedBox(height: ResponsiveLayoutUtilBase.kPopupSpaceHeight),
AlertCloseButton(bottom: closeButtonBottom),
AlertCloseButton(bottom: closeButtonBottom, onTap: onClose),
],
),
),