dev
dart 225 lines 8.73 KB
Raw
1 import 'package:cake_wallet/core/backup_service_v3.dart';
2 import 'package:cake_wallet/core/execution_state.dart';
3 import 'package:cake_wallet/generated/i18n.dart';
4 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
5 import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
6 import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
7 import 'package:cake_wallet/utils/responsive_layout_util.dart';
8 import 'package:cake_wallet/utils/show_pop_up.dart';
9 import 'package:flutter/material.dart';
10 import 'package:cake_wallet/src/widgets/primary_button.dart';
11 import 'package:cake_wallet/view_model/restore_from_backup_view_model.dart';
12 import 'package:cake_wallet/src/screens/base_page.dart';
13 import 'package:file_picker/file_picker.dart';
14 import "package:flutter/services.dart";
15 import 'package:flutter_mobx/flutter_mobx.dart';
16 import 'package:mobx/mobx.dart';
17
18 class RestoreFromBackupPage extends BasePage {
19 RestoreFromBackupPage(this.restoreFromBackupViewModel)
20 : textEditingController = TextEditingController();
21
22 final RestoreFromBackupViewModel restoreFromBackupViewModel;
23 final TextEditingController textEditingController;
24 bool _isStateReactionSet = false;
25
26 @override
27 String get title => S.current.restore_title_from_backup;
28
29 @override
30 Function(BuildContext)? get pushToNextWidget => (context) {
31 FocusScopeNode currentFocus = FocusScope.of(context);
32 if (!currentFocus.hasPrimaryFocus) {
33 currentFocus.focusedChild?.unfocus();
34 }
35 };
36
37 @override
38 Widget body(BuildContext context) {
39 if (!_isStateReactionSet) {
40 _isStateReactionSet = true;
41
42 reaction((_) => restoreFromBackupViewModel.state, (ExecutionState state) {
43 if (state is FailureState) {
44 WidgetsBinding.instance.addPostFrameCallback((_) {
45 showPopUp<void>(
46 context: context,
47 builder: (BuildContext context) {
48 return AlertWithOneAction(
49 alertTitle: S.of(context).error,
50 alertContent: state.error,
51 buttonText: S.of(context).ok,
52 buttonAction: () => Navigator.of(context).pop(),
53 );
54 });
55 });
56 }
57 });
58 }
59
60 return Center(
61 child: ConstrainedBox(
62 constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
63 child: Padding(
64 padding: EdgeInsets.only(bottom: 24, left: 8, right: 8),
65 child: Column(
66 children: [
67 Expanded(
68 child: Container(
69 child: Center(
70 child: Column(
71 mainAxisSize: MainAxisSize.min,
72 children: [
73 BaseTextFormField(
74 obscureText: true,
75 enableSuggestions: false,
76 autocorrect: false,
77 hintText: S.of(context).enter_backup_password,
78 keyboardType: TextInputType.visiblePassword,
79 controller: textEditingController,
80 textStyle: Theme.of(context).textTheme.bodyLarge?.copyWith(
81 fontSize: 26,
82 color: Theme.of(context).colorScheme.onSurface,
83 ),
84 ),
85 Observer(
86 builder: (_) {
87 if (restoreFromBackupViewModel.filePath.isNotEmpty) {
88 return Column(
89 children: [
90 const SizedBox(height: 100),
91 Row(
92 children: [
93 Text(
94 "File Name: ",
95 style: Theme.of(context).textTheme.bodyMedium!.copyWith(
96 fontSize: 18.0,
97 fontWeight: FontWeight.bold,
98 color: titleColor(context),
99 ),
100 ),
101 Expanded(
102 child: Text(
103 restoreFromBackupViewModel.filePath.split("/").last,
104 style: Theme.of(context).textTheme.bodyMedium!.copyWith(
105 fontSize: 18.0,
106 fontWeight: FontWeight.bold,
107 color: titleColor(context),
108 ),
109 ),
110 ),
111 ],
112 ),
113 ],
114 );
115 }
116
117 return const SizedBox();
118 },
119 ),
120 ],
121 ),
122 ),
123 ),
124 ),
125 Container(
126 child: Row(
127 children: [
128 Expanded(
129 child: PrimaryButton(
130 onPressed: () => presentFilePicker(),
131 text: S.of(context).select_backup_file,
132 color: Theme.of(context).colorScheme.error,
133 textColor: Theme.of(context).colorScheme.onError,
134 ),
135 ),
136 SizedBox(width: 20),
137 Expanded(
138 child: Observer(
139 builder: (_) {
140 return LoadingPrimaryButton(
141 isLoading: restoreFromBackupViewModel.state is IsExecutingState,
142 onPressed: () => onImportHandler(context),
143 text: S.of(context).import,
144 color: Theme.of(context).colorScheme.primary,
145 textColor: Theme.of(context).colorScheme.onPrimary,
146 );
147 },
148 ),
149 ),
150 ],
151 ),
152 ),
153 ],
154 ),
155 ),
156 ),
157 );
158 }
159
160 Future<void> presentFilePicker() async {
161 final result = await FilePicker.platform.pickFiles();
162
163 if (result?.files.isEmpty ?? true) {
164 return;
165 }
166
167 restoreFromBackupViewModel.filePath = result!.files.first.path!;
168 }
169
170 Future<void> onImportHandler(BuildContext context) async {
171 if (textEditingController.text.isEmpty || (restoreFromBackupViewModel.filePath.isEmpty)) {
172 await showPopUp<void>(
173 context: context,
174 builder: (_) {
175 return AlertWithOneAction(
176 alertTitle: S.current.error,
177 alertContent: S.of(context).please_select_backup_file,
178 buttonText: S.of(context).ok,
179 buttonAction: () => Navigator.of(context).pop());
180 });
181
182 return;
183 }
184
185 FocusManager.instance.primaryFocus?.unfocus();
186 try {
187 await restoreFromBackupViewModel.import(textEditingController.text);
188 await SystemChannels.textInput.invokeMethod<void>("TextInput.hide");
189 } on IncompatibleBackupAppException catch (e) {
190 final isConfirmed = await showPopUp<bool>(
191 context: context,
192 builder: (_) {
193 return AlertWithTwoActions(
194 alertTitle: S.current.warning,
195 alertContent:
196 'This backup was created in ${e.sourceAppName}, but you are restoring it in ${e.currentAppName}. '
197 'Please make sure this is the correct backup file before continuing.',
198 leftButtonText: S.of(context).cancel,
199 rightButtonText: S.of(context).ok,
200 actionLeftButton: () => Navigator.of(context).pop(false),
201 actionRightButton: () => Navigator.of(context).pop(true),
202 );
203 },
204 );
205
206 if (isConfirmed ?? false) {
207 await restoreFromBackupViewModel.import(
208 textEditingController.text,
209 checkBackupApp: false,
210 );
211 }
212 } catch (e) {
213 await showPopUp<void>(
214 context: context,
215 builder: (_) {
216 return AlertWithOneAction(
217 alertTitle: S.current.error,
218 alertContent: e.toString(),
219 buttonText: S.of(context).ok,
220 buttonAction: () => Navigator.of(context).pop());
221 });
222 }
223 textEditingController.text = '';
224 }
225 }