From 999df80e6032181320207a3b5d9e900d73fd8a99 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 29 Apr 2024 17:30:46 -0600 Subject: [PATCH] refactor "new ms config import" to step 1b --- .../new/import_new_frost_ms_wallet_view.dart | 433 ------------------ .../select_new_frost_import_type_view.dart | 80 +++- ..._step_1.dart => frost_create_step_1a.dart} | 2 +- .../new/steps/frost_create_step_1b.dart | 368 +++++++++++++++ .../new/steps/frost_route_generator.dart | 18 +- lib/route_generator.dart | 19 - 6 files changed, 451 insertions(+), 469 deletions(-) delete mode 100644 lib/pages/add_wallet_views/frost_ms/new/import_new_frost_ms_wallet_view.dart rename lib/pages/add_wallet_views/frost_ms/new/steps/{frost_create_step_1.dart => frost_create_step_1a.dart} (99%) create mode 100644 lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1b.dart diff --git a/lib/pages/add_wallet_views/frost_ms/new/import_new_frost_ms_wallet_view.dart b/lib/pages/add_wallet_views/frost_ms/new/import_new_frost_ms_wallet_view.dart deleted file mode 100644 index 531d7971f..000000000 --- a/lib/pages/add_wallet_views/frost_ms/new/import_new_frost_ms_wallet_view.dart +++ /dev/null @@ -1,433 +0,0 @@ -import 'dart:async'; - -import 'package:barcode_scan2/barcode_scan2.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:stackwallet/notifications/show_flush_bar.dart'; -import 'package:stackwallet/pages/add_wallet_views/frost_ms/frost_scaffold.dart'; -import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart'; -import 'package:stackwallet/pages/home_view/home_view.dart'; -import 'package:stackwallet/pages_desktop_specific/desktop_home_view.dart'; -import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart'; -import 'package:stackwallet/services/frost.dart'; -import 'package:stackwallet/themes/stack_colors.dart'; -import 'package:stackwallet/utilities/assets.dart'; -import 'package:stackwallet/utilities/constants.dart'; -import 'package:stackwallet/utilities/logger.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/frost_mascot.dart'; -import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart'; -import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart'; -import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; -import 'package:stackwallet/widgets/stack_dialog.dart'; -import 'package:stackwallet/widgets/stack_text_field.dart'; -import 'package:stackwallet/widgets/textfield_icon_button.dart'; - -class ImportNewFrostMsWalletView extends ConsumerStatefulWidget { - const ImportNewFrostMsWalletView({ - super.key, - required this.walletName, - required this.frostCurrency, - }); - - static const String routeName = "/importNewFrostMsWalletView"; - - final String walletName; - final FrostCurrency frostCurrency; - - @override - ConsumerState createState() => - _ImportNewFrostMsWalletViewState(); -} - -class _ImportNewFrostMsWalletViewState - extends ConsumerState { - late final TextEditingController myNameFieldController, configFieldController; - late final FocusNode myNameFocusNode, configFocusNode; - - bool _nameEmpty = true, _configEmpty = true; - - @override - void initState() { - myNameFieldController = TextEditingController(); - configFieldController = TextEditingController(); - myNameFocusNode = FocusNode(); - configFocusNode = FocusNode(); - super.initState(); - } - - @override - void dispose() { - myNameFieldController.dispose(); - configFieldController.dispose(); - myNameFocusNode.dispose(); - configFocusNode.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return ConditionalParent( - condition: Util.isDesktop, - builder: (child) => DesktopScaffold( - background: Theme.of(context).extension()!.background, - appBar: const DesktopAppBar( - isCompactHeight: false, - leading: AppBarBackButton(), - // TODO: [prio=high] get rid of placeholder text?? - trailing: FrostMascot( - title: 'Lorem ipsum', - body: - 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam est justo, ', - ), - ), - body: SizedBox( - width: 480, - child: child, - ), - ), - child: ConditionalParent( - condition: !Util.isDesktop, - builder: (child) => Background( - child: Scaffold( - backgroundColor: - Theme.of(context).extension()!.background, - appBar: AppBar( - leading: AppBarBackButton( - onPressed: () { - Navigator.of(context).pop(); - }, - ), - title: Text( - "Import FROST multisig config", - style: STextStyles.navBarTitle(context), - ), - ), - body: SafeArea( - child: LayoutBuilder( - builder: (context, constraints) { - return SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: constraints.maxHeight, - ), - child: IntrinsicHeight( - child: Padding( - padding: const EdgeInsets.all(16), - child: child, - ), - ), - ), - ); - }, - ), - ), - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox( - height: 16, - ), - ClipRRect( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - child: TextField( - key: const Key("frMyNameTextFieldKey"), - controller: myNameFieldController, - onChanged: (_) { - setState(() { - _nameEmpty = myNameFieldController.text.isEmpty; - }); - }, - focusNode: myNameFocusNode, - readOnly: false, - autocorrect: false, - enableSuggestions: false, - style: STextStyles.field(context), - decoration: standardInputDecoration( - "My name", - myNameFocusNode, - context, - ).copyWith( - contentPadding: const EdgeInsets.only( - left: 16, - top: 6, - bottom: 8, - right: 5, - ), - suffixIcon: Padding( - padding: _nameEmpty - ? const EdgeInsets.only(right: 8) - : const EdgeInsets.only(right: 0), - child: UnconstrainedBox( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - !_nameEmpty - ? TextFieldIconButton( - semanticsLabel: - "Clear Button. Clears The Config Field.", - key: const Key("frMyNameClearButtonKey"), - onTap: () { - myNameFieldController.text = ""; - - setState(() { - _nameEmpty = true; - }); - }, - child: const XIcon(), - ) - : TextFieldIconButton( - semanticsLabel: - "Paste Button. Pastes From Clipboard To Name Field.", - key: const Key("frMyNamePasteButtonKey"), - onTap: () async { - final ClipboardData? data = - await Clipboard.getData( - Clipboard.kTextPlain); - if (data?.text != null && - data!.text!.isNotEmpty) { - myNameFieldController.text = - data.text!.trim(); - } - - setState(() { - _nameEmpty = - myNameFieldController.text.isEmpty; - }); - }, - child: _nameEmpty - ? const ClipboardIcon() - : const XIcon(), - ), - ], - ), - ), - ), - ), - ), - ), - const SizedBox( - height: 16, - ), - ClipRRect( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - child: TextField( - key: const Key("frConfigTextFieldKey"), - controller: configFieldController, - onChanged: (_) { - setState(() { - _configEmpty = configFieldController.text.isEmpty; - }); - }, - focusNode: configFocusNode, - readOnly: false, - autocorrect: false, - enableSuggestions: false, - style: STextStyles.field(context), - decoration: standardInputDecoration( - "Enter config", - configFocusNode, - context, - ).copyWith( - contentPadding: const EdgeInsets.only( - left: 16, - top: 6, - bottom: 8, - right: 5, - ), - suffixIcon: Padding( - padding: _configEmpty - ? const EdgeInsets.only(right: 8) - : const EdgeInsets.only(right: 0), - child: UnconstrainedBox( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - !_configEmpty - ? TextFieldIconButton( - semanticsLabel: - "Clear Button. Clears The Config Field.", - key: const Key("frConfigClearButtonKey"), - onTap: () { - configFieldController.text = ""; - - setState(() { - _configEmpty = true; - }); - }, - child: const XIcon(), - ) - : TextFieldIconButton( - semanticsLabel: - "Paste Button. Pastes From Clipboard To Config Field Input.", - key: const Key("frConfigPasteButtonKey"), - onTap: () async { - final ClipboardData? data = - await Clipboard.getData( - Clipboard.kTextPlain); - if (data?.text != null && - data!.text!.isNotEmpty) { - configFieldController.text = - data.text!.trim(); - } - - setState(() { - _configEmpty = - configFieldController.text.isEmpty; - }); - }, - child: _configEmpty - ? const ClipboardIcon() - : const XIcon(), - ), - if (_configEmpty) - TextFieldIconButton( - semanticsLabel: - "Scan QR Button. Opens Camera For Scanning QR Code.", - key: const Key("frConfigScanQrButtonKey"), - onTap: () async { - try { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed( - const Duration(milliseconds: 75)); - } - - final qrResult = await BarcodeScanner.scan(); - - configFieldController.text = - qrResult.rawContent; - - setState(() { - _configEmpty = - configFieldController.text.isEmpty; - }); - } on PlatformException catch (e, s) { - Logging.instance.log( - "Failed to get camera permissions while trying to scan qr code: $e\n$s", - level: LogLevel.Warning, - ); - } - }, - child: const QrCodeIcon(), - ) - ], - ), - ), - ), - ), - ), - ), - const SizedBox( - height: 16, - ), - if (!Util.isDesktop) const Spacer(), - const SizedBox( - height: 16, - ), - PrimaryButton( - label: "Start key generation", - enabled: !_nameEmpty && !_configEmpty, - onPressed: () async { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - } - - final config = configFieldController.text; - - if (!Frost.validateEncodedMultisigConfig( - encodedConfig: config)) { - return await showDialog( - context: context, - builder: (_) => StackOkDialog( - title: "Invalid config", - desktopPopRootNavigator: Util.isDesktop, - ), - ); - } - - if (!Frost.getParticipants(multisigConfig: config) - .contains(myNameFieldController.text)) { - return await showDialog( - context: context, - builder: (_) => StackOkDialog( - title: "My name not found in config participants", - desktopPopRootNavigator: Util.isDesktop, - ), - ); - } - - ref.read(pFrostMyName.state).state = myNameFieldController.text; - ref.read(pFrostMultisigConfig.notifier).state = config; - - ref.read(pFrostStartKeyGenData.state).state = - Frost.startKeyGeneration( - multisigConfig: ref.read(pFrostMultisigConfig.state).state!, - myName: ref.read(pFrostMyName.state).state!, - ); - - ref.read(pFrostCreateNewArgs.state).state = ( - ( - walletName: widget.walletName, - frostCurrency: widget.frostCurrency, - ), - FrostRouteGenerator.createNewConfigStepRoutes, - () { - // successful completion of steps - if (Util.isDesktop) { - Navigator.of(context).popUntil( - ModalRoute.withName( - DesktopHomeView.routeName, - ), - ); - } else { - unawaited( - Navigator.of(context).pushNamedAndRemoveUntil( - HomeView.routeName, - (route) => false, - ), - ); - } - - ref.read(pFrostMultisigConfig.state).state = null; - ref.read(pFrostStartKeyGenData.state).state = null; - ref.read(pFrostSecretSharesData.state).state = null; - ref.read(pFrostCreateNewArgs.state).state = null; - - unawaited( - showFloatingFlushBar( - type: FlushBarType.success, - message: "Your wallet is set up.", - iconAsset: Assets.svg.check, - context: context, - ), - ); - } - ); - - await Navigator.of(context).pushNamed( - FrostStepScaffold.routeName, - ); - }, - ), - ], - ), - ), - ); - } -} 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 index e35822cac..7a797ed9a 100644 --- 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 @@ -1,8 +1,16 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.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/notifications/show_flush_bar.dart'; +import 'package:stackwallet/pages/add_wallet_views/frost_ms/frost_scaffold.dart'; +import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart'; +import 'package:stackwallet/pages/home_view/home_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/desktop_home_view.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart'; +import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/text_styles.dart'; @@ -17,7 +25,7 @@ import 'package:stackwallet/widgets/desktop/primary_button.dart'; import 'package:stackwallet/widgets/dialogs/simple_mobile_dialog.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; -class SelectNewFrostImportTypeView extends StatefulWidget { +class SelectNewFrostImportTypeView extends ConsumerStatefulWidget { const SelectNewFrostImportTypeView({ super.key, required this.walletName, @@ -30,12 +38,12 @@ class SelectNewFrostImportTypeView extends StatefulWidget { final FrostCurrency frostCurrency; @override - State createState() => + ConsumerState createState() => _SelectNewFrostImportTypeViewState(); } class _SelectNewFrostImportTypeViewState - extends State { + extends ConsumerState { _ImportOption _selectedOption = _ImportOption.multisigNew; @override @@ -133,18 +141,60 @@ class _SelectNewFrostImportTypeViewState final String route; switch (_selectedOption) { case _ImportOption.multisigNew: - route = ImportNewFrostMsWalletView.routeName; - case _ImportOption.resharerExisting: - route = NewImportResharerConfigView.routeName; - } + ref.read(pFrostCreateNewArgs.state).state = ( + ( + walletName: widget.walletName, + frostCurrency: widget.frostCurrency, + ), + FrostRouteGenerator.importNewConfigStepRoutes, + () { + // successful completion of steps + if (Util.isDesktop) { + Navigator.of(context).popUntil( + ModalRoute.withName( + DesktopHomeView.routeName, + ), + ); + } else { + unawaited( + Navigator.of(context).pushNamedAndRemoveUntil( + HomeView.routeName, + (route) => false, + ), + ); + } - await Navigator.of(context).pushNamed( - route, - arguments: ( - walletName: widget.walletName, - frostCurrency: widget.frostCurrency, - ), - ); + ref.read(pFrostMultisigConfig.state).state = null; + ref.read(pFrostStartKeyGenData.state).state = null; + ref.read(pFrostSecretSharesData.state).state = null; + ref.read(pFrostCreateNewArgs.state).state = null; + + unawaited( + showFloatingFlushBar( + type: FlushBarType.success, + message: "Your wallet is set up.", + iconAsset: Assets.svg.check, + context: context, + ), + ); + } + ); + + await Navigator.of(context).pushNamed( + FrostStepScaffold.routeName, + ); + break; + + case _ImportOption.resharerExisting: + await Navigator.of(context).pushNamed( + NewImportResharerConfigView.routeName, + arguments: ( + walletName: widget.walletName, + frostCurrency: widget.frostCurrency, + ), + ); + break; + } }, ) ], diff --git a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1.dart b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1a.dart similarity index 99% rename from lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1.dart rename to lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1a.dart index 78c297a97..f4fab1000 100644 --- a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1.dart +++ b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1a.dart @@ -21,7 +21,7 @@ import 'package:stackwallet/widgets/frost_step_user_steps.dart'; class FrostCreateStep1a extends ConsumerStatefulWidget { const FrostCreateStep1a({super.key}); - static const String routeName = "/frostCreateStep1"; + static const String routeName = "/frostCreateStep1a"; static const String title = "Multisig group info"; @override diff --git a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1b.dart b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1b.dart new file mode 100644 index 000000000..88d3af415 --- /dev/null +++ b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1b.dart @@ -0,0 +1,368 @@ +import 'dart:async'; + +import 'package:barcode_scan2/barcode_scan2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_2.dart'; +import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart'; +import 'package:stackwallet/providers/frost_wallet/frost_wallet_providers.dart'; +import 'package:stackwallet/services/frost.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/logger.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/frost_step_user_steps.dart'; +import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart'; +import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart'; +import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; +import 'package:stackwallet/widgets/stack_dialog.dart'; +import 'package:stackwallet/widgets/stack_text_field.dart'; +import 'package:stackwallet/widgets/textfield_icon_button.dart'; + +class FrostCreateStep1b extends ConsumerStatefulWidget { + const FrostCreateStep1b({super.key}); + + static const String routeName = "/frostCreateStep1b"; + static const String title = "Import group info"; + + @override + ConsumerState createState() => _FrostCreateStep1bState(); +} + +class _FrostCreateStep1bState extends ConsumerState { + static const info = [ + "Scan the config QR code or paste the code provided by the group creator.", + "Enter your name EXACTLY as the group creator entered it. When in doubt, " + "double check with them. The names are case-sensitive.", + "Wait for other participants to finish entering their information.", + "Verify that everyone has filled out their forms before continuing. If you " + "try to continue before everyone is ready, the process will be canceled.", + "Check the box and press “Generate keys”.", + ]; + + late final TextEditingController myNameFieldController, configFieldController; + late final FocusNode myNameFocusNode, configFocusNode; + + bool _nameEmpty = true, _configEmpty = true, _userVerifyContinue = false; + + @override + void initState() { + myNameFieldController = TextEditingController(); + configFieldController = TextEditingController(); + myNameFocusNode = FocusNode(); + configFocusNode = FocusNode(); + super.initState(); + } + + @override + void dispose() { + myNameFieldController.dispose(); + configFieldController.dispose(); + myNameFocusNode.dispose(); + configFocusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + const FrostStepUserSteps( + userSteps: info, + ), + const SizedBox( + height: 16, + ), + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + key: const Key("frMyNameTextFieldKey"), + controller: myNameFieldController, + onChanged: (_) { + setState(() { + _nameEmpty = myNameFieldController.text.isEmpty; + }); + }, + focusNode: myNameFocusNode, + readOnly: false, + autocorrect: false, + enableSuggestions: false, + style: STextStyles.field(context), + decoration: standardInputDecoration( + "My name", + myNameFocusNode, + context, + ).copyWith( + contentPadding: const EdgeInsets.only( + left: 16, + top: 6, + bottom: 8, + right: 5, + ), + suffixIcon: Padding( + padding: _nameEmpty + ? const EdgeInsets.only(right: 8) + : const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + !_nameEmpty + ? TextFieldIconButton( + semanticsLabel: + "Clear Button. Clears The Config Field.", + key: const Key("frMyNameClearButtonKey"), + onTap: () { + myNameFieldController.text = ""; + + setState(() { + _nameEmpty = true; + }); + }, + child: const XIcon(), + ) + : TextFieldIconButton( + semanticsLabel: + "Paste Button. Pastes From Clipboard To Name Field.", + key: const Key("frMyNamePasteButtonKey"), + onTap: () async { + final ClipboardData? data = + await Clipboard.getData( + Clipboard.kTextPlain); + if (data?.text != null && + data!.text!.isNotEmpty) { + myNameFieldController.text = + data.text!.trim(); + } + + setState(() { + _nameEmpty = + myNameFieldController.text.isEmpty; + }); + }, + child: _nameEmpty + ? const ClipboardIcon() + : const XIcon(), + ), + ], + ), + ), + ), + ), + ), + ), + const SizedBox( + height: 16, + ), + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + key: const Key("frConfigTextFieldKey"), + controller: configFieldController, + onChanged: (_) { + setState(() { + _configEmpty = configFieldController.text.isEmpty; + }); + }, + focusNode: configFocusNode, + readOnly: false, + autocorrect: false, + enableSuggestions: false, + style: STextStyles.field(context), + decoration: standardInputDecoration( + "Enter config", + configFocusNode, + context, + ).copyWith( + contentPadding: const EdgeInsets.only( + left: 16, + top: 6, + bottom: 8, + right: 5, + ), + suffixIcon: Padding( + padding: _configEmpty + ? const EdgeInsets.only(right: 8) + : const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + !_configEmpty + ? TextFieldIconButton( + semanticsLabel: + "Clear Button. Clears The Config Field.", + key: const Key("frConfigClearButtonKey"), + onTap: () { + configFieldController.text = ""; + + setState(() { + _configEmpty = true; + }); + }, + child: const XIcon(), + ) + : TextFieldIconButton( + semanticsLabel: + "Paste Button. Pastes From Clipboard To Config Field Input.", + key: const Key("frConfigPasteButtonKey"), + onTap: () async { + final ClipboardData? data = + await Clipboard.getData( + Clipboard.kTextPlain); + if (data?.text != null && + data!.text!.isNotEmpty) { + configFieldController.text = + data.text!.trim(); + } + + setState(() { + _configEmpty = + configFieldController.text.isEmpty; + }); + }, + child: _configEmpty + ? const ClipboardIcon() + : const XIcon(), + ), + if (_configEmpty) + TextFieldIconButton( + semanticsLabel: + "Scan QR Button. Opens Camera For Scanning QR Code.", + key: const Key("frConfigScanQrButtonKey"), + onTap: () async { + try { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: 75)); + } + + final qrResult = await BarcodeScanner.scan(); + + configFieldController.text = + qrResult.rawContent; + + setState(() { + _configEmpty = + configFieldController.text.isEmpty; + }); + } on PlatformException catch (e, s) { + Logging.instance.log( + "Failed to get camera permissions while trying to scan qr code: $e\n$s", + level: LogLevel.Warning, + ); + } + }, + child: const QrCodeIcon(), + ) + ], + ), + ), + ), + ), + ), + ), + const SizedBox( + height: 16, + ), + if (!Util.isDesktop) const Spacer(), + const SizedBox( + height: 16, + ), + GestureDetector( + onTap: () { + setState(() { + _userVerifyContinue = !_userVerifyContinue; + }); + }, + child: Container( + color: Colors.transparent, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 20, + height: 26, + child: Checkbox( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + value: _userVerifyContinue, + onChanged: (value) => setState( + () => _userVerifyContinue = value == true, + ), + ), + ), + const SizedBox( + width: 12, + ), + Expanded( + child: Text( + "I have verified that everyone has joined the group", + style: STextStyles.w500_14(context), + ), + ), + ], + ), + ), + ), + const SizedBox( + height: 16, + ), + PrimaryButton( + label: "Start key generation", + enabled: _userVerifyContinue && !_nameEmpty && !_configEmpty, + onPressed: () async { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + } + + final config = configFieldController.text; + + if (!Frost.validateEncodedMultisigConfig(encodedConfig: config)) { + return await showDialog( + context: context, + builder: (_) => StackOkDialog( + title: "Invalid config", + desktopPopRootNavigator: Util.isDesktop, + ), + ); + } + + if (!Frost.getParticipants(multisigConfig: config) + .contains(myNameFieldController.text)) { + return await showDialog( + context: context, + builder: (_) => StackOkDialog( + title: "My name not found in config participants", + desktopPopRootNavigator: Util.isDesktop, + ), + ); + } + + ref.read(pFrostMyName.state).state = myNameFieldController.text; + ref.read(pFrostMultisigConfig.notifier).state = config; + + ref.read(pFrostStartKeyGenData.state).state = + Frost.startKeyGeneration( + multisigConfig: ref.read(pFrostMultisigConfig.state).state!, + myName: ref.read(pFrostMyName.state).state!, + ); + ref.read(pFrostCreateCurrentStep.state).state = 2; + Navigator.of(context).pushNamed( + FrostCreateStep2.routeName, + ); + }, + ) + ], + ), + ); + } +} diff --git a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart index 8c7c21e72..5e848fd48 100644 --- a/lib/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart +++ b/lib/pages/add_wallet_views/frost_ms/new/steps/frost_route_generator.dart @@ -1,7 +1,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1.dart'; +import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1a.dart'; +import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_1b.dart'; import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_2.dart'; import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_3.dart'; import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/steps/frost_create_step_4.dart'; @@ -30,6 +31,14 @@ abstract class FrostRouteGenerator { (routeName: FrostCreateStep5.routeName, title: FrostCreateStep5.title), ]; + static const List importNewConfigStepRoutes = [ + (routeName: FrostCreateStep1b.routeName, title: FrostCreateStep1b.title), + (routeName: FrostCreateStep2.routeName, title: FrostCreateStep2.title), + (routeName: FrostCreateStep3.routeName, title: FrostCreateStep3.title), + (routeName: FrostCreateStep4.routeName, title: FrostCreateStep4.title), + (routeName: FrostCreateStep5.routeName, title: FrostCreateStep5.title), + ]; + static Route generateRoute(RouteSettings settings) { final args = settings.arguments; @@ -41,6 +50,13 @@ abstract class FrostRouteGenerator { settings: settings, ); + case FrostCreateStep1b.routeName: + return RouteGenerator.getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => const FrostCreateStep1b(), + settings: settings, + ); + case FrostCreateStep2.routeName: return RouteGenerator.getRoute( shouldUseMaterialRoute: useMaterialPageRoute, diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 41155ebc8..594a586ea 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -28,7 +28,6 @@ import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/add_wallet_vi import 'package:stackwallet/pages/add_wallet_views/create_or_restore_wallet_view/create_or_restore_wallet_view.dart'; import 'package:stackwallet/pages/add_wallet_views/frost_ms/frost_scaffold.dart'; import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/create_new_frost_ms_wallet_view.dart'; -import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/import_new_frost_ms_wallet_view.dart'; import 'package:stackwallet/pages/add_wallet_views/frost_ms/new/select_new_frost_import_type_view.dart'; import 'package:stackwallet/pages/add_wallet_views/frost_ms/restore/restore_frost_ms_wallet_view.dart'; import 'package:stackwallet/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart'; @@ -500,24 +499,6 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); - case ImportNewFrostMsWalletView.routeName: - if (args is ({ - String walletName, - FrostCurrency frostCurrency, - })) { - return getRoute( - shouldUseMaterialRoute: useMaterialPageRoute, - builder: (_) => ImportNewFrostMsWalletView( - walletName: args.walletName, - frostCurrency: args.frostCurrency, - ), - settings: RouteSettings( - name: settings.name, - ), - ); - } - return _routeError("${settings.name} invalid args: ${args.toString()}"); - case NewImportResharerConfigView.routeName: if (args is ({ String walletName,