diff --git a/lib/pages/paynym/paynym_home_view.dart b/lib/pages/paynym/paynym_home_view.dart index ba12e517f..cdfba4529 100644 --- a/lib/pages/paynym/paynym_home_view.dart +++ b/lib/pages/paynym/paynym_home_view.dart @@ -7,9 +7,11 @@ import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/pages/paynym/add_new_paynym_follow_view.dart'; import 'package:stackwallet/pages/paynym/dialogs/paynym_qr_popup.dart'; +import 'package:stackwallet/pages/paynym/subwidgets/desktop_paynym_details.dart'; import 'package:stackwallet/pages/paynym/subwidgets/paynym_bot.dart'; import 'package:stackwallet/pages/paynym/subwidgets/paynym_followers_list.dart'; import 'package:stackwallet/pages/paynym/subwidgets/paynym_following_list.dart'; +import 'package:stackwallet/providers/ui/selected_paynym_details_item_Provider.dart'; import 'package:stackwallet/providers/wallet/my_paynym_account_state_provider.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; @@ -553,9 +555,47 @@ class _PaynymHomeViewState extends ConsumerState { condition: isDesktop, builder: (child) => Padding( padding: const EdgeInsets.only(left: 24), - child: SizedBox( - width: 490, - child: child, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 490, + child: child, + ), + const SizedBox( + width: 24, + ), + if (ref + .watch(selectedPaynymDetailsItemProvider.state) + .state != + null) + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 600, + ), + child: DesktopPaynymDetails( + walletId: widget.walletId, + accountLite: ref + .watch(selectedPaynymDetailsItemProvider + .state) + .state!, + ), + ), + ], + ), + ), + if (ref + .watch(selectedPaynymDetailsItemProvider.state) + .state != + null) + const SizedBox( + width: 24, + ), + ], ), ), child: ConditionalParent( diff --git a/lib/pages/paynym/subwidgets/desktop_paynym_details.dart b/lib/pages/paynym/subwidgets/desktop_paynym_details.dart new file mode 100644 index 000000000..ce6dedabe --- /dev/null +++ b/lib/pages/paynym/subwidgets/desktop_paynym_details.dart @@ -0,0 +1,271 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:qr_flutter/qr_flutter.dart'; +import 'package:stackwallet/models/paynym/paynym_account_lite.dart'; +import 'package:stackwallet/pages/paynym/dialogs/confirm_paynym_connect_dialog.dart'; +import 'package:stackwallet/pages/paynym/subwidgets/paynym_bot.dart'; +import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; +import 'package:stackwallet/route_generator.dart'; +import 'package:stackwallet/services/coins/coin_paynym_extension.dart'; +import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.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/widgets/custom_buttons/blue_text_button.dart'; +import 'package:stackwallet/widgets/custom_buttons/paynym_follow_toggle_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/loading_indicator.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +import '../../../notifications/show_flush_bar.dart'; + +class DesktopPaynymDetails extends ConsumerStatefulWidget { + const DesktopPaynymDetails({ + Key? key, + required this.walletId, + required this.accountLite, + }) : super(key: key); + + final String walletId; + final PaynymAccountLite accountLite; + + @override + ConsumerState createState() => + _PaynymDetailsPopupState(); +} + +class _PaynymDetailsPopupState extends ConsumerState { + Future _onConnectPressed() async { + bool canPop = false; + unawaited( + showDialog( + context: context, + builder: (context) => WillPopScope( + onWillPop: () async => canPop, + child: const LoadingIndicator( + width: 200, + ), + ), + ), + ); + + final wallet = ref + .read(walletsChangeNotifierProvider) + .getManager(widget.walletId) + .wallet as DogecoinWallet; + + // sanity check to prevent second notifcation tx + if (wallet.hasConnectedConfirmed(widget.accountLite.code)) { + canPop = true; + Navigator.of(context).pop(); + // TODO show info popup + return; + } else if (wallet.hasConnected(widget.accountLite.code)) { + canPop = true; + Navigator.of(context).pop(); + // TODO show info popup + return; + } + + final rates = await wallet.fees; + + Map preparedTx; + + try { + preparedTx = await wallet.buildNotificationTx( + selectedTxFeeRate: rates.medium, + targetPaymentCodeString: widget.accountLite.code, + ); + } on InsufficientBalanceException catch (e) { + if (mounted) { + canPop = true; + Navigator.of(context).pop(); + } + // TODO show info popup + print(e); + return; + } + + if (mounted) { + // We have enough balance and prepared tx should be good to go. + + canPop = true; + // close loading + Navigator.of(context).pop(); + + // Close details + Navigator.of(context).pop(); + + // show info pop up + await showDialog( + context: context, + builder: (context) => ConfirmPaynymConnectDialog( + nymName: widget.accountLite.nymName, + onConfirmPressed: () { + // + print("CONFIRM NOTIF TX: $preparedTx"); + + Navigator.of(context).push( + RouteGenerator.getRoute( + builder: (_) => ConfirmTransactionView( + walletId: wallet.walletId, + transactionInfo: { + "hex": preparedTx["hex"], + "recipient": preparedTx["recipientPaynym"], + "recipientAmt": preparedTx["amount"], + "fee": preparedTx["fee"], + "vSize": preparedTx["vSize"], + "note": "PayNym connect" + }, + ), + ), + ); + }, + amount: (preparedTx["amount"] as int) + (preparedTx["fee"] as int), + coin: wallet.coin, + ), + ); + } + } + + @override + Widget build(BuildContext context) { + return RoundedWhiteContainer( + padding: const EdgeInsets.all(0), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + PayNymBot( + paymentCodeString: widget.accountLite.code, + size: 32, + ), + const SizedBox( + width: 12, + ), + Text( + widget.accountLite.nymName, + style: STextStyles.desktopTextSmall(context), + ), + ], + ), + const SizedBox( + height: 20, + ), + Row( + children: [ + Expanded( + child: PrimaryButton( + label: "Connect", + buttonHeight: ButtonHeight.s, + icon: SvgPicture.asset( + Assets.svg.circlePlusFilled, + width: 16, + height: 16, + color: Theme.of(context) + .extension()! + .buttonTextPrimary, + ), + iconSpacing: 6, + onPressed: _onConnectPressed, + ), + ), + const SizedBox( + width: 20, + ), + Expanded( + child: PaynymFollowToggleButton( + walletId: widget.walletId, + paymentCodeStringToFollow: widget.accountLite.code, + style: PaynymFollowToggleButtonStyle.detailsDesktop, + ), + ), + ], + ), + ], + ), + ), + Container( + color: Theme.of(context).extension()!.backgroundAppBar, + height: 1, + ), + Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "PayNym address", + style: STextStyles.desktopTextExtraExtraSmall(context), + ), + const SizedBox( + height: 8, + ), + Row( + children: [ + Expanded( + child: ConstrainedBox( + constraints: const BoxConstraints(minHeight: 100), + child: Text( + widget.accountLite.code, + style: STextStyles.desktopTextExtraExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ), + ), + ), + ), + const SizedBox( + width: 20, + ), + QrImage( + padding: const EdgeInsets.all(0), + size: 100, + data: widget.accountLite.code, + foregroundColor: + Theme.of(context).extension()!.textDark, + ), + ], + ), + const SizedBox( + height: 8, + ), + BlueTextButton( + text: "Copy", + onTap: () async { + await Clipboard.setData( + ClipboardData( + text: widget.accountLite.code, + ), + ); + unawaited( + showFloatingFlushBar( + type: FlushBarType.info, + message: "Copied to clipboard", + iconAsset: Assets.svg.copy, + context: context, + ), + ); + }, + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/paynym/subwidgets/paynym_card_button.dart b/lib/pages/paynym/subwidgets/paynym_card_button.dart index f69700b87..75c4d2cc0 100644 --- a/lib/pages/paynym/subwidgets/paynym_card_button.dart +++ b/lib/pages/paynym/subwidgets/paynym_card_button.dart @@ -1,14 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:stackwallet/models/paynym/paynym_account_lite.dart'; import 'package:stackwallet/pages/paynym/dialogs/paynym_details_popup.dart'; import 'package:stackwallet/pages/paynym/subwidgets/paynym_bot.dart'; +import 'package:stackwallet/providers/ui/selected_paynym_details_item_Provider.dart'; import 'package:stackwallet/utilities/constants.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/utilities/util.dart'; +import 'package:stackwallet/widgets/rounded_container.dart'; -class PaynymCardButton extends StatefulWidget { +class PaynymCardButton extends ConsumerStatefulWidget { const PaynymCardButton({ Key? key, required this.walletId, @@ -19,81 +22,100 @@ class PaynymCardButton extends StatefulWidget { final PaynymAccountLite accountLite; @override - State createState() => _PaynymCardButtonState(); + ConsumerState createState() => _PaynymCardButtonState(); } -class _PaynymCardButtonState extends State { +class _PaynymCardButtonState extends ConsumerState { final isDesktop = Util.isDesktop; @override Widget build(BuildContext context) { return Padding( - padding: isDesktop - ? const EdgeInsets.symmetric( - vertical: 8, - horizontal: 12, - ) - : const EdgeInsets.all(4), - child: RawMaterialButton( + padding: const EdgeInsets.all(4), + child: RoundedContainer( padding: const EdgeInsets.all(0), - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - ), - onPressed: () { - showDialog( - context: context, - builder: (context) => PaynymDetailsPopup( - accountLite: widget.accountLite, - walletId: widget.walletId, + color: isDesktop && + ref + .watch(selectedPaynymDetailsItemProvider.state) + .state + ?.nymId == + widget.accountLite.nymId + ? Theme.of(context) + .extension()! + .accentColorDark + .withOpacity(0.08) + : Colors.transparent, + child: RawMaterialButton( + padding: const EdgeInsets.all(0), + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, ), - ); - }, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - PayNymBot( - size: 32, - paymentCodeString: widget.accountLite.code, - ), - const SizedBox( - width: 12, - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - widget.accountLite.nymName, - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall(context) - .copyWith( - color: Theme.of(context) - .extension()! - .textFieldActiveText, - ) - : STextStyles.w500_12(context), - ), - const SizedBox( - height: 2, - ), - Text( - Format.shorten(widget.accountLite.code, 12, 5), - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall(context) - : STextStyles.w500_12(context).copyWith( - color: Theme.of(context) - .extension()! - .textSubtitle1, - ), - ), - ], + ), + onPressed: () { + if (isDesktop) { + ref.read(selectedPaynymDetailsItemProvider.state).state = + widget.accountLite; + } else { + showDialog( + context: context, + builder: (context) => PaynymDetailsPopup( + accountLite: widget.accountLite, + walletId: widget.walletId, ), - ), - ], + ); + } + }, + child: Padding( + padding: isDesktop + ? const EdgeInsets.symmetric( + vertical: 8, + horizontal: 12, + ) + : const EdgeInsets.all(8.0), + child: Row( + children: [ + PayNymBot( + size: 32, + paymentCodeString: widget.accountLite.code, + ), + const SizedBox( + width: 12, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + widget.accountLite.nymName, + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textFieldActiveText, + ) + : STextStyles.w500_12(context), + ), + const SizedBox( + height: 2, + ), + Text( + Format.shorten(widget.accountLite.code, 12, 5), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context) + : STextStyles.w500_12(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ), + ), + ], + ), + ), + ], + ), ), ), ), diff --git a/lib/providers/ui/selected_paynym_details_item_Provider.dart b/lib/providers/ui/selected_paynym_details_item_Provider.dart new file mode 100644 index 000000000..153145710 --- /dev/null +++ b/lib/providers/ui/selected_paynym_details_item_Provider.dart @@ -0,0 +1,5 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/models/paynym/paynym_account_lite.dart'; + +final selectedPaynymDetailsItemProvider = + StateProvider.autoDispose((_) => null); diff --git a/lib/widgets/custom_buttons/paynym_follow_toggle_button.dart b/lib/widgets/custom_buttons/paynym_follow_toggle_button.dart index d5a0ea07f..ea3771d95 100644 --- a/lib/widgets/custom_buttons/paynym_follow_toggle_button.dart +++ b/lib/widgets/custom_buttons/paynym_follow_toggle_button.dart @@ -21,6 +21,7 @@ import 'package:stackwallet/widgets/loading_indicator.dart'; enum PaynymFollowToggleButtonStyle { primary, detailsPopup, + detailsDesktop, } class PaynymFollowToggleButton extends ConsumerStatefulWidget { @@ -288,6 +289,21 @@ class _PaynymFollowToggleButtonState iconSpacing: 4, onPressed: _onPressed, ); + + case PaynymFollowToggleButtonStyle.detailsDesktop: + return SecondaryButton( + label: isFollowing ? "Unfollow" : "Follow", + buttonHeight: ButtonHeight.s, + icon: SvgPicture.asset( + isFollowing ? Assets.svg.userMinus : Assets.svg.userPlus, + width: 16, + height: 16, + color: + Theme.of(context).extension()!.buttonTextSecondary, + ), + iconSpacing: 6, + onPressed: _onPressed, + ); } } } diff --git a/lib/widgets/desktop/primary_button.dart b/lib/widgets/desktop/primary_button.dart index 95c28696e..a4fb4c35c 100644 --- a/lib/widgets/desktop/primary_button.dart +++ b/lib/widgets/desktop/primary_button.dart @@ -149,9 +149,20 @@ class PrimaryButton extends StatelessWidget { width: iconSpacing, ), if (label != null) - Text( - label!, - style: getStyle(isDesktop, context), + Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + label!, + style: getStyle(isDesktop, context), + ), + if (buttonHeight != null && buttonHeight == ButtonHeight.s) + const SizedBox( + height: 2, + ), + ], ), ], ), diff --git a/lib/widgets/desktop/secondary_button.dart b/lib/widgets/desktop/secondary_button.dart index 5603815b4..244ee356a 100644 --- a/lib/widgets/desktop/secondary_button.dart +++ b/lib/widgets/desktop/secondary_button.dart @@ -152,9 +152,20 @@ class SecondaryButton extends StatelessWidget { width: iconSpacing, ), if (label != null) - Text( - label!, - style: getStyle(isDesktop, context), + Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + label!, + style: getStyle(isDesktop, context), + ), + if (buttonHeight != null && buttonHeight == ButtonHeight.s) + const SizedBox( + height: 2, + ), + ], ), ], ),