Skip to content

Commit

Permalink
Merge pull request #1644 from bitmark-inc/import_mnemonic_ui
Browse files Browse the repository at this point in the history
Import mnemonic UI
  • Loading branch information
phuocbitmark authored Apr 24, 2024
2 parents 044b252 + 69010d6 commit 8bf2542
Showing 1 changed file with 134 additions and 40 deletions.
174 changes: 134 additions & 40 deletions lib/screen/onboarding/import_address/import_seeds.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import 'dart:async';
import 'dart:math';

import 'package:autonomy_flutter/common/injector.dart';
import 'package:autonomy_flutter/screen/app_router.dart';
import 'package:autonomy_flutter/screen/onboarding/import_address/select_addresses.dart';
import 'package:autonomy_flutter/service/account_service.dart';
import 'package:autonomy_flutter/util/log.dart';
import 'package:autonomy_flutter/util/style.dart';
import 'package:autonomy_flutter/util/ui_helper.dart';
import 'package:autonomy_flutter/view/au_text_field.dart';
import 'package:autonomy_flutter/view/au_toggle.dart';
import 'package:autonomy_flutter/view/back_appbar.dart';
import 'package:autonomy_flutter/view/primary_button.dart';
import 'package:autonomy_flutter/view/responsive.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:feralfile_app_theme/feral_file_app_theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:sentry/sentry.dart';

Expand All @@ -26,14 +26,31 @@ class ImportSeedsPage extends StatefulWidget {
}

class _ImportSeedsPageState extends State<ImportSeedsPage> {
bool isError = false;
final TextEditingController _phraseTextController = TextEditingController();
bool _isError = false;
static const _rowNumber = 12;
static const _maxWords = 24;
final List<TextEditingController> _mnemonicControllers =
List.generate(_maxWords, (_) => TextEditingController(), growable: false);
final List<FocusNode> _focusNodes =
List.generate(_maxWords + 1, (_) => FocusNode(), growable: false);
final TextEditingController _passphraseTextController =
TextEditingController();
bool _isSubmissionEnabled = false;
bool _obscureText = true;
bool _passphraseObscureText = true;

@override
void dispose() {
for (var element in _mnemonicControllers) {
element.dispose();
}
for (var element in _focusNodes) {
element.dispose();
}
_passphraseTextController.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) => Scaffold(
appBar: getBackAppBar(
Expand All @@ -53,43 +70,39 @@ class _ImportSeedsPageState extends State<ImportSeedsPage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
addTitleSpace(),
const SizedBox(height: 34),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'reveal_secret_phrase'.tr(),
style: Theme.of(context).textTheme.ppMori400Black14,
),
AuToggle(
value: !_obscureText,
onToggle: (value) {
setState(() {
_obscureText = !value;
});
},
),
],
),
const SizedBox(height: 30),
Text(
'input_your_mnemonic'.tr(),
'enter_your_mnemonic'.tr(),
style: Theme.of(context).textTheme.ppMori400Black14,
),
const SizedBox(height: 5),
AuTextField(
labelSemantics: 'enter_seed',
title: '',
enableSuggestions: false,
obscureText: _obscureText,
placeholder: 'enter_recovery_phrase'.tr(),
hintMaxLines: 1,
controller: _phraseTextController,
isError: isError,
onChanged: (value) {
final numberOfWords = value.trim().split(' ').length;
setState(() {
_isSubmissionEnabled =
[12, 15, 18, 21, 24].contains(numberOfWords);
isError = false;
});
},
suffix: IconButton(
icon: SvgPicture.asset(
_obscureText
? 'assets/images/unhide.svg'
: 'assets/images/hide.svg',
colorFilter: const ColorFilter.mode(
AppColor.primaryBlack, BlendMode.srcIn),
),
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
Table(
children: List.generate(
_rowNumber,
(index) => _tableRow(context, index, _rowNumber),
),
border: TableBorder.all(
color:
_isError ? AppColor.red : AppColor.auLightGrey,
borderRadius: BorderRadius.circular(10)),
),
const SizedBox(height: 20),
RichText(
Expand Down Expand Up @@ -121,6 +134,7 @@ class _ImportSeedsPageState extends State<ImportSeedsPage> {
placeholder: 'enter_passphrase_placeholder'.tr(),
hintMaxLines: 1,
controller: _passphraseTextController,
focusNode: _focusNodes[_maxWords],
suffix: IconButton(
icon: SvgPicture.asset(
_passphraseObscureText
Expand Down Expand Up @@ -150,7 +164,7 @@ class _ImportSeedsPageState extends State<ImportSeedsPage> {
const SizedBox(height: 20),
PrimaryAsyncButton(
text: 'continue'.tr(),
enabled: _isSubmissionEnabled && !isError,
enabled: _isSubmissionEnabled && !_isError,
onTap: () async => _import(),
),
],
Expand All @@ -161,12 +175,12 @@ class _ImportSeedsPageState extends State<ImportSeedsPage> {
Future<void> _import() async {
try {
setState(() {
isError = false;
_isError = false;
});
final accountService = injector<AccountService>();

final persona = await accountService.importPersona(
_phraseTextController.text.trim(),
_getMnemonic(),
_passphraseTextController.text.trim(),
);
if (!mounted) {
Expand All @@ -183,8 +197,88 @@ class _ImportSeedsPageState extends State<ImportSeedsPage> {
unawaited(Sentry.captureException(exception));
UIHelper.hideInfoDialog(context);
setState(() {
isError = true;
_isError = true;
});
}
}

TableRow _tableRow(BuildContext context, int index, int itemsEachCol) =>
TableRow(children: [
_rowItem(context, index),
_rowItem(context, index + itemsEachCol),
]);

Widget _rowItem(BuildContext context, int index) {
final theme = Theme.of(context);
NumberFormat formatter = NumberFormat('00');
final controller = _mnemonicControllers[index];

return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
children: [
Container(
width: 32,
alignment: Alignment.centerRight,
child: Text(formatter.format(index + 1),
style: theme.textTheme.ppMori400Grey14),
),
const SizedBox(width: 16),
Expanded(
child: TextField(
enableSuggestions: false,
focusNode: _focusNodes[index],
autocorrect: false,
obscureText: _obscureText,
controller: controller,
decoration: InputDecoration(
contentPadding: const EdgeInsets.fromLTRB(0, 3, 0, 0),
isDense: true,
border: InputBorder.none,
hintStyle: ResponsiveLayout.isMobile
? theme.textTheme.ppMori400Black14
.copyWith(color: AppColor.auQuickSilver)
: theme.textTheme.ppMori400Black16
.copyWith(color: AppColor.auQuickSilver, fontSize: 20),
),
onSubmitted: (value) {
if (index < _maxWords) {
FocusScope.of(context).requestFocus(_focusNodes[index + 1]);
}
},
style: theme.textTheme.ppMori400Black14
.copyWith(color: _isError ? AppColor.red : null),
onChanged: (value) {
if (value.contains(' ')) {
final words = value.split(' ');
if (words.last.isEmpty) {
words.removeLast();
}
final wordsLeft = _maxWords - index;
final wordsToInsertNum = min(wordsLeft, words.length);
for (var i = 0; i < wordsToInsertNum; i++) {
if (i != wordsToInsertNum || words[i].isNotEmpty) {
_mnemonicControllers[index + i].text = words[i];
}
}
FocusScope.of(context)
.requestFocus(_focusNodes[index + wordsToInsertNum]);
}

final numberOfWords = _getMnemonic().split(' ').length;
setState(() {
_isSubmissionEnabled =
[12, 15, 18, 21, 24].contains(numberOfWords);
_isError = false;
});
},
),
),
],
),
);
}

String _getMnemonic() =>
_mnemonicControllers.map((e) => e.text.trim()).join(' ').trim();
}

0 comments on commit 8bf2542

Please sign in to comment.