dev
dart 285 lines 10.7 KB
Raw
1 import 'package:another_flushbar/flushbar.dart';
2 import 'package:cake_wallet/core/new_wallet_arguments.dart';
3 import 'package:cake_wallet/core/auth_service.dart';
4 import 'package:cake_wallet/entities/desktop_dropdown_item.dart';
5 import 'package:cake_wallet/generated/i18n.dart';
6 import 'package:cake_wallet/routes.dart';
7 import 'package:cake_wallet/src/screens/auth/auth_page.dart';
8 import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/dropdown_item_widget.dart';
9 import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart';
10 import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
11 import 'package:cake_wallet/store/settings_store.dart';
12 import 'package:cake_wallet/utils/show_bar.dart';
13 import 'package:cake_wallet/utils/show_pop_up.dart';
14 import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
15 import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
16 import 'package:cake_wallet/wallet_type_utils.dart';
17 import 'package:cw_core/wallet_type.dart';
18 import 'package:flutter/material.dart';
19 import 'package:flutter_mobx/flutter_mobx.dart';
20 import 'package:flutter_svg/svg.dart';
21
22 class DesktopWalletSelectionDropDown extends StatefulWidget {
23 final WalletListViewModel walletListViewModel;
24 final AuthService _authService;
25
26 DesktopWalletSelectionDropDown(this.walletListViewModel, this._authService, {Key? key})
27 : super(key: key);
28
29 @override
30 State<DesktopWalletSelectionDropDown> createState() => _DesktopWalletSelectionDropDownState();
31 }
32
33 class _DesktopWalletSelectionDropDownState extends State<DesktopWalletSelectionDropDown> {
34 final moneroIcon =
35 Image.asset('assets/new-ui/crypto_full_icons/monero.svg', height: 24, width: 24);
36 final bitcoinIcon =
37 Image.asset('assets/new-ui/crypto_full_icons/bitcoin.svg', height: 24, width: 24);
38 final tBitcoinIcon = Image.asset('assets/images/tbtc.png', height: 24, width: 24);
39 final litecoinIcon =
40 Image.asset('assets/new-ui/crypto_full_icons/litecoin.svg', height: 24, width: 24);
41 final havenIcon = Image.asset('assets/images/haven_logo.webp', height: 24, width: 24);
42 final ethereumIcon =
43 Image.asset('assets/new-ui/crypto_full_icons/ethereum.svg', height: 24, width: 24);
44 final polygonIcon =
45 Image.asset('assets/new-ui/crypto_full_icons/polygon.svg', height: 24, width: 24);
46 final bitcoinCashIcon =
47 Image.asset('assets/new-ui/crypto_full_icons/bitcoin-cash.svg', height: 24, width: 24);
48 final baseIcon = Image.asset('assets/new-ui/crypto_full_icons/base.svg', height: 24, width: 24);
49 final arbitrumIcon =
50 Image.asset('assets/new-ui/crypto_full_icons/arbitrum.svg', height: 24, width: 24);
51 final bscIcon = Image.asset('assets/new-ui/crypto_full_icons/bnb.svg', height: 24, width: 24);
52 final nanoIcon = Image.asset('assets/new-ui/crypto_full_icons/nano.svg', height: 24, width: 24);
53 final bananoIcon = Image.asset('assets/new-ui/crypto_full_icons/nano.svg', height: 24, width: 24);
54 final solanaIcon =
55 Image.asset('assets/new-ui/crypto_full_icons/solana.svg', height: 24, width: 24);
56 final tronIcon = Image.asset('assets/new-ui/crypto_full_icons/tron.svg', height: 24, width: 24);
57 final wowneroIcon =
58 Image.asset('assets/new-ui/crypto_full_icons/wownero.svg', height: 24, width: 24);
59 final zanoIcon = Image.asset('assets/new-ui/crypto_full_icons/zano.svg', height: 24, width: 24);
60 final decredIcon =
61 Image.asset('assets/new-ui/crypto_full_icons/decred.svg', height: 24, width: 24);
62 final dogeIcon =
63 Image.asset('assets/new-ui/crypto_full_icons/dogecoin.svg', height: 24, width: 24);
64 final zcashIcon = Image.asset('assets/new-ui/crypto_full_icons/zcash.svg', height: 24, width: 24);
65 final nonWalletTypeIcon = Image.asset('assets/images/close.png', height: 24, width: 24);
66
67 Image _newWalletImage(BuildContext context) => Image.asset(
68 'assets/images/new_wallet.png',
69 height: 12,
70 width: 12,
71 color: Theme.of(context).colorScheme.onSurface,
72 );
73
74 Image _restoreWalletImage(BuildContext context) => Image.asset(
75 'assets/images/restore_wallet.png',
76 height: 12,
77 width: 12,
78 color: Theme.of(context).colorScheme.onSurface,
79 );
80
81 Flushbar<void>? _progressBar;
82
83 @override
84 Widget build(BuildContext context) {
85 final themeData = Theme.of(context);
86 return Observer(builder: (context) {
87 final dropDownItems = [
88 ...widget.walletListViewModel.wallets
89 .map((wallet) => DesktopDropdownItem(
90 isSelected: wallet.isCurrent,
91 child: ConstrainedBox(
92 constraints: BoxConstraints(maxWidth: 500),
93 child: DropDownItemWidget(
94 title: wallet.name,
95 image: wallet.isEnabled
96 ? _imageFor(type: wallet.type, isTestnet: wallet.isTestnet)
97 : nonWalletTypeIcon,
98 ),
99 ),
100 onSelected: () => _onSelectedWallet(wallet),
101 ))
102 .toList(),
103 DesktopDropdownItem(
104 onSelected: () => _navigateToCreateWallet(),
105 child: DropDownItemWidget(
106 title: S.of(context).create_new,
107 image: _newWalletImage(context),
108 ),
109 ),
110 DesktopDropdownItem(
111 onSelected: () => _navigateToRestoreWallet(),
112 child: DropDownItemWidget(
113 title: S.of(context).restore_wallet,
114 image: _restoreWalletImage(context),
115 ),
116 ),
117 ];
118
119 final selectedItem = dropDownItems.firstWhere(
120 (element) => element.isSelected,
121 orElse: () => dropDownItems.first,
122 );
123
124 return DropdownButton<DesktopDropdownItem>(
125 items: dropDownItems
126 .map(
127 (wallet) => DropdownMenuItem<DesktopDropdownItem>(
128 child: wallet.child,
129 value: wallet,
130 ),
131 )
132 .toList(),
133 onChanged: (item) {
134 item?.onSelected();
135 },
136 dropdownColor: themeData.colorScheme.surface,
137 style: themeData.textTheme.bodyMedium,
138 selectedItemBuilder: (context) => dropDownItems.map((item) => item.child).toList(),
139 value: selectedItem,
140 underline: const SizedBox(),
141 focusColor: Colors.transparent,
142 borderRadius: BorderRadius.circular(15.0),
143 );
144 });
145 }
146
147 void _onSelectedWallet(WalletListItem selectedWallet) async {
148 if (selectedWallet.isCurrent || !selectedWallet.isEnabled) return;
149
150 WidgetsBinding.instance.addPostFrameCallback((_) async {
151 final confirmed = await showPopUp<bool>(
152 context: context,
153 builder: (dialogContext) {
154 return AlertWithTwoActions(
155 alertTitle: S.of(context).change_wallet_alert_title,
156 alertContent: S.of(context).change_wallet_alert_content(selectedWallet.name),
157 leftButtonText: S.of(context).cancel,
158 rightButtonText: S.of(context).change,
159 actionLeftButton: () => Navigator.of(dialogContext).pop(false),
160 actionRightButton: () => Navigator.of(dialogContext).pop(true));
161 }) ??
162 false;
163
164 if (confirmed) {
165 await _loadWallet(selectedWallet);
166 }
167 });
168 }
169
170 Widget _imageFor({required WalletType type, bool? isTestnet}) {
171 switch (type) {
172 case WalletType.bitcoin:
173 if (isTestnet == true) return tBitcoinIcon;
174 return bitcoinIcon;
175 case WalletType.monero:
176 return moneroIcon;
177 case WalletType.wownero:
178 return wowneroIcon;
179 case WalletType.litecoin:
180 return litecoinIcon;
181 case WalletType.haven:
182 return havenIcon;
183 case WalletType.ethereum:
184 return ethereumIcon;
185 case WalletType.bitcoinCash:
186 return bitcoinCashIcon;
187 case WalletType.nano:
188 return nanoIcon;
189 case WalletType.banano:
190 return bananoIcon;
191 case WalletType.polygon:
192 return polygonIcon;
193 case WalletType.solana:
194 return solanaIcon;
195 case WalletType.tron:
196 return tronIcon;
197 case WalletType.zano:
198 return zanoIcon;
199 case WalletType.decred:
200 return decredIcon;
201 case WalletType.dogecoin:
202 return dogeIcon;
203 case WalletType.base:
204 return baseIcon;
205 case WalletType.arbitrum:
206 return arbitrumIcon;
207 case WalletType.bsc:
208 return bscIcon;
209 case WalletType.zcash:
210 return zcashIcon;
211 case WalletType.none:
212 return nonWalletTypeIcon;
213 }
214 }
215
216 Future<void> _loadWallet(WalletListItem wallet) async {
217 if (SettingsStoreBase.walletPasswordDirectInput) {
218 Navigator.of(context).pushNamed(Routes.walletUnlockLoadable,
219 arguments: WalletUnlockArguments(
220 callback: (bool isAuthenticatedSuccessfully, AuthPageState auth) async {
221 if (isAuthenticatedSuccessfully) {
222 auth.close();
223 setState(() {});
224 }
225 },
226 walletName: wallet.name,
227 walletType: wallet.type));
228 return;
229 }
230
231 widget._authService.authenticateAction(
232 context,
233 onAuthSuccess: (isAuthenticatedSuccessfully) async {
234 if (!isAuthenticatedSuccessfully) return;
235
236 try {
237 if (mounted) {
238 changeProcessText(S.of(context).wallet_list_loading_wallet(wallet.name));
239 }
240 await widget.walletListViewModel.loadWallet(wallet);
241 hideProgressText();
242 setState(() {});
243 } catch (e) {
244 if (mounted) {
245 changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString()));
246 }
247 }
248 },
249 conditionToDetermineIfToUse2FA:
250 widget.walletListViewModel.shouldRequireTOTP2FAForAccessingWallet,
251 );
252 }
253
254 void _navigateToCreateWallet() {
255 if (isSingleCoin) {
256 widget._authService.authenticateAction(
257 context,
258 route: Routes.newWallet,
259 arguments: NewWalletArguments(type: widget.walletListViewModel.currentWalletType),
260 conditionToDetermineIfToUse2FA:
261 widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
262 );
263 } else {
264 widget._authService.authenticateAction(
265 context,
266 route: Routes.newWalletType,
267 conditionToDetermineIfToUse2FA:
268 widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets,
269 );
270 }
271 }
272
273 void _navigateToRestoreWallet() {
274 Navigator.of(context).pushNamed(Routes.restoreOptions, arguments: false);
275 }
276
277 void changeProcessText(String text) {
278 _progressBar = createBar<void>(text, context, duration: null)..show(context);
279 }
280
281 void hideProgressText() {
282 _progressBar?.dismiss();
283 _progressBar = null;
284 }
285 }