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
+}