mobile coin control sort

This commit is contained in:
julian 2023-03-16 16:42:46 -06:00
parent c856e42322
commit 77f167171f
2 changed files with 300 additions and 113 deletions

View file

@ -19,13 +19,18 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/app_bar_field.dart';
import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/dropdown_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/expandable2.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/toggle.dart';
import 'package:tuple/tuple.dart';
import '../../widgets/animated_widgets/rotate_icon.dart';
import '../../widgets/rounded_container.dart';
enum CoinControlViewType {
manage,
use;
@ -60,6 +65,9 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
CCSortDescriptor _sort = CCSortDescriptor.age;
Map<String, List<Id>>? _map;
List<Id>? _list;
final Set<UTXO> _selectedAvailable = {};
final Set<UTXO> _selectedBlocked = {};
@ -115,17 +123,29 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
),
);
final ids = MainDB.instance.queryUTXOsSync(
walletId: widget.walletId,
filter: _isSearching
? CCFilter.all
: _showBlocked
? CCFilter.frozen
: CCFilter.available,
sort: _sort,
searchTerm: _isSearching ? searchController.text : "",
coin: coin,
);
if (_sort == CCSortDescriptor.address && !_isSearching) {
_list = null;
_map = MainDB.instance.queryUTXOsGroupedByAddressSync(
walletId: widget.walletId,
filter: CCFilter.all,
sort: _sort,
searchTerm: "",
coin: coin,
);
} else {
_map = null;
_list = MainDB.instance.queryUTXOsSync(
walletId: widget.walletId,
filter: _isSearching
? CCFilter.all
: _showBlocked
? CCFilter.frozen
: CCFilter.available,
sort: _sort,
searchTerm: _isSearching ? searchController.text : "",
coin: coin,
);
}
return WillPopScope(
onWillPop: () async {
@ -223,22 +243,18 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
),
AspectRatio(
aspectRatio: 1,
child: AppBarIconButton(
size: 36,
icon: SvgPicture.asset(
Assets.svg.verticalEllipsis,
width: 20,
height: 20,
color: Theme.of(context)
.extension<StackColors>()!
.topNavIconPrimary,
),
onPressed: () {
// show search
setState(() {
_isSearching = true;
});
child: JDropdownIconButton(
mobileAppBar: true,
groupValue: _sort,
items: CCSortDescriptor.values.toSet(),
onSelectionChanged: (CCSortDescriptor? newValue) {
if (newValue != null && newValue != _sort) {
setState(() {
_sort = newValue;
});
}
},
displayPrefix: "Sort by",
),
),
],
@ -273,7 +289,7 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
const SizedBox(
height: 10,
),
if (!_isSearching)
if (!(_isSearching || _map != null))
SizedBox(
height: 48,
child: Toggle(
@ -307,14 +323,14 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
if (_isSearching)
Expanded(
child: ListView.separated(
itemCount: ids.length,
itemCount: _list!.length,
separatorBuilder: (context, _) => const SizedBox(
height: 10,
),
itemBuilder: (context, index) {
final utxo = MainDB.instance.isar.utxos
.where()
.idEqualTo(ids[index])
.idEqualTo(_list![index])
.findFirstSync()!;
final isSelected =
@ -365,65 +381,219 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
),
),
if (!_isSearching)
Expanded(
child: ListView.separated(
itemCount: ids.length,
separatorBuilder: (context, _) => const SizedBox(
height: 10,
),
itemBuilder: (context, index) {
final utxo = MainDB.instance.isar.utxos
.where()
.idEqualTo(ids[index])
.findFirstSync()!;
_list != null
? Expanded(
child: ListView.separated(
itemCount: _list!.length,
separatorBuilder: (context, _) =>
const SizedBox(
height: 10,
),
itemBuilder: (context, index) {
final utxo = MainDB.instance.isar.utxos
.where()
.idEqualTo(_list![index])
.findFirstSync()!;
final isSelected = _showBlocked
? _selectedBlocked.contains(utxo)
: _selectedAvailable.contains(utxo);
final isSelected = _showBlocked
? _selectedBlocked.contains(utxo)
: _selectedAvailable.contains(utxo);
return UtxoCard(
key: Key(
"${utxo.walletId}_${utxo.id}_$isSelected"),
walletId: widget.walletId,
utxo: utxo,
canSelect: widget.type ==
CoinControlViewType.manage ||
(widget.type == CoinControlViewType.use &&
!_showBlocked &&
utxo.isConfirmed(
currentChainHeight,
coin.requiredConfirmations,
)),
initialSelectedState: isSelected,
onSelectedChanged: (value) {
if (value) {
_showBlocked
? _selectedBlocked.add(utxo)
: _selectedAvailable.add(utxo);
} else {
_showBlocked
? _selectedBlocked.remove(utxo)
: _selectedAvailable.remove(utxo);
}
setState(() {});
},
onPressed: () async {
final result =
await Navigator.of(context).pushNamed(
UtxoDetailsView.routeName,
arguments: Tuple2(
utxo.id,
widget.walletId,
),
);
if (mounted && result == "refresh") {
setState(() {});
}
},
);
},
),
),
return UtxoCard(
key: Key(
"${utxo.walletId}_${utxo.id}_$isSelected"),
walletId: widget.walletId,
utxo: utxo,
canSelect: widget.type ==
CoinControlViewType.manage ||
(widget.type ==
CoinControlViewType.use &&
!_showBlocked &&
utxo.isConfirmed(
currentChainHeight,
coin.requiredConfirmations,
)),
initialSelectedState: isSelected,
onSelectedChanged: (value) {
if (value) {
_showBlocked
? _selectedBlocked.add(utxo)
: _selectedAvailable.add(utxo);
} else {
_showBlocked
? _selectedBlocked.remove(utxo)
: _selectedAvailable
.remove(utxo);
}
setState(() {});
},
onPressed: () async {
final result =
await Navigator.of(context)
.pushNamed(
UtxoDetailsView.routeName,
arguments: Tuple2(
utxo.id,
widget.walletId,
),
);
if (mounted && result == "refresh") {
setState(() {});
}
},
);
},
),
)
: Expanded(
child: ListView.separated(
itemCount: _map!.entries.length,
separatorBuilder: (context, _) =>
const SizedBox(
height: 10,
),
itemBuilder: (context, index) {
final entry =
_map!.entries.elementAt(index);
final _controller =
RotateIconController();
return Expandable2(
border: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
background: Theme.of(context)
.extension<StackColors>()!
.popupBG,
animationDurationMultiplier:
0.2 * entry.value.length,
onExpandWillChange: (state) {
if (state ==
Expandable2State.expanded) {
_controller.forward?.call();
} else {
_controller.reverse?.call();
}
},
header: RoundedContainer(
padding: const EdgeInsets.all(14),
color: Colors.transparent,
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
entry.key,
style:
STextStyles.w600_14(
context),
),
const SizedBox(
height: 2,
),
Text(
"${entry.value.length} "
"output${entry.value.length > 1 ? "s" : ""}",
style:
STextStyles.w500_12(
context)
.copyWith(
color: Theme.of(context)
.extension<
StackColors>()!
.textSubtitle1,
),
),
],
),
),
RotateIcon(
animationDurationMultiplier:
0.2 * entry.value.length,
icon: SvgPicture.asset(
Assets.svg.chevronDown,
width: 14,
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
curve: Curves.easeInOut,
controller: _controller,
),
],
),
),
children: entry.value.map(
(id) {
final utxo = MainDB
.instance.isar.utxos
.where()
.idEqualTo(id)
.findFirstSync()!;
final isSelected = _selectedBlocked
.contains(utxo) ||
_selectedAvailable
.contains(utxo);
return UtxoCard(
key: Key(
"${utxo.walletId}_${utxo.id}_$isSelected"),
walletId: widget.walletId,
utxo: utxo,
canSelect: widget.type ==
CoinControlViewType
.manage ||
(widget.type ==
CoinControlViewType
.use &&
!utxo.isBlocked &&
utxo.isConfirmed(
currentChainHeight,
coin.requiredConfirmations,
)),
initialSelectedState: isSelected,
onSelectedChanged: (value) {
if (value) {
utxo.isBlocked
? _selectedBlocked
.add(utxo)
: _selectedAvailable
.add(utxo);
} else {
utxo.isBlocked
? _selectedBlocked
.remove(utxo)
: _selectedAvailable
.remove(utxo);
}
setState(() {});
},
onPressed: () async {
final result =
await Navigator.of(context)
.pushNamed(
UtxoDetailsView.routeName,
arguments: Tuple2(
utxo.id,
widget.walletId,
),
);
if (mounted &&
result == "refresh") {
setState(() {});
}
},
);
},
).toList(),
);
},
),
),
],
),
),

View file

@ -5,6 +5,7 @@ import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/animated_widgets/rotate_icon.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
@ -132,12 +133,14 @@ class JDropdownIconButton<T> extends StatefulWidget {
this.onSelectionChanged,
this.groupValue,
this.redrawOnScreenSizeChanged = false,
this.mobileAppBar = false,
}) : super(key: key);
final String displayPrefix;
final void Function(T?)? onSelectionChanged;
final T? groupValue;
final Set<T> items;
final bool mobileAppBar;
/// setting this to true should be done carefully
final bool redrawOnScreenSizeChanged;
@ -211,37 +214,51 @@ class _JDropdownIconButtonState<T> extends State<JDropdownIconButton<T>> {
});
}
return SizedBox(
key: _key,
height: 56,
width: 56,
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonStyle(context)
?.copyWith(
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
side: BorderSide(
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackBorderSecondary,
width: 1,
),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
),
),
onPressed: _isOpen ? close : open,
child: SvgPicture.asset(
if (widget.mobileAppBar) {
return AppBarIconButton(
key: _key,
size: 36,
icon: SvgPicture.asset(
Assets.svg.list,
width: 20,
height: 20,
color: Theme.of(context).extension<StackColors>()!.topNavIconPrimary,
),
),
);
onPressed: _isOpen ? close : open,
);
} else {
return SizedBox(
key: _key,
height: 56,
width: 56,
child: TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getSecondaryEnabledButtonStyle(context)
?.copyWith(
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
side: BorderSide(
color: Theme.of(context)
.extension<StackColors>()!
.buttonBackBorderSecondary,
width: 1,
),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
),
),
onPressed: _isOpen ? close : open,
child: SvgPicture.asset(
Assets.svg.list,
width: 20,
height: 20,
),
),
);
}
}
}