From 1f75c6b6e792fc97a56cdf1ba74309571c0f5164 Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 25 Apr 2024 11:45:28 -0600 Subject: [PATCH] select frost import config type selection screen --- .../select_new_frost_import_type_view.dart | 394 ++++++++++++++++++ lib/route_generator.dart | 18 + 2 files changed, 412 insertions(+) create mode 100644 lib/pages/add_wallet_views/frost_ms/new/select_new_frost_import_type_view.dart diff --git a/lib/pages/add_wallet_views/frost_ms/new/select_new_frost_import_type_view.dart b/lib/pages/add_wallet_views/frost_ms/new/select_new_frost_import_type_view.dart new file mode 100644 index 000000000..80205afd9 --- /dev/null +++ b/lib/pages/add_wallet_views/frost_ms/new/select_new_frost_import_type_view.dart @@ -0,0 +1,394 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/import_new_frost_ms_wallet_view.dart'; +import 'package:stackwallet/pages/settings_views/wallet_settings_view/frost_ms/resharing/new/new_import_resharer_config_view.dart'; +import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/wallets/crypto_currency/intermediate/frost_currency.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/conditional_parent.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/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class SelectNewFrostImportTypeView extends StatefulWidget { + const SelectNewFrostImportTypeView({ + super.key, + required this.walletName, + required this.frostCurrency, + }); + + static const String routeName = "/selectNewFrostImportTypeView"; + + final String walletName; + final FrostCurrency frostCurrency; + + @override + State createState() => + _SelectNewFrostImportTypeViewState(); +} + +class _SelectNewFrostImportTypeViewState + extends State { + _ImportOption _selectedOption = _ImportOption.multisigNew; + + @override + Widget build(BuildContext context) { + return ConditionalParent( + condition: Util.isDesktop, + builder: (content) => DesktopScaffold( + appBar: const DesktopAppBar( + leading: AppBarBackButton(), + trailing: ExitToMyStackButton(), + isCompactHeight: false, + ), + body: SizedBox( + width: 480, + child: content, + ), + ), + child: ConditionalParent( + condition: !Util.isDesktop, + builder: (content) => Background( + child: Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, + ), + actions: [ + AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + size: 36, + icon: SvgPicture.asset( + Assets.svg.circleQuestion, + width: 20, + height: 20, + colorFilter: ColorFilter.mode( + Theme.of(context) + .extension()! + .topNavIconPrimary, + BlendMode.srcIn, + ), + ), + onPressed: () async { + await showDialog( + context: context, + builder: (_) => const _FrostJoinInfoDialog(), + ); + }, + ), + ), + ], + ), + body: Container( + color: Theme.of(context).extension()!.background, + child: Padding( + padding: const EdgeInsets.all(16), + child: LayoutBuilder( + builder: (ctx, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: + BoxConstraints(minHeight: constraints.maxHeight), + child: IntrinsicHeight( + child: content, + ), + ), + ); + }, + ), + ), + ), + ), + ), + child: Column( + children: [ + ..._ImportOption.values.map( + (e) => Padding( + padding: const EdgeInsets.only(bottom: 16), + child: _ImportOptionCard( + onPressed: () => setState(() => _selectedOption = e), + title: e.info, + description: e.description, + value: e, + groupValue: _selectedOption, + ), + ), + ), + const Spacer(), + PrimaryButton( + label: "Continue", + onPressed: () async { + final String route; + switch (_selectedOption) { + case _ImportOption.multisigNew: + route = ImportNewFrostMsWalletView.routeName; + case _ImportOption.resharerExisting: + route = NewImportResharerConfigView.routeName; + } + + await Navigator.of(context).pushNamed( + route, + arguments: ( + walletName: widget.walletName, + frostCurrency: widget.frostCurrency, + ), + ); + }, + ) + ], + ), + ), + ); + } +} + +enum _ImportOption { + multisigNew, + resharerExisting; + + String get info { + switch (this) { + case _ImportOption.multisigNew: + return "I want to join a new group"; + case _ImportOption.resharerExisting: + return "I want to join an existing group"; + } + } + + String get description { + switch (this) { + case _ImportOption.multisigNew: + return "You are currently participating in the process of creating a new group"; + case _ImportOption.resharerExisting: + return "You are joining an existing group through the process of resharing"; + } + } +} + +class _ImportOptionCard extends StatefulWidget { + const _ImportOptionCard({ + super.key, + required this.onPressed, + required this.title, + required this.description, + required this.value, + required this.groupValue, + }); + + final VoidCallback onPressed; + final String title; + final String description; + final _ImportOption value; + final _ImportOption groupValue; + + @override + State<_ImportOptionCard> createState() => _ImportOptionCardState(); +} + +class _ImportOptionCardState extends State<_ImportOptionCard> { + @override + Widget build(BuildContext context) { + return RoundedWhiteContainer( + padding: const EdgeInsets.all(0), + onPressed: widget.onPressed, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(6.0), + child: Radio( + value: widget.value, + groupValue: widget.groupValue, + activeColor: Theme.of(context) + .extension()! + .radioButtonIconEnabled, + onChanged: (_) => widget.onPressed(), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.only( + top: 12.0, + right: 12.0, + bottom: 12.0, + ), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: Text( + widget.title, + style: STextStyles.w600_16(context), + ), + ), + ], + ), + const SizedBox( + height: 2, + ), + Row( + children: [ + Expanded( + child: Text( + widget.description, + style: STextStyles.w500_14(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle1, + ), + ), + ), + ], + ), + ], + ), + ), + ), + ], + ), + ); + } +} + +class _FrostJoinInfoDialog extends StatelessWidget { + const _FrostJoinInfoDialog({super.key}); + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Padding( + padding: const EdgeInsets.all(16), + child: Material( + borderRadius: BorderRadius.circular( + 20, + ), + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).extension()!.popupBG, + borderRadius: BorderRadius.circular( + 20, + ), + ), + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // TODO: [prio=high] need text from designers! + Text( + "Join a group", + style: STextStyles.w600_20(context), + ), + const SizedBox( + height: 12, + ), + Text( + "Text here", + style: STextStyles.w400_16(context), + ), + const SizedBox( + height: 8, + ), + Text( + "What is resharing?", + style: STextStyles.w600_16(context), + ), + const SizedBox( + height: 8, + ), + Text( + "In cryptocurrency, you are your own bank." + " Imagine keeping cash at home. If that cash" + " burns down or gets stolen, you lose it and" + " nobody will help you get your money back.", + style: STextStyles.w400_16(context), + ), + const SizedBox( + height: 8, + ), + Text( + "Since cryptocurrency is digital money, your " + "wallet key is like that “cash” you keep at " + "home. If you lose your phone or if you " + "forget your wallet PIN, but you have your " + "wallet key, your crypto money will be safe. " + "That is why you should keep your wallet key " + "safe.", + style: STextStyles.w400_16(context), + ), + const SizedBox( + height: 12, + ), + Text( + "Why write it down?", + style: STextStyles.w600_16(context), + ), + const SizedBox( + height: 8, + ), + Text( + "You do not put your cash on display, do you?" + " Keeping your wallet key on a digital device" + " is like having it on display for thieves - " + "malicious software and hackers. Write your " + "wallet key down on paper in multiple copies " + "and keep them in a real, physical safe.", + style: STextStyles.w400_16(context), + ), + ], + ), + ), + ), + const SizedBox( + height: 16, + ), + Row( + children: [ + const Spacer(), + const SizedBox( + width: 16, + ), + Expanded( + child: SecondaryButton( + label: "Close", + onPressed: Navigator.of(context).pop, + ), + ), + ], + ), + ], + ), + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/route_generator.dart b/lib/route_generator.dart index ace0e7c88..70b4773fa 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -503,6 +503,24 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case SelectNewFrostImportTypeView.routeName: + if (args is ({ + String walletName, + FrostCurrency frostCurrency, + })) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => SelectNewFrostImportTypeView( + walletName: args.walletName, + frostCurrency: args.frostCurrency, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + case ImportNewFrostMsWalletView.routeName: if (args is ({ String walletName,