mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-17 09:47:37 +00:00
basic desktop coin control layout
This commit is contained in:
parent
8b9ad5e263
commit
397112e0c9
2 changed files with 363 additions and 10 deletions
|
@ -1,14 +1,36 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/db/main_db.dart';
|
||||
import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/coin_control/utxo_row.dart';
|
||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
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/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
class DesktopCoinControlView extends StatefulWidget {
|
||||
enum CCFilter {
|
||||
frozen,
|
||||
available,
|
||||
all;
|
||||
}
|
||||
|
||||
enum CCSortDescriptor {
|
||||
address,
|
||||
age,
|
||||
value;
|
||||
}
|
||||
|
||||
class DesktopCoinControlView extends ConsumerStatefulWidget {
|
||||
const DesktopCoinControlView({
|
||||
Key? key,
|
||||
required this.walletId,
|
||||
|
@ -19,17 +41,82 @@ class DesktopCoinControlView extends StatefulWidget {
|
|||
final String walletId;
|
||||
|
||||
@override
|
||||
State<DesktopCoinControlView> createState() => _DesktopCoinControlViewState();
|
||||
ConsumerState<DesktopCoinControlView> createState() =>
|
||||
_DesktopCoinControlViewState();
|
||||
}
|
||||
|
||||
class _DesktopCoinControlViewState extends State<DesktopCoinControlView> {
|
||||
class _DesktopCoinControlViewState
|
||||
extends ConsumerState<DesktopCoinControlView> {
|
||||
late final TextEditingController _searchController;
|
||||
final searchFieldFocusNode = FocusNode();
|
||||
|
||||
String _searchString = "";
|
||||
CCFilter _filter = CCFilter.all;
|
||||
CCSortDescriptor _sort = CCSortDescriptor.age;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_searchController = TextEditingController();
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.dispose();
|
||||
searchFieldFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
final coin = ref.watch(
|
||||
walletsChangeNotifierProvider.select(
|
||||
(value) => value
|
||||
.getManager(
|
||||
widget.walletId,
|
||||
)
|
||||
.coin,
|
||||
),
|
||||
);
|
||||
|
||||
final currentChainHeight = ref.watch(
|
||||
walletsChangeNotifierProvider.select(
|
||||
(value) => value
|
||||
.getManager(
|
||||
widget.walletId,
|
||||
)
|
||||
.currentHeight,
|
||||
),
|
||||
);
|
||||
|
||||
final ids = MainDB.instance
|
||||
.getUTXOs(widget.walletId)
|
||||
.filter()
|
||||
.group((q) {
|
||||
final qq = q.group(
|
||||
(q) => q.usedIsNull().or().usedEqualTo(false),
|
||||
);
|
||||
switch (_filter) {
|
||||
case CCFilter.frozen:
|
||||
return qq.and().isBlockedEqualTo(true);
|
||||
case CCFilter.available:
|
||||
return qq.and().isBlockedEqualTo(false);
|
||||
case CCFilter.all:
|
||||
return qq;
|
||||
}
|
||||
})
|
||||
.idProperty()
|
||||
.findAllSync();
|
||||
|
||||
return DesktopScaffold(
|
||||
appBar: DesktopAppBar(
|
||||
background: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
leading: Expanded(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 32,
|
||||
|
@ -51,7 +138,7 @@ class _DesktopCoinControlViewState extends State<DesktopCoinControlView> {
|
|||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 15,
|
||||
width: 18,
|
||||
),
|
||||
SvgPicture.asset(
|
||||
Assets.svg.coinControl.gamePad,
|
||||
|
@ -79,19 +166,86 @@ class _DesktopCoinControlViewState extends State<DesktopCoinControlView> {
|
|||
padding: const EdgeInsets.all(24),
|
||||
child: Row(
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 490,
|
||||
Expanded(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
controller: _searchController,
|
||||
focusNode: searchFieldFocusNode,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_searchString = value;
|
||||
});
|
||||
},
|
||||
style:
|
||||
STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
height: 1.8,
|
||||
),
|
||||
decoration: standardInputDecoration(
|
||||
"Search...",
|
||||
searchFieldFocusNode,
|
||||
context,
|
||||
desktopMed: true,
|
||||
).copyWith(
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 18,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.search,
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
),
|
||||
suffixIcon: _searchController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
_searchController.text = "";
|
||||
_searchString = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: const TextField(),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 24,
|
||||
),
|
||||
SecondaryButton(
|
||||
buttonHeight: ButtonHeight.l,
|
||||
width: 200,
|
||||
label: "Show all outputs",
|
||||
onPressed: () {
|
||||
//
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
width: 24,
|
||||
),
|
||||
SecondaryButton(
|
||||
buttonHeight: ButtonHeight.l,
|
||||
width: 200,
|
||||
label: "Sort by",
|
||||
onPressed: () {
|
||||
//
|
||||
|
@ -101,8 +255,82 @@ class _DesktopCoinControlViewState extends State<DesktopCoinControlView> {
|
|||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
color: Colors.green,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24,
|
||||
),
|
||||
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()!;
|
||||
|
||||
// final isSelected = _showBlocked
|
||||
// ? _selectedBlocked.contains(utxo)
|
||||
// : _selectedAvailable.contains(utxo);
|
||||
|
||||
return UtxoRow(
|
||||
utxo: utxo,
|
||||
walletId: widget.walletId,
|
||||
onSelectedChanged: (value) {
|
||||
// if (value) {
|
||||
// _showBlocked
|
||||
// ? _selectedBlocked.add(utxo)
|
||||
// : _selectedAvailable.add(utxo);
|
||||
// } else {
|
||||
// _showBlocked
|
||||
// ? _selectedBlocked.remove(utxo)
|
||||
// : _selectedAvailable.remove(utxo);
|
||||
// }
|
||||
// setState(() {});
|
||||
},
|
||||
initialSelectedState: false,
|
||||
);
|
||||
|
||||
// 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(() {});
|
||||
// }
|
||||
// },
|
||||
// );
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
125
lib/pages_desktop_specific/coin_control/utxo_row.dart
Normal file
125
lib/pages_desktop_specific/coin_control/utxo_row.dart
Normal file
|
@ -0,0 +1,125 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/utxo_status_icon.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
import '../../db/main_db.dart';
|
||||
import '../../providers/global/wallets_provider.dart';
|
||||
import '../../utilities/format.dart';
|
||||
import '../../utilities/text_styles.dart';
|
||||
import '../../utilities/theme/stack_colors.dart';
|
||||
|
||||
class UtxoRow extends ConsumerStatefulWidget {
|
||||
const UtxoRow({
|
||||
Key? key,
|
||||
required this.utxo,
|
||||
required this.walletId,
|
||||
required this.onSelectedChanged,
|
||||
required this.initialSelectedState,
|
||||
this.onPressed,
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
final UTXO utxo;
|
||||
final void Function(bool) onSelectedChanged;
|
||||
final bool initialSelectedState;
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
@override
|
||||
ConsumerState<UtxoRow> createState() => _UtxoRowState();
|
||||
}
|
||||
|
||||
class _UtxoRowState extends ConsumerState<UtxoRow> {
|
||||
late Stream<UTXO?> stream;
|
||||
late UTXO utxo;
|
||||
|
||||
late bool _selected;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_selected = widget.initialSelectedState;
|
||||
utxo = widget.utxo;
|
||||
|
||||
stream = MainDB.instance.watchUTXO(id: utxo.id);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType");
|
||||
|
||||
final coin = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getManager(widget.walletId).coin));
|
||||
|
||||
final currentChainHeight = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getManager(widget.walletId).currentHeight));
|
||||
|
||||
return RoundedWhiteContainer(
|
||||
child: Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: _selected,
|
||||
onChanged: (value) => setState(() {
|
||||
_selected = value!;
|
||||
}),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
UTXOStatusIcon(
|
||||
blocked: utxo.isBlocked,
|
||||
status: utxo.isConfirmed(
|
||||
currentChainHeight,
|
||||
coin.requiredConfirmations,
|
||||
)
|
||||
? UTXOStatusIconStatus.confirmed
|
||||
: UTXOStatusIconStatus.unconfirmed,
|
||||
background: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
selected: false,
|
||||
width: 32,
|
||||
height: 32,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Text(
|
||||
"${Format.satoshisToAmount(
|
||||
utxo.value,
|
||||
coin: coin,
|
||||
).toStringAsFixed(coin.decimals)} ${coin.ticker}",
|
||||
textAlign: TextAlign.right,
|
||||
style: STextStyles.w600_14(context),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Expanded(
|
||||
flex: 13,
|
||||
child: Text(
|
||||
utxo.name.isNotEmpty ? utxo.name : utxo.address ?? utxo.txid,
|
||||
textAlign: TextAlign.center,
|
||||
style: STextStyles.w500_12(context).copyWith(
|
||||
color:
|
||||
Theme.of(context).extension<StackColors>()!.textSubtitle1,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
SecondaryButton(
|
||||
width: 120,
|
||||
buttonHeight: ButtonHeight.xs,
|
||||
label: "Details",
|
||||
onPressed: () {
|
||||
//todo
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue