diff --git a/lib/pages/coin_control/utxo_details_view.dart b/lib/pages/coin_control/utxo_details_view.dart index 180cae39d..adceb5a0d 100644 --- a/lib/pages/coin_control/utxo_details_view.dart +++ b/lib/pages/coin_control/utxo_details_view.dart @@ -5,6 +5,7 @@ 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/pages/wallet_view/transaction_views/transaction_details_view.dart'; import 'package:stackwallet/providers/global/wallets_provider.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/format.dart'; @@ -16,6 +17,8 @@ import 'package:stackwallet/widgets/conditional_parent.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/simple_copy_button.dart'; import 'package:stackwallet/widgets/custom_buttons/simple_edit_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; @@ -36,6 +39,7 @@ class UtxoDetailsView extends ConsumerStatefulWidget { } class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> { + final isDesktop = Util.isDesktop; static const double _spacing = 12; late Stream<UTXO?> streamUTXO; @@ -94,7 +98,7 @@ class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> { ); return ConditionalParent( - condition: !Util.isDesktop, + condition: !isDesktop, builder: (child) => Background( child: Scaffold( backgroundColor: @@ -129,134 +133,86 @@ class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> { ), ), ), - child: StreamBuilder<UTXO?>( - stream: streamUTXO, - builder: (context, snapshot) { - if (snapshot.hasData) { - utxo = snapshot.data!; - } - - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const SizedBox( - height: 10, - ), - RoundedWhiteContainer( - child: Row( + child: ConditionalParent( + condition: isDesktop, + builder: (child) { + return DesktopDialog( + maxHeight: double.infinity, + child: Column( + children: [ + Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - "${Format.satoshisToAmount( - utxo!.value, - coin: coin, - ).toStringAsFixed( - coin.decimals, - )} ${coin.ticker}", - style: STextStyles.pageTitleH2(context), - ), - Text( - utxo!.isBlocked - ? "Frozen" - : confirmed - ? "Available" - : "Unconfirmed", - style: STextStyles.w500_14(context).copyWith( - color: utxo!.isBlocked - ? const Color(0xFF7FA2D4) // todo theme - : confirmed - ? Theme.of(context) - .extension<StackColors>()! - .accentColorGreen - : Theme.of(context) - .extension<StackColors>()! - .accentColorYellow, + Padding( + padding: const EdgeInsets.only(left: 32), + child: Text( + "Output details", + style: STextStyles.desktopH3(context), ), ), - ], - ), - ), - const SizedBox( - height: _spacing, - ), - RoundedWhiteContainer( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Label", - style: STextStyles.w500_14(context).copyWith( - color: Theme.of(context) - .extension<StackColors>()! - .textSubtitle1, - ), - ), - SimpleEditButton( - editValue: utxo!.name, - editLabel: "label", - onValueChanged: (newName) { - MainDB.instance.putUTXO( - utxo!.copyWith( - name: newName, - ), - ); - }, - ), - ], - ), - const SizedBox( - height: 4, - ), - Text( - utxo!.name, - style: STextStyles.w500_14(context), + DesktopDialogCloseButton( + onPressedOverride: () { + Navigator.of(context) + .pop(_popWithRefresh ? "refresh" : null); + }, ), ], ), - ), - const SizedBox( - height: _spacing, - ), - RoundedWhiteContainer( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Address", - style: STextStyles.w500_14(context).copyWith( - color: Theme.of(context) - .extension<StackColors>()! - .textSubtitle1, - ), - ), - SimpleCopyButton( - data: utxo!.address!, - ), - ], - ), - const SizedBox( - height: 4, - ), - Text( - utxo!.address!, - style: STextStyles.w500_14(context), - ), - ], + child, + ], + ), + ); + }, + child: StreamBuilder<UTXO?>( + stream: streamUTXO, + builder: (context, snapshot) { + if (snapshot.hasData) { + utxo = snapshot.data!; + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox( + height: 10, + ), + RoundedWhiteContainer( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "${Format.satoshisToAmount( + utxo!.value, + coin: coin, + ).toStringAsFixed( + coin.decimals, + )} ${coin.ticker}", + style: STextStyles.pageTitleH2(context), + ), + Text( + utxo!.isBlocked + ? "Frozen" + : confirmed + ? "Available" + : "Unconfirmed", + style: STextStyles.w500_14(context).copyWith( + color: utxo!.isBlocked + ? const Color(0xFF7FA2D4) // todo theme + : confirmed + ? Theme.of(context) + .extension<StackColors>()! + .accentColorGreen + : Theme.of(context) + .extension<StackColors>()! + .accentColorYellow, + ), + ), + ], + ), ), - ), - if (label != null && label!.value.isNotEmpty) const SizedBox( height: _spacing, ), - if (label != null && label!.value.isNotEmpty) RoundedWhiteContainer( child: Column( mainAxisSize: MainAxisSize.min, @@ -266,15 +222,23 @@ class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "Address label", + "Label", style: STextStyles.w500_14(context).copyWith( color: Theme.of(context) .extension<StackColors>()! .textSubtitle1, ), ), - SimpleCopyButton( - data: label!.value, + SimpleEditButton( + editValue: utxo!.name, + editLabel: "label", + onValueChanged: (newName) { + MainDB.instance.putUTXO( + utxo!.copyWith( + name: newName, + ), + ); + }, ), ], ), @@ -282,135 +246,218 @@ class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> { height: 4, ), Text( - label!.value, + utxo!.name, style: STextStyles.w500_14(context), ), ], ), ), - const SizedBox( - height: _spacing, - ), - RoundedWhiteContainer( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Transaction ID", - style: STextStyles.w500_14(context).copyWith( - color: Theme.of(context) - .extension<StackColors>()! - .textSubtitle1, + const SizedBox( + height: _spacing, + ), + RoundedWhiteContainer( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Address", + style: STextStyles.w500_14(context).copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .textSubtitle1, + ), ), + isDesktop + ? IconCopyButton( + data: utxo!.address!, + ) + : SimpleCopyButton( + data: utxo!.address!, + ), + ], + ), + const SizedBox( + height: 4, + ), + Text( + utxo!.address!, + style: STextStyles.w500_14(context), + ), + ], + ), + ), + if (label != null && label!.value.isNotEmpty) + const SizedBox( + height: _spacing, + ), + if (label != null && label!.value.isNotEmpty) + RoundedWhiteContainer( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Address label", + style: STextStyles.w500_14(context).copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .textSubtitle1, + ), + ), + isDesktop + ? IconCopyButton( + data: utxo!.address!, + ) + : SimpleCopyButton( + data: label!.value, + ), + ], ), - SimpleCopyButton( - data: utxo!.txid, + const SizedBox( + height: 4, + ), + Text( + label!.value, + style: STextStyles.w500_14(context), ), ], ), - const SizedBox( - height: 4, - ), - Text( - utxo!.txid, - style: STextStyles.w500_14(context), - ), - ], + ), + const SizedBox( + height: _spacing, ), - ), - const SizedBox( - height: _spacing, - ), - RoundedWhiteContainer( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Confirmations", - style: STextStyles.w500_14(context).copyWith( - color: Theme.of(context) - .extension<StackColors>()! - .textSubtitle1, - ), - ), - const SizedBox( - height: 4, - ), - Text( - "${utxo!.getConfirmations(currentHeight)}", - style: STextStyles.w500_14(context), - ), - ], - ), - ), - const SizedBox( - height: _spacing, - ), - if (utxo!.isBlocked) - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - RoundedWhiteContainer( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + RoundedWhiteContainer( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Freeze reason", - style: STextStyles.w500_14(context).copyWith( - color: Theme.of(context) - .extension<StackColors>()! - .textSubtitle1, - ), - ), - SimpleEditButton( - editValue: utxo!.blockedReason ?? "", - editLabel: "freeze reason", - onValueChanged: (newReason) { - MainDB.instance.putUTXO( - utxo!.copyWith( - blockedReason: newReason, - ), - ); - }, - ), - ], - ), - const SizedBox( - height: 4, - ), Text( - utxo!.blockedReason ?? "", - style: STextStyles.w500_14(context), + "Transaction ID", + style: STextStyles.w500_14(context).copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .textSubtitle1, + ), ), + isDesktop + ? IconCopyButton( + data: utxo!.address!, + ) + : SimpleCopyButton( + data: utxo!.txid, + ), ], ), - ), - const SizedBox( - height: _spacing, - ), - ], + const SizedBox( + height: 4, + ), + Text( + utxo!.txid, + style: STextStyles.w500_14(context), + ), + ], + ), ), - const Spacer(), - SecondaryButton( - label: utxo!.isBlocked ? "Unfreeze" : "Freeze", - onPressed: _toggleFreeze, - ), - const SizedBox( - height: 16, - ), - ], - ); - }, + const SizedBox( + height: _spacing, + ), + RoundedWhiteContainer( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Confirmations", + style: STextStyles.w500_14(context).copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .textSubtitle1, + ), + ), + const SizedBox( + height: 4, + ), + Text( + "${utxo!.getConfirmations(currentHeight)}", + style: STextStyles.w500_14(context), + ), + ], + ), + ), + const SizedBox( + height: _spacing, + ), + if (utxo!.isBlocked) + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + RoundedWhiteContainer( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Freeze reason", + style: STextStyles.w500_14(context).copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .textSubtitle1, + ), + ), + SimpleEditButton( + editValue: utxo!.blockedReason ?? "", + editLabel: "freeze reason", + onValueChanged: (newReason) { + MainDB.instance.putUTXO( + utxo!.copyWith( + blockedReason: newReason, + ), + ); + }, + ), + ], + ), + const SizedBox( + height: 4, + ), + Text( + utxo!.blockedReason ?? "", + style: STextStyles.w500_14(context), + ), + ], + ), + ), + const SizedBox( + height: _spacing, + ), + ], + ), + if (!isDesktop) const Spacer(), + SecondaryButton( + label: utxo!.isBlocked ? "Unfreeze" : "Freeze", + onPressed: _toggleFreeze, + ), + const SizedBox( + height: 16, + ), + ], + ); + }, + ), ), ); } diff --git a/lib/pages_desktop_specific/coin_control/desktop_coin_control_view.dart b/lib/pages_desktop_specific/coin_control/desktop_coin_control_view.dart index a291b35c6..6bc0f3034 100644 --- a/lib/pages_desktop_specific/coin_control/desktop_coin_control_view.dart +++ b/lib/pages_desktop_specific/coin_control/desktop_coin_control_view.dart @@ -275,6 +275,7 @@ class _DesktopCoinControlViewState // : _selectedAvailable.contains(utxo); return UtxoRow( + key: Key("${utxo.walletId}_${utxo.id}_${utxo.isBlocked}"), utxo: utxo, walletId: widget.walletId, onSelectedChanged: (value) { diff --git a/lib/pages_desktop_specific/coin_control/utxo_row.dart b/lib/pages_desktop_specific/coin_control/utxo_row.dart index f1f5e2481..9269ea038 100644 --- a/lib/pages_desktop_specific/coin_control/utxo_row.dart +++ b/lib/pages_desktop_specific/coin_control/utxo_row.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; +import 'package:stackwallet/pages/coin_control/utxo_details_view.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'; @@ -57,69 +58,85 @@ class _UtxoRowState extends ConsumerState<UtxoRow> { 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, + return StreamBuilder<UTXO?>( + stream: stream, + builder: (context, snapshot) { + if (snapshot.hasData) { + utxo = snapshot.data!; + } + + 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: () async { + await showDialog<String?>( + context: context, + builder: (context) => UtxoDetailsView( + utxoId: utxo.id, + walletId: widget.walletId, + ), + ); + }, + ) + ], ), - const SizedBox( - width: 10, - ), - SecondaryButton( - width: 120, - buttonHeight: ButtonHeight.xs, - label: "Details", - onPressed: () { - //todo - }, - ) - ], - ), + ); + }, ); } } diff --git a/lib/widgets/custom_buttons/simple_edit_button.dart b/lib/widgets/custom_buttons/simple_edit_button.dart index af1c9259e..931de7aa4 100644 --- a/lib/widgets/custom_buttons/simple_edit_button.dart +++ b/lib/widgets/custom_buttons/simple_edit_button.dart @@ -4,8 +4,12 @@ import 'package:stackwallet/pages/generic/single_field_edit_view.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/utilities/util.dart'; import 'package:tuple/tuple.dart'; +import '../desktop/desktop_dialog.dart'; +import '../icon_widgets/pencil_icon.dart'; + class SimpleEditButton extends StatelessWidget { const SimpleEditButton({ Key? key, @@ -20,36 +24,78 @@ class SimpleEditButton extends StatelessWidget { @override Widget build(BuildContext context) { - return GestureDetector( - onTap: () async { - final result = await Navigator.of(context).pushNamed( - SingleFieldEditView.routeName, - arguments: Tuple2( - editValue, - editLabel, + if (Util.isDesktop) { + return SizedBox( + height: 26, + width: 26, + child: RawMaterialButton( + fillColor: + Theme.of(context).extension<StackColors>()!.buttonBackSecondary, + elevation: 0, + hoverElevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), ), - ); - if (result is String && result != editValue) { - onValueChanged(result); - } - }, - child: Row( - children: [ - SvgPicture.asset( - Assets.svg.pencil, - width: 10, - height: 10, - color: Theme.of(context).extension<StackColors>()!.infoItemIcons, + onPressed: () async { + final result = await showDialog<String?>( + context: context, + builder: (context) { + return DesktopDialog( + maxWidth: 580, + maxHeight: 360, + child: SingleFieldEditView( + initialValue: editValue, + label: editLabel, + ), + ); + }, + ); + if (result is String && result != editValue) { + onValueChanged(result); + } + }, + child: Padding( + padding: const EdgeInsets.all(5), + child: PencilIcon( + width: 16, + height: 16, + color: Theme.of(context).extension<StackColors>()!.textDark, + ), ), - const SizedBox( - width: 4, - ), - Text( - "Edit", - style: STextStyles.link2(context), - ), - ], - ), - ); + ), + ); + } else { + return GestureDetector( + onTap: () async { + final result = await Navigator.of(context).pushNamed( + SingleFieldEditView.routeName, + arguments: Tuple2( + editValue, + editLabel, + ), + ); + if (result is String && result != editValue) { + onValueChanged(result); + } + }, + child: Row( + children: [ + SvgPicture.asset( + Assets.svg.pencil, + width: 10, + height: 10, + color: Theme.of(context).extension<StackColors>()!.infoItemIcons, + ), + const SizedBox( + width: 4, + ), + Text( + "Edit", + style: STextStyles.link2(context), + ), + ], + ), + ); + } } }