Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cat-voices): registration seed phrase step #897

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
import 'package:catalyst_voices/pages/registration/create_keychain/stage/stages.dart';
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
import 'package:flutter/material.dart';

class CreateKeychainPanel extends StatelessWidget {
final CreateKeychainStage stage;
final SeedPhraseState seedPhraseState;

const CreateKeychainPanel({
super.key,
required this.stage,
required this.seedPhraseState,
});

@override
Widget build(BuildContext context) {
return switch (stage) {
CreateKeychainStage.splash => const SplashPanel(),
CreateKeychainStage.instructions => const InstructionsPanel(),
CreateKeychainStage.seedPhrase ||
CreateKeychainStage.seedPhrase => SeedPhrasePanel(
seedPhrase: seedPhraseState.seedPhrase,
isStoreSeedPhraseConfirmed: seedPhraseState.isStoredConfirmed,
isNextEnabled: seedPhraseState.isStoredConfirmed,
),
CreateKeychainStage.checkSeedPhraseInstructions ||
CreateKeychainStage.checkSeedPhrase ||
CreateKeychainStage.checkSeedPhraseResult ||
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import 'package:catalyst_voices/widgets/widgets.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 SeedPhrasePanel extends StatelessWidget {
final SeedPhrase? seedPhrase;
final bool isStoreSeedPhraseConfirmed;
final bool isNextEnabled;

const SeedPhrasePanel({
super.key,
required this.seedPhrase,
required this.isStoreSeedPhraseConfirmed,
required this.isNextEnabled,
});

@override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: _Body(
words: seedPhrase?.mnemonicWords ?? [],
onDownloadTap: _downloadSeedPhrase,
),
),
const SizedBox(height: 10),
_SeedPhraseStoredConfirmation(isConfirmed: isStoreSeedPhraseConfirmed),
const SizedBox(height: 10),
_Navigation(isNextEnabled: isNextEnabled),
],
);
}

Future<void> _downloadSeedPhrase() async {}
}

class _Body extends StatelessWidget {
final List<String> words;
final VoidCallback? onDownloadTap;

const _Body({
required this.words,
this.onDownloadTap,
});

@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
SeedPhrasesViewer(words: words),
const SizedBox(height: 10),
VoicesTextButton(
onTap: onDownloadTap,
child: Text(context.l10n.createKeychainSeedPhraseDownload),
),
],
),
);
}
}

class _SeedPhraseStoredConfirmation extends StatelessWidget {
final bool isConfirmed;

const _SeedPhraseStoredConfirmation({
this.isConfirmed = false,
});

@override
Widget build(BuildContext context) {
return VoicesCheckbox(
value: isConfirmed,
label: Text(context.l10n.createKeychainSeedPhraseStoreConfirmation),
onChanged: (value) {
final event = SeedPhraseStoreConfirmationEvent(value: value);
RegistrationBloc.of(context).add(event);
},
);
}
}

class _Navigation extends StatelessWidget {
final bool isNextEnabled;

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

@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: isNextEnabled
? () => RegistrationBloc.of(context).add(const NextStepEvent())
: null,
),
),
],
);
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export 'instructions_panel.dart';
export 'seed_phrase_panel.dart';
export 'splash_panel.dart';
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ class _Footer extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Offstage(
offstage: progress == null,
Visibility.maintain(
visible: progress != null,
child: VoicesLinearProgressIndicator(value: progress ?? 0),
),
const SizedBox(height: 10),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'package:catalyst_voices/pages/registration/pictures/task_picture.dart';
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
import 'package:flutter/material.dart';

class KeychainPicture extends StatelessWidget {
final TaskPictureType type;

const KeychainPicture({
super.key,
this.type = TaskPictureType.normal,
});

@override
Widget build(BuildContext context) {
return TaskPicture(
child: TaskPictureIconBox(
type: type,
child: VoicesAssets.images.keychain.buildIcon(allowSize: false),
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import 'package:catalyst_voices/pages/registration/pictures/task_picture.dart';
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
import 'package:flutter/material.dart';

const _wordsPerColumnCount = 6;
const _firstHighlightIndex = [0];
const _allHighlightIndex = [0, 1, 2, 3, 4, 5];

class SeedPhrasePicture extends StatelessWidget {
final bool indicateSelection;
final TaskPictureType type;

const SeedPhrasePicture({
super.key,
this.indicateSelection = false,
this.type = TaskPictureType.normal,
});

@override
Widget build(BuildContext context) {
return TaskPicture(
child: TaskPictureIconBox(
type: type,
child: LayoutBuilder(
builder: (context, constraints) {
return Padding(
padding: EdgeInsets.all(constraints.maxWidth * 0.176),
child: Stack(
alignment: Alignment.center,
children: [
_Words(
indicateSelection: indicateSelection,
type: type,
),
if (type == TaskPictureType.success)
VoicesAssets.icons.check.buildIcon(size: 48),
if (type == TaskPictureType.error)
VoicesAssets.icons.x.buildIcon(size: 48),
],
),
);
},
),
),
);
}
}

class _Words extends StatelessWidget {
const _Words({
required this.indicateSelection,
required this.type,
});

final bool indicateSelection;
final TaskPictureType type;

@override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: _WordsColumn(
key: const ValueKey('LeftColumnKey'),
highlightIndexes: switch (type) {
TaskPictureType.normal when indicateSelection =>
_firstHighlightIndex,
TaskPictureType.normal => const [],
TaskPictureType.success ||
TaskPictureType.error =>
_allHighlightIndex,
},
count: _wordsPerColumnCount,
type: type,
),
),
const SizedBox(width: 6),
Expanded(
child: _WordsColumn(
key: const ValueKey('RightColumnKey'),
highlightIndexes: switch (type) {
TaskPictureType.normal => const [],
TaskPictureType.success ||
TaskPictureType.error =>
_allHighlightIndex,
},
count: _wordsPerColumnCount,
type: type,
),
),
],
);
}
}

class _WordsColumn extends StatelessWidget {
final List<int> highlightIndexes;
final int count;
final TaskPictureType type;

const _WordsColumn({
super.key,
this.highlightIndexes = const [],
this.count = 6,
required this.type,
});

@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: List.generate(
count,
(index) {
return _Word(
isSelected: highlightIndexes.contains(index),
type: type,
);
},
),
);
}
}

class _Word extends StatelessWidget {
final bool isSelected;
final TaskPictureType type;

const _Word({
this.isSelected = false,
required this.type,
});

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final color = type.foregroundColor(theme, isHighlight: isSelected);

return AnimatedContainer(
height: 6.72,
duration: const Duration(milliseconds: 200),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(1.12),
),
);
}
}
Loading
Loading