From 4327abb5317a9b4acc2c5bf24a1c617f5b63d72e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20Moli=C5=84ski?= <47773413+damian-molinski@users.noreply.github.com> Date: Thu, 26 Sep 2024 13:14:22 +0200 Subject: [PATCH 1/4] feat(cat-voices): registration dialog structure (#886) * feat: create keychain structure * fix: learn more button positioning * fix: localizations * feat: Keychain creation Instructions stage * refactor: introduce master AccountSetupDialog * refactor: unified RegistrationDialog * fix: use body in _HeaderStrings --------- Co-authored-by: Dominik Toton <166132265+dtscalac@users.noreply.github.com> --- .../lib/dependency/dependencies.dart | 4 +- .../create_keychain_panel.dart | 28 +++++ .../stage/instructions_panel.dart | 59 ++++++++++ .../create_keychain/stage/splash_panel.dart | 37 ++++++ .../create_keychain/stage/stages.dart | 2 + .../get_started/get_started_panel.dart} | 105 +++++------------ .../pages/registration/information_panel.dart | 109 ++++++++++++++++++ .../link_wallet/intro/intro_panel.dart | 36 ++++++ .../intro/link_wallet_intro_dialog.dart | 62 ---------- .../link_wallet/link_wallet_dialog.dart | 50 -------- .../select_wallet/select_wallet_dialog.dart | 25 ---- .../select_wallet/select_wallet_panel.dart | 10 ++ .../link_wallet/wallet_link_panel.dart | 21 ++++ .../registration/registration_dialog.dart | 57 +++++++++ .../registration/registration_info_panel.dart | 85 ++++++++++++++ .../task_picture.dart | 0 .../lib/pages/spaces/spaces_shell_page.dart | 24 +--- .../lib/widgets/buttons/voices_buttons.dart | 36 ++++++ .../lib/src/catalyst_voices_blocs.dart | 1 + .../keychain_creation_controller.dart | 64 ++++++++++ .../controllers/wallet_link_controller.dart | 44 +++++++ .../lib/src/registration/registration.dart | 3 + .../src/registration/registration_bloc.dart | 104 +++++++++++++++++ .../src/registration/registration_event.dart | 32 +++++ .../registration/registration_navigator.dart | 11 ++ .../src/registration/registration_state.dart | 68 +++++++++++ .../catalyst_voices_localizations.dart | 54 ++++++++- .../catalyst_voices_localizations_en.dart | 27 ++++- .../catalyst_voices_localizations_es.dart | 27 ++++- .../lib/l10n/intl_en.arb | 20 +++- .../lib/src/catalyst_voices_models.dart | 1 + .../src/registration/create_account_type.dart | 1 + .../registration/create_keychain_stage.dart | 12 ++ .../lib/src/registration/registration.dart | 3 + .../src/registration/wallet_link_stage.dart} | 2 +- 35 files changed, 972 insertions(+), 252 deletions(-) create mode 100644 catalyst_voices/lib/pages/registration/create_keychain/create_keychain_panel.dart create mode 100644 catalyst_voices/lib/pages/registration/create_keychain/stage/instructions_panel.dart create mode 100644 catalyst_voices/lib/pages/registration/create_keychain/stage/splash_panel.dart create mode 100644 catalyst_voices/lib/pages/registration/create_keychain/stage/stages.dart rename catalyst_voices/lib/pages/{account/creation/get_started/account_create_dialog.dart => registration/get_started/get_started_panel.dart} (64%) create mode 100644 catalyst_voices/lib/pages/registration/information_panel.dart create mode 100644 catalyst_voices/lib/pages/registration/link_wallet/intro/intro_panel.dart delete mode 100644 catalyst_voices/lib/pages/registration/link_wallet/intro/link_wallet_intro_dialog.dart delete mode 100644 catalyst_voices/lib/pages/registration/link_wallet/link_wallet_dialog.dart delete mode 100644 catalyst_voices/lib/pages/registration/link_wallet/select_wallet/select_wallet_dialog.dart create mode 100644 catalyst_voices/lib/pages/registration/link_wallet/select_wallet/select_wallet_panel.dart create mode 100644 catalyst_voices/lib/pages/registration/link_wallet/wallet_link_panel.dart create mode 100644 catalyst_voices/lib/pages/registration/registration_dialog.dart create mode 100644 catalyst_voices/lib/pages/registration/registration_info_panel.dart rename catalyst_voices/lib/pages/{account/creation => registration}/task_picture.dart (100%) create mode 100644 catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/keychain_creation_controller.dart create mode 100644 catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/wallet_link_controller.dart create mode 100644 catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration.dart create mode 100644 catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_bloc.dart create mode 100644 catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_event.dart create mode 100644 catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_navigator.dart create mode 100644 catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_state.dart create mode 100644 catalyst_voices/packages/catalyst_voices_models/lib/src/registration/create_account_type.dart create mode 100644 catalyst_voices/packages/catalyst_voices_models/lib/src/registration/create_keychain_stage.dart create mode 100644 catalyst_voices/packages/catalyst_voices_models/lib/src/registration/registration.dart rename catalyst_voices/{lib/pages/registration/link_wallet/link_wallet_stage.dart => packages/catalyst_voices_models/lib/src/registration/wallet_link_stage.dart} (89%) diff --git a/catalyst_voices/lib/dependency/dependencies.dart b/catalyst_voices/lib/dependency/dependencies.dart index 3c9a6b4ba0..713343dc16 100644 --- a/catalyst_voices/lib/dependency/dependencies.dart +++ b/catalyst_voices/lib/dependency/dependencies.dart @@ -27,7 +27,9 @@ final class Dependencies extends DependencyProvider { authenticationRepository: get(), ), ) - ..registerLazySingleton(SessionBloc.new); + ..registerLazySingleton(SessionBloc.new) + // Factory will rebuild it each time needed + ..registerFactory(RegistrationBloc.new); } void _registerRepositories() { diff --git a/catalyst_voices/lib/pages/registration/create_keychain/create_keychain_panel.dart b/catalyst_voices/lib/pages/registration/create_keychain/create_keychain_panel.dart new file mode 100644 index 0000000000..da7629c063 --- /dev/null +++ b/catalyst_voices/lib/pages/registration/create_keychain/create_keychain_panel.dart @@ -0,0 +1,28 @@ +import 'package:catalyst_voices/pages/registration/create_keychain/stage/stages.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class CreateKeychainPanel extends StatelessWidget { + final CreateKeychainStage stage; + + const CreateKeychainPanel({ + super.key, + required this.stage, + }); + + @override + Widget build(BuildContext context) { + return switch (stage) { + CreateKeychainStage.splash => const SplashPanel(), + CreateKeychainStage.instructions => const InstructionsPanel(), + CreateKeychainStage.seedPhrase || + CreateKeychainStage.checkSeedPhraseInstructions || + CreateKeychainStage.checkSeedPhrase || + CreateKeychainStage.checkSeedPhraseResult || + CreateKeychainStage.unlockPasswordInstructions || + CreateKeychainStage.unlockPasswordCreate || + CreateKeychainStage.created => + const Placeholder(), + }; + } +} diff --git a/catalyst_voices/lib/pages/registration/create_keychain/stage/instructions_panel.dart b/catalyst_voices/lib/pages/registration/create_keychain/stage/instructions_panel.dart new file mode 100644 index 0000000000..c09e7b281a --- /dev/null +++ b/catalyst_voices/lib/pages/registration/create_keychain/stage/instructions_panel.dart @@ -0,0 +1,59 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:flutter/material.dart'; + +class InstructionsPanel extends StatelessWidget { + const InstructionsPanel({super.key}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final textColor = theme.colors.textOnPrimaryLevel0; + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + context.l10n.accountInstructionsTitle, + style: theme.textTheme.titleMedium?.copyWith(color: textColor), + ), + const SizedBox(height: 24), + Text( + context.l10n.accountInstructionsMessage, + style: theme.textTheme.bodyMedium?.copyWith(color: textColor), + ), + const Spacer(), + const _Navigation(), + ], + ); + } +} + +class _Navigation extends StatelessWidget { + const _Navigation(); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: VoicesBackButton( + onTap: () { + RegistrationBloc.of(context).add(const PreviousStepEvent()); + }, + ), + ), + const SizedBox(width: 10), + Expanded( + child: VoicesNextButton( + onTap: () { + RegistrationBloc.of(context).add(const NextStepEvent()); + }, + ), + ), + ], + ); + } +} diff --git a/catalyst_voices/lib/pages/registration/create_keychain/stage/splash_panel.dart b/catalyst_voices/lib/pages/registration/create_keychain/stage/splash_panel.dart new file mode 100644 index 0000000000..484cb67d31 --- /dev/null +++ b/catalyst_voices/lib/pages/registration/create_keychain/stage/splash_panel.dart @@ -0,0 +1,37 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:flutter/material.dart'; + +class SplashPanel extends StatelessWidget { + const SplashPanel({super.key}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final textColor = theme.colors.textOnPrimaryLevel0; + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + context.l10n.accountCreationSplashTitle, + style: theme.textTheme.titleMedium?.copyWith(color: textColor), + ), + const SizedBox(height: 24), + Text( + context.l10n.accountCreationSplashMessage, + style: theme.textTheme.bodyMedium?.copyWith(color: textColor), + ), + const Spacer(), + VoicesFilledButton( + child: Text(context.l10n.accountCreationSplashNextButton), + onTap: () { + RegistrationBloc.of(context).add(const NextStepEvent()); + }, + ), + ], + ); + } +} diff --git a/catalyst_voices/lib/pages/registration/create_keychain/stage/stages.dart b/catalyst_voices/lib/pages/registration/create_keychain/stage/stages.dart new file mode 100644 index 0000000000..69b95fc8bc --- /dev/null +++ b/catalyst_voices/lib/pages/registration/create_keychain/stage/stages.dart @@ -0,0 +1,2 @@ +export 'instructions_panel.dart'; +export 'splash_panel.dart'; diff --git a/catalyst_voices/lib/pages/account/creation/get_started/account_create_dialog.dart b/catalyst_voices/lib/pages/registration/get_started/get_started_panel.dart similarity index 64% rename from catalyst_voices/lib/pages/account/creation/get_started/account_create_dialog.dart rename to catalyst_voices/lib/pages/registration/get_started/get_started_panel.dart index ea69f13db6..30c15365c7 100644 --- a/catalyst_voices/lib/pages/account/creation/get_started/account_create_dialog.dart +++ b/catalyst_voices/lib/pages/registration/get_started/get_started_panel.dart @@ -1,79 +1,13 @@ -import 'package:catalyst_voices/pages/account/creation/task_picture.dart'; -import 'package:catalyst_voices/widgets/buttons/voices_buttons.dart'; -import 'package:catalyst_voices/widgets/modals/voices_desktop_dialog.dart'; -import 'package:catalyst_voices/widgets/modals/voices_dialog.dart'; import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; import 'package:flutter/material.dart'; -enum AccountCreateType { - createNew, - recover; - - SvgGenImage get _icon => switch (this) { - AccountCreateType.createNew => VoicesAssets.icons.colorSwatch, - AccountCreateType.recover => VoicesAssets.icons.download, - }; - - String _getTitle(VoicesLocalizations l10n) => switch (this) { - AccountCreateType.createNew => l10n.accountCreationCreate, - AccountCreateType.recover => l10n.accountCreationRecover, - }; - - String _getSubtitle(VoicesLocalizations l10n) { - return l10n.accountCreationOnThisDevice; - } -} - -class AccountCreateDialog extends StatelessWidget { - const AccountCreateDialog._(); - - static Future show(BuildContext context) { - return VoicesDialog.show( - context: context, - builder: (context) => const AccountCreateDialog._(), - ); - } - - @override - Widget build(BuildContext context) { - return const VoicesDesktopPanelsDialog( - left: _LeftPanel(), - right: _RightPanel(), - ); - } -} - -class _LeftPanel extends StatelessWidget { - const _LeftPanel(); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - context.l10n.getStarted, - style: theme.textTheme.titleLarge?.copyWith( - color: theme.colors.textOnPrimaryLevel0, - ), - ), - const SizedBox(height: 12), - const Expanded(child: Center(child: TaskKeychainPicture())), - const SizedBox(height: 32), - // TODO(damian-molinski): External url redirect - VoicesLearnMoreButton(onTap: () {}), - ], - ); - } -} - -class _RightPanel extends StatelessWidget { - const _RightPanel(); +class GetStartedPanel extends StatelessWidget { + const GetStartedPanel({super.key}); @override Widget build(BuildContext context) { @@ -106,12 +40,15 @@ class _RightPanel extends StatelessWidget { const SizedBox(height: 24), Column( mainAxisSize: MainAxisSize.min, - children: AccountCreateType.values + children: CreateAccountType.values .map((type) { - return _AccountCreateTypeTile( + return _CreateAccountTypeTile( key: ValueKey(type), type: type, - onTap: () => Navigator.of(context).pop(type), + onTap: () { + final event = CreateAccountTypeEvent(type: type); + RegistrationBloc.of(context).add(event); + }, ); }) .separatedBy(const SizedBox(height: 12)) @@ -122,11 +59,11 @@ class _RightPanel extends StatelessWidget { } } -class _AccountCreateTypeTile extends StatelessWidget { - final AccountCreateType type; +class _CreateAccountTypeTile extends StatelessWidget { + final CreateAccountType type; final VoidCallback? onTap; - const _AccountCreateTypeTile({ + const _CreateAccountTypeTile({ super.key, required this.type, this.onTap, @@ -185,3 +122,19 @@ class _AccountCreateTypeTile extends StatelessWidget { ); } } + +extension _CreateAccountTypeExt on CreateAccountType { + SvgGenImage get _icon => switch (this) { + CreateAccountType.createNew => VoicesAssets.icons.colorSwatch, + CreateAccountType.recover => VoicesAssets.icons.download, + }; + + String _getTitle(VoicesLocalizations l10n) => switch (this) { + CreateAccountType.createNew => l10n.accountCreationCreate, + CreateAccountType.recover => l10n.accountCreationRecover, + }; + + String _getSubtitle(VoicesLocalizations l10n) { + return l10n.accountCreationOnThisDevice; + } +} diff --git a/catalyst_voices/lib/pages/registration/information_panel.dart b/catalyst_voices/lib/pages/registration/information_panel.dart new file mode 100644 index 0000000000..4d15bc3539 --- /dev/null +++ b/catalyst_voices/lib/pages/registration/information_panel.dart @@ -0,0 +1,109 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +class InformationPanel extends StatelessWidget { + final String title; + final String? subtitle; + final String? body; + final Widget picture; + final double? progress; + + const InformationPanel({ + super.key, + required this.title, + this.subtitle, + this.body, + required this.picture, + this.progress, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _Header( + title: title, + subtitle: subtitle, + body: body, + ), + const SizedBox(height: 12), + Expanded(child: Center(child: picture)), + const SizedBox(height: 12), + _Footer( + progress: progress, + ), + ], + ); + } +} + +class _Header extends StatelessWidget { + final String title; + final String? subtitle; + final String? body; + + const _Header({ + required this.title, + this.subtitle, + this.body, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final textColor = theme.colors.textOnPrimaryLevel0; + + final subtitle = this.subtitle; + final body = this.body; + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: theme.textTheme.titleLarge?.copyWith(color: textColor), + ), + if (subtitle != null) + Text( + subtitle, + style: theme.textTheme.titleMedium?.copyWith(color: textColor), + ), + if (body != null) + Text( + body, + style: theme.textTheme.bodyMedium?.copyWith(color: textColor), + ), + ].separatedBy(const SizedBox(height: 12)).toList(), + ); + } +} + +class _Footer extends StatelessWidget { + final double? progress; + + const _Footer({ + this.progress, + }); + + @override + Widget build(BuildContext context) { + final progress = this.progress; + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Offstage( + offstage: progress == null, + child: VoicesLinearProgressIndicator(value: progress ?? 0), + ), + const SizedBox(height: 10), + VoicesLearnMoreButton(onTap: () {}), + ], + ); + } +} diff --git a/catalyst_voices/lib/pages/registration/link_wallet/intro/intro_panel.dart b/catalyst_voices/lib/pages/registration/link_wallet/intro/intro_panel.dart new file mode 100644 index 0000000000..c55a067c1d --- /dev/null +++ b/catalyst_voices/lib/pages/registration/link_wallet/intro/intro_panel.dart @@ -0,0 +1,36 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:flutter/material.dart'; + +class IntroPanel extends StatelessWidget { + const IntroPanel({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 24), + Text( + context.l10n.walletLink_intro_title, + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: 24), + Text( + context.l10n.walletLink_intro_content, + style: Theme.of(context).textTheme.bodyMedium, + ), + const Spacer(), + VoicesFilledButton( + leading: VoicesAssets.icons.wallet.buildIcon(), + onTap: () { + RegistrationBloc.of(context).add(const NextStepEvent()); + }, + child: Text(context.l10n.chooseCardanoWallet), + ), + ], + ); + } +} diff --git a/catalyst_voices/lib/pages/registration/link_wallet/intro/link_wallet_intro_dialog.dart b/catalyst_voices/lib/pages/registration/link_wallet/intro/link_wallet_intro_dialog.dart deleted file mode 100644 index 066cc83863..0000000000 --- a/catalyst_voices/lib/pages/registration/link_wallet/intro/link_wallet_intro_dialog.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:catalyst_voices/pages/account/creation/task_picture.dart'; -import 'package:catalyst_voices/widgets/widgets.dart'; -import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; -import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; -import 'package:flutter/material.dart'; - -/// The initial screen for the link wallet flow during registration. -class LinkWalletIntroDialog extends StatelessWidget { - final VoidCallback onSelectWallet; - - const LinkWalletIntroDialog({ - super.key, - required this.onSelectWallet, - }); - - @override - Widget build(BuildContext context) { - return VoicesDesktopPanelsDialog( - left: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - context.l10n.walletLink_header, - style: Theme.of(context).textTheme.titleLarge, - ), - const SizedBox(height: 12), - Text( - context.l10n.walletLink_subheader, - style: Theme.of(context).textTheme.titleMedium, - ), - const SizedBox(height: 50), - const TaskKeychainPicture(), - const Spacer(), - VoicesLearnMoreButton( - onTap: () {}, - ), - ], - ), - right: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const SizedBox(height: 24), - Text( - context.l10n.walletLink_intro_title, - style: Theme.of(context).textTheme.titleMedium, - ), - const SizedBox(height: 24), - Text( - context.l10n.walletLink_intro_content, - style: Theme.of(context).textTheme.bodyMedium, - ), - const Spacer(), - VoicesFilledButton( - leading: VoicesAssets.icons.wallet.buildIcon(), - onTap: onSelectWallet, - child: Text(context.l10n.chooseCardanoWallet), - ), - ], - ), - ); - } -} diff --git a/catalyst_voices/lib/pages/registration/link_wallet/link_wallet_dialog.dart b/catalyst_voices/lib/pages/registration/link_wallet/link_wallet_dialog.dart deleted file mode 100644 index edfd4312ec..0000000000 --- a/catalyst_voices/lib/pages/registration/link_wallet/link_wallet_dialog.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:catalyst_cardano/catalyst_cardano.dart'; -import 'package:catalyst_voices/pages/registration/link_wallet/intro/link_wallet_intro_dialog.dart'; -import 'package:catalyst_voices/pages/registration/link_wallet/link_wallet_stage.dart'; -import 'package:catalyst_voices/pages/registration/link_wallet/select_wallet/select_wallet_dialog.dart'; -import 'package:catalyst_voices/widgets/modals/voices_dialog.dart'; -import 'package:flutter/material.dart'; - -/// The link wallet flow consisting -/// of [LinkWalletStage]'s during the registration. -class LinkWalletDialog extends StatefulWidget { - const LinkWalletDialog._(); - - /// Shows the [LinkWalletDialog] flow. - static Future show({required BuildContext context}) { - return VoicesDialog.show( - context: context, - routeSettings: const RouteSettings(name: '/register/link-wallet'), - builder: (context) => const LinkWalletDialog._(), - ); - } - - @override - State createState() => _LinkWalletDialogState(); -} - -class _LinkWalletDialogState extends State { - LinkWalletStage _stage = LinkWalletStage.intro; - - @override - Widget build(BuildContext context) { - return switch (_stage) { - LinkWalletStage.intro => LinkWalletIntroDialog( - onSelectWallet: _onSelectWallet, - ), - LinkWalletStage.selectWallet => SelectWalletDialog( - onSelectedWallet: _onSelectedWallet, - ), - }; - } - - void _onSelectWallet() { - setState(() { - _stage = LinkWalletStage.selectWallet; - }); - } - - void _onSelectedWallet(CardanoWallet wallet) { - // TODO(dtscalac): store selected wallet and proceed to next stage - } -} diff --git a/catalyst_voices/lib/pages/registration/link_wallet/select_wallet/select_wallet_dialog.dart b/catalyst_voices/lib/pages/registration/link_wallet/select_wallet/select_wallet_dialog.dart deleted file mode 100644 index 6799dafcff..0000000000 --- a/catalyst_voices/lib/pages/registration/link_wallet/select_wallet/select_wallet_dialog.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:catalyst_cardano/catalyst_cardano.dart'; -import 'package:catalyst_voices/widgets/widgets.dart'; -import 'package:flutter/material.dart'; - -// TODO(dtscalac): add content for the screen -class SelectWalletDialog extends StatelessWidget { - final ValueChanged onSelectedWallet; - - const SelectWalletDialog({ - super.key, - required this.onSelectedWallet, - }); - - @override - Widget build(BuildContext context) { - return const VoicesDesktopPanelsDialog( - left: Column( - children: [], - ), - right: Column( - children: [], - ), - ); - } -} diff --git a/catalyst_voices/lib/pages/registration/link_wallet/select_wallet/select_wallet_panel.dart b/catalyst_voices/lib/pages/registration/link_wallet/select_wallet/select_wallet_panel.dart new file mode 100644 index 0000000000..855808d8f0 --- /dev/null +++ b/catalyst_voices/lib/pages/registration/link_wallet/select_wallet/select_wallet_panel.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class SelectWalletPanel extends StatelessWidget { + const SelectWalletPanel({super.key}); + + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} diff --git a/catalyst_voices/lib/pages/registration/link_wallet/wallet_link_panel.dart b/catalyst_voices/lib/pages/registration/link_wallet/wallet_link_panel.dart new file mode 100644 index 0000000000..dcb668f4e9 --- /dev/null +++ b/catalyst_voices/lib/pages/registration/link_wallet/wallet_link_panel.dart @@ -0,0 +1,21 @@ +import 'package:catalyst_voices/pages/registration/link_wallet/intro/intro_panel.dart'; +import 'package:catalyst_voices/pages/registration/link_wallet/select_wallet/select_wallet_panel.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class WalletLinkPanel extends StatelessWidget { + final WalletLinkStage stage; + + const WalletLinkPanel({ + super.key, + required this.stage, + }); + + @override + Widget build(BuildContext context) { + return switch (stage) { + WalletLinkStage.intro => const IntroPanel(), + WalletLinkStage.selectWallet => const SelectWalletPanel(), + }; + } +} diff --git a/catalyst_voices/lib/pages/registration/registration_dialog.dart b/catalyst_voices/lib/pages/registration/registration_dialog.dart new file mode 100644 index 0000000000..653764e746 --- /dev/null +++ b/catalyst_voices/lib/pages/registration/registration_dialog.dart @@ -0,0 +1,57 @@ +import 'package:catalyst_voices/dependency/dependencies.dart'; +import 'package:catalyst_voices/pages/registration/create_keychain/create_keychain_panel.dart'; +import 'package:catalyst_voices/pages/registration/get_started/get_started_panel.dart'; +import 'package:catalyst_voices/pages/registration/link_wallet/wallet_link_panel.dart'; +import 'package:catalyst_voices/pages/registration/registration_info_panel.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class RegistrationDialog extends StatelessWidget { + const RegistrationDialog._(); + + static Future show(BuildContext context) { + return VoicesDialog.show( + context: context, + routeSettings: const RouteSettings(name: '/registration'), + builder: (context) => const RegistrationDialog._(), + ); + } + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => Dependencies.instance.get(), + child: BlocBuilder( + builder: (context, state) { + return _RegistrationDialog(state: state); + }, + ), + ); + } +} + +class _RegistrationDialog extends StatelessWidget { + final RegistrationState state; + + const _RegistrationDialog({ + required this.state, + }); + + @override + Widget build(BuildContext context) { + return VoicesDesktopPanelsDialog( + left: RegistrationInfoPanel( + state: state, + ), + right: switch (state) { + GetStarted() => const GetStartedPanel(), + FinishAccountCreation() => const Placeholder(), + Recover() => const Placeholder(), + CreateKeychain(:final stage) => CreateKeychainPanel(stage: stage), + WalletLink(:final stage) => WalletLinkPanel(stage: stage), + }, + ); + } +} diff --git a/catalyst_voices/lib/pages/registration/registration_info_panel.dart b/catalyst_voices/lib/pages/registration/registration_info_panel.dart new file mode 100644 index 0000000000..e33113d351 --- /dev/null +++ b/catalyst_voices/lib/pages/registration/registration_info_panel.dart @@ -0,0 +1,85 @@ +import 'package:catalyst_voices/pages/registration/information_panel.dart'; +import 'package:catalyst_voices/pages/registration/task_picture.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class _HeaderStrings { + final String title; + final String? subtitle; + final String? body; + + _HeaderStrings({ + required this.title, + this.subtitle, + this.body, + }); +} + +class RegistrationInfoPanel extends StatelessWidget { + final RegistrationState state; + + const RegistrationInfoPanel({ + super.key, + required this.state, + }); + + @override + Widget build(BuildContext context) { + final headerStrings = _buildHeaderStrings(context); + + return InformationPanel( + title: headerStrings.title, + subtitle: headerStrings.subtitle, + body: headerStrings.body, + picture: const TaskKeychainPicture(), + ); + } + + _HeaderStrings _buildHeaderStrings(BuildContext context) { + _HeaderStrings buildKeychainStageHeader(CreateKeychainStage stage) { + return switch (stage) { + CreateKeychainStage.splash || + CreateKeychainStage.instructions => + _HeaderStrings(title: context.l10n.catalystKeychain), + + // TODO(damian-molinski): Extract to l10n in next step + CreateKeychainStage.seedPhrase => _HeaderStrings( + title: 'Catalyst Keychain', + subtitle: 'Write down your 12 Catalyst 
security words', + body: 'Make sure you create an offline backup ' + 'of your recovery phrase as well.', + ), + CreateKeychainStage.checkSeedPhraseInstructions || + CreateKeychainStage.checkSeedPhrase || + CreateKeychainStage.checkSeedPhraseResult || + CreateKeychainStage.unlockPasswordInstructions || + CreateKeychainStage.unlockPasswordCreate || + CreateKeychainStage.created => + _HeaderStrings(title: 'TODO'), + }; + } + + _HeaderStrings buildWalletStageHeader(WalletLinkStage stage) { + return switch (stage) { + WalletLinkStage.intro => _HeaderStrings( + title: 'Link keys to your 
Catalyst Keychain', + subtitle: 'Link your Cardano wallet', + ), + WalletLinkStage.selectWallet => _HeaderStrings( + title: 'Link keys to your 
Catalyst Keychain', + subtitle: 'Link your Cardano wallet', + ), + }; + } + + return switch (state) { + GetStarted() => _HeaderStrings(title: context.l10n.getStarted), + FinishAccountCreation() => _HeaderStrings(title: 'TODO'), + Recover() => _HeaderStrings(title: 'TODO'), + CreateKeychain(:final stage) => buildKeychainStageHeader(stage), + WalletLink(:final stage) => buildWalletStageHeader(stage), + }; + } +} diff --git a/catalyst_voices/lib/pages/account/creation/task_picture.dart b/catalyst_voices/lib/pages/registration/task_picture.dart similarity index 100% rename from catalyst_voices/lib/pages/account/creation/task_picture.dart rename to catalyst_voices/lib/pages/registration/task_picture.dart diff --git a/catalyst_voices/lib/pages/spaces/spaces_shell_page.dart b/catalyst_voices/lib/pages/spaces/spaces_shell_page.dart index 8f5b6f9c00..ebea205e41 100644 --- a/catalyst_voices/lib/pages/spaces/spaces_shell_page.dart +++ b/catalyst_voices/lib/pages/spaces/spaces_shell_page.dart @@ -1,5 +1,5 @@ import 'package:catalyst_voices/common/ext/ext.dart'; -import 'package:catalyst_voices/pages/account/creation/get_started/account_create_dialog.dart'; +import 'package:catalyst_voices/pages/registration/registration_dialog.dart'; import 'package:catalyst_voices/pages/spaces/drawer/spaces_drawer.dart'; import 'package:catalyst_voices/widgets/widgets.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; @@ -63,7 +63,7 @@ class _SpacesShellPageState extends State { automaticallyImplyLeading: false, actions: [ SessionActionHeader( - onGetStartedTap: _showAccountGetStarted, + onGetStartedTap: _showAccountSetup, ), const SessionStateHeader(), ], @@ -81,23 +81,7 @@ class _SpacesShellPageState extends State { ); } - Future _showAccountGetStarted() async { - final type = await AccountCreateDialog.show(context); - if (type == null) { - return; - } - - if (mounted) { - switch (type) { - case AccountCreateType.createNew: - _showCreateAccountFlow().ignore(); - case AccountCreateType.recover: - _showRecoverAccountFlow().ignore(); - } - } + Future _showAccountSetup() async { + await RegistrationDialog.show(context); } - - Future _showCreateAccountFlow() async {} - - Future _showRecoverAccountFlow() async {} } diff --git a/catalyst_voices/lib/widgets/buttons/voices_buttons.dart b/catalyst_voices/lib/widgets/buttons/voices_buttons.dart index a12419eb16..3b5c495f1d 100644 --- a/catalyst_voices/lib/widgets/buttons/voices_buttons.dart +++ b/catalyst_voices/lib/widgets/buttons/voices_buttons.dart @@ -1,6 +1,8 @@ import 'dart:async'; +import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart'; import 'package:catalyst_voices/widgets/buttons/voices_icon_button.dart'; +import 'package:catalyst_voices/widgets/buttons/voices_outlined_button.dart'; import 'package:catalyst_voices/widgets/buttons/voices_text_button.dart'; import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; @@ -167,3 +169,37 @@ class VoicesLearnMoreButton extends StatelessWidget { ); } } + +class VoicesNextButton extends StatelessWidget { + final VoidCallback? onTap; + + const VoicesNextButton({ + super.key, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return VoicesFilledButton( + onTap: onTap, + child: Text(context.l10n.next), + ); + } +} + +class VoicesBackButton extends StatelessWidget { + final VoidCallback? onTap; + + const VoicesBackButton({ + super.key, + this.onTap, + }); + + @override + Widget build(BuildContext context) { + return VoicesOutlinedButton( + onTap: onTap, + child: Text(context.l10n.back), + ); + } +} diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/catalyst_voices_blocs.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/catalyst_voices_blocs.dart index e21458b916..00c5c6e0cc 100644 --- a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/catalyst_voices_blocs.dart +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/catalyst_voices_blocs.dart @@ -1,4 +1,5 @@ export 'authentication/authentication.dart'; export 'brand/brand.dart'; export 'login/login.dart'; +export 'registration/registration.dart'; export 'session/session.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/keychain_creation_controller.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/keychain_creation_controller.dart new file mode 100644 index 0000000000..4a52f4b46b --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/keychain_creation_controller.dart @@ -0,0 +1,64 @@ +import 'package:catalyst_voices_blocs/src/registration/registration_navigator.dart'; +import 'package:catalyst_voices_blocs/src/registration/registration_state.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; + +abstract interface class KeychainCreationController {} + +final class RegistrationKeychainCreationController + implements + KeychainCreationController, + RegistrationNavigator { + CreateKeychainStage _stage; + + RegistrationKeychainCreationController({ + CreateKeychainStage stage = CreateKeychainStage.splash, + }) : _stage = stage; + + @override + CreateKeychain? nextStep() { + final nextStep = switch (_stage) { + CreateKeychainStage.splash => + const CreateKeychain(stage: CreateKeychainStage.instructions), + CreateKeychainStage.instructions => throw UnimplementedError(), + CreateKeychainStage.seedPhrase => throw UnimplementedError(), + CreateKeychainStage.checkSeedPhraseInstructions => + throw UnimplementedError(), + CreateKeychainStage.checkSeedPhrase => throw UnimplementedError(), + CreateKeychainStage.checkSeedPhraseResult => throw UnimplementedError(), + CreateKeychainStage.unlockPasswordInstructions => + throw UnimplementedError(), + CreateKeychainStage.unlockPasswordCreate => throw UnimplementedError(), + CreateKeychainStage.created => null, + }; + + if (nextStep != null) { + _stage = nextStep.stage; + } + + return nextStep; + } + + @override + CreateKeychain? previousStep() { + final previousStep = switch (_stage) { + CreateKeychainStage.splash => null, + CreateKeychainStage.instructions => + const CreateKeychain(stage: CreateKeychainStage.splash), + CreateKeychainStage.seedPhrase => throw UnimplementedError(), + CreateKeychainStage.checkSeedPhraseInstructions => + throw UnimplementedError(), + CreateKeychainStage.checkSeedPhrase => throw UnimplementedError(), + CreateKeychainStage.checkSeedPhraseResult => throw UnimplementedError(), + CreateKeychainStage.unlockPasswordInstructions => + throw UnimplementedError(), + CreateKeychainStage.unlockPasswordCreate => throw UnimplementedError(), + CreateKeychainStage.created => throw UnimplementedError(), + }; + + if (previousStep != null) { + _stage = previousStep.stage; + } + + return previousStep; + } +} diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/wallet_link_controller.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/wallet_link_controller.dart new file mode 100644 index 0000000000..826ad04473 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/wallet_link_controller.dart @@ -0,0 +1,44 @@ +import 'package:catalyst_voices_blocs/src/registration/registration_navigator.dart'; +import 'package:catalyst_voices_blocs/src/registration/registration_state.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; + +abstract interface class WalletLinkController {} + +final class RegistrationWalletLinkController + implements WalletLinkController, RegistrationNavigator { + WalletLinkStage _stage; + + RegistrationWalletLinkController({ + WalletLinkStage stage = WalletLinkStage.intro, + }) : _stage = stage; + + @override + WalletLink? nextStep() { + final nextStep = switch (_stage) { + WalletLinkStage.intro => + const WalletLink(stage: WalletLinkStage.selectWallet), + WalletLinkStage.selectWallet => null, + }; + + if (nextStep != null) { + _stage = nextStep.stage; + } + + return nextStep; + } + + @override + WalletLink? previousStep() { + final previousStep = switch (_stage) { + WalletLinkStage.intro => null, + WalletLinkStage.selectWallet => + const WalletLink(stage: WalletLinkStage.intro), + }; + + if (previousStep != null) { + _stage = previousStep.stage; + } + + return previousStep; + } +} diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration.dart new file mode 100644 index 0000000000..b22d82608f --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration.dart @@ -0,0 +1,3 @@ +export 'registration_bloc.dart'; +export 'registration_event.dart'; +export 'registration_state.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_bloc.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_bloc.dart new file mode 100644 index 0000000000..f1466c29ef --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_bloc.dart @@ -0,0 +1,104 @@ +import 'package:catalyst_voices_blocs/src/registration/controllers/keychain_creation_controller.dart'; +import 'package:catalyst_voices_blocs/src/registration/controllers/wallet_link_controller.dart'; +import 'package:catalyst_voices_blocs/src/registration/registration_event.dart'; +import 'package:catalyst_voices_blocs/src/registration/registration_state.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +/// Manages the registration state. +final class RegistrationBloc extends Bloc + implements KeychainCreationController, WalletLinkController { + final RegistrationKeychainCreationController _keychainCreationController; + final RegistrationWalletLinkController _walletLinkController; + + RegistrationBloc() + : _keychainCreationController = RegistrationKeychainCreationController(), + _walletLinkController = RegistrationWalletLinkController(), + super(const GetStarted()) { + on(_handleRegistrationEvent); + } + + /// Returns [RegistrationBloc] if found in widget tree. Does not add + /// rebuild dependency when called. + static RegistrationBloc of(BuildContext context) { + return context.read(); + } + + /// Returns [RegistrationBloc] if found in widget tree. Adds rebuild + /// dependency when called so you can not call it in initState. + static RegistrationBloc watch(BuildContext context) { + return context.watch(); + } + + void _handleRegistrationEvent( + RegistrationEvent event, + Emitter emit, + ) { + final nextState = switch (event) { + CreateAccountTypeEvent(:final type) => _createAccountNextStep(type), + NextStepEvent() => _nextStep(), + PreviousStepEvent() => _previousStep(), + }; + + emit(nextState); + } + + RegistrationState _createAccountNextStep(CreateAccountType type) { + return switch (type) { + CreateAccountType.createNew => const CreateKeychain(), + CreateAccountType.recover => const Recover(), + }; + } + + RegistrationState _nextStep() { + /// Nested function. Responsible only for keychain steps logic. + RegistrationState keychainNextStep() { + final nextStep = _keychainCreationController.nextStep(); + + return nextStep ?? const FinishAccountCreation(); + } + + /// Nested function. Responsible only for wallet link steps logic. + RegistrationState walletLinkNextStep() { + final nextStep = _walletLinkController.nextStep(); + + return nextStep ?? state; + } + + return switch (state) { + GetStarted() => throw StateError( + 'GetStarted has two routes that may go to. ' + 'NextStep is not valid here.', + ), + FinishAccountCreation() => throw UnimplementedError(), + Recover() => throw UnimplementedError(), + CreateKeychain() => keychainNextStep(), + WalletLink() => walletLinkNextStep(), + }; + } + + RegistrationState _previousStep() { + /// Nested function. Responsible only for keychain steps logic. + RegistrationState keychainPreviousStep() { + final previousStep = _keychainCreationController.previousStep(); + + return previousStep ?? const GetStarted(); + } + + /// Nested function. Responsible only for wallet link steps logic. + RegistrationState walletLinkPreviousStep() { + final previousStep = _walletLinkController.previousStep(); + + return previousStep ?? const FinishAccountCreation(); + } + + return switch (state) { + GetStarted() => throw StateError('GetStarted is initial step.'), + FinishAccountCreation() => throw UnimplementedError(), + Recover() => throw UnimplementedError(), + CreateKeychain() => keychainPreviousStep(), + WalletLink() => walletLinkPreviousStep(), + }; + } +} diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_event.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_event.dart new file mode 100644 index 0000000000..18ccb57338 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_event.dart @@ -0,0 +1,32 @@ +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:equatable/equatable.dart'; + +/// Describes events that change the registration. +sealed class RegistrationEvent extends Equatable { + const RegistrationEvent(); +} + +final class CreateAccountTypeEvent extends RegistrationEvent { + final CreateAccountType type; + + const CreateAccountTypeEvent({ + required this.type, + }); + + @override + List get props => [type]; +} + +final class NextStepEvent extends RegistrationEvent { + const NextStepEvent(); + + @override + List get props => []; +} + +final class PreviousStepEvent extends RegistrationEvent { + const PreviousStepEvent(); + + @override + List get props => []; +} diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_navigator.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_navigator.dart new file mode 100644 index 0000000000..680bb5e878 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_navigator.dart @@ -0,0 +1,11 @@ +import 'package:catalyst_voices_blocs/src/registration/registration_state.dart'; + +// Note. Maybe make it non null and add hasNextStep / hasPreviousStep +/// Abstraction for navigation between different [RegistrationState] steps. +abstract interface class RegistrationNavigator { + /// Returns null if there is no next step. + T? nextStep(); + + /// Returns null if there is no previous step. + T? previousStep(); +} diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_state.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_state.dart new file mode 100644 index 0000000000..c4db67a0a7 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_state.dart @@ -0,0 +1,68 @@ +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:equatable/equatable.dart'; + +/// Determines the state of registration flow. +/// It consists of 4 separate steps +/// - [GetStarted] which is first one. +/// - [FinishAccountCreation] is special step in case of partially created +/// account. +/// - [Recover] when want to start with existing one. +/// - [CreateNew] is entire flow in it self and has two distinguish sub-steps +/// - [CreateKeychain] where user is creating new keychain. +/// - [WalletLink] where user is linking Keychain with wallet. +sealed class RegistrationState extends Equatable { + const RegistrationState(); +} + +/// User decides where to go here [CreateNew] or [Recover] route. +final class GetStarted extends RegistrationState { + const GetStarted(); + + @override + List get props => []; +} + +/// When [CreateKeychain] is completed but [WalletLink] not. +final class FinishAccountCreation extends RegistrationState { + const FinishAccountCreation(); + + @override + List get props => []; +} + +/// User enters existing seed phrase here. +final class Recover extends RegistrationState { + const Recover(); + + @override + List get props => []; +} + +/// Encapsulates entire process of registration. +sealed class CreateNew extends RegistrationState { + const CreateNew(); +} + +/// Building up information for creating new Keychain. +final class CreateKeychain extends CreateNew { + final CreateKeychainStage stage; + + const CreateKeychain({ + this.stage = CreateKeychainStage.splash, + }); + + @override + List get props => [stage]; +} + +/// Linking existing keychain with wallet. +final class WalletLink extends CreateNew { + final WalletLinkStage stage; + + const WalletLink({ + this.stage = WalletLinkStage.intro, + }); + + @override + List get props => [stage]; +} diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart index b7f6afd7ab..96ed9fe09f 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart @@ -754,12 +754,6 @@ abstract class VoicesLocalizations { /// **'Profile & Keychain'** String get profileAndKeychain; - /// Title of Catalyst Keychain card - /// - /// In en, this message translates to: - /// **'Catalyst Keychain'** - String get catalystKeychain; - /// Action on Catalyst Keychain card /// /// In en, this message translates to: @@ -801,6 +795,54 @@ abstract class VoicesLocalizations { /// In en, this message translates to: /// **'Default'** String get defaultRole; + + /// No description provided for @catalystKeychain. + /// + /// In en, this message translates to: + /// **'Catalyst Keychain'** + String get catalystKeychain; + + /// No description provided for @accountCreationSplashTitle. + /// + /// In en, this message translates to: + /// **'Create your Catalyst Keychain'** + String get accountCreationSplashTitle; + + /// No description provided for @accountCreationSplashMessage. + /// + /// In en, this message translates to: + /// **'Your keychain is your ticket to participate in 
distributed innovation on the global stage. 

Once you have it, you\'ll be able to enter different spaces, discover awesome ideas, and share your feedback to hep improve ideas. 

As you add new keys to your keychain, you\'ll be able to enter new spaces, unlock new rewards opportunities, and have your voice heard in community decisions.'** + String get accountCreationSplashMessage; + + /// No description provided for @accountCreationSplashNextButton. + /// + /// In en, this message translates to: + /// **'Create your Keychain now'** + String get accountCreationSplashNextButton; + + /// No description provided for @accountInstructionsTitle. + /// + /// In en, this message translates to: + /// **'Great! Your Catalyst Keychain 
has been created.'** + String get accountInstructionsTitle; + + /// No description provided for @accountInstructionsMessage. + /// + /// In en, this message translates to: + /// **'On the next screen, you\'re going to see 12 words. 
This is called your \"seed phrase\". 

It\'s like a super secure password that only you know, 
that allows you to prove ownership of your keychain. 

You\'ll use it to login and recover your account on 
different devices, so be sure to put it somewhere safe!\n\nYou need to write this seed phrase down with pen and paper, so get this ready.'** + String get accountInstructionsMessage; + + /// For example in button that goes to next stage of registration + /// + /// In en, this message translates to: + /// **'Next'** + String get next; + + /// For example in button that goes to previous stage of registration + /// + /// In en, this message translates to: + /// **'Back'** + String get back; } class _VoicesLocalizationsDelegate extends LocalizationsDelegate { diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart index da28f6cac2..4cd757de9e 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart @@ -391,9 +391,6 @@ class VoicesLocalizationsEn extends VoicesLocalizations { @override String get profileAndKeychain => 'Profile & Keychain'; - @override - String get catalystKeychain => 'Catalyst Keychain'; - @override String get removeKeychain => 'Remove Keychain'; @@ -414,4 +411,28 @@ class VoicesLocalizationsEn extends VoicesLocalizations { @override String get defaultRole => 'Default'; + + @override + String get catalystKeychain => 'Catalyst Keychain'; + + @override + String get accountCreationSplashTitle => 'Create your Catalyst Keychain'; + + @override + String get accountCreationSplashMessage => 'Your keychain is your ticket to participate in 
distributed innovation on the global stage. 

Once you have it, you\'ll be able to enter different spaces, discover awesome ideas, and share your feedback to hep improve ideas. 

As you add new keys to your keychain, you\'ll be able to enter new spaces, unlock new rewards opportunities, and have your voice heard in community decisions.'; + + @override + String get accountCreationSplashNextButton => 'Create your Keychain now'; + + @override + String get accountInstructionsTitle => 'Great! Your Catalyst Keychain 
has been created.'; + + @override + String get accountInstructionsMessage => 'On the next screen, you\'re going to see 12 words. 
This is called your \"seed phrase\". 

It\'s like a super secure password that only you know, 
that allows you to prove ownership of your keychain. 

You\'ll use it to login and recover your account on 
different devices, so be sure to put it somewhere safe!\n\nYou need to write this seed phrase down with pen and paper, so get this ready.'; + + @override + String get next => 'Next'; + + @override + String get back => 'Back'; } diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart index b319a90a12..338ca7acb5 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart @@ -391,9 +391,6 @@ class VoicesLocalizationsEs extends VoicesLocalizations { @override String get profileAndKeychain => 'Profile & Keychain'; - @override - String get catalystKeychain => 'Catalyst Keychain'; - @override String get removeKeychain => 'Remove Keychain'; @@ -414,4 +411,28 @@ class VoicesLocalizationsEs extends VoicesLocalizations { @override String get defaultRole => 'Default'; + + @override + String get catalystKeychain => 'Catalyst Keychain'; + + @override + String get accountCreationSplashTitle => 'Create your Catalyst Keychain'; + + @override + String get accountCreationSplashMessage => 'Your keychain is your ticket to participate in 
distributed innovation on the global stage. 

Once you have it, you\'ll be able to enter different spaces, discover awesome ideas, and share your feedback to hep improve ideas. 

As you add new keys to your keychain, you\'ll be able to enter new spaces, unlock new rewards opportunities, and have your voice heard in community decisions.'; + + @override + String get accountCreationSplashNextButton => 'Create your Keychain now'; + + @override + String get accountInstructionsTitle => 'Great! Your Catalyst Keychain 
has been created.'; + + @override + String get accountInstructionsMessage => 'On the next screen, you\'re going to see 12 words. 
This is called your \"seed phrase\". 

It\'s like a super secure password that only you know, 
that allows you to prove ownership of your keychain. 

You\'ll use it to login and recover your account on 
different devices, so be sure to put it somewhere safe!\n\nYou need to write this seed phrase down with pen and paper, so get this ready.'; + + @override + String get next => 'Next'; + + @override + String get back => 'Back'; } diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb b/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb index f09843eabb..a97306a76d 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb @@ -459,7 +459,7 @@ "accountCreationGetStartedTitle": "Welcome to Catalyst", "accountCreationGetStatedDesc": "If you already have a Catalyst keychain you can restore it on this device, or you can create a new Catalyst Keychain.", "accountCreationGetStatedWhatNext": "What do you want to do?", - "myAccountProfileKeychain": "My Account / Profile & Keychain", + "myAccountProfileKeychain": "My Account / Profile & Keychain", "@myAccountProfileKeychain": { "description": "Title of My Account page" }, @@ -471,10 +471,6 @@ "@profileAndKeychain": { "description": "Tab on My Account page" }, - "catalystKeychain": "Catalyst Keychain", - "@catalystKeychain": { - "description": "Title of Catalyst Keychain card" - }, "removeKeychain": "Remove Keychain", "@removeKeychain": { "description": "Action on Catalyst Keychain card" @@ -502,5 +498,19 @@ "defaultRole": "Default", "@defaultRole": { "description": "Related to account role" + }, + "catalystKeychain": "Catalyst Keychain", + "accountCreationSplashTitle": "Create your Catalyst Keychain", + "accountCreationSplashMessage": "Your keychain is your ticket to participate in \u2028distributed innovation on the global stage. \u2028\u2028Once you have it, you'll be able to enter different spaces, discover awesome ideas, and share your feedback to hep improve ideas. \u2028\u2028As you add new keys to your keychain, you'll be able to enter new spaces, unlock new rewards opportunities, and have your voice heard in community decisions.", + "accountCreationSplashNextButton": "Create your Keychain now", + "accountInstructionsTitle": "Great! Your Catalyst Keychain \u2028has been created.", + "accountInstructionsMessage": "On the next screen, you're going to see 12 words. \u2028This is called your \"seed phrase\". \u2028\u2028It's like a super secure password that only you know, \u2028that allows you to prove ownership of your keychain. \u2028\u2028You'll use it to login and recover your account on \u2028different devices, so be sure to put it somewhere safe!\n\nYou need to write this seed phrase down with pen and paper, so get this ready.", + "next": "Next", + "@next": { + "description": "For example in button that goes to next stage of registration" + }, + "back": "Back", + "@back": { + "description": "For example in button that goes to previous stage of registration" } } \ No newline at end of file diff --git a/catalyst_voices/packages/catalyst_voices_models/lib/src/catalyst_voices_models.dart b/catalyst_voices/packages/catalyst_voices_models/lib/src/catalyst_voices_models.dart index 3afed13082..2cbe5ea91d 100644 --- a/catalyst_voices/packages/catalyst_voices_models/lib/src/catalyst_voices_models.dart +++ b/catalyst_voices/packages/catalyst_voices_models/lib/src/catalyst_voices_models.dart @@ -7,6 +7,7 @@ export 'errors/errors.dart'; export 'proposal/funded_proposal.dart'; export 'proposal/pending_proposal.dart'; export 'proposal/proposal_status.dart'; +export 'registration/registration.dart'; export 'session_data.dart'; export 'space.dart'; export 'treasury/treasury_campaign_builder.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/create_account_type.dart b/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/create_account_type.dart new file mode 100644 index 0000000000..891e4e7e63 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/create_account_type.dart @@ -0,0 +1 @@ +enum CreateAccountType { createNew, recover } diff --git a/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/create_keychain_stage.dart b/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/create_keychain_stage.dart new file mode 100644 index 0000000000..61ca5e435a --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/create_keychain_stage.dart @@ -0,0 +1,12 @@ +/// Describes the keychain creation flow during registration. +enum CreateKeychainStage { + splash, + instructions, + seedPhrase, + checkSeedPhraseInstructions, + checkSeedPhrase, + checkSeedPhraseResult, + unlockPasswordInstructions, + unlockPasswordCreate, + created, +} diff --git a/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/registration.dart b/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/registration.dart new file mode 100644 index 0000000000..67b3eeedfe --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/registration.dart @@ -0,0 +1,3 @@ +export 'create_account_type.dart'; +export 'create_keychain_stage.dart'; +export 'wallet_link_stage.dart'; diff --git a/catalyst_voices/lib/pages/registration/link_wallet/link_wallet_stage.dart b/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/wallet_link_stage.dart similarity index 89% rename from catalyst_voices/lib/pages/registration/link_wallet/link_wallet_stage.dart rename to catalyst_voices/packages/catalyst_voices_models/lib/src/registration/wallet_link_stage.dart index 4ca59a842e..364571f6c1 100644 --- a/catalyst_voices/lib/pages/registration/link_wallet/link_wallet_stage.dart +++ b/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/wallet_link_stage.dart @@ -1,5 +1,5 @@ /// Describes the link wallet flow during registration. -enum LinkWalletStage { +enum WalletLinkStage { /// The welcome screen for the link wallet flow. intro, From d372dfadd500d599d5a7ba246e031e418a492d9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 19:02:25 +0700 Subject: [PATCH 2/4] build(deps-dev): bump vite in /utilities/wallet-tester (#863) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.1.7 to 5.4.7. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v5.4.7/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.4.7/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Steven Johnson --- utilities/wallet-tester/package-lock.json | 382 ++++++++++++---------- utilities/wallet-tester/package.json | 2 +- 2 files changed, 215 insertions(+), 169 deletions(-) diff --git a/utilities/wallet-tester/package-lock.json b/utilities/wallet-tester/package-lock.json index 0cce2b825c..c1873bd412 100644 --- a/utilities/wallet-tester/package-lock.json +++ b/utilities/wallet-tester/package-lock.json @@ -37,7 +37,7 @@ "postcss": "^8.4.35", "tailwindcss": "^3.4.1", "typescript": "^5.4.2", - "vite": "^5.1.7", + "vite": "^5.4.7", "vite-tsconfig-paths": "^4.3.1" }, "optionalDependencies": { @@ -521,9 +521,9 @@ "integrity": "sha512-QQkavjEug/EmBFg02bmSg0eLYiOaRa1lmRG8q6fGRkx3O9BX1iZQDZJmirPDBTkTO3CpNB0q9se8DQFFcjREIw==" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], @@ -537,9 +537,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -553,9 +553,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], @@ -569,9 +569,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -585,9 +585,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -601,9 +601,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -617,9 +617,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -633,9 +633,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], @@ -649,9 +649,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -665,9 +665,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -681,9 +681,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -697,9 +697,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], @@ -713,9 +713,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -729,9 +729,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], @@ -745,9 +745,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -761,9 +761,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -777,9 +777,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -793,9 +793,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -809,9 +809,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -825,9 +825,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], @@ -841,9 +841,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -857,9 +857,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], @@ -873,9 +873,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -1463,9 +1463,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", + "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", "cpu": [ "arm" ], @@ -1476,9 +1476,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", + "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", "cpu": [ "arm64" ], @@ -1489,9 +1489,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", + "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", "cpu": [ "arm64" ], @@ -1502,9 +1502,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", + "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", "cpu": [ "x64" ], @@ -1515,9 +1515,22 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", + "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", + "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", "cpu": [ "arm" ], @@ -1528,9 +1541,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", + "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", "cpu": [ "arm64" ], @@ -1541,9 +1554,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", + "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", "cpu": [ "arm64" ], @@ -1553,10 +1566,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", + "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", + "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", "cpu": [ "riscv64" ], @@ -1566,10 +1592,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", + "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", + "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", "cpu": [ "x64" ], @@ -1580,9 +1619,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", + "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", "cpu": [ "x64" ], @@ -1593,9 +1632,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", + "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", "cpu": [ "arm64" ], @@ -1606,9 +1645,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", + "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", "cpu": [ "ia32" ], @@ -1619,9 +1658,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", + "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", "cpu": [ "x64" ], @@ -3474,9 +3513,9 @@ } }, "node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, "bin": { @@ -3486,29 +3525,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/escalade": { @@ -5604,9 +5643,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "dev": true }, "node_modules/picomatch": { @@ -5649,9 +5688,9 @@ } }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -5669,8 +5708,8 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -6103,9 +6142,9 @@ } }, "node_modules/rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", + "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -6118,19 +6157,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", + "@rollup/rollup-android-arm-eabi": "4.22.4", + "@rollup/rollup-android-arm64": "4.22.4", + "@rollup/rollup-darwin-arm64": "4.22.4", + "@rollup/rollup-darwin-x64": "4.22.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", + "@rollup/rollup-linux-arm-musleabihf": "4.22.4", + "@rollup/rollup-linux-arm64-gnu": "4.22.4", + "@rollup/rollup-linux-arm64-musl": "4.22.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", + "@rollup/rollup-linux-riscv64-gnu": "4.22.4", + "@rollup/rollup-linux-s390x-gnu": "4.22.4", + "@rollup/rollup-linux-x64-gnu": "4.22.4", + "@rollup/rollup-linux-x64-musl": "4.22.4", + "@rollup/rollup-win32-arm64-msvc": "4.22.4", + "@rollup/rollup-win32-ia32-msvc": "4.22.4", + "@rollup/rollup-win32-x64-msvc": "4.22.4", "fsevents": "~2.3.2" } }, @@ -6331,9 +6373,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -6963,14 +7005,14 @@ "dev": true }, "node_modules/vite": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.7.tgz", - "integrity": "sha512-sgnEEFTZYMui/sTlH1/XEnVNHMujOahPLGMxn1+5sIT45Xjng1Ec1K78jRP15dSmVgg5WBin9yO81j3o9OxofA==", + "version": "5.4.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.7.tgz", + "integrity": "sha512-5l2zxqMEPVENgvzTuBpHer2awaetimj2BGkhBPdnwKbPNOlHsODU+oiazEZzLK7KhAnOrO+XGYJYn4ZlUhDtDQ==", "dev": true, "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.35", - "rollup": "^4.2.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -6989,6 +7031,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -7006,6 +7049,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, diff --git a/utilities/wallet-tester/package.json b/utilities/wallet-tester/package.json index ac77cf7219..2be5b52baf 100644 --- a/utilities/wallet-tester/package.json +++ b/utilities/wallet-tester/package.json @@ -42,7 +42,7 @@ "postcss": "^8.4.35", "tailwindcss": "^3.4.1", "typescript": "^5.4.2", - "vite": "^5.1.7", + "vite": "^5.4.7", "vite-tsconfig-paths": "^4.3.1" }, "optionalDependencies": { From 6ddcf4942411dd6cbbda49ab7553c6c47630d56c Mon Sep 17 00:00:00 2001 From: Dominik Toton <166132265+dtscalac@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:46:49 +0200 Subject: [PATCH 3/4] feat(cat-voices): define wallet link flow (#892) * refactor: move link_wallet_dialog * refactor: align l10n keys * refactor: move link-wallet to another folder * feat: add content * feat: define the rest of stages for wallet link * feat: add missing panels * refactor: rename folder * chore: sort imports * chore: reformat --- .../select_wallet/select_wallet_panel.dart | 10 ------ .../link_wallet/wallet_link_panel.dart | 21 ------------ .../registration/registration_dialog.dart | 2 +- .../registration/registration_info_panel.dart | 27 ++++++++++----- .../intro/intro_panel.dart | 4 +-- .../rbac_transaction_panel.dart | 34 +++++++++++++++++++ .../roles_chooser/roles_chooser_panel.dart | 34 +++++++++++++++++++ .../roles_summary/roles_summary_panel.dart | 34 +++++++++++++++++++ .../select_wallet/select_wallet_panel.dart | 34 +++++++++++++++++++ .../wallet_details/wallet_details_panel.dart | 34 +++++++++++++++++++ .../wallet_link/wallet_link_panel.dart | 29 ++++++++++++++++ .../controllers/wallet_link_controller.dart | 18 +++++++++- .../catalyst_voices_localizations.dart | 22 +++++++++--- .../catalyst_voices_localizations_en.dart | 14 +++++--- .../catalyst_voices_localizations_es.dart | 14 +++++--- .../lib/l10n/intl_en.arb | 26 +++++++++----- .../src/registration/wallet_link_stage.dart | 12 +++++++ 17 files changed, 303 insertions(+), 66 deletions(-) delete mode 100644 catalyst_voices/lib/pages/registration/link_wallet/select_wallet/select_wallet_panel.dart delete mode 100644 catalyst_voices/lib/pages/registration/link_wallet/wallet_link_panel.dart rename catalyst_voices/lib/pages/registration/{link_wallet => wallet_link}/intro/intro_panel.dart (91%) create mode 100644 catalyst_voices/lib/pages/registration/wallet_link/rbac_transaction/rbac_transaction_panel.dart create mode 100644 catalyst_voices/lib/pages/registration/wallet_link/roles_chooser/roles_chooser_panel.dart create mode 100644 catalyst_voices/lib/pages/registration/wallet_link/roles_summary/roles_summary_panel.dart create mode 100644 catalyst_voices/lib/pages/registration/wallet_link/select_wallet/select_wallet_panel.dart create mode 100644 catalyst_voices/lib/pages/registration/wallet_link/wallet_details/wallet_details_panel.dart create mode 100644 catalyst_voices/lib/pages/registration/wallet_link/wallet_link_panel.dart diff --git a/catalyst_voices/lib/pages/registration/link_wallet/select_wallet/select_wallet_panel.dart b/catalyst_voices/lib/pages/registration/link_wallet/select_wallet/select_wallet_panel.dart deleted file mode 100644 index 855808d8f0..0000000000 --- a/catalyst_voices/lib/pages/registration/link_wallet/select_wallet/select_wallet_panel.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/material.dart'; - -class SelectWalletPanel extends StatelessWidget { - const SelectWalletPanel({super.key}); - - @override - Widget build(BuildContext context) { - return const Placeholder(); - } -} diff --git a/catalyst_voices/lib/pages/registration/link_wallet/wallet_link_panel.dart b/catalyst_voices/lib/pages/registration/link_wallet/wallet_link_panel.dart deleted file mode 100644 index dcb668f4e9..0000000000 --- a/catalyst_voices/lib/pages/registration/link_wallet/wallet_link_panel.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:catalyst_voices/pages/registration/link_wallet/intro/intro_panel.dart'; -import 'package:catalyst_voices/pages/registration/link_wallet/select_wallet/select_wallet_panel.dart'; -import 'package:catalyst_voices_models/catalyst_voices_models.dart'; -import 'package:flutter/material.dart'; - -class WalletLinkPanel extends StatelessWidget { - final WalletLinkStage stage; - - const WalletLinkPanel({ - super.key, - required this.stage, - }); - - @override - Widget build(BuildContext context) { - return switch (stage) { - WalletLinkStage.intro => const IntroPanel(), - WalletLinkStage.selectWallet => const SelectWalletPanel(), - }; - } -} diff --git a/catalyst_voices/lib/pages/registration/registration_dialog.dart b/catalyst_voices/lib/pages/registration/registration_dialog.dart index 653764e746..f2115810bd 100644 --- a/catalyst_voices/lib/pages/registration/registration_dialog.dart +++ b/catalyst_voices/lib/pages/registration/registration_dialog.dart @@ -1,8 +1,8 @@ import 'package:catalyst_voices/dependency/dependencies.dart'; import 'package:catalyst_voices/pages/registration/create_keychain/create_keychain_panel.dart'; import 'package:catalyst_voices/pages/registration/get_started/get_started_panel.dart'; -import 'package:catalyst_voices/pages/registration/link_wallet/wallet_link_panel.dart'; import 'package:catalyst_voices/pages/registration/registration_info_panel.dart'; +import 'package:catalyst_voices/pages/registration/wallet_link/wallet_link_panel.dart'; import 'package:catalyst_voices/widgets/widgets.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; import 'package:flutter/material.dart'; diff --git a/catalyst_voices/lib/pages/registration/registration_info_panel.dart b/catalyst_voices/lib/pages/registration/registration_info_panel.dart index e33113d351..85842d255f 100644 --- a/catalyst_voices/lib/pages/registration/registration_info_panel.dart +++ b/catalyst_voices/lib/pages/registration/registration_info_panel.dart @@ -47,7 +47,7 @@ class RegistrationInfoPanel extends StatelessWidget { // TODO(damian-molinski): Extract to l10n in next step CreateKeychainStage.seedPhrase => _HeaderStrings( title: 'Catalyst Keychain', - subtitle: 'Write down your 12 Catalyst 
security words', + subtitle: 'Write down your 12 Catalyst security words', body: 'Make sure you create an offline backup ' 'of your recovery phrase as well.', ), @@ -61,15 +61,24 @@ class RegistrationInfoPanel extends StatelessWidget { }; } - _HeaderStrings buildWalletStageHeader(WalletLinkStage stage) { + _HeaderStrings buildWalletLinkStageHeader(WalletLinkStage stage) { return switch (stage) { - WalletLinkStage.intro => _HeaderStrings( - title: 'Link keys to your 
Catalyst Keychain', - subtitle: 'Link your Cardano wallet', + WalletLinkStage.intro || + WalletLinkStage.selectWallet || + WalletLinkStage.walletDetails => + _HeaderStrings( + title: context.l10n.walletLinkHeader, + subtitle: context.l10n.walletLinkWalletSubheader, ), - WalletLinkStage.selectWallet => _HeaderStrings( - title: 'Link keys to your 
Catalyst Keychain', - subtitle: 'Link your Cardano wallet', + WalletLinkStage.rolesChooser || + WalletLinkStage.rolesSummary => + _HeaderStrings( + title: context.l10n.walletLinkHeader, + subtitle: context.l10n.walletLinkRolesSubheader, + ), + WalletLinkStage.rbacTransaction => _HeaderStrings( + title: context.l10n.walletLinkHeader, + subtitle: context.l10n.walletLinkTransactionSubheader, ), }; } @@ -79,7 +88,7 @@ class RegistrationInfoPanel extends StatelessWidget { FinishAccountCreation() => _HeaderStrings(title: 'TODO'), Recover() => _HeaderStrings(title: 'TODO'), CreateKeychain(:final stage) => buildKeychainStageHeader(stage), - WalletLink(:final stage) => buildWalletStageHeader(stage), + WalletLink(:final stage) => buildWalletLinkStageHeader(stage), }; } } diff --git a/catalyst_voices/lib/pages/registration/link_wallet/intro/intro_panel.dart b/catalyst_voices/lib/pages/registration/wallet_link/intro/intro_panel.dart similarity index 91% rename from catalyst_voices/lib/pages/registration/link_wallet/intro/intro_panel.dart rename to catalyst_voices/lib/pages/registration/wallet_link/intro/intro_panel.dart index c55a067c1d..d61ce2af09 100644 --- a/catalyst_voices/lib/pages/registration/link_wallet/intro/intro_panel.dart +++ b/catalyst_voices/lib/pages/registration/wallet_link/intro/intro_panel.dart @@ -14,12 +14,12 @@ class IntroPanel extends StatelessWidget { children: [ const SizedBox(height: 24), Text( - context.l10n.walletLink_intro_title, + context.l10n.walletLinkIntroTitle, style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 24), Text( - context.l10n.walletLink_intro_content, + context.l10n.walletLinkIntroContent, style: Theme.of(context).textTheme.bodyMedium, ), const Spacer(), diff --git a/catalyst_voices/lib/pages/registration/wallet_link/rbac_transaction/rbac_transaction_panel.dart b/catalyst_voices/lib/pages/registration/wallet_link/rbac_transaction/rbac_transaction_panel.dart new file mode 100644 index 0000000000..4848892b93 --- /dev/null +++ b/catalyst_voices/lib/pages/registration/wallet_link/rbac_transaction/rbac_transaction_panel.dart @@ -0,0 +1,34 @@ +import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:flutter/material.dart'; + +// TODO(dtscalac): define content +class RbacTransactionPanel extends StatelessWidget { + const RbacTransactionPanel({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Spacer(), + VoicesFilledButton( + leading: VoicesAssets.icons.wallet.buildIcon(), + onTap: () { + RegistrationBloc.of(context).add(const PreviousStepEvent()); + }, + child: const Text('Previous'), + ), + const SizedBox(height: 12), + VoicesFilledButton( + leading: VoicesAssets.icons.wallet.buildIcon(), + onTap: () { + RegistrationBloc.of(context).add(const NextStepEvent()); + }, + child: const Text('Next'), + ), + ], + ); + } +} diff --git a/catalyst_voices/lib/pages/registration/wallet_link/roles_chooser/roles_chooser_panel.dart b/catalyst_voices/lib/pages/registration/wallet_link/roles_chooser/roles_chooser_panel.dart new file mode 100644 index 0000000000..5fbf8589df --- /dev/null +++ b/catalyst_voices/lib/pages/registration/wallet_link/roles_chooser/roles_chooser_panel.dart @@ -0,0 +1,34 @@ +import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:flutter/material.dart'; + +// TODO(dtscalac): define content +class RolesChooserPanel extends StatelessWidget { + const RolesChooserPanel({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Spacer(), + VoicesFilledButton( + leading: VoicesAssets.icons.wallet.buildIcon(), + onTap: () { + RegistrationBloc.of(context).add(const PreviousStepEvent()); + }, + child: const Text('Previous'), + ), + const SizedBox(height: 12), + VoicesFilledButton( + leading: VoicesAssets.icons.wallet.buildIcon(), + onTap: () { + RegistrationBloc.of(context).add(const NextStepEvent()); + }, + child: const Text('Next'), + ), + ], + ); + } +} diff --git a/catalyst_voices/lib/pages/registration/wallet_link/roles_summary/roles_summary_panel.dart b/catalyst_voices/lib/pages/registration/wallet_link/roles_summary/roles_summary_panel.dart new file mode 100644 index 0000000000..c8af179f27 --- /dev/null +++ b/catalyst_voices/lib/pages/registration/wallet_link/roles_summary/roles_summary_panel.dart @@ -0,0 +1,34 @@ +import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:flutter/material.dart'; + +// TODO(dtscalac): define content +class RolesSummaryPanel extends StatelessWidget { + const RolesSummaryPanel({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Spacer(), + VoicesFilledButton( + leading: VoicesAssets.icons.wallet.buildIcon(), + onTap: () { + RegistrationBloc.of(context).add(const PreviousStepEvent()); + }, + child: const Text('Previous'), + ), + const SizedBox(height: 12), + VoicesFilledButton( + leading: VoicesAssets.icons.wallet.buildIcon(), + onTap: () { + RegistrationBloc.of(context).add(const NextStepEvent()); + }, + child: const Text('Next'), + ), + ], + ); + } +} diff --git a/catalyst_voices/lib/pages/registration/wallet_link/select_wallet/select_wallet_panel.dart b/catalyst_voices/lib/pages/registration/wallet_link/select_wallet/select_wallet_panel.dart new file mode 100644 index 0000000000..89e5dc1681 --- /dev/null +++ b/catalyst_voices/lib/pages/registration/wallet_link/select_wallet/select_wallet_panel.dart @@ -0,0 +1,34 @@ +import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:flutter/material.dart'; + +// TODO(dtscalac): define content +class SelectWalletPanel extends StatelessWidget { + const SelectWalletPanel({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Spacer(), + VoicesFilledButton( + leading: VoicesAssets.icons.wallet.buildIcon(), + onTap: () { + RegistrationBloc.of(context).add(const PreviousStepEvent()); + }, + child: const Text('Previous'), + ), + const SizedBox(height: 12), + VoicesFilledButton( + leading: VoicesAssets.icons.wallet.buildIcon(), + onTap: () { + RegistrationBloc.of(context).add(const NextStepEvent()); + }, + child: const Text('Next'), + ), + ], + ); + } +} diff --git a/catalyst_voices/lib/pages/registration/wallet_link/wallet_details/wallet_details_panel.dart b/catalyst_voices/lib/pages/registration/wallet_link/wallet_details/wallet_details_panel.dart new file mode 100644 index 0000000000..8c1bc2cbfb --- /dev/null +++ b/catalyst_voices/lib/pages/registration/wallet_link/wallet_details/wallet_details_panel.dart @@ -0,0 +1,34 @@ +import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:flutter/material.dart'; + +// TODO(dtscalac): define content +class WalletDetailsPanel extends StatelessWidget { + const WalletDetailsPanel({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const Spacer(), + VoicesFilledButton( + leading: VoicesAssets.icons.wallet.buildIcon(), + onTap: () { + RegistrationBloc.of(context).add(const PreviousStepEvent()); + }, + child: const Text('Previous'), + ), + const SizedBox(height: 12), + VoicesFilledButton( + leading: VoicesAssets.icons.wallet.buildIcon(), + onTap: () { + RegistrationBloc.of(context).add(const NextStepEvent()); + }, + child: const Text('Next'), + ), + ], + ); + } +} diff --git a/catalyst_voices/lib/pages/registration/wallet_link/wallet_link_panel.dart b/catalyst_voices/lib/pages/registration/wallet_link/wallet_link_panel.dart new file mode 100644 index 0000000000..a6bae7b7c7 --- /dev/null +++ b/catalyst_voices/lib/pages/registration/wallet_link/wallet_link_panel.dart @@ -0,0 +1,29 @@ +import 'package:catalyst_voices/pages/registration/wallet_link/intro/intro_panel.dart'; +import 'package:catalyst_voices/pages/registration/wallet_link/rbac_transaction/rbac_transaction_panel.dart'; +import 'package:catalyst_voices/pages/registration/wallet_link/roles_chooser/roles_chooser_panel.dart'; +import 'package:catalyst_voices/pages/registration/wallet_link/roles_summary/roles_summary_panel.dart'; +import 'package:catalyst_voices/pages/registration/wallet_link/select_wallet/select_wallet_panel.dart'; +import 'package:catalyst_voices/pages/registration/wallet_link/wallet_details/wallet_details_panel.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class WalletLinkPanel extends StatelessWidget { + final WalletLinkStage stage; + + const WalletLinkPanel({ + super.key, + required this.stage, + }); + + @override + Widget build(BuildContext context) { + return switch (stage) { + WalletLinkStage.intro => const IntroPanel(), + WalletLinkStage.selectWallet => const SelectWalletPanel(), + WalletLinkStage.walletDetails => const WalletDetailsPanel(), + WalletLinkStage.rolesChooser => const RolesChooserPanel(), + WalletLinkStage.rolesSummary => const RolesSummaryPanel(), + WalletLinkStage.rbacTransaction => const RbacTransactionPanel(), + }; + } +} diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/wallet_link_controller.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/wallet_link_controller.dart index 826ad04473..1ab1279649 100644 --- a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/wallet_link_controller.dart +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/wallet_link_controller.dart @@ -17,7 +17,15 @@ final class RegistrationWalletLinkController final nextStep = switch (_stage) { WalletLinkStage.intro => const WalletLink(stage: WalletLinkStage.selectWallet), - WalletLinkStage.selectWallet => null, + WalletLinkStage.selectWallet => + const WalletLink(stage: WalletLinkStage.walletDetails), + WalletLinkStage.walletDetails => + const WalletLink(stage: WalletLinkStage.rolesChooser), + WalletLinkStage.rolesChooser => + const WalletLink(stage: WalletLinkStage.rolesSummary), + WalletLinkStage.rolesSummary => + const WalletLink(stage: WalletLinkStage.rbacTransaction), + WalletLinkStage.rbacTransaction => null, }; if (nextStep != null) { @@ -33,6 +41,14 @@ final class RegistrationWalletLinkController WalletLinkStage.intro => null, WalletLinkStage.selectWallet => const WalletLink(stage: WalletLinkStage.intro), + WalletLinkStage.walletDetails => + const WalletLink(stage: WalletLinkStage.selectWallet), + WalletLinkStage.rolesChooser => + const WalletLink(stage: WalletLinkStage.walletDetails), + WalletLinkStage.rolesSummary => + const WalletLink(stage: WalletLinkStage.rolesChooser), + WalletLinkStage.rbacTransaction => + const WalletLink(stage: WalletLinkStage.rolesSummary), }; if (previousStep != null) { diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart index 96ed9fe09f..39521ede2f 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart @@ -680,25 +680,37 @@ abstract class VoicesLocalizations { /// /// In en, this message translates to: /// **'Link keys to your Catalyst Keychain'** - String get walletLink_header; + String get walletLinkHeader; - /// A subheader in link wallet flow in registration. + /// A subheader in link wallet flow in registration for wallet connection. /// /// In en, this message translates to: /// **'Link your Cardano wallet'** - String get walletLink_subheader; + String get walletLinkWalletSubheader; + + /// A subheader in link wallet flow in registration for role chooser state. + /// + /// In en, this message translates to: + /// **'Select your Catalyst roles'** + String get walletLinkRolesSubheader; + + /// A subheader in link wallet flow in registration for RBAC transaction. + /// + /// In en, this message translates to: + /// **'Sign your Catalyst roles to the\nCardano mainnet'** + String get walletLinkTransactionSubheader; /// A title in link wallet flow on intro screen. /// /// In en, this message translates to: /// **'Link Cardano Wallet & Catalyst Roles to you Catalyst Keychain.'** - String get walletLink_intro_title; + String get walletLinkIntroTitle; /// A message (content) in link wallet flow on intro screen. /// /// In en, this message translates to: /// **'You\'re almost there! This is the final and most important step in your account setup.\n\nWe\'re going to link a Cardano Wallet to your Catalyst Keychain, so you can start collecting Role Keys.\n\nRole Keys allow you to enter new spaces, discover new ways to participate, and unlock new ways to earn rewards.\n\nWe\'ll start with your Voter Key by default. You can decide to add a Proposer Key and Drep key if you want, or you can always add them later.'** - String get walletLink_intro_content; + String get walletLinkIntroContent; /// No description provided for @accountCreationCreate. /// diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart index 4cd757de9e..6de0f1cd16 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart @@ -353,16 +353,22 @@ class VoicesLocalizationsEn extends VoicesLocalizations { String get learnMore => 'Learn More'; @override - String get walletLink_header => 'Link keys to your Catalyst Keychain'; + String get walletLinkHeader => 'Link keys to your Catalyst Keychain'; @override - String get walletLink_subheader => 'Link your Cardano wallet'; + String get walletLinkWalletSubheader => 'Link your Cardano wallet'; @override - String get walletLink_intro_title => 'Link Cardano Wallet & Catalyst Roles to you Catalyst Keychain.'; + String get walletLinkRolesSubheader => 'Select your Catalyst roles'; @override - String get walletLink_intro_content => 'You\'re almost there! This is the final and most important step in your account setup.\n\nWe\'re going to link a Cardano Wallet to your Catalyst Keychain, so you can start collecting Role Keys.\n\nRole Keys allow you to enter new spaces, discover new ways to participate, and unlock new ways to earn rewards.\n\nWe\'ll start with your Voter Key by default. You can decide to add a Proposer Key and Drep key if you want, or you can always add them later.'; + String get walletLinkTransactionSubheader => 'Sign your Catalyst roles to the\nCardano mainnet'; + + @override + String get walletLinkIntroTitle => 'Link Cardano Wallet & Catalyst Roles to you Catalyst Keychain.'; + + @override + String get walletLinkIntroContent => 'You\'re almost there! This is the final and most important step in your account setup.\n\nWe\'re going to link a Cardano Wallet to your Catalyst Keychain, so you can start collecting Role Keys.\n\nRole Keys allow you to enter new spaces, discover new ways to participate, and unlock new ways to earn rewards.\n\nWe\'ll start with your Voter Key by default. You can decide to add a Proposer Key and Drep key if you want, or you can always add them later.'; @override String get accountCreationCreate => 'Create a new 
Catalyst Keychain'; diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart index 338ca7acb5..ccef81b46c 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart @@ -353,16 +353,22 @@ class VoicesLocalizationsEs extends VoicesLocalizations { String get learnMore => 'Learn More'; @override - String get walletLink_header => 'Link keys to your Catalyst Keychain'; + String get walletLinkHeader => 'Link keys to your Catalyst Keychain'; @override - String get walletLink_subheader => 'Link your Cardano wallet'; + String get walletLinkWalletSubheader => 'Link your Cardano wallet'; @override - String get walletLink_intro_title => 'Link Cardano Wallet & Catalyst Roles to you Catalyst Keychain.'; + String get walletLinkRolesSubheader => 'Select your Catalyst roles'; @override - String get walletLink_intro_content => 'You\'re almost there! This is the final and most important step in your account setup.\n\nWe\'re going to link a Cardano Wallet to your Catalyst Keychain, so you can start collecting Role Keys.\n\nRole Keys allow you to enter new spaces, discover new ways to participate, and unlock new ways to earn rewards.\n\nWe\'ll start with your Voter Key by default. You can decide to add a Proposer Key and Drep key if you want, or you can always add them later.'; + String get walletLinkTransactionSubheader => 'Sign your Catalyst roles to the\nCardano mainnet'; + + @override + String get walletLinkIntroTitle => 'Link Cardano Wallet & Catalyst Roles to you Catalyst Keychain.'; + + @override + String get walletLinkIntroContent => 'You\'re almost there! This is the final and most important step in your account setup.\n\nWe\'re going to link a Cardano Wallet to your Catalyst Keychain, so you can start collecting Role Keys.\n\nRole Keys allow you to enter new spaces, discover new ways to participate, and unlock new ways to earn rewards.\n\nWe\'ll start with your Voter Key by default. You can decide to add a Proposer Key and Drep key if you want, or you can always add them later.'; @override String get accountCreationCreate => 'Create a new 
Catalyst Keychain'; diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb b/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb index a97306a76d..c9bad9cabd 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb @@ -434,20 +434,28 @@ "@learnMore": { "description": "A label on a clickable element that can show more content." }, - "walletLink_header": "Link keys to your Catalyst Keychain", - "@walletLink_header": { + "walletLinkHeader": "Link keys to your Catalyst Keychain", + "@walletLinkHeader": { "description": "A header in link wallet flow in registration." }, - "walletLink_subheader": "Link your Cardano wallet", - "@walletLink_subheader": { - "description": "A subheader in link wallet flow in registration." + "walletLinkWalletSubheader": "Link your Cardano wallet", + "@walletLinkWalletSubheader": { + "description": "A subheader in link wallet flow in registration for wallet connection." }, - "walletLink_intro_title": "Link Cardano Wallet & Catalyst Roles to you Catalyst Keychain.", - "@walletLink_intro_title": { + "walletLinkRolesSubheader": "Select your Catalyst roles", + "@walletLinkRolesSubheader": { + "description": "A subheader in link wallet flow in registration for role chooser state." + }, + "walletLinkTransactionSubheader": "Sign your Catalyst roles to the\nCardano mainnet", + "@walletLinkTransactionSubheader": { + "description": "A subheader in link wallet flow in registration for RBAC transaction." + }, + "walletLinkIntroTitle": "Link Cardano Wallet & Catalyst Roles to you Catalyst Keychain.", + "@walletLinkIntroTitle": { "description": "A title in link wallet flow on intro screen." }, - "walletLink_intro_content": "You're almost there! This is the final and most important step in your account setup.\n\nWe're going to link a Cardano Wallet to your Catalyst Keychain, so you can start collecting Role Keys.\n\nRole Keys allow you to enter new spaces, discover new ways to participate, and unlock new ways to earn rewards.\n\nWe'll start with your Voter Key by default. You can decide to add a Proposer Key and Drep key if you want, or you can always add them later.", - "@walletLink_intro_content": { + "walletLinkIntroContent": "You're almost there! This is the final and most important step in your account setup.\n\nWe're going to link a Cardano Wallet to your Catalyst Keychain, so you can start collecting Role Keys.\n\nRole Keys allow you to enter new spaces, discover new ways to participate, and unlock new ways to earn rewards.\n\nWe'll start with your Voter Key by default. You can decide to add a Proposer Key and Drep key if you want, or you can always add them later.", + "@walletLinkIntroContent": { "description": "A message (content) in link wallet flow on intro screen." }, "accountCreationCreate": "Create a new \u2028Catalyst Keychain", diff --git a/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/wallet_link_stage.dart b/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/wallet_link_stage.dart index 364571f6c1..4b03ab1e87 100644 --- a/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/wallet_link_stage.dart +++ b/catalyst_voices/packages/catalyst_voices_models/lib/src/registration/wallet_link_stage.dart @@ -5,4 +5,16 @@ enum WalletLinkStage { /// A screen where the user is asked to connect the cardano wallet. selectWallet, + + /// Wallet details after successfully connecting a wallet. + walletDetails, + + /// A user is asked to select the user roles. + rolesChooser, + + /// Summary of chosen user roles. + rolesSummary, + + /// The user submits an RBAC transaction to finish the registration. + rbacTransaction, } From b76bd6ddf2245f0b89238981e5f66d253dbcaa72 Mon Sep 17 00:00:00 2001 From: Dominik Toton <166132265+dtscalac@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:37:57 +0200 Subject: [PATCH 4/4] feat(cat-voices): wallet selection logic (#893) * refactor: move link_wallet_dialog * refactor: align l10n keys * refactor: move link-wallet to another folder * feat: add content * feat: define the rest of stages for wallet link * feat: add missing panels * refactor: rename folder * chore: sort imports * chore: reformat * feat: add translations * chore: add wallet dependencies * feat: add generic error indicator * feat: add voices future builder * feat: update select wallet panel content * fix: error builder alignment * refactor: remove unused inheritance * feat: add empty state * fix: padding * fix: unwanted rebuilds * refactor: resign from future builder in favor of value listenable * feat: improve loading * refactor: improve error and loader builder, use minimum loading delay * refactor: result_type * refactor: cleanup --- .../select_wallet/select_wallet_panel.dart | 136 ++++++++++++-- .../infrastructure/voices_future_builder.dart | 168 ++++++++++++++++++ .../indicators/voices_error_indicator.dart | 59 ++++++ catalyst_voices/lib/widgets/widgets.dart | 1 + .../controllers/wallet_link_controller.dart | 33 +++- .../src/registration/registration_bloc.dart | 11 ++ .../catalyst_voices_blocs/pubspec.yaml | 5 + .../catalyst_voices_localizations.dart | 36 ++++ .../catalyst_voices_localizations_en.dart | 18 ++ .../catalyst_voices_localizations_es.dart | 18 ++ .../lib/l10n/intl_en.arb | 24 +++ .../lib/src/catalyst_voices_shared.dart | 1 + .../lib/src/utils/future_ext.dart | 16 ++ .../catalyst_voices_view_models/pubspec.yaml | 3 + catalyst_voices/pubspec.yaml | 1 + .../voices_future_builder_test.dart | 56 ++++++ .../voices_status_indicator_test.dart | 2 +- .../examples/voices_indicators_example.dart | 13 ++ 18 files changed, 588 insertions(+), 13 deletions(-) create mode 100644 catalyst_voices/lib/widgets/common/infrastructure/voices_future_builder.dart create mode 100644 catalyst_voices/lib/widgets/indicators/voices_error_indicator.dart create mode 100644 catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/future_ext.dart create mode 100644 catalyst_voices/test/widgets/common/infrastructure/voices_future_builder_test.dart diff --git a/catalyst_voices/lib/pages/registration/wallet_link/select_wallet/select_wallet_panel.dart b/catalyst_voices/lib/pages/registration/wallet_link/select_wallet/select_wallet_panel.dart index 89e5dc1681..f240e8d37c 100644 --- a/catalyst_voices/lib/pages/registration/wallet_link/select_wallet/select_wallet_panel.dart +++ b/catalyst_voices/lib/pages/registration/wallet_link/select_wallet/select_wallet_panel.dart @@ -1,9 +1,13 @@ -import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart'; +import 'dart:async'; + +import 'package:catalyst_cardano/catalyst_cardano.dart'; +import 'package:catalyst_voices/widgets/widgets.dart'; import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; import 'package:flutter/material.dart'; +import 'package:result_type/result_type.dart'; -// TODO(dtscalac): define content class SelectWalletPanel extends StatelessWidget { const SelectWalletPanel({super.key}); @@ -12,23 +16,133 @@ class SelectWalletPanel extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const Spacer(), - VoicesFilledButton( - leading: VoicesAssets.icons.wallet.buildIcon(), + const SizedBox(height: 24), + Text( + context.l10n.walletLinkSelectWalletTitle, + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: 24), + Text( + context.l10n.walletLinkSelectWalletContent, + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 40), + const Expanded(child: _Wallets()), + const SizedBox(height: 24), + VoicesBackButton( onTap: () { RegistrationBloc.of(context).add(const PreviousStepEvent()); }, - child: const Text('Previous'), ), - const SizedBox(height: 12), - VoicesFilledButton( - leading: VoicesAssets.icons.wallet.buildIcon(), + const SizedBox(height: 10), + VoicesTextButton( + trailing: VoicesAssets.icons.externalLink.buildIcon(), + onTap: () {}, + child: Text(context.l10n.seeAllSupportedWallets), + ), + ], + ); + } +} + +class _Wallets extends StatefulWidget { + const _Wallets(); + + @override + State<_Wallets> createState() => _WalletsState(); +} + +class _WalletsState extends State<_Wallets> { + @override + void initState() { + super.initState(); + + final bloc = RegistrationBloc.of(context); + if (bloc.cardanoWallets.value == null) { + unawaited(bloc.refreshCardanoWallets()); + } + } + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: RegistrationBloc.of(context).cardanoWallets, + builder: (context, result, _) { + return switch (result) { + Success(:final value) => value.isNotEmpty + ? _WalletsList(wallets: value) + : _WalletsEmpty(onRetry: _onRetry), + Failure() => _WalletsError(onRetry: _onRetry), + _ => const Center(child: VoicesCircularProgressIndicator()), + }; + }, + ); + } + + void _onRetry() { + unawaited(RegistrationBloc.of(context).refreshCardanoWallets()); + } +} + +class _WalletsList extends StatelessWidget { + final List wallets; + + const _WalletsList({required this.wallets}); + + @override + Widget build(BuildContext context) { + return ListView.builder( + itemCount: wallets.length, + itemBuilder: (context, index) { + final wallet = wallets[index]; + return VoicesWalletTile( + iconSrc: wallet.icon, + name: Text(wallet.name), onTap: () { RegistrationBloc.of(context).add(const NextStepEvent()); }, - child: const Text('Next'), + ); + }, + ); + } +} + +class _WalletsEmpty extends StatelessWidget { + final VoidCallback onRetry; + + const _WalletsEmpty({required this.onRetry}); + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.topCenter, + child: SizedBox( + width: double.infinity, + child: VoicesErrorIndicator( + message: context.l10n.noWalletFound, + onRetry: onRetry, ), - ], + ), + ); + } +} + +class _WalletsError extends StatelessWidget { + final VoidCallback onRetry; + + const _WalletsError({required this.onRetry}); + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment.topCenter, + child: SizedBox( + width: double.infinity, + child: VoicesErrorIndicator( + message: context.l10n.somethingWentWrong, + onRetry: onRetry, + ), + ), ); } } diff --git a/catalyst_voices/lib/widgets/common/infrastructure/voices_future_builder.dart b/catalyst_voices/lib/widgets/common/infrastructure/voices_future_builder.dart new file mode 100644 index 0000000000..53698f8fb9 --- /dev/null +++ b/catalyst_voices/lib/widgets/common/infrastructure/voices_future_builder.dart @@ -0,0 +1,168 @@ +import 'package:catalyst_voices/widgets/indicators/voices_circular_progress_indicator.dart'; +import 'package:catalyst_voices/widgets/indicators/voices_error_indicator.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/material.dart'; + +/// A callback that generates a new [Future] of type [T]. +typedef VoicesFutureProvider = Future Function(); + +/// A callback that builds a widget from a [T] value. +/// +/// Call [onRetry] if your data state contains +/// the retry button, it will reload the widget. +typedef VoicesFutureDataBuilder = Widget Function( + BuildContext context, + T value, + VoidCallback onRetry, +); + +/// A callback that builds a widget in an error state. +/// +/// Call [onRetry] if your error state contains +/// the retry button, it will reload the widget. +typedef VoicesFutureErrorBuilder = Widget Function( + BuildContext context, + Object? error, + VoidCallback onRetry, +); + +/// A [FutureBuilder] which simplifies handling a [Future] gently. +class VoicesFutureBuilder extends StatefulWidget { + /// The future provider, make sure to return a fresh future + /// each time it is called, the widget takes care of caching + /// the future internally. + final VoicesFutureProvider future; + + /// The builder called to build a child + /// when the [future] finishes successfully. + final VoicesFutureDataBuilder dataBuilder; + + /// The builder called to build a child + /// when the [future] finishes with an error. + /// + /// If not provided then [VoicesErrorIndicator] is used instead. + final VoicesFutureErrorBuilder errorBuilder; + + /// The builder called to build a child + /// when the [future] hasn't finished yet. + /// + /// If not provided then a centered [VoicesCircularProgressIndicator] + /// is used instead. + final WidgetBuilder loaderBuilder; + + /// The minimum duration during which the loader state is shown. + /// + /// It is useful to delay a future which finishes in a split second + /// as this results in jumpy UI, not giving the user enough time to see + /// the loader state before data or error states are shown. + /// + /// Pass [Duration.zero] to disable it. + final Duration minimumDelay; + + const VoicesFutureBuilder({ + super.key, + required this.future, + required this.dataBuilder, + this.errorBuilder = _defaultErrorBuilder, + this.loaderBuilder = _defaultLoaderBuilder, + this.minimumDelay = const Duration(milliseconds: 300), + }); + + @override + State createState() => _VoicesFutureBuilderState(); +} + +class _VoicesFutureBuilderState + extends State> { + Future? _future; + + @override + void initState() { + super.initState(); + + // ignore: discarded_futures + _future = _makeDelayedFuture(); + } + + @override + void dispose() { + _future = null; + super.dispose(); + } + + @override + void didUpdateWidget(VoicesFutureBuilder oldWidget) { + super.didUpdateWidget(oldWidget); + + if (widget.future != oldWidget.future) { + // ignore: discarded_futures + _future = _makeDelayedFuture(); + } + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: _future, + builder: (context, snapshot) { + if (snapshot.hasError) { + return widget.errorBuilder(context, snapshot.error, _onRetry); + } + + final data = snapshot.data; + if (data == null) { + return widget.loaderBuilder(context); + } + + return widget.dataBuilder(context, data, _onRetry); + }, + ); + } + + void _onRetry() { + setState(() { + // ignore: discarded_futures + _future = _makeDelayedFuture(); + }); + } + + Future _makeDelayedFuture() async { + return widget.future().withMinimumDelay(widget.minimumDelay); + } +} + +Widget _defaultErrorBuilder( + BuildContext context, + Object? error, + VoidCallback onRetry, +) { + return _Error(onRetry: onRetry); +} + +Widget _defaultLoaderBuilder(BuildContext context) { + return const _Loader(); +} + +class _Error extends StatelessWidget { + final VoidCallback onRetry; + + const _Error({required this.onRetry}); + + @override + Widget build(BuildContext context) { + return VoicesErrorIndicator( + message: context.l10n.somethingWentWrong, + onRetry: onRetry, + ); + } +} + +class _Loader extends StatelessWidget { + const _Loader(); + + @override + Widget build(BuildContext context) { + return const Center(child: VoicesCircularProgressIndicator()); + } +} diff --git a/catalyst_voices/lib/widgets/indicators/voices_error_indicator.dart b/catalyst_voices/lib/widgets/indicators/voices_error_indicator.dart new file mode 100644 index 0000000000..b34675fe93 --- /dev/null +++ b/catalyst_voices/lib/widgets/indicators/voices_error_indicator.dart @@ -0,0 +1,59 @@ +import 'package:catalyst_voices/widgets/widgets.dart'; +import 'package:catalyst_voices_assets/catalyst_voices_assets.dart'; +import 'package:catalyst_voices_brands/catalyst_voices_brands.dart'; +import 'package:catalyst_voices_localization/catalyst_voices_localization.dart'; +import 'package:flutter/material.dart'; + +/// A generic error state with optional retry button. +class VoicesErrorIndicator extends StatelessWidget { + /// The description of the error. + final String message; + + /// The callback called when refresh button is tapped. + /// + /// If null then retry button is hidden. + final VoidCallback? onRetry; + + const VoicesErrorIndicator({ + super.key, + required this.message, + this.onRetry, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16), + decoration: BoxDecoration( + border: Border.all( + color: Theme.of(context).colors.outlineBorderVariant!, + width: 1, + ), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + VoicesAssets.icons.exclamation.buildIcon( + color: Theme.of(context).colors.iconsError, + size: 20, + ), + const SizedBox(width: 10), + Text( + message, + style: Theme.of(context).textTheme.titleSmall?.copyWith(height: 1), + ), + if (onRetry != null) ...[ + const SizedBox(width: 10), + VoicesTextButton( + leading: VoicesAssets.icons.refresh.buildIcon(), + onTap: onRetry, + child: Text(context.l10n.retry), + ), + ], + ], + ), + ); + } +} diff --git a/catalyst_voices/lib/widgets/widgets.dart b/catalyst_voices/lib/widgets/widgets.dart index ff32170b6e..0f2fe87976 100644 --- a/catalyst_voices/lib/widgets/widgets.dart +++ b/catalyst_voices/lib/widgets/widgets.dart @@ -32,6 +32,7 @@ export 'headers/section_header.dart'; export 'headers/segment_header.dart'; export 'indicators/process_progress_indicator.dart'; export 'indicators/voices_circular_progress_indicator.dart'; +export 'indicators/voices_error_indicator.dart'; export 'indicators/voices_linear_progress_indicator.dart'; export 'indicators/voices_no_internet_connection_banner.dart'; export 'indicators/voices_password_strength_indicator.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/wallet_link_controller.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/wallet_link_controller.dart index 1ab1279649..8917312fb4 100644 --- a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/wallet_link_controller.dart +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/controllers/wallet_link_controller.dart @@ -1,11 +1,25 @@ +import 'package:catalyst_cardano/catalyst_cardano.dart'; import 'package:catalyst_voices_blocs/src/registration/registration_navigator.dart'; import 'package:catalyst_voices_blocs/src/registration/registration_state.dart'; import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; +import 'package:flutter/foundation.dart'; +import 'package:result_type/result_type.dart'; -abstract interface class WalletLinkController {} +// ignore: one_member_abstracts +abstract interface class WalletLinkController { + /// A value listenable with available cardano wallets. + ValueListenable, Exception>?> get cardanoWallets; + + /// Refreshes the [cardanoWallets]. + Future refreshCardanoWallets(); +} final class RegistrationWalletLinkController implements WalletLinkController, RegistrationNavigator { + final ValueNotifier, Exception>?> _wallets = + ValueNotifier(null); + WalletLinkStage _stage; RegistrationWalletLinkController({ @@ -57,4 +71,21 @@ final class RegistrationWalletLinkController return previousStep; } + + @override + ValueListenable, Exception>?> get cardanoWallets => + _wallets; + + @override + Future refreshCardanoWallets() async { + try { + _wallets.value = null; + + final wallets = + await CatalystCardano.instance.getWallets().withMinimumDelay(); + _wallets.value = Success(wallets); + } on Exception catch (error) { + _wallets.value = Failure(error); + } + } } diff --git a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_bloc.dart b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_bloc.dart index f1466c29ef..c03585012a 100644 --- a/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_bloc.dart +++ b/catalyst_voices/packages/catalyst_voices_blocs/lib/src/registration/registration_bloc.dart @@ -1,10 +1,13 @@ +import 'package:catalyst_cardano/catalyst_cardano.dart'; import 'package:catalyst_voices_blocs/src/registration/controllers/keychain_creation_controller.dart'; import 'package:catalyst_voices_blocs/src/registration/controllers/wallet_link_controller.dart'; import 'package:catalyst_voices_blocs/src/registration/registration_event.dart'; import 'package:catalyst_voices_blocs/src/registration/registration_state.dart'; import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:result_type/result_type.dart'; /// Manages the registration state. final class RegistrationBloc extends Bloc @@ -101,4 +104,12 @@ final class RegistrationBloc extends Bloc WalletLink() => walletLinkPreviousStep(), }; } + + @override + ValueListenable, Exception>?> get cardanoWallets => + _walletLinkController.cardanoWallets; + + @override + Future refreshCardanoWallets() async => + _walletLinkController.refreshCardanoWallets(); } diff --git a/catalyst_voices/packages/catalyst_voices_blocs/pubspec.yaml b/catalyst_voices/packages/catalyst_voices_blocs/pubspec.yaml index e3f11a3fcc..c759c635c1 100644 --- a/catalyst_voices/packages/catalyst_voices_blocs/pubspec.yaml +++ b/catalyst_voices/packages/catalyst_voices_blocs/pubspec.yaml @@ -9,6 +9,9 @@ environment: dependencies: bloc_concurrency: ^0.2.2 + catalyst_cardano: ^0.3.0 + catalyst_cardano_serialization: ^0.4.0 + catalyst_cardano_web: ^0.3.0 catalyst_voices_brands: path: ../catalyst_voices_brands catalyst_voices_models: @@ -17,6 +20,8 @@ dependencies: path: ../catalyst_voices_repositories catalyst_voices_services: path: ../catalyst_voices_services + catalyst_voices_shared: + path: ../catalyst_voices_shared catalyst_voices_view_models: path: ../catalyst_voices_view_models collection: ^1.18.0 diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart index 39521ede2f..fed990d61d 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart @@ -712,6 +712,24 @@ abstract class VoicesLocalizations { /// **'You\'re almost there! This is the final and most important step in your account setup.\n\nWe\'re going to link a Cardano Wallet to your Catalyst Keychain, so you can start collecting Role Keys.\n\nRole Keys allow you to enter new spaces, discover new ways to participate, and unlock new ways to earn rewards.\n\nWe\'ll start with your Voter Key by default. You can decide to add a Proposer Key and Drep key if you want, or you can always add them later.'** String get walletLinkIntroContent; + /// A title in link wallet flow on select wallet screen. + /// + /// In en, this message translates to: + /// **'Select the Cardano wallet to link\nto your Catalyst Keychain.'** + String get walletLinkSelectWalletTitle; + + /// A message (content) in link wallet flow on select wallet screen. + /// + /// In en, this message translates to: + /// **'To complete this action, you\'ll submit a signed transaction to Cardano. There will be an ADA transaction fee.'** + String get walletLinkSelectWalletContent; + + /// Message shown when redirecting to external content that describes which wallets are supported. + /// + /// In en, this message translates to: + /// **'See all supported wallets'** + String get seeAllSupportedWallets; + /// No description provided for @accountCreationCreate. /// /// In en, this message translates to: @@ -855,6 +873,24 @@ abstract class VoicesLocalizations { /// In en, this message translates to: /// **'Back'** String get back; + + /// Retry action when something goes wrong. + /// + /// In en, this message translates to: + /// **'Retry'** + String get retry; + + /// Error description when something goes wrong. + /// + /// In en, this message translates to: + /// **'Something went wrong.'** + String get somethingWentWrong; + + /// A description when no wallet extension was found. + /// + /// In en, this message translates to: + /// **'No wallet found.'** + String get noWalletFound; } class _VoicesLocalizationsDelegate extends LocalizationsDelegate { diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart index 6de0f1cd16..850a07bbe0 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart @@ -370,6 +370,15 @@ class VoicesLocalizationsEn extends VoicesLocalizations { @override String get walletLinkIntroContent => 'You\'re almost there! This is the final and most important step in your account setup.\n\nWe\'re going to link a Cardano Wallet to your Catalyst Keychain, so you can start collecting Role Keys.\n\nRole Keys allow you to enter new spaces, discover new ways to participate, and unlock new ways to earn rewards.\n\nWe\'ll start with your Voter Key by default. You can decide to add a Proposer Key and Drep key if you want, or you can always add them later.'; + @override + String get walletLinkSelectWalletTitle => 'Select the Cardano wallet to link\nto your Catalyst Keychain.'; + + @override + String get walletLinkSelectWalletContent => 'To complete this action, you\'ll submit a signed transaction to Cardano. There will be an ADA transaction fee.'; + + @override + String get seeAllSupportedWallets => 'See all supported wallets'; + @override String get accountCreationCreate => 'Create a new 
Catalyst Keychain'; @@ -441,4 +450,13 @@ class VoicesLocalizationsEn extends VoicesLocalizations { @override String get back => 'Back'; + + @override + String get retry => 'Retry'; + + @override + String get somethingWentWrong => 'Something went wrong.'; + + @override + String get noWalletFound => 'No wallet found.'; } diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart index ccef81b46c..795fd9e380 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart @@ -370,6 +370,15 @@ class VoicesLocalizationsEs extends VoicesLocalizations { @override String get walletLinkIntroContent => 'You\'re almost there! This is the final and most important step in your account setup.\n\nWe\'re going to link a Cardano Wallet to your Catalyst Keychain, so you can start collecting Role Keys.\n\nRole Keys allow you to enter new spaces, discover new ways to participate, and unlock new ways to earn rewards.\n\nWe\'ll start with your Voter Key by default. You can decide to add a Proposer Key and Drep key if you want, or you can always add them later.'; + @override + String get walletLinkSelectWalletTitle => 'Select the Cardano wallet to link\nto your Catalyst Keychain.'; + + @override + String get walletLinkSelectWalletContent => 'To complete this action, you\'ll submit a signed transaction to Cardano. There will be an ADA transaction fee.'; + + @override + String get seeAllSupportedWallets => 'See all supported wallets'; + @override String get accountCreationCreate => 'Create a new 
Catalyst Keychain'; @@ -441,4 +450,13 @@ class VoicesLocalizationsEs extends VoicesLocalizations { @override String get back => 'Back'; + + @override + String get retry => 'Retry'; + + @override + String get somethingWentWrong => 'Something went wrong.'; + + @override + String get noWalletFound => 'No wallet found.'; } diff --git a/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb b/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb index c9bad9cabd..760be780be 100644 --- a/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb +++ b/catalyst_voices/packages/catalyst_voices_localization/lib/l10n/intl_en.arb @@ -458,6 +458,18 @@ "@walletLinkIntroContent": { "description": "A message (content) in link wallet flow on intro screen." }, + "walletLinkSelectWalletTitle": "Select the Cardano wallet to link\nto your Catalyst Keychain.", + "@walletLinkSelectWalletTitle": { + "description": "A title in link wallet flow on select wallet screen." + }, + "walletLinkSelectWalletContent": "To complete this action, you'll submit a signed transaction to Cardano. There will be an ADA transaction fee.", + "@walletLinkSelectWalletContent": { + "description": "A message (content) in link wallet flow on select wallet screen." + }, + "seeAllSupportedWallets": "See all supported wallets", + "@seeAllSupportedWallets": { + "description": "Message shown when redirecting to external content that describes which wallets are supported." + }, "accountCreationCreate": "Create a new \u2028Catalyst Keychain", "accountCreationRecover": "Recover your\u2028Catalyst Keychain", "accountCreationOnThisDevice": "On this device", @@ -520,5 +532,17 @@ "back": "Back", "@back": { "description": "For example in button that goes to previous stage of registration" + }, + "retry": "Retry", + "@retry": { + "description": "Retry action when something goes wrong." + }, + "somethingWentWrong": "Something went wrong.", + "@somethingWentWrong": { + "description": "Error description when something goes wrong." + }, + "noWalletFound": "No wallet found.", + "@noWalletFound": { + "description": "A description when no wallet extension was found." } } \ No newline at end of file diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/catalyst_voices_shared.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/catalyst_voices_shared.dart index b0ad652197..f47e0ebb5a 100644 --- a/catalyst_voices/packages/catalyst_voices_shared/lib/src/catalyst_voices_shared.dart +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/catalyst_voices_shared.dart @@ -9,5 +9,6 @@ export 'responsive/responsive_builder.dart'; export 'responsive/responsive_child.dart'; export 'responsive/responsive_padding.dart'; export 'utils/date_time_ext.dart'; +export 'utils/future_ext.dart'; export 'utils/iterable_ext.dart'; export 'utils/typedefs.dart'; diff --git a/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/future_ext.dart b/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/future_ext.dart new file mode 100644 index 0000000000..dd215d0545 --- /dev/null +++ b/catalyst_voices/packages/catalyst_voices_shared/lib/src/utils/future_ext.dart @@ -0,0 +1,16 @@ +extension FutureExt on Future { + /// The minimum loading delay after which the state changes from loading + /// to success/failure will not be perceived too jumpy. + /// + /// Use it to avoid showing a loading state for split second. + static const Duration minimumDelay = Duration(milliseconds: 300); + + /// Returns the result of awaiting the [Future] + /// but applies [delay] to it or [minimumDelay] if [delay] is null. + Future withMinimumDelay([Duration delay = minimumDelay]) async { + final delayed = Future.delayed(delay); + final result = await this; + await delayed; + return result; + } +} diff --git a/catalyst_voices/packages/catalyst_voices_view_models/pubspec.yaml b/catalyst_voices/packages/catalyst_voices_view_models/pubspec.yaml index 5268e3c258..0880d5c290 100644 --- a/catalyst_voices/packages/catalyst_voices_view_models/pubspec.yaml +++ b/catalyst_voices/packages/catalyst_voices_view_models/pubspec.yaml @@ -8,6 +8,9 @@ environment: flutter: ">=3.24.1" dependencies: + catalyst_cardano: ^0.3.0 + catalyst_cardano_serialization: ^0.4.0 + catalyst_cardano_web: ^0.3.0 equatable: ^2.0.5 flutter: sdk: flutter diff --git a/catalyst_voices/pubspec.yaml b/catalyst_voices/pubspec.yaml index e18d1bbbaf..f40af71fe3 100644 --- a/catalyst_voices/pubspec.yaml +++ b/catalyst_voices/pubspec.yaml @@ -46,6 +46,7 @@ dependencies: go_router: ^14.0.2 google_fonts: ^6.2.1 intl: ^0.19.0 + result_type: ^0.2.0 sentry_flutter: ^8.8.0 url_launcher: ^6.2.2 url_strategy: ^0.3.0 diff --git a/catalyst_voices/test/widgets/common/infrastructure/voices_future_builder_test.dart b/catalyst_voices/test/widgets/common/infrastructure/voices_future_builder_test.dart new file mode 100644 index 0000000000..9a7ff45d03 --- /dev/null +++ b/catalyst_voices/test/widgets/common/infrastructure/voices_future_builder_test.dart @@ -0,0 +1,56 @@ +import 'package:catalyst_voices/widgets/common/infrastructure/voices_future_builder.dart'; +import 'package:catalyst_voices/widgets/indicators/voices_circular_progress_indicator.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../helpers/helpers.dart'; + +void main() { + group(VoicesFutureBuilder, () { + testWidgets('Displays data when future completes successfully', + (tester) async { + await tester.pumpApp( + VoicesFutureBuilder( + future: () async => 'Test Data', + dataBuilder: (_, __, ___) => const Text('Success'), + ), + ); + + // Let the future start + await tester.pump(const Duration(milliseconds: 100)); + + // Shows loading indicator while waiting for data + expect(find.byType(VoicesCircularProgressIndicator), findsOneWidget); + + // Let the future finish + await tester.pump(const Duration(seconds: 1)); + + // Shows success state + expect(find.text('Success'), findsOneWidget); + }); + + testWidgets('Displays error when future completes with an error', + (tester) async { + // Act + await tester.pumpApp( + VoicesFutureBuilder( + future: () async => throw Exception('Error'), + dataBuilder: (_, __, ___) => const SizedBox.shrink(), + errorBuilder: (_, __, ___) => const Text('Error Occurred'), + ), + ); + + // Let the future start + await tester.pump(const Duration(milliseconds: 100)); + + // Shows loading indicator while waiting for data + expect(find.byType(VoicesCircularProgressIndicator), findsOneWidget); + + // Let the future finish + await tester.pump(const Duration(seconds: 1)); + + // Shows error state + expect(find.text('Error Occurred'), findsOneWidget); + }); + }); +} diff --git a/catalyst_voices/test/widgets/indicators/voices_status_indicator_test.dart b/catalyst_voices/test/widgets/indicators/voices_status_indicator_test.dart index bd7469e25f..a9df84e169 100644 --- a/catalyst_voices/test/widgets/indicators/voices_status_indicator_test.dart +++ b/catalyst_voices/test/widgets/indicators/voices_status_indicator_test.dart @@ -11,7 +11,7 @@ void main() { // Arrange const status = 'QR VERIFIED'; const title = 'Your QR code verified successfully'; - const body = 'You can now use your QR-code 
to login into Catalyst.'; + const body = 'You can now use your QR-code to login into Catalyst.'; const colors = VoicesColorScheme.optional( successContainer: Colors.green, diff --git a/catalyst_voices/uikit_example/lib/examples/voices_indicators_example.dart b/catalyst_voices/uikit_example/lib/examples/voices_indicators_example.dart index 60c4b6c294..684f897298 100644 --- a/catalyst_voices/uikit_example/lib/examples/voices_indicators_example.dart +++ b/catalyst_voices/uikit_example/lib/examples/voices_indicators_example.dart @@ -80,6 +80,19 @@ class VoicesIndicatorsExample extends StatelessWidget { VoicesCircularProgressIndicator(value: 0.75, showTrack: false), ], ), + const Text('Generic error indicator'), + Row( + children: [ + VoicesErrorIndicator( + message: 'Something went wrong', + onRetry: () {}, + ), + const SizedBox(width: 16), + const VoicesErrorIndicator( + message: 'Something went wrong', + ), + ], + ), const Text('No Internet Connection Banner'), const NoInternetConnectionBanner(), const Text('Password strength indicator'),