Skip to content

Commit

Permalink
feat(cat-voices): mocked recover wallet (#990)
Browse files Browse the repository at this point in the history
* chore: extract common widgets from wallet link flow

* fix: seed phrase scrollbar scroll controller

* chore: wip

* feat: recovered account summary panel

* feat: finish recovery results panel

* fix: use bytes ShelleyAddress

* fix: typo

* chore: PR review fixes

* chore: update recoverCardanoWalletDetails note about returned type

* chore: use ShelleyAddress.fromBech32 for testNet address
  • Loading branch information
damian-molinski authored Oct 11, 2024
1 parent c77bdcd commit 43997ae
Show file tree
Hide file tree
Showing 26 changed files with 809 additions and 256 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:catalyst_voices/pages/registration/recover/seed_phrase/account_details_panel.dart';
import 'package:catalyst_voices/pages/registration/recover/seed_phrase/seed_phrase_input_panel.dart';
import 'package:catalyst_voices/pages/registration/recover/seed_phrase/seed_phrase_instructions_panel.dart';
import 'package:catalyst_voices/pages/registration/widgets/placeholder_panel.dart';
Expand All @@ -18,7 +19,7 @@ class RecoverSeedPhrasePanel extends StatelessWidget {
RecoverSeedPhraseStage.seedPhraseInstructions =>
const SeedPhraseInstructionsPanel(),
RecoverSeedPhraseStage.seedPhrase => const SeedPhraseInputPanel(),
RecoverSeedPhraseStage.linkedWallet => const PlaceholderPanel(),
RecoverSeedPhraseStage.accountDetails => const AccountDetailsPanel(),
RecoverSeedPhraseStage.unlockPasswordInstructions =>
const PlaceholderPanel(),
RecoverSeedPhraseStage.unlockPassword => const PlaceholderPanel(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import 'dart:async';

import 'package:catalyst_voices/pages/registration/recover/bloc_recover_builder.dart';
import 'package:catalyst_voices/pages/registration/widgets/wallet_connection_status.dart';
import 'package:catalyst_voices/pages/registration/widgets/wallet_summary.dart';
import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart';
import 'package:catalyst_voices/widgets/buttons/voices_text_button.dart';
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_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_view_models/catalyst_voices_view_models.dart';
import 'package:flutter/material.dart';
import 'package:result_type/result_type.dart';

class AccountDetailsPanel extends StatelessWidget {
const AccountDetailsPanel({
super.key,
});

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final textColor = theme.colors.textOnPrimaryLevel1;

return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 24),
Text(
context.l10n.recoveryAccountTitle,
style: theme.textTheme.titleMedium?.copyWith(color: textColor),
),
const SizedBox(height: 24),
Expanded(
child: SingleChildScrollView(
child: _BlocAccountSummery(
onRetry: () => unawaited(_retryAccountRestore(context)),
),
),
),
const SizedBox(height: 24),
const _BlocNavigation(),
],
);
}

Future<void> _retryAccountRestore(BuildContext context) async {
final recover = RegistrationCubit.of(context).recover;
await recover.recoverAccount();
}
}

class _BlocAccountSummery extends StatelessWidget {
final VoidCallback? onRetry;

const _BlocAccountSummery({
this.onRetry,
});

@override
Widget build(BuildContext context) {
return BlocRecoverBuilder<Result<AccountSummaryData, LocalizedException>?>(
selector: (state) => state.accountDetails,
builder: (context, state) {
return switch (state) {
Success<AccountSummaryData, LocalizedException>(:final value) =>
_RecoveredAccountSummary(
walletConnection: value.walletConnection,
walletSummary: value.walletSummary,
),
Failure<AccountSummaryData, LocalizedException>(:final value) =>
_RecoverAccountFailure(
exception: value,
onRetry: onRetry,
),
_ => const Center(child: VoicesCircularProgressIndicator()),
};
},
);
}
}

class _RecoveredAccountSummary extends StatelessWidget {
final WalletConnectionData walletConnection;
final WalletSummaryData walletSummary;

const _RecoveredAccountSummary({
required this.walletConnection,
required this.walletSummary,
});

@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
WalletConnectionStatus(
icon: walletConnection.icon,
name: walletConnection.name,
isConnected: walletConnection.isConnected,
),
const SizedBox(height: 8),
const _RecoverStatusText(),
const SizedBox(height: 24),
WalletSummary(
balance: walletSummary.balance,
address: walletSummary.address,
clipboardAddress: walletSummary.clipboardAddress,
),
],
);
}
}

class _RecoverAccountFailure extends StatelessWidget {
final LocalizedException exception;
final VoidCallback? onRetry;

const _RecoverAccountFailure({
required this.exception,
this.onRetry,
});

@override
Widget build(BuildContext context) {
return VoicesErrorIndicator(
message: exception.message(context),
onRetry: onRetry,
);
}
}

class _RecoverStatusText extends StatelessWidget {
const _RecoverStatusText();

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final textColor = theme.colors.textOnPrimaryLevel1;

return Text(
context.l10n.recoveryAccountSuccessTitle,
style: theme.textTheme.titleMedium?.copyWith(color: textColor),
);
}
}

class _BlocNavigation extends StatelessWidget {
const _BlocNavigation();

@override
Widget build(BuildContext context) {
return BlocRecoverBuilder<bool>(
selector: (state) => state.isAccountSummaryNextEnabled,
builder: (context, state) {
return _Navigation(
isNextEnabled: state,
);
},
);
}
}

class _Navigation extends StatelessWidget {
final bool isNextEnabled;

const _Navigation({
this.isNextEnabled = false,
});

@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
VoicesFilledButton(
onTap: isNextEnabled
? () => RegistrationCubit.of(context).nextStep()
: null,
child: Text(context.l10n.recoveryAccountDetailsAction),
),
const SizedBox(height: 10),
VoicesTextButton(
onTap: () => RegistrationCubit.of(context).previousStep(),
child: Text(context.l10n.back),
),
],
);
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:async';

import 'package:catalyst_voices/pages/registration/recover/bloc_recover_builder.dart';
import 'package:catalyst_voices/pages/registration/widgets/registration_stage_message.dart';
import 'package:catalyst_voices/pages/registration/widgets/registration_stage_navigation.dart';
Expand Down Expand Up @@ -59,11 +61,22 @@ class _SeedPhraseInputPanelState extends State<SeedPhraseInputPanel> {
onResetTap: _resetControllerWords,
),
const SizedBox(height: 12),
const _BlocNavigation(),
_BlocNavigation(
onNextTap: _recoverAccountAndGoNextStage,
),
],
);
}

void _recoverAccountAndGoNextStage() {
final registration = RegistrationCubit.of(context);

// Note. success or failure will be shown in next stage
unawaited(registration.recover.recoverAccount());

registration.nextStep();
}

Future<void> _uploadSeedPhrase() async {
// TODO(damian-molinski): Import implementation for KeychainCreation
}
Expand Down Expand Up @@ -105,14 +118,19 @@ class _BlocSeedPhraseField extends StatelessWidget {
}

class _BlocNavigation extends StatelessWidget {
const _BlocNavigation();
final VoidCallback onNextTap;

const _BlocNavigation({
required this.onNextTap,
});

@override
Widget build(BuildContext context) {
return BlocRecoverBuilder(
selector: (state) => state.isSeedPhraseValid,
builder: (context, state) {
return RegistrationBackNextNavigation(
onNextTap: onNextTap,
isNextEnabled: state,
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ class _RegistrationPicture extends StatelessWidget {
return switch (stage) {
RecoverSeedPhraseStage.seedPhraseInstructions ||
RecoverSeedPhraseStage.seedPhrase ||
RecoverSeedPhraseStage.linkedWallet =>
RecoverSeedPhraseStage.accountDetails =>
const KeychainPicture(),
RecoverSeedPhraseStage.unlockPasswordInstructions ||
RecoverSeedPhraseStage.unlockPassword =>
Expand Down
Loading

0 comments on commit 43997ae

Please sign in to comment.