dev
dart 392 lines 16.6 KB
Raw
1 import 'package:cake_wallet/core/execution_state.dart';
2 import 'package:cake_wallet/core/wallet_name_validator.dart';
3 import 'package:cw_core/generate_name.dart';
4 import 'package:cake_wallet/entities/seed_type.dart';
5 import 'package:cake_wallet/generated/i18n.dart';
6 import 'package:cake_wallet/main.dart';
7 import 'package:cake_wallet/routes.dart';
8 import 'package:cake_wallet/src/screens/base_page.dart';
9 import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
10 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
11 import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
12 import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
13 import 'package:cake_wallet/src/widgets/gradient_background.dart';
14 import 'package:cake_wallet/src/widgets/picker.dart';
15 import 'package:cake_wallet/src/widgets/primary_button.dart';
16 import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
17 import 'package:cake_wallet/src/widgets/seed_language_picker.dart';
18 import 'package:cake_wallet/src/widgets/seed_language_selector.dart';
19 import 'package:cake_wallet/utils/responsive_layout_util.dart';
20 import 'package:cake_wallet/utils/show_pop_up.dart';
21 import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
22 import 'package:cake_wallet/view_model/wallet_new_vm.dart';
23 import 'package:cw_core/wallet_type.dart';
24 import 'package:flutter/material.dart';
25 import 'package:flutter_mobx/flutter_mobx.dart';
26 import 'package:mobx/mobx.dart';
27
28 class NewWalletPage extends BasePage {
29 NewWalletPage(
30 this._walletNewVM,
31 this._seedSettingsViewModel, {
32 this.isChildWallet = false,
33 });
34
35 final WalletNewVM _walletNewVM;
36 final SeedSettingsViewModel _seedSettingsViewModel;
37 final bool isChildWallet;
38
39 final welcomeImageLight = 'assets/new-ui/hero/wallet_type_light.svg';
40 final welcomeImageDark = 'assets/new-ui/hero/wallet_type_dark.svg';
41
42 @override
43 bool get gradientBackground => true;
44
45 @override
46 Widget Function(BuildContext, Widget) get rootWrapper =>
47 (BuildContext context, Widget scaffold) => GradientBackground(scaffold: scaffold);
48
49 @override
50 String get title => S.current.new_wallet;
51
52 @override
53 Function(BuildContext)? get pushToNextWidget => (context) {
54 FocusScopeNode currentFocus = FocusScope.of(context);
55 if (!currentFocus.hasPrimaryFocus) {
56 currentFocus.focusedChild?.unfocus();
57 }
58 };
59
60 @override
61 Widget body(BuildContext context) => WalletNameForm(
62 _walletNewVM,
63 currentTheme.isDark ? welcomeImageDark : welcomeImageLight,
64 _seedSettingsViewModel,
65 isChildWallet,
66 );
67 }
68
69 class WalletNameForm extends StatefulWidget {
70 WalletNameForm(
71 this._walletNewVM,
72 this.walletImage,
73 this._seedSettingsViewModel,
74 this.isChildWallet,
75 );
76
77 final WalletNewVM _walletNewVM;
78 final bool isChildWallet;
79 final String walletImage;
80 final SeedSettingsViewModel _seedSettingsViewModel;
81
82 @override
83 _WalletNameFormState createState() => _WalletNameFormState(_walletNewVM);
84 }
85
86 class _WalletNameFormState extends State<WalletNameForm> {
87 _WalletNameFormState(this._walletNewVM)
88 : _formKey = GlobalKey<FormState>(),
89 _languageSelectorKey = GlobalKey<SeedLanguageSelectorState>(),
90 _nameController = TextEditingController(),
91 _passwordController = _walletNewVM.hasWalletPassword ? TextEditingController() : null,
92 _repeatedPasswordController =
93 _walletNewVM.hasWalletPassword ? TextEditingController() : null;
94
95 static const aspectRatioImage = 1.5;
96 bool _formProcessing = false;
97
98 final GlobalKey<FormState> _formKey;
99 final GlobalKey<SeedLanguageSelectorState> _languageSelectorKey;
100 final WalletNewVM _walletNewVM;
101 final TextEditingController _nameController;
102 final TextEditingController? _passwordController;
103 final TextEditingController? _repeatedPasswordController;
104 ReactionDisposer? _stateReaction;
105
106 @override
107 void initState() {
108 _stateReaction ??= reaction((_) => _walletNewVM.state, (ExecutionState state) async {
109 if (state is ExecutedSuccessfullyState) {
110 if (widget.isChildWallet) {
111 Navigator.of(navigatorKey.currentContext ?? context)
112 .pushNamed(Routes.walletGroupExistingSeedDescriptionPage);
113 } else {
114 Navigator.of(navigatorKey.currentContext ?? context).pushNamed(Routes.preSeedPage);
115 }
116 }
117
118 if (state is FailureState) {
119 WidgetsBinding.instance.addPostFrameCallback((_) {
120 if (mounted) {
121 showPopUp<void>(
122 context: context,
123 builder: (_) {
124 return AlertWithOneAction(
125 key: ValueKey('new_wallet_page_failure_dialog_key'),
126 buttonKey: ValueKey('new_wallet_page_failure_dialog_button_key'),
127 alertTitle: S.current.new_wallet,
128 alertContent: state.error,
129 buttonText: S.of(context).ok,
130 buttonAction: () => Navigator.of(context).pop(),
131 );
132 });
133 }
134 });
135 }
136 });
137
138 _setSeedType(MoneroSeedType.defaultSeedType);
139 super.initState();
140 }
141
142 @override
143 Widget build(BuildContext context) {
144 return Padding(
145 padding: EdgeInsets.only(top: 24),
146 child: ScrollableWithBottomSection(
147 contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
148 content: Center(
149 child: ConstrainedBox(
150 constraints:
151 BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
152 child: Column(
153 crossAxisAlignment: CrossAxisAlignment.center,
154 children: [
155 Padding(
156 padding: EdgeInsets.only(left: 24, right: 12),
157 child: AspectRatio(
158 aspectRatio: aspectRatioImage,
159 child: FittedBox(
160 child: CakeImageWidget(imageUrl: widget.walletImage),
161 fit: BoxFit.contain,
162 ),
163 ),
164 ),
165 SizedBox(height: 48),
166 Padding(
167 padding: EdgeInsets.only(top: 24),
168 child: Form(
169 key: _formKey,
170 child: Column(
171 children: [
172 BaseTextFormField(
173 key: ValueKey('new_wallet_page_wallet_name_textformfield_key'),
174 onChanged: (value) => _walletNewVM.name = value,
175 controller: _nameController,
176 textStyle: Theme.of(context).textTheme.titleMedium?.copyWith(
177 color: Theme.of(context).colorScheme.onSurface,
178 ),
179 placeholderTextStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
180 color: Theme.of(context).colorScheme.onSurfaceVariant,
181 height: 1.4,
182 fontSize: 16.0,
183 ),
184 hintText: S.of(context).wallet_name,
185 suffixIcon: Semantics(
186 label: S.of(context).generate_name,
187 child: IconButton(
188 key: ValueKey(
189 'new_wallet_page_wallet_name_textformfield_generate_name_button_key',
190 ),
191 onPressed: () async {
192 final rName = await generateName();
193 FocusManager.instance.primaryFocus?.unfocus();
194
195 setState(() {
196 _nameController.text = rName;
197 _walletNewVM.name = rName;
198 _nameController.selection = TextSelection.fromPosition(
199 TextPosition(offset: _nameController.text.length),
200 );
201 });
202 },
203 icon: Container(
204 padding: const EdgeInsets.all(8),
205 decoration: BoxDecoration(
206 borderRadius: BorderRadius.circular(12.0),
207 color: Theme.of(context).colorScheme.surface,
208 ),
209 width: 34,
210 height: 34,
211 child: Image.asset('assets/images/refresh_icon.png',
212 color: Theme.of(context).colorScheme.onSurfaceVariant),
213 ),
214 ),
215 ),
216 validator: WalletNameValidator(),
217 ),
218 if (_walletNewVM.hasWalletPassword) ...[
219 BaseTextFormField(
220 key: ValueKey('password'),
221 onChanged: (value) => _walletNewVM.walletPassword = value,
222 controller: _passwordController,
223 textAlign: TextAlign.center,
224 obscureText: true,
225 textStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
226 fontSize: 20.0,
227 fontWeight: FontWeight.w600,
228 color: Theme.of(context).colorScheme.onSurface,
229 ),
230 placeholderTextStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
231 color: Theme.of(context).colorScheme.onSurfaceVariant,
232 height: 1.4,
233 fontSize: 18.0,
234 fontWeight: FontWeight.w500,
235 ),
236 hintText: S.of(context).password,
237 ),
238 BaseTextFormField(
239 key: ValueKey('repeat_wallet_password'),
240 onChanged: (value) => _walletNewVM.repeatedWalletPassword = value,
241 controller: _repeatedPasswordController,
242 textAlign: TextAlign.center,
243 obscureText: true,
244 textStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
245 fontSize: 20.0,
246 fontWeight: FontWeight.w600,
247 color: Theme.of(context).colorScheme.onSurface,
248 ),
249 placeholderTextStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
250 color: Theme.of(context).colorScheme.onSurfaceVariant,
251 height: 1.4,
252 fontSize: 18.0,
253 fontWeight: FontWeight.w500,
254 ),
255 hintText: S.of(context).repeat_wallet_password,
256 ),
257 ],
258 ],
259 ),
260 ),
261 ),
262 if (_walletNewVM.showLanguageSelector) ...[
263 if (_walletNewVM.hasSeedType) ...[
264 Observer(
265 builder: (BuildContext build) => Padding(
266 padding: EdgeInsets.only(top: 24),
267 child: SelectButton(
268 key: ValueKey('new_wallet_page_monero_seed_type_button_key'),
269 text: widget._seedSettingsViewModel.moneroSeedType.title,
270 borderRadius: BorderRadius.circular(18),
271 onTap: () async {
272 await showPopUp<void>(
273 context: context,
274 builder: (_) => Picker(
275 items: MoneroSeedType.all
276 .where((e) => // exclude bip39 in case of Wownero
277 widget._walletNewVM.type != WalletType.wownero ||
278 e.raw != MoneroSeedType.bip39.raw)
279 .toList(),
280 selectedAtIndex: isPolyseed ? 1 : 0,
281 onItemSelected: _setSeedType,
282 isSeparated: false,
283 ),
284 );
285 },
286 ),
287 ),
288 ),
289 ],
290 Observer(
291 builder: (BuildContext build) => Padding(
292 padding: EdgeInsets.only(top: 10),
293 child: SeedLanguageSelector(
294 borderRadius: BorderRadius.circular(18),
295 key: _languageSelectorKey,
296 buttonKey: ValueKey('new_wallet_page_seed_language_selector_button_key'),
297 initialSelected: defaultSeedLanguage,
298 seedType: _walletNewVM.hasSeedType
299 ? widget._seedSettingsViewModel.moneroSeedType
300 : MoneroSeedType.legacy,
301 ),
302 ),
303 )
304 ],
305 ],
306 ),
307 ),
308 ),
309 bottomSectionPadding: EdgeInsets.all(24),
310 bottomSection: Column(
311 children: [
312 PrimaryButton(
313 key: ValueKey('new_wallet_page_advanced_settings_button_key'),
314 onPressed: () {
315 Navigator.of(context).pushNamed(Routes.advancedPrivacySettings, arguments: {
316 "type": _walletNewVM.type,
317 "useTestnet": _walletNewVM.useTestnet,
318 "toggleTestnet": _walletNewVM.toggleUseTestnet,
319 "zcashNetwork": _walletNewVM.zcashNetwork,
320 "setZcashNetwork": _walletNewVM.setZcashNetwork,
321 "isChildWallet": widget.isChildWallet,
322 });
323 },
324 text: S.of(context).advanced_settings,
325 color: Theme.of(context).colorScheme.surfaceContainer,
326 textColor: Theme.of(context).colorScheme.onSecondaryContainer,
327 ),
328 const SizedBox(height: 12),
329 Observer(
330 builder: (context) {
331 return LoadingPrimaryButton(
332 key: ValueKey('new_wallet_page_confirm_button_key'),
333 onPressed: _confirmForm,
334 text: S.of(context).seed_language_next,
335 color: Theme.of(context).colorScheme.primary,
336 textColor: Theme.of(context).colorScheme.onPrimary,
337 isLoading: _walletNewVM.state is IsExecutingState,
338 isDisabled: _walletNewVM.name.isEmpty,
339 );
340 },
341 ),
342 ],
343 ),
344 ),
345 );
346 }
347
348 void _confirmForm() async {
349 if (_formProcessing) return;
350 _formProcessing = true;
351 try {
352 if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
353 _formProcessing = false;
354 return;
355 }
356 if (await _walletNewVM.nameExists(_walletNewVM.name)) {
357 await showPopUp<void>(
358 context: context,
359 builder: (_) {
360 return AlertWithOneAction(
361 alertTitle: '',
362 alertContent: S.of(context).wallet_name_exists,
363 buttonText: S.of(context).ok,
364 buttonAction: () {
365 if (mounted && Navigator.of(context).canPop()) {
366 Navigator.of(context).pop();
367 }
368 });
369 });
370 } else {
371 await _walletNewVM.create(
372 options: _walletNewVM.hasLanguageSelector
373 ? [
374 _languageSelectorKey.currentState?.selected ?? defaultSeedLanguage,
375 widget._seedSettingsViewModel.moneroSeedType
376 ]
377 : null);
378 }
379 } catch (e) {
380 _formProcessing = false;
381 rethrow;
382 }
383 _formProcessing = false;
384 }
385
386 bool get isPolyseed => widget._seedSettingsViewModel.moneroSeedType == MoneroSeedType.polyseed;
387
388 void _setSeedType(MoneroSeedType item) {
389 widget._seedSettingsViewModel.setMoneroSeedType(item);
390 _languageSelectorKey.currentState?.selected = defaultSeedLanguage; // Reset Seed language
391 }
392 }