dev
dart 411 lines 17.4 KB
Raw
1 import "dart:async";
2
3 import "package:cake_wallet/bitcoin/bitcoin.dart";
4 import "package:cake_wallet/generated/i18n.dart";
5 import "package:cake_wallet/main.dart";
6 import "package:cake_wallet/new-ui/widgets/coins_page/token_image_widget.dart";
7 import "package:cake_wallet/new-ui/widgets/new_primary_button.dart";
8 import "package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart";
9 import "package:cake_wallet/new-ui/widgets/send_page/l2_send_external_modal.dart";
10 import "package:cake_wallet/new-ui/widgets/send_page/send_address_input.dart";
11 import "package:cake_wallet/src/widgets/cake_image_widget.dart";
12 import "package:cake_wallet/src/widgets/new_list_row/new_simple_checkbox.dart";
13 import "package:cake_wallet/view_model/contact_list/contact_list_view_model.dart";
14 import "package:cake_wallet/view_model/send/send_view_model.dart";
15 import "package:cake_wallet/view_model/wallet_switcher_view_model.dart";
16 import "package:cw_core/currency_for_wallet_type.dart";
17 import "package:cw_core/wallet_info.dart";
18 import "package:cw_core/wallet_type.dart";
19 import "package:flutter/cupertino.dart";
20 import "package:flutter/material.dart";
21 import "package:modal_bottom_sheet/modal_bottom_sheet.dart";
22
23 enum L2Actions { deposit, withdraw }
24
25 class L2ActionWalletSelector extends StatefulWidget {
26 const L2ActionWalletSelector({
27 required this.showOtherWallets,
28 required this.sendViewModel,
29 required this.action,
30 required this.onSendInitiated,
31 required this.contactListViewModel,
32 required this.walletSwitcherViewModel,
33 super.key,
34 });
35
36 final bool showOtherWallets;
37 final SendViewModel sendViewModel;
38 final L2Actions action;
39 final VoidCallback onSendInitiated;
40 final ContactListViewModel contactListViewModel;
41 final WalletSwitcherViewModel walletSwitcherViewModel;
42
43 @override
44 State<L2ActionWalletSelector> createState() => _L2ActionWalletSelectorState();
45 }
46
47 class _L2ActionWalletSelectorState extends State<L2ActionWalletSelector> {
48 final TextEditingController addressController = TextEditingController();
49 List<WalletInfo> items = [];
50 bool textEntered = false;
51 int _selectedWalletIndex = 0;
52 bool _isLoading = false;
53
54 @override
55 void initState() {
56 super.initState();
57 if (widget.showOtherWallets) {
58 () async {
59 items.addAll(
60 (await WalletInfo.getAll()).where(
61 (item) =>
62 item.type == widget.sendViewModel.walletType && item.hardwareWalletType == null,
63 ),
64 );
65 items.sort((a, b) {
66 if (a.name == widget.sendViewModel.wallet.name) {
67 return -1;
68 } else if (b.name == widget.sendViewModel.wallet.name) {
69 return 1;
70 }
71 return 0;
72 });
73 setState(() {});
74 }.call();
75 }
76 addressController.addListener(() {
77 setState(() {
78 textEntered = addressController.text.isNotEmpty;
79 });
80 });
81 }
82
83 @override
84 Widget build(BuildContext context) {
85 return Column(
86 mainAxisSize: MainAxisSize.min,
87 children: [
88 ModalTopBar(
89 title: widget.action == L2Actions.deposit
90 ? "${S.of(context).send_from}..."
91 : "${S.of(context).receive_to}...",
92 leadingIcon: const Icon(Icons.arrow_back_ios_new),
93 leadingSemanticLabel: S.of(context).seed_alert_back,
94 onLeadingPressed: Navigator.of(context).pop,
95 ),
96 Flexible(
97 child: SafeArea(
98 child: Padding(
99 padding: const EdgeInsets.symmetric(horizontal: 16),
100 child: Column(
101 spacing: 12,
102 mainAxisAlignment: MainAxisAlignment.spaceBetween,
103 children: [
104 if (!widget.showOtherWallets)
105 Column(spacing: 8, crossAxisAlignment: CrossAxisAlignment.start, children: [
106 Text(S.of(context).choose_wallet),
107 WalletRow(
108 isCurrent: true,
109 currencyIconPath: widget.sendViewModel.wallet.currency.iconPath ?? "",
110 walletName: widget.sendViewModel.wallet.name,
111 onTap: () {
112 Navigator.of(context).push(CupertinoPageRoute(
113 builder: (context) => Material(
114 child: L2ActionWalletSelector(
115 showOtherWallets: true,
116 sendViewModel: widget.sendViewModel,
117 action: widget.action,
118 onSendInitiated: widget.onSendInitiated,
119 contactListViewModel: widget.contactListViewModel,
120 walletSwitcherViewModel: widget.walletSwitcherViewModel,
121 ))));
122 },
123 )
124 ]),
125 if (widget.showOtherWallets) ...[
126 Flexible(
127 child: ListView.builder(
128 shrinkWrap: true,
129 itemCount: items.length,
130 itemBuilder: (context, index) {
131 final item = items[index];
132 return Padding(
133 padding: const EdgeInsets.symmetric(vertical: 4),
134 child: WalletRow(
135 currencyIconPath: getCryptoCurrencyIconForWalletListItem(item.type),
136 walletName: item.name,
137 isCurrent: item.name == widget.sendViewModel.wallet.name,
138 isSelected: _selectedWalletIndex == index && !textEntered,
139 onTap: () {
140 setState(() {
141 addressController.text = "";
142 _selectedWalletIndex = index;
143 });
144 },
145 ),
146 );
147 }),
148 ),
149 Column(
150 spacing: 12,
151 children: [
152 // Container(
153 // height: 64,
154 // decoration: BoxDecoration(
155 // borderRadius: BorderRadius.circular(16),
156 // color: Theme.of(context).colorScheme.surfaceContainer,
157 // ),
158 // child: Material(
159 // borderRadius: BorderRadius.circular(16),
160 // color: Colors.transparent,
161 // child: InkWell(
162 // borderRadius: BorderRadius.circular(16),
163 // onTap: () {
164 // Navigator.of(context).push(CupertinoPageRoute(
165 // builder: (context) => Material(
166 // child: L2ActionWalletSelector(
167 // showOtherWallets: true,
168 // sendViewModel: widget.sendViewModel,
169 // action: widget.action,
170 // onSendInitiated: widget.onSendInitiated,
171 // contactListViewModel:
172 // widget.contactListViewModel,walletSwitcherViewModel: widget.walletSwitcherViewModel,))));
173 // },
174 // child: Padding(
175 // padding: const EdgeInsets.symmetric(horizontal: 12.0),
176 // child: Row(
177 // spacing: 10,
178 // children: [
179 // SvgPicture.asset(
180 // "assets/new-ui/select_wallet.svg",
181 // colorFilter: ColorFilter.mode(
182 // Theme.of(context).colorScheme.primary,
183 // BlendMode.srcIn),
184 // ),
185 // Text(
186 // S.of(context).select_other_wallet,
187 // style: TextStyle(
188 // color: Theme.of(context).colorScheme.primary,fontWeight: FontWeight.w500,fontSize:15),
189 // )
190 // ],
191 // ),
192 // ),
193 // ),
194 // ),
195 // ),
196 if (widget.action == L2Actions.withdraw)
197 Row(
198 children: [
199 Flexible(
200 child: NewSendAddressInput(
201 addressController: addressController,
202 selectedCurrency: widget.sendViewModel.selectedCryptoCurrency,
203 onEditingComplete: () {}),
204 ),
205 ],
206 ),
207 ],
208 ),
209 ],
210 Column(
211 spacing: 8,
212 children: [
213 if (widget.action == L2Actions.deposit && widget.showOtherWallets) ...[
214 Container(
215 height: 1,
216 color: Theme.of(context).colorScheme.onSurfaceVariant.withAlpha(64),
217 ),
218 const SizedBox(),
219 Container(
220 height: 52,
221 decoration: BoxDecoration(
222 borderRadius: BorderRadius.circular(16),
223 color: Theme.of(context).colorScheme.surfaceContainer,
224 ),
225 child: Material(
226 borderRadius: BorderRadius.circular(16),
227 color: Colors.transparent,
228 child: InkWell(
229 borderRadius: BorderRadius.circular(16),
230 onTap: () {
231 Navigator.of(context, rootNavigator: true).pop();
232 showCupertinoModalBottomSheet(
233 context: navigatorKey.currentContext ?? context,
234 builder: (context) {
235 return Material(
236 child: L2SendExternalModal(
237 sendViewModel: widget.sendViewModel,
238 ),
239 );
240 },
241 );
242 },
243 child: Padding(
244 padding: const EdgeInsets.symmetric(horizontal: 12),
245 child: Row(
246 mainAxisAlignment: MainAxisAlignment.center,
247 spacing: 10,
248 children: [
249 CakeImageWidget(
250 imageUrl: "assets/new-ui/send_from_external.svg",
251 colorFilter: ColorFilter.mode(
252 Theme.of(context).colorScheme.primary, BlendMode.srcIn),
253 ),
254 Text(
255 S.of(context).send_from_external,
256 style: TextStyle(
257 color: Theme.of(context).colorScheme.primary,
258 fontWeight: FontWeight.w500,
259 fontSize: 15,
260 ),
261 )
262 ],
263 ),
264 ),
265 ),
266 ),
267 ),
268 ],
269 NewPrimaryButton(
270 onPressed: () async {
271 if (widget.sendViewModel.wallet.type == WalletType.bitcoin ||
272 widget.sendViewModel.wallet.type == WalletType.litecoin) {
273 if (widget.action == L2Actions.withdraw) {
274 widget.sendViewModel.outputs.first.address =
275 bitcoin!.getUnusedSegwitAddress(widget.sendViewModel.wallet)!;
276 }
277
278 if (widget.showOtherWallets) {
279 if (widget.action == L2Actions.deposit) {
280 await _handleChangeWallet(items[_selectedWalletIndex]);
281 } else {
282 if (items[_selectedWalletIndex].name !=
283 widget.sendViewModel.wallet.name) {
284 widget.sendViewModel.outputs.first.address =
285 addressController.text.isNotEmpty
286 ? addressController.text
287 : items[_selectedWalletIndex].address;
288 }
289 }
290 }
291 widget.onSendInitiated();
292 }
293 },
294 text: S.of(context).continue_text,
295 color: Theme.of(context).colorScheme.primary,
296 textColor: Theme.of(context).colorScheme.onPrimary,
297 isLoading: _isLoading,
298 ),
299 const SizedBox()
300 ],
301 ),
302 ],
303 ),
304 ),
305 ),
306 )
307 ],
308 );
309 }
310
311 Future<void> _handleChangeWallet(WalletInfo wallet) async {
312 if (wallet == widget.sendViewModel.wallet) {
313 return;
314 }
315 setState(() {
316 _isLoading = true;
317 });
318 // this waits for the ui to animate nicely
319 // if wallet switching is done alongside animations, they lag HARD
320 await Future.delayed(const Duration(milliseconds: 500));
321 widget.walletSwitcherViewModel.selectWallet(wallet);
322 final success = await widget.walletSwitcherViewModel.switchToSelectedWallet();
323 if (success) {
324 await Future.delayed(const Duration(seconds: 2));
325 await widget.sendViewModel.updateWalletBalance();
326 if (bitcoin != null) {
327 await bitcoin!.updateFeeRates(widget.sendViewModel.wallet);
328 }
329 }
330 if (mounted) {
331 setState(() {
332 _isLoading = false;
333 });
334 }
335 }
336 }
337
338 class WalletRow extends StatelessWidget {
339 const WalletRow({
340 required this.currencyIconPath,
341 required this.walletName,
342 required this.onTap,
343 this.isLoading = false,
344 this.isCurrent = false,
345 this.isSelected,
346 super.key,
347 });
348
349 final String currencyIconPath;
350 final String walletName;
351 final VoidCallback onTap;
352 final bool isCurrent;
353 final bool isLoading;
354 final bool? isSelected;
355
356 @override
357 Widget build(BuildContext context) {
358 return Container(
359 height: isCurrent ? 64 : 48,
360 decoration: BoxDecoration(
361 color: Theme.of(context).colorScheme.surfaceContainer,
362 borderRadius: BorderRadius.circular(16),
363 ),
364 child: Material(
365 borderRadius: BorderRadius.circular(16),
366 color: Colors.transparent,
367 child: InkWell(
368 borderRadius: BorderRadius.circular(16),
369 onTap: onTap,
370 child: Padding(
371 padding: const EdgeInsets.symmetric(horizontal: 12),
372 child: Row(
373 mainAxisAlignment: MainAxisAlignment.spaceBetween,
374 children: [
375 Row(
376 spacing: 12,
377 children: [
378 TokenImageWidget(
379 imageUrl: currencyIconPath,
380 size: 24,
381 ),
382 Column(
383 mainAxisAlignment: MainAxisAlignment.center,
384 crossAxisAlignment: CrossAxisAlignment.start,
385 children: [
386 Text(walletName),
387 if (isCurrent)
388 Text(
389 S.of(context).current_wallet,
390 style: TextStyle(
391 fontSize: 12,
392 color: Theme.of(context).colorScheme.onSurfaceVariant),
393 )
394 ],
395 )
396 ],
397 ),
398 isSelected != null
399 ? NewSimpleCheckbox(value: isSelected!, onChanged: (val) {})
400 : Icon(
401 Icons.chevron_right,
402 color: Theme.of(context).colorScheme.onSurfaceVariant,
403 )
404 ],
405 ),
406 ),
407 ),
408 ),
409 );
410 }
411 }