CW-894-Improve-UX-for-restore-from-seeds-keys-page (#1986)

* Update wallet_restore_page.dart * Fix conflicts with main [skip ci] --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>

Serhii committed Feb 17, 2025 at 23:38 UTC 7c7ea039469c35906c48e235e37cdd94195c0af4
1 file changed +304 -223
lib/src/screens/restore/wallet_restore_page.dart
+304 -223
@@ -20,10 +20,20 @@ import 'package:flutter/material.dart';
20 import 'package:flutter_mobx/flutter_mobx.dart';
21 import 'package:keyboard_actions/keyboard_actions.dart';
22 import 'package:mobx/mobx.dart';
23 -import 'package:smooth_page_indicator/smooth_page_indicator.dart';
23
24 class WalletRestorePage extends BasePage {
26 - WalletRestorePage(this.walletRestoreViewModel, this.seedSettingsViewModel);
25 + WalletRestorePage(this.walletRestoreViewModel, this.seedSettingsViewModel)
26 + : walletRestoreFromSeedFormKey = GlobalKey<WalletRestoreFromSeedFormState>(),
27 + walletRestoreFromKeysFormKey = GlobalKey<WalletRestoreFromKeysFromState>(),
28 + _blockHeightFocusNode = FocusNode();
29 +
30 + final WalletRestoreViewModel walletRestoreViewModel;
31 + final SeedSettingsViewModel seedSettingsViewModel;
32 + final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
33 + final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey;
34 + final FocusNode _blockHeightFocusNode;
35 +
36 + bool _formProcessing = false;
37
38 @override
39 Widget middle(BuildContext context) => Observer(
@@ -38,8 +48,9 @@ class WalletRestorePage extends BasePage {
48 color: titleColor(context)),
49 ));
50
41 - final WalletRestoreViewModel walletRestoreViewModel;
42 - final SeedSettingsViewModel seedSettingsViewModel;
51 + // DerivationType derivationType = DerivationType.unknown;
52 + // String? derivationPath = null;
53 + DerivationInfo? derivationInfo;
54
55 @override
56 Function(BuildContext)? get pushToNextWidget => (context) {
@@ -51,137 +62,6 @@ class WalletRestorePage extends BasePage {
62
63 @override
64 Widget body(BuildContext context) {
54 - return WalletRestorePageBody(walletRestoreViewModel, seedSettingsViewModel);
55 - }
56 -}
57 -
58 -class WalletRestorePageBody extends StatefulWidget {
59 - WalletRestorePageBody(this.walletRestoreViewModel, this.seedSettingsViewModel);
60 -
61 - final WalletRestoreViewModel walletRestoreViewModel;
62 - final SeedSettingsViewModel seedSettingsViewModel;
63 -
64 - @override
65 - State<WalletRestorePageBody> createState() =>
66 - _WalletRestorePageBodyState(walletRestoreViewModel, seedSettingsViewModel);
67 -}
68 -
69 -class _WalletRestorePageBodyState extends State<WalletRestorePageBody> {
70 - _WalletRestorePageBodyState(this.walletRestoreViewModel, this.seedSettingsViewModel)
71 - : walletRestoreFromSeedFormKey = GlobalKey<WalletRestoreFromSeedFormState>(),
72 - walletRestoreFromKeysFormKey = GlobalKey<WalletRestoreFromKeysFromState>(),
73 - _pages = [],
74 - _blockHeightFocusNode = FocusNode(),
75 - _controller = PageController(initialPage: 0) {
76 - walletRestoreViewModel.availableModes.forEach((mode) {
77 - switch (mode) {
78 - case WalletRestoreMode.seed:
79 - _pages.add(WalletRestoreFromSeedForm(
80 - seedSettingsViewModel: seedSettingsViewModel,
81 - displayBlockHeightSelector:
82 - walletRestoreViewModel.hasBlockchainHeightLanguageSelector,
83 - displayLanguageSelector: walletRestoreViewModel.hasSeedLanguageSelector,
84 - type: walletRestoreViewModel.type,
85 - key: walletRestoreFromSeedFormKey,
86 - blockHeightFocusNode: _blockHeightFocusNode,
87 - onHeightOrDateEntered: (value) {
88 - if (_isValidSeed()) {
89 - walletRestoreViewModel.isButtonEnabled = value;
90 - }
91 - },
92 - onSeedChange: (String seed) {
93 - final isPolyseed = walletRestoreViewModel.isPolyseed(seed);
94 - _validateOnChange(isPolyseed: isPolyseed);
95 - },
96 - onLanguageChange: (String language) {
97 - final isPolyseed = language.startsWith("POLYSEED_");
98 - _validateOnChange(isPolyseed: isPolyseed);
99 - },
100 - displayWalletPassword: walletRestoreViewModel.hasWalletPassword,
101 - onPasswordChange: (String password) =>
102 - walletRestoreViewModel.walletPassword = password,
103 - onRepeatedPasswordChange: (String repeatedPassword) =>
104 - walletRestoreViewModel.repeatedWalletPassword = repeatedPassword));
105 - break;
106 - case WalletRestoreMode.keys:
107 - _pages.add(WalletRestoreFromKeysFrom(
108 - key: walletRestoreFromKeysFormKey,
109 - walletRestoreViewModel: walletRestoreViewModel,
110 - onPrivateKeyChange: (String seed) {
111 - if (walletRestoreViewModel.type == WalletType.nano ||
112 - walletRestoreViewModel.type == WalletType.banano) {
113 - walletRestoreViewModel.isButtonEnabled = _isValidSeedKey();
114 - }
115 - },
116 - displayPrivateKeyField: walletRestoreViewModel.hasRestoreFromPrivateKey,
117 - displayWalletPassword: walletRestoreViewModel.hasWalletPassword,
118 - onPasswordChange: (String password) =>
119 - walletRestoreViewModel.walletPassword = password,
120 - onRepeatedPasswordChange: (String repeatedPassword) =>
121 - walletRestoreViewModel.repeatedWalletPassword = repeatedPassword,
122 - onHeightOrDateEntered: (value) => walletRestoreViewModel.isButtonEnabled = value));
123 - break;
124 - default:
125 - break;
126 - }
127 - });
128 - }
129 -
130 - final WalletRestoreViewModel walletRestoreViewModel;
131 - final SeedSettingsViewModel seedSettingsViewModel;
132 - final PageController _controller;
133 - final List<Widget> _pages;
134 - final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
135 - final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey;
136 - final FocusNode _blockHeightFocusNode;
137 -
138 - bool _formProcessing = false;
139 -
140 - // DerivationType derivationType = DerivationType.unknown;
141 - // String? derivationPath = null;
142 - DerivationInfo? derivationInfo;
143 -
144 - @override
145 - void initState() {
146 - super.initState();
147 -
148 - reaction((_) => walletRestoreViewModel.state, (ExecutionState state) {
149 - if (state is FailureState) {
150 - WidgetsBinding.instance.addPostFrameCallback((_) {
151 - showPopUp<void>(
152 - context: context,
153 - builder: (_) {
154 - return AlertWithOneAction(
155 - alertTitle: S.current.new_wallet,
156 - alertContent: state.error,
157 - buttonText: S.of(context).ok,
158 - buttonAction: () => Navigator.of(context).pop());
159 - });
160 - });
161 - }
162 - });
163 -
164 - reaction((_) => walletRestoreViewModel.mode, (WalletRestoreMode mode) {
165 - walletRestoreViewModel.isButtonEnabled = false;
166 - walletRestoreViewModel.walletPassword = null;
167 - walletRestoreViewModel.repeatedWalletPassword = null;
168 -
169 - walletRestoreFromSeedFormKey
170 - .currentState!.blockchainHeightKey.currentState!.restoreHeightController.text = '';
171 - walletRestoreFromSeedFormKey
172 - .currentState!.blockchainHeightKey.currentState!.dateController.text = '';
173 - walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text = '';
174 -
175 - walletRestoreFromKeysFormKey
176 - .currentState!.blockchainHeightKey.currentState!.restoreHeightController.text = '';
177 - walletRestoreFromKeysFormKey
178 - .currentState!.blockchainHeightKey.currentState!.dateController.text = '';
179 - walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text = '';
180 - });
181 - }
182 -
183 - @override
184 - Widget build(BuildContext context) {
65 return KeyboardActions(
66 config: KeyboardActionsConfig(
67 keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
@@ -202,41 +82,18 @@ class _WalletRestorePageBodyState extends State<WalletRestorePageBody> {
82 constraints:
83 BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
84 child: Column(
205 - mainAxisAlignment: MainAxisAlignment.center,
85 children: [
86 Expanded(
208 - child: PageView.builder(
209 - onPageChanged: (page) {
210 - walletRestoreViewModel.mode =
211 - page == 0 ? WalletRestoreMode.seed : WalletRestoreMode.keys;
212 - },
213 - controller: _controller,
214 - itemCount: _pages.length,
215 - itemBuilder: (_, index) => SingleChildScrollView(child: _pages[index]),
87 + child: _WalletRestorePageBody(
88 + walletRestoreViewModel: walletRestoreViewModel,
89 + seedSettingsViewModel: seedSettingsViewModel,
90 + walletRestoreFromSeedFormKey: walletRestoreFromSeedFormKey,
91 + walletRestoreFromKeysFormKey: walletRestoreFromKeysFormKey,
92 + blockHeightFocusNode: _blockHeightFocusNode,
93 + derivationInfo: derivationInfo,
94 + onDerivationInfoChanged: (info) => derivationInfo = info,
95 ),
96 ),
218 - if (_pages.length > 1)
219 - Padding(
220 - padding: EdgeInsets.only(top: 10),
221 - child: Semantics(
222 - button: false,
223 - label: 'Page Indicator',
224 - hint: 'Swipe to change restore mode',
225 - excludeSemantics: true,
226 - child: SmoothPageIndicator(
227 - controller: _controller,
228 - count: _pages.length,
229 - effect: ColorTransitionEffect(
230 - spacing: 6.0,
231 - radius: 6.0,
232 - dotWidth: 6.0,
233 - dotHeight: 6.0,
234 - dotColor: Theme.of(context).hintColor.withOpacity(0.5),
235 - activeDotColor: Theme.of(context).hintColor,
236 - ),
237 - ),
238 - ),
239 - ),
97 Padding(
98 padding: EdgeInsets.only(top: 20, bottom: 24, left: 24, right: 24),
99 child: Column(
@@ -245,9 +102,7 @@ class _WalletRestorePageBodyState extends State<WalletRestorePageBody> {
102 builder: (context) {
103 return LoadingPrimaryButton(
104 key: ValueKey('wallet_restore_seed_or_key_restore_button_key'),
248 - onPressed: () async {
249 - await _confirmForm(context);
250 - },
105 + onPressed: () async => await _confirmForm(context),
106 text: S.of(context).restore_recover,
107 color: Theme.of(context)
108 .extension<WalletListTheme>()!
@@ -285,60 +140,6 @@ class _WalletRestorePageBodyState extends State<WalletRestorePageBody> {
140 );
141 }
142
288 - void _validateOnChange({bool isPolyseed = false}) {
289 - if (!isPolyseed && walletRestoreViewModel.hasBlockchainHeightLanguageSelector) {
290 - final hasHeight = walletRestoreFromSeedFormKey
291 - .currentState?.blockchainHeightKey.currentState?.restoreHeightController.text.isNotEmpty;
292 -
293 - if (hasHeight == true) {
294 - walletRestoreViewModel.isButtonEnabled = _isValidSeed();
295 - }
296 - } else {
297 - walletRestoreViewModel.isButtonEnabled = _isValidSeed();
298 - }
299 - }
300 -
301 - bool _isValidSeed() {
302 - final seedPhrase =
303 - walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text;
304 - if (walletRestoreViewModel.isPolyseed(seedPhrase)) return true;
305 -
306 - final seedWords = seedPhrase.split(' ');
307 -
308 - if (seedWords.length == 14 && walletRestoreViewModel.type == WalletType.wownero) return true;
309 - if (seedWords.length == 26 && walletRestoreViewModel.type == WalletType.zano) return true;
310 -
311 - if ((walletRestoreViewModel.type == WalletType.monero ||
312 - walletRestoreViewModel.type == WalletType.wownero ||
313 - walletRestoreViewModel.type == WalletType.haven) &&
314 - seedWords.length != WalletRestoreViewModelBase.moneroSeedMnemonicLength) {
315 - return false;
316 - }
317 -
318 - // bip39:
319 - final validBip39SeedLengths = [12, 18, 24];
320 - final nonBip39WalletTypes = [WalletType.monero, WalletType.wownero, WalletType.haven];
321 - // if it's a bip39 wallet and the length is not valid return false
322 - if (!nonBip39WalletTypes.contains(walletRestoreViewModel.type) &&
323 - !(validBip39SeedLengths.contains(seedWords.length))) {
324 - return false;
325 - }
326 -
327 - final words =
328 - walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.words.toSet();
329 - return seedWords.toSet().difference(words).toSet().isEmpty;
330 - }
331 -
332 - bool _isValidSeedKey() {
333 - final seedKey = walletRestoreFromKeysFormKey.currentState!.privateKeyController.text;
334 -
335 - if (seedKey.length != 64 && seedKey.length != 128) {
336 - return false;
337 - }
338 -
339 - return true;
340 - }
341 -
143 Map<String, dynamic> _credentials() {
144 final credentials = <String, dynamic>{};
145
@@ -466,3 +267,283 @@ class _WalletRestorePageBodyState extends State<WalletRestorePageBody> {
267 });
268 }
269 }
270 +
271 +class _WalletRestorePageBody extends StatefulWidget {
272 + const _WalletRestorePageBody({
273 + Key? key,
274 + required this.walletRestoreViewModel,
275 + required this.seedSettingsViewModel,
276 + required this.walletRestoreFromSeedFormKey,
277 + required this.walletRestoreFromKeysFormKey,
278 + required this.blockHeightFocusNode,
279 + required this.derivationInfo,
280 + required this.onDerivationInfoChanged,
281 + }) : super(key: key);
282 +
283 + final WalletRestoreViewModel walletRestoreViewModel;
284 + final SeedSettingsViewModel seedSettingsViewModel;
285 + final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
286 + final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey;
287 + final FocusNode blockHeightFocusNode;
288 + final DerivationInfo? derivationInfo;
289 + final void Function(DerivationInfo?) onDerivationInfoChanged;
290 +
291 + @override
292 + State<_WalletRestorePageBody> createState() => _WalletRestorePageBodyState(
293 + walletRestoreViewModel: walletRestoreViewModel,
294 + seedSettingsViewModel: seedSettingsViewModel,
295 + walletRestoreFromSeedFormKey: walletRestoreFromSeedFormKey,
296 + walletRestoreFromKeysFormKey: walletRestoreFromKeysFormKey,
297 + blockHeightFocusNode: blockHeightFocusNode,
298 + derivationInfo: derivationInfo);
299 +}
300 +
301 +class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
302 + with SingleTickerProviderStateMixin {
303 + _WalletRestorePageBodyState(
304 + {required this.walletRestoreViewModel,
305 + required this.seedSettingsViewModel,
306 + required this.walletRestoreFromSeedFormKey,
307 + required this.walletRestoreFromKeysFormKey,
308 + required this.blockHeightFocusNode,
309 + required this.derivationInfo});
310 +
311 + final WalletRestoreViewModel walletRestoreViewModel;
312 + final SeedSettingsViewModel seedSettingsViewModel;
313 + final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
314 + final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey;
315 + final FocusNode blockHeightFocusNode;
316 + DerivationInfo? derivationInfo;
317 +
318 + late TabController _tabController;
319 +
320 + late bool _hasKeysTab;
321 +
322 + @override
323 + void initState() {
324 + super.initState();
325 +
326 + _hasKeysTab = widget.walletRestoreViewModel.availableModes.contains(WalletRestoreMode.keys);
327 + final tabCount = _hasKeysTab ? 2 : 1;
328 + _tabController = TabController(length: tabCount, vsync: this);
329 +
330 + _tabController.addListener(() {
331 + if (!_tabController.indexIsChanging) {
332 + widget.walletRestoreViewModel.mode =
333 + _tabController.index == 0 ? WalletRestoreMode.seed : WalletRestoreMode.keys;
334 + }
335 + });
336 +
337 + reaction<WalletRestoreMode>(
338 + (_) => widget.walletRestoreViewModel.mode,
339 + (mode) {
340 + final index = mode == WalletRestoreMode.seed ? 0 : 1;
341 + if (_tabController.index != index) {
342 + _tabController.animateTo(index);
343 + }
344 + },
345 + );
346 +
347 + reaction((_) => walletRestoreViewModel.state, (ExecutionState state) {
348 + if (state is FailureState) {
349 + WidgetsBinding.instance.addPostFrameCallback((_) {
350 + showPopUp<void>(
351 + context: context,
352 + builder: (_) {
353 + return AlertWithOneAction(
354 + alertTitle: S.current.new_wallet,
355 + alertContent: state.error,
356 + buttonText: S.of(context).ok,
357 + buttonAction: () => Navigator.of(context).pop());
358 + });
359 + });
360 + }
361 + });
362 +
363 + reaction((_) => walletRestoreViewModel.mode, (WalletRestoreMode mode) {
364 + walletRestoreViewModel.isButtonEnabled = false;
365 + walletRestoreViewModel.walletPassword = null;
366 + walletRestoreViewModel.repeatedWalletPassword = null;
367 +
368 + walletRestoreFromSeedFormKey
369 + .currentState!.blockchainHeightKey.currentState!.restoreHeightController.text = '';
370 + walletRestoreFromSeedFormKey
371 + .currentState!.blockchainHeightKey.currentState!.dateController.text = '';
372 + walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text = '';
373 +
374 + walletRestoreFromKeysFormKey
375 + .currentState!.blockchainHeightKey.currentState!.restoreHeightController.text = '';
376 + walletRestoreFromKeysFormKey
377 + .currentState!.blockchainHeightKey.currentState!.dateController.text = '';
378 + walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text = '';
379 + });
380 + }
381 +
382 + @override
383 + void dispose() {
384 + _tabController.dispose();
385 + super.dispose();
386 + }
387 +
388 + @override
389 + Widget build(BuildContext context) {
390 + return Container(
391 + child: Column(
392 + children: [
393 + Padding(
394 + padding: const EdgeInsets.symmetric(horizontal: 22),
395 + child: TabBar(
396 + controller: _tabController,
397 + splashFactory: NoSplash.splashFactory,
398 + indicatorSize: TabBarIndicatorSize.label,
399 + isScrollable: true,
400 + labelStyle: TextStyle(
401 + fontSize: 18,
402 + fontFamily: 'Lato',
403 + fontWeight: FontWeight.w600,
404 + color: Theme.of(context).appBarTheme.titleTextStyle!.color),
405 + unselectedLabelStyle: TextStyle(
406 + fontSize: 18,
407 + fontFamily: 'Lato',
408 + fontWeight: FontWeight.w600,
409 + color: Theme.of(context).appBarTheme.titleTextStyle!.color?.withOpacity(0.5)),
410 + labelColor: Theme.of(context).appBarTheme.titleTextStyle!.color,
411 + indicatorColor: Theme.of(context).appBarTheme.titleTextStyle!.color,
412 + indicatorPadding: EdgeInsets.zero,
413 + labelPadding: const EdgeInsets.only(right: 24),
414 + tabAlignment: TabAlignment.start,
415 + dividerColor: Colors.transparent,
416 + padding: EdgeInsets.zero,
417 + tabs: [
418 + Tab(text: S.of(context).widgets_seed),
419 + if (_hasKeysTab) Tab(text: S.of(context).keys),
420 + ],
421 + ),
422 + ),
423 + const SizedBox(height: 20),
424 + Expanded(
425 + child: TabBarView(
426 + controller: _tabController,
427 + children: [
428 + SingleChildScrollView(
429 + padding: const EdgeInsets.symmetric(horizontal: 8),
430 + child: _buildWalletRestoreFromSeedTab(),
431 + ),
432 + if (_hasKeysTab)
433 + SingleChildScrollView(
434 + padding: const EdgeInsets.symmetric(horizontal: 8),
435 + child: _buildWalletRestoreFromKeysTab(),
436 + ),
437 + ],
438 + ),
439 + ),
440 + ],
441 + ),
442 + );
443 + }
444 +
445 + WalletRestoreFromKeysFrom _buildWalletRestoreFromKeysTab() {
446 + return WalletRestoreFromKeysFrom(
447 + key: widget.walletRestoreFromKeysFormKey,
448 + walletRestoreViewModel: widget.walletRestoreViewModel,
449 + displayPrivateKeyField: widget.walletRestoreViewModel.hasRestoreFromPrivateKey,
450 + displayWalletPassword: widget.walletRestoreViewModel.hasWalletPassword,
451 + onPrivateKeyChange: (String seed) {
452 + // For nano/banano, set button state if valid seed key
453 + if (widget.walletRestoreViewModel.type == WalletType.nano ||
454 + widget.walletRestoreViewModel.type == WalletType.banano) {
455 + widget.walletRestoreViewModel.isButtonEnabled = _isValidSeedKey();
456 + }
457 + },
458 + onPasswordChange: (String password) =>
459 + widget.walletRestoreViewModel.walletPassword = password,
460 + onRepeatedPasswordChange: (String repeatedPassword) =>
461 + widget.walletRestoreViewModel.repeatedWalletPassword = repeatedPassword,
462 + onHeightOrDateEntered: (value) => widget.walletRestoreViewModel.isButtonEnabled = value,
463 + );
464 + }
465 +
466 + WalletRestoreFromSeedForm _buildWalletRestoreFromSeedTab() {
467 + return WalletRestoreFromSeedForm(
468 + key: widget.walletRestoreFromSeedFormKey,
469 + seedSettingsViewModel: widget.seedSettingsViewModel,
470 + displayBlockHeightSelector: widget.walletRestoreViewModel.hasBlockchainHeightLanguageSelector,
471 + displayLanguageSelector: widget.walletRestoreViewModel.hasSeedLanguageSelector,
472 + type: widget.walletRestoreViewModel.type,
473 + blockHeightFocusNode: widget.blockHeightFocusNode,
474 + onHeightOrDateEntered: (value) {
475 + // set button state
476 + if (_isValidSeed()) {
477 + widget.walletRestoreViewModel.isButtonEnabled = value;
478 + }
479 + },
480 + onSeedChange: (String seed) {
481 + final isPolyseed = widget.walletRestoreViewModel.isPolyseed(seed);
482 + _validateOnChange(isPolyseed: isPolyseed);
483 + },
484 + onLanguageChange: (String language) {
485 + final isPolyseed = language.startsWith("POLYSEED_");
486 + _validateOnChange(isPolyseed: isPolyseed);
487 + },
488 + displayWalletPassword: widget.walletRestoreViewModel.hasWalletPassword,
489 + onPasswordChange: (String password) =>
490 + widget.walletRestoreViewModel.walletPassword = password,
491 + onRepeatedPasswordChange: (String repeatedPassword) =>
492 + widget.walletRestoreViewModel.repeatedWalletPassword = repeatedPassword,
493 + );
494 + }
495 +
496 + void _validateOnChange({bool isPolyseed = false}) {
497 + if (!isPolyseed && walletRestoreViewModel.hasBlockchainHeightLanguageSelector) {
498 + final hasHeight = walletRestoreFromSeedFormKey
499 + .currentState?.blockchainHeightKey.currentState?.restoreHeightController.text.isNotEmpty;
500 +
501 + if (hasHeight == true) {
502 + walletRestoreViewModel.isButtonEnabled = _isValidSeed();
503 + }
504 + } else {
505 + walletRestoreViewModel.isButtonEnabled = _isValidSeed();
506 + }
507 + }
508 +
509 + bool _isValidSeed() {
510 + final seedPhrase =
511 + walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text;
512 + if (walletRestoreViewModel.isPolyseed(seedPhrase)) return true;
513 +
514 + final seedWords = seedPhrase.split(' ');
515 +
516 + if (seedWords.length == 14 && walletRestoreViewModel.type == WalletType.wownero) return true;
517 + if (seedWords.length == 26 && walletRestoreViewModel.type == WalletType.zano) return true;
518 +
519 + if ((walletRestoreViewModel.type == WalletType.monero ||
520 + walletRestoreViewModel.type == WalletType.wownero ||
521 + walletRestoreViewModel.type == WalletType.haven) &&
522 + seedWords.length != WalletRestoreViewModelBase.moneroSeedMnemonicLength) {
523 + return false;
524 + }
525 +
526 + // bip39:
527 + final validBip39SeedLengths = [12, 18, 24];
528 + final nonBip39WalletTypes = [WalletType.monero, WalletType.wownero, WalletType.haven];
529 + // if it's a bip39 wallet and the length is not valid return false
530 + if (!nonBip39WalletTypes.contains(walletRestoreViewModel.type) &&
531 + !(validBip39SeedLengths.contains(seedWords.length))) {
532 + return false;
533 + }
534 +
535 + final words =
536 + walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.words.toSet();
537 + return seedWords.toSet().difference(words).toSet().isEmpty;
538 + }
539 +
540 + bool _isValidSeedKey() {
541 + final seedKey = walletRestoreFromKeysFormKey.currentState!.privateKeyController.text;
542 +
543 + if (seedKey.length != 64 && seedKey.length != 128) {
544 + return false;
545 + }
546 +
547 + return true;
548 + }
549 +}