diff --git a/lib/db/main_db.dart b/lib/db/main_db.dart index 717b68390..f0a8c0c5e 100644 --- a/lib/db/main_db.dart +++ b/lib/db/main_db.dart @@ -182,6 +182,13 @@ class MainDB { }); } + Stream watchUTXO({ + required Id id, + bool fireImmediately = false, + }) { + return isar.utxos.watchObject(id, fireImmediately: fireImmediately); + } + // transaction notes QueryBuilder getTransactionNotes(String walletId) => diff --git a/lib/pages/coin_control/coin_control_view.dart b/lib/pages/coin_control/coin_control_view.dart index 4237e0283..2dde5b672 100644 --- a/lib/pages/coin_control/coin_control_view.dart +++ b/lib/pages/coin_control/coin_control_view.dart @@ -4,6 +4,7 @@ import 'package:isar/isar.dart'; import 'package:stackwallet/db/main_db.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/pages/coin_control/utxo_card.dart'; +import 'package:stackwallet/pages/coin_control/utxo_details_view.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; @@ -11,6 +12,7 @@ import 'package:stackwallet/widgets/background.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/toggle.dart'; +import 'package:tuple/tuple.dart'; class CoinControlView extends ConsumerStatefulWidget { const CoinControlView({ @@ -40,6 +42,8 @@ class _CoinControlViewState extends ConsumerState { .idProperty() .findAllSync(); + print(ids); + return Background( child: Scaffold( backgroundColor: Theme.of(context).extension()!.background, @@ -116,6 +120,15 @@ class _CoinControlViewState extends ConsumerState { key: Key("${utxo.walletId}_${utxo.id}"), walletId: widget.walletId, utxo: utxo, + onPressed: () { + Navigator.of(context).pushNamed( + UtxoDetailsView.routeName, + arguments: Tuple2( + utxo.id, + widget.walletId, + ), + ); + }, ); }, ), diff --git a/lib/pages/coin_control/utxo_card.dart b/lib/pages/coin_control/utxo_card.dart index 1f7412db1..b1c41958f 100644 --- a/lib/pages/coin_control/utxo_card.dart +++ b/lib/pages/coin_control/utxo_card.dart @@ -6,11 +6,13 @@ import 'package:stackwallet/db/main_db.dart'; import 'package:stackwallet/models/isar/models/isar_models.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/enums/coin_enum.dart'; import 'package:stackwallet/utilities/format.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; -import 'package:stackwallet/widgets/rounded_white_container.dart'; +import 'package:stackwallet/widgets/conditional_parent.dart'; +import 'package:stackwallet/widgets/rounded_container.dart'; class UtxoCard extends ConsumerStatefulWidget { const UtxoCard({ @@ -18,11 +20,13 @@ class UtxoCard extends ConsumerStatefulWidget { required this.utxo, required this.walletId, this.selectable = false, + this.onPressed, }) : super(key: key); final String walletId; final UTXO utxo; final bool selectable; + final VoidCallback? onPressed; @override ConsumerState createState() => _UtxoCardState(); @@ -59,54 +63,76 @@ class _UtxoCardState extends ConsumerState { } } - return RoundedWhiteContainer( - child: Row( - children: [ - SvgPicture.asset( - _selected - ? Assets.svg.coinControl.selected - : utxo.isBlocked - ? Assets.svg.coinControl.blocked - : Assets.svg.coinControl.unBlocked, - width: 32, - height: 32, - ), - const SizedBox( - width: 10, - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "${Format.satoshisToAmount( - utxo.value, - coin: coin, - ).toStringAsFixed(coin.decimals)} ${coin.ticker}", - style: STextStyles.w600_14(context), - ), - const SizedBox( - height: 2, - ), - Row( - children: [ - Flexible( - child: Text( - label ?? utxo.address ?? utxo.txid, - style: STextStyles.w500_12(context).copyWith( - color: Theme.of(context) - .extension()! - .textSubtitle1, + return ConditionalParent( + condition: widget.onPressed != null, + builder: (child) => MaterialButton( + padding: const EdgeInsets.all(0), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + color: Theme.of(context).extension()!.popupBG, + elevation: 0, + disabledElevation: 0, + hoverElevation: 0, + focusElevation: 0, + highlightElevation: 0, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(Constants.size.circularBorderRadius), + ), + onPressed: widget.onPressed, + child: child, + ), + child: RoundedContainer( + color: widget.onPressed == null + ? Theme.of(context).extension()!.popupBG + : Colors.transparent, + child: Row( + children: [ + SvgPicture.asset( + _selected + ? Assets.svg.coinControl.selected + : utxo.isBlocked + ? Assets.svg.coinControl.blocked + : Assets.svg.coinControl.unBlocked, + width: 32, + height: 32, + ), + const SizedBox( + width: 10, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "${Format.satoshisToAmount( + utxo.value, + coin: coin, + ).toStringAsFixed(coin.decimals)} ${coin.ticker}", + style: STextStyles.w600_14(context), + ), + const SizedBox( + height: 2, + ), + Row( + children: [ + Flexible( + child: Text( + label ?? utxo.address ?? utxo.txid, + style: STextStyles.w500_12(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ), ), ), - ), - ], - ), - ], + ], + ), + ], + ), ), - ), - ], + ], + ), ), ); } diff --git a/lib/pages/coin_control/utxo_details_view.dart b/lib/pages/coin_control/utxo_details_view.dart new file mode 100644 index 000000000..461e982eb --- /dev/null +++ b/lib/pages/coin_control/utxo_details_view.dart @@ -0,0 +1,99 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:isar/isar.dart'; +import 'package:stackwallet/db/main_db.dart'; +import 'package:stackwallet/models/isar/models/isar_models.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/format.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class UtxoDetailsView extends ConsumerStatefulWidget { + const UtxoDetailsView({ + Key? key, + required this.utxoId, + required this.walletId, + }) : super(key: key); + + static const routeName = "/utxoDetails"; + + final Id utxoId; + final String walletId; + + @override + ConsumerState createState() => _UtxoDetailsViewState(); +} + +class _UtxoDetailsViewState extends ConsumerState { + late Stream stream; + + UTXO? utxo; + + @override + void initState() { + utxo = MainDB.instance.isar.utxos + .where() + .idEqualTo(widget.utxoId) + .findFirstSync()!; + + stream = MainDB.instance.watchUTXO(id: widget.utxoId); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + final coin = ref.watch(walletsChangeNotifierProvider + .select((value) => value.getManager(widget.walletId).coin)); + return Background( + child: Scaffold( + backgroundColor: Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + body: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + ), + child: StreamBuilder( + stream: stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + utxo = snapshot.data!; + } + + return Column( + children: [ + const SizedBox( + height: 10, + ), + RoundedWhiteContainer( + child: Row( + children: [ + Text( + "${Format.satoshisToAmount( + utxo!.value, + coin: coin, + ).toStringAsFixed( + coin.decimals, + )} ${coin.ticker}", + ), + ], + ), + ) + ], + ); + }, + ), + ), + ), + ); + } +} diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 3a7968e05..5dfb42e95 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -2,6 +2,7 @@ import 'package:decimal/decimal.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:isar/isar.dart'; import 'package:stackwallet/models/buy/response_objects/quote.dart'; import 'package:stackwallet/models/contact_address_entry.dart'; import 'package:stackwallet/models/exchange/incomplete_exchange.dart'; @@ -28,6 +29,7 @@ import 'package:stackwallet/pages/buy_view/buy_in_wallet_view.dart'; import 'package:stackwallet/pages/buy_view/buy_quote_preview.dart'; import 'package:stackwallet/pages/buy_view/buy_view.dart'; import 'package:stackwallet/pages/coin_control/coin_control_view.dart'; +import 'package:stackwallet/pages/coin_control/utxo_details_view.dart'; import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart'; import 'package:stackwallet/pages/exchange_view/edit_trade_note_view.dart'; import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_1_view.dart'; @@ -199,11 +201,11 @@ class RouteGenerator { builder: (_) => const AddWalletView(), settings: RouteSettings(name: settings.name)); - case PaynymClaimView.routeName: + case CoinControlView.routeName: if (args is String) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: (_) => PaynymClaimView( + builder: (_) => CoinControlView( walletId: args, ), settings: RouteSettings( @@ -213,11 +215,26 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); - case CoinControlView.routeName: + case UtxoDetailsView.routeName: + if (args is Tuple2) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => UtxoDetailsView( + walletId: args.item2, + utxoId: args.item1, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + + case PaynymClaimView.routeName: if (args is String) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, - builder: (_) => CoinControlView( + builder: (_) => PaynymClaimView( walletId: args, ), settings: RouteSettings(