Skip to content

Commit

Permalink
feat(cat-voices): account creation type dialog (#881)
Browse files Browse the repository at this point in the history
* wip

* feat: localization + rename dialog

* fix: missing comma

* refactor: extract learn more button

---------

Co-authored-by: Dominik Toton <[email protected]>
  • Loading branch information
damian-molinski and dtscalac committed Sep 25, 2024
1 parent e75eab9 commit a14909c
Show file tree
Hide file tree
Showing 8 changed files with 320 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
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_brands/catalyst_voices_brands.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.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<AccountCreateType?> 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();

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

return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 24),
Text(
context.l10n.accountCreationGetStartedTitle,
style: theme.textTheme.titleMedium?.copyWith(
color: theme.colors.textOnPrimaryLevel1,
),
),
const SizedBox(height: 12),
Text(
context.l10n.accountCreationGetStatedDesc,
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colors.textOnPrimaryLevel1,
),
),
const SizedBox(height: 32),
Text(
context.l10n.accountCreationGetStatedWhatNext,
style: theme.textTheme.titleSmall?.copyWith(
color: theme.colors.textOnPrimaryLevel0,
),
),
const SizedBox(height: 24),
Column(
mainAxisSize: MainAxisSize.min,
children: AccountCreateType.values
.map<Widget>((type) {
return _AccountCreateTypeTile(
key: ValueKey(type),
type: type,
onTap: () => Navigator.of(context).pop(type),
);
})
.separatedBy(const SizedBox(height: 12))
.toList(),
),
],
);
}
}

class _AccountCreateTypeTile extends StatelessWidget {
final AccountCreateType type;
final VoidCallback? onTap;

const _AccountCreateTypeTile({
super.key,
required this.type,
this.onTap,
});

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

return ConstrainedBox(
constraints: const BoxConstraints.tightFor(height: 80),
child: Material(
color: theme.colorScheme.primary,
borderRadius: BorderRadius.circular(12),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
children: [
type._icon.buildIcon(
size: 48,
color: theme.colorScheme.onPrimary,
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
type._getTitle(context.l10n),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.titleSmall?.copyWith(
color: theme.colorScheme.onPrimary,
),
),
Text(
type._getSubtitle(context.l10n),
maxLines: 1,
overflow: TextOverflow.clip,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onPrimary,
),
),
],
),
),
],
),
),
),
),
);
}
}
45 changes: 37 additions & 8 deletions catalyst_voices/lib/pages/spaces/spaces_shell_page.dart
Original file line number Diff line number Diff line change
@@ -1,4 +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/spaces/drawer/spaces_drawer.dart';
import 'package:catalyst_voices/widgets/widgets.dart';
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
Expand All @@ -7,7 +8,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class SpacesShellPage extends StatelessWidget {
class SpacesShellPage extends StatefulWidget {
final Space space;
final Widget child;

Expand Down Expand Up @@ -40,6 +41,11 @@ class SpacesShellPage extends StatelessWidget {
required this.child,
});

@override
State<SpacesShellPage> createState() => _SpacesShellPageState();
}

class _SpacesShellPageState extends State<SpacesShellPage> {
@override
Widget build(BuildContext context) {
final sessionBloc = context.watch<SessionBloc>();
Expand All @@ -48,27 +54,50 @@ class SpacesShellPage extends StatelessWidget {

return CallbackShortcuts(
bindings: <ShortcutActivator, VoidCallback>{
for (final entry in _spacesShortcutsActivators.entries)
for (final entry in SpacesShellPage._spacesShortcutsActivators.entries)
entry.value: () => entry.key.go(context),
},
child: Scaffold(
appBar: VoicesAppBar(
leading: isVisitor ? null : const DrawerToggleButton(),
automaticallyImplyLeading: false,
actions: const [
SessionActionHeader(),
SessionStateHeader(),
actions: [
SessionActionHeader(
onGetStartedTap: _showAccountGetStarted,
),
const SessionStateHeader(),
],
),
drawer: isVisitor
? null
: SpacesDrawer(
space: space,
spacesShortcutsActivators: _spacesShortcutsActivators,
space: widget.space,
spacesShortcutsActivators:
SpacesShellPage._spacesShortcutsActivators,
isUnlocked: isUnlocked,
),
body: child,
body: widget.child,
),
);
}

Future<void> _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<void> _showCreateAccountFlow() async {}

Future<void> _showRecoverAccountFlow() async {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ import 'package:flutter_bloc/flutter_bloc.dart';

/// Displays current session action and toggling to next when clicked.
class SessionActionHeader extends StatelessWidget {
const SessionActionHeader({super.key});
final VoidCallback? onGetStartedTap;

const SessionActionHeader({
super.key,
this.onGetStartedTap,
});

@override
Widget build(BuildContext context) {
return BlocBuilder<SessionBloc, SessionState>(
builder: (context, state) {
return switch (state) {
VisitorSessionState() => const _GetStartedButton(),
VisitorSessionState() => _GetStartedButton(onTap: onGetStartedTap),
GuestSessionState() => const _UnlockButton(),
ActiveUserSessionState() => const _LockButton(),
};
Expand All @@ -25,14 +30,16 @@ class SessionActionHeader extends StatelessWidget {
}

class _GetStartedButton extends StatelessWidget {
const _GetStartedButton();
final VoidCallback? onTap;

const _GetStartedButton({
this.onTap,
});

@override
Widget build(BuildContext context) {
return VoicesFilledButton(
onTap: () {
context.read<SessionBloc>().add(const ActiveUserSessionEvent());
},
onTap: onTap,
child: Text(context.l10n.getStarted),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';

/// Manages the user session.
final class SessionBloc extends Bloc<SessionEvent, SessionState> {
SessionBloc()
: super(
const ActiveUserSessionState(
user: User(name: 'Account'),
),
) {
SessionBloc() : super(const VisitorSessionState()) {
on<SessionEvent>(_handleSessionEvent);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,42 @@ abstract class VoicesLocalizations {
/// 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;

/// No description provided for @accountCreationCreate.
///
/// In en, this message translates to:
/// **'Create a new 
Catalyst Keychain'**
String get accountCreationCreate;

/// No description provided for @accountCreationRecover.
///
/// In en, this message translates to:
/// **'Recover your
Catalyst Keychain'**
String get accountCreationRecover;

/// Indicates that created keychain will be stored in this device only
///
/// In en, this message translates to:
/// **'On this device'**
String get accountCreationOnThisDevice;

/// No description provided for @accountCreationGetStartedTitle.
///
/// In en, this message translates to:
/// **'Welcome to Catalyst'**
String get accountCreationGetStartedTitle;

/// No description provided for @accountCreationGetStatedDesc.
///
/// In en, this message translates to:
/// **'If you already have a Catalyst keychain you can restore it on this device, or you can create a new Catalyst Keychain.'**
String get accountCreationGetStatedDesc;

/// No description provided for @accountCreationGetStatedWhatNext.
///
/// In en, this message translates to:
/// **'What do you want to do?'**
String get accountCreationGetStatedWhatNext;
}

class _VoicesLocalizationsDelegate extends LocalizationsDelegate<VoicesLocalizations> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,4 +363,22 @@ class VoicesLocalizationsEn extends VoicesLocalizations {

@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.';

@override
String get accountCreationCreate => 'Create a new 
Catalyst Keychain';

@override
String get accountCreationRecover => 'Recover your
Catalyst Keychain';

@override
String get accountCreationOnThisDevice => 'On this device';

@override
String get accountCreationGetStartedTitle => 'Welcome to Catalyst';

@override
String get accountCreationGetStatedDesc => 'If you already have a Catalyst keychain you can restore it on this device, or you can create a new Catalyst Keychain.';

@override
String get accountCreationGetStatedWhatNext => 'What do you want to do?';
}
Loading

0 comments on commit a14909c

Please sign in to comment.