mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-18 05:51:34 +02:00
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:
co-authored by
fossephate
Omar Hatem
parent
049e4def27
commit
2ff81df9e7
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user