recovery phrase view and mnemonic table view desktop ui layout

This commit is contained in:
julian 2022-09-18 12:26:21 -06:00
parent f63acf4fa7
commit 98252ac4fb
7 changed files with 297 additions and 188 deletions

View file

@ -1,3 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -8,6 +10,8 @@ import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart'; import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart';
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart'; import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_warning_view/new_wallet_recovery_phrase_warning_view.dart';
import 'package:stackwallet/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart'; import 'package:stackwallet/pages/add_wallet_views/verify_recovery_phrase_view/verify_recovery_phrase_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart';
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/exit_to_my_stack_button.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/coins/manager.dart'; import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
@ -17,6 +21,8 @@ import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart'; import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
class NewWalletRecoveryPhraseView extends ConsumerStatefulWidget { class NewWalletRecoveryPhraseView extends ConsumerStatefulWidget {
@ -46,12 +52,14 @@ class _NewWalletRecoveryPhraseViewState
late Manager _manager; late Manager _manager;
late List<String> _mnemonic; late List<String> _mnemonic;
late ClipboardInterface _clipboardInterface; late ClipboardInterface _clipboardInterface;
late final bool isDesktop;
@override @override
void initState() { void initState() {
_manager = widget.manager; _manager = widget.manager;
_mnemonic = widget.mnemonic; _mnemonic = widget.mnemonic;
_clipboardInterface = widget.clipboardInterface; _clipboardInterface = widget.clipboardInterface;
isDesktop = Platform.isMacOS || Platform.isWindows || Platform.isLinux;
super.initState(); super.initState();
} }
@ -67,144 +75,228 @@ class _NewWalletRecoveryPhraseViewState
await _manager.exitCurrentWallet(); await _manager.exitCurrentWallet();
} }
Future<void> _copy() async {
final words = await _manager.mnemonic;
await _clipboardInterface.setData(ClipboardData(text: words.join(" ")));
unawaited(showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
));
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType"); debugPrint("BUILD: $runtimeType");
return WillPopScope( return WillPopScope(
onWillPop: onWillPop, onWillPop: onWillPop,
child: Scaffold( child: MasterScaffold(
appBar: AppBar( isDesktop: isDesktop,
leading: AppBarBackButton( appBar: isDesktop
onPressed: () async { ? DesktopAppBar(
await delete(); isCompactHeight: false,
leading: AppBarBackButton(
onPressed: () async {
await delete();
if (mounted) { if (mounted) {
Navigator.of(context).popUntil( Navigator.of(context).popUntil(
ModalRoute.withName( ModalRoute.withName(
NewWalletRecoveryPhraseWarningView.routeName, NewWalletRecoveryPhraseWarningView.routeName,
),
);
}
// Navigator.of(context).pop();
},
), ),
); trailing: ExitToMyStackButton(
} onPressed: () async {
// Navigator.of(context).pop(); await delete();
}, if (mounted) {
), Navigator.of(context).popUntil(
actions: [ ModalRoute.withName(DesktopHomeView.routeName),
Padding( );
padding: const EdgeInsets.all(10), }
child: AspectRatio( },
aspectRatio: 1,
child: AppBarIconButton(
color: CFColors.almostWhite,
shadows: const [],
icon: SvgPicture.asset(
Assets.svg.copy,
width: 24,
height: 24,
), ),
onPressed: () async { )
final words = await _manager.mnemonic; : AppBar(
await _clipboardInterface leading: AppBarBackButton(
.setData(ClipboardData(text: words.join(" "))); onPressed: () async {
showFloatingFlushBar( await delete();
type: FlushBarType.info,
message: "Copied to clipboard",
iconAsset: Assets.svg.copy,
context: context,
);
},
),
),
),
],
),
body: Container(
color: CFColors.almostWhite,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(
height: 4,
),
Text(
_manager.walletName,
textAlign: TextAlign.center,
style: STextStyles.label.copyWith(
fontSize: 12,
),
),
const SizedBox(
height: 4,
),
Text(
"Recovery Phrase",
textAlign: TextAlign.center,
style: STextStyles.pageTitleH1,
),
const SizedBox(
height: 16,
),
Container(
decoration: BoxDecoration(
color: CFColors.white,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Text(
"Please write down your recovery phrase in the correct order and save it to keep your funds secure. You will also be asked to verify the words on the next screen.",
style: STextStyles.label.copyWith(
color: CFColors.stackAccent,
),
),
),
),
const SizedBox(
height: 8,
),
Expanded(
child: SingleChildScrollView(
child: MnemonicTable(
words: _mnemonic,
),
),
),
const SizedBox(
height: 16,
),
TextButton(
onPressed: () async {
final int next = Random().nextInt(_mnemonic.length);
ref
.read(verifyMnemonicWordIndexStateProvider.state)
.update((state) => next);
ref if (mounted) {
.read(verifyMnemonicCorrectWordStateProvider.state) Navigator.of(context).popUntil(
.update((state) => _mnemonic[next]); ModalRoute.withName(
NewWalletRecoveryPhraseWarningView.routeName,
Navigator.of(context).pushNamed( ),
VerifyRecoveryPhraseView.routeName, );
arguments: Tuple2(_manager, _mnemonic), }
); },
}, ),
style: Theme.of(context).textButtonTheme.style?.copyWith( actions: [
backgroundColor: MaterialStateProperty.all<Color>( Padding(
CFColors.stackAccent, padding: const EdgeInsets.all(10),
child: AspectRatio(
aspectRatio: 1,
child: AppBarIconButton(
color: CFColors.almostWhite,
shadows: const [],
icon: SvgPicture.asset(
Assets.svg.copy,
width: 24,
height: 24,
),
onPressed: () async {
await _copy();
},
), ),
), ),
child: Text( ),
"I saved my recovery phrase", ],
style: STextStyles.button,
),
), ),
], body: Container(
color: CFColors.background,
width: isDesktop ? 600 : null,
child: Padding(
padding: isDesktop
? const EdgeInsets.all(0)
: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (!isDesktop)
const SizedBox(
height: 4,
),
if (!isDesktop)
Text(
_manager.walletName,
textAlign: TextAlign.center,
style: STextStyles.label.copyWith(
fontSize: 12,
),
),
SizedBox(
height: isDesktop ? 24 : 4,
),
Text(
"Recovery Phrase",
textAlign: TextAlign.center,
style: isDesktop
? STextStyles.desktopH2
: STextStyles.pageTitleH1,
),
const SizedBox(
height: 16,
),
Container(
decoration: BoxDecoration(
color: isDesktop
? CFColors.background
: CFColors.popupBackground,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius),
),
child: Padding(
padding: isDesktop
? const EdgeInsets.all(0)
: const EdgeInsets.all(12),
child: Text(
"Please write down your recovery phrase in the correct order and save it to keep your funds secure. You will also be asked to verify the words on the next screen.",
textAlign: TextAlign.center,
style: isDesktop
? STextStyles.desktopSubtitleH2
: STextStyles.label.copyWith(
color: CFColors.stackAccent,
),
),
),
),
SizedBox(
height: isDesktop ? 21 : 8,
),
if (!isDesktop)
Expanded(
child: SingleChildScrollView(
child: MnemonicTable(
words: _mnemonic,
isDesktop: isDesktop,
),
),
),
if (isDesktop)
MnemonicTable(
words: _mnemonic,
isDesktop: isDesktop,
),
SizedBox(
height: isDesktop ? 24 : 16,
),
if (isDesktop)
SizedBox(
height: 70,
child: TextButton(
onPressed: () async {
await _copy();
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.svg.copy,
width: 20,
height: 20,
),
const SizedBox(
width: 10,
),
Text(
"Copy to clipboard",
style: STextStyles.desktopButtonSecondaryEnabled,
)
],
),
),
),
if (isDesktop)
const SizedBox(
height: 16,
),
ConstrainedBox(
constraints: BoxConstraints(
minHeight: isDesktop ? 70 : 0,
),
child: TextButton(
onPressed: () async {
final int next = Random().nextInt(_mnemonic.length);
ref
.read(verifyMnemonicWordIndexStateProvider.state)
.update((state) => next);
ref
.read(verifyMnemonicCorrectWordStateProvider.state)
.update((state) => _mnemonic[next]);
unawaited(Navigator.of(context).pushNamed(
VerifyRecoveryPhraseView.routeName,
arguments: Tuple2(_manager, _mnemonic),
));
},
style: CFColors.getPrimaryEnabledButtonColor(context),
child: Text(
"I saved my recovery phrase",
style: isDesktop
? STextStyles.desktopButtonEnabled
: STextStyles.button,
),
),
),
],
),
), ),
), ),
), ));
),
);
} }
} }

View file

@ -1,19 +1,20 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart';
import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table_item.dart'; import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table_item.dart';
class MnemonicTable extends StatelessWidget { class MnemonicTable extends StatelessWidget {
const MnemonicTable({ const MnemonicTable({
Key? key, Key? key,
required this.words, required this.words,
required this.isDesktop,
}) : super(key: key); }) : super(key: key);
final List<String> words; final List<String> words;
final bool isDesktop;
static const wordsPerRow = 3;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType"); debugPrint("BUILD: $runtimeType");
final wordsPerRow = isDesktop ? 4 : 3;
final int rows = words.length ~/ wordsPerRow; final int rows = words.length ~/ wordsPerRow;
@ -26,51 +27,54 @@ class MnemonicTable extends StatelessWidget {
children: [ children: [
for (int i = 1; i <= rows; i++) for (int i = 1; i <= rows; i++)
Padding( Padding(
padding: const EdgeInsets.symmetric(vertical: 5), padding: EdgeInsets.symmetric(vertical: isDesktop ? 8 : 5),
child: Row( child: Row(
children: [ children: [
for (int j = 1; j <= wordsPerRow; j++) ...[ for (int j = 1; j <= wordsPerRow; j++) ...[
if (j > 1) if (j > 1)
const SizedBox( SizedBox(
width: 6, width: isDesktop ? 10 : 6,
), ),
Expanded( Expanded(
child: MnemonicTableItem( child: MnemonicTableItem(
number: ++index, number: ++index,
word: words[index - 1], word: words[index - 1],
isDesktop: isDesktop,
), ),
), ),
], ],
], ],
), ),
), ),
Padding( if (index != words.length)
padding: const EdgeInsets.symmetric(vertical: 5), Padding(
child: Row( padding: EdgeInsets.symmetric(vertical: isDesktop ? 8 : 5),
children: [ child: Row(
for (int i = index; i < words.length; i++) ...[ children: [
if (i > index) for (int i = index; i < words.length; i++) ...[
if (i > index)
SizedBox(
width: isDesktop ? 10 : 6,
),
Expanded(
child: MnemonicTableItem(
number: i + 1,
word: words[i],
isDesktop: isDesktop,
),
),
],
for (int i = remainder; i < wordsPerRow; i++) ...[
const SizedBox( const SizedBox(
width: 6, width: 6,
), ),
Expanded( Expanded(
child: MnemonicTableItem( child: Container(),
number: i + 1,
word: words[i],
), ),
), ],
], ],
for (int i = remainder; i < wordsPerRow; i++) ...[ ),
const SizedBox(
width: 6,
),
Expanded(
child: Container(),
),
],
],
), ),
),
], ],
); );
} }

View file

@ -1,49 +1,53 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class MnemonicTableItem extends StatelessWidget { class MnemonicTableItem extends StatelessWidget {
const MnemonicTableItem({ const MnemonicTableItem({
Key? key, Key? key,
required this.number, required this.number,
required this.word, required this.word,
required this.isDesktop,
}) : super(key: key); }) : super(key: key);
final int number; final int number;
final String word; final String word;
final bool isDesktop;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType"); debugPrint("BUILD: $runtimeType");
return Container( return RoundedWhiteContainer(
decoration: BoxDecoration( padding: isDesktop
color: CFColors.white, ? const EdgeInsets.symmetric(horizontal: 12, vertical: 9)
borderRadius: BorderRadius.circular( : const EdgeInsets.all(8),
Constants.size.circularBorderRadius, child: Row(
), mainAxisAlignment: MainAxisAlignment.start,
), children: [
child: Padding( Text(
padding: const EdgeInsets.all(8), number.toString(),
child: Row( style: isDesktop
mainAxisAlignment: MainAxisAlignment.start, ? STextStyles.desktopTextExtraSmall.copyWith(
children: [ color: CFColors.gray3,
Text( )
number.toString(), : STextStyles.baseXS.copyWith(
style: STextStyles.baseXS.copyWith( color: CFColors.gray3,
color: CFColors.gray3, fontSize: 10,
fontSize: 10, ),
), ),
), const SizedBox(
const SizedBox( width: 8,
width: 8, ),
), Text(
Text( word,
word, style: isDesktop
style: STextStyles.baseXS, ? STextStyles.desktopTextExtraSmall.copyWith(
), color: CFColors.textDark,
], )
), : STextStyles.baseXS,
),
],
), ),
); );
} }

View file

@ -93,6 +93,7 @@ class RecoverPhraseView extends StatelessWidget {
child: SingleChildScrollView( child: SingleChildScrollView(
child: MnemonicTable( child: MnemonicTable(
words: mnemonic, words: mnemonic,
isDesktop: false,
), ),
), ),
), ),

View file

@ -123,6 +123,7 @@ class WalletBackupView extends ConsumerWidget {
child: SingleChildScrollView( child: SingleChildScrollView(
child: MnemonicTable( child: MnemonicTable(
words: mnemonic, words: mnemonic,
isDesktop: false,
), ),
), ),
), ),

View file

@ -140,6 +140,7 @@ class _DeleteWalletRecoveryPhraseViewState
child: SingleChildScrollView( child: SingleChildScrollView(
child: MnemonicTable( child: MnemonicTable(
words: _mnemonic, words: _mnemonic,
isDesktop: false,
), ),
), ),
), ),

View file

@ -4,7 +4,12 @@ import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
class ExitToMyStackButton extends StatelessWidget { class ExitToMyStackButton extends StatelessWidget {
const ExitToMyStackButton({Key? key}) : super(key: key); const ExitToMyStackButton({
Key? key,
this.onPressed,
}) : super(key: key);
final VoidCallback? onPressed;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -16,11 +21,12 @@ class ExitToMyStackButton extends StatelessWidget {
height: 56, height: 56,
child: TextButton( child: TextButton(
style: CFColors.getSmallSecondaryEnabledButtonColor(context), style: CFColors.getSmallSecondaryEnabledButtonColor(context),
onPressed: () { onPressed: onPressed ??
Navigator.of(context).popUntil( () {
ModalRoute.withName(DesktopHomeView.routeName), Navigator.of(context).popUntil(
); ModalRoute.withName(DesktopHomeView.routeName),
}, );
},
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 30, horizontal: 30,