dev
dart 330 lines 12.8 KB
Raw
1 import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
3 import 'package:cake_wallet/di.dart';
4 import 'package:cake_wallet/src/screens/base_page.dart';
5 import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart';
6 import 'package:cake_wallet/entities/preferences_key.dart';
7 import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
8 import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
9 import 'package:cake_wallet/zcash/zcash.dart';
10 import 'package:cw_core/receive_page_option.dart';
11 import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option_picker.dart';
12 import 'package:cake_wallet/src/widgets/gradient_background.dart';
13 import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
14 import 'package:cake_wallet/utils/responsive_layout_util.dart';
15 import 'package:cake_wallet/utils/share_util.dart';
16 import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart';
17 import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
18 import 'package:cw_core/utils/print_verbose.dart';
19 import 'package:cw_core/wallet_type.dart';
20 import 'package:flutter/material.dart';
21 import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
22 import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
23 import 'package:cake_wallet/routes.dart';
24 import 'package:cake_wallet/generated/i18n.dart';
25 import 'package:flutter_mobx/flutter_mobx.dart';
26 import 'package:keyboard_actions/keyboard_actions.dart';
27 import 'package:mobx/mobx.dart';
28 import 'package:shared_preferences/shared_preferences.dart';
29
30 class AddressPage extends BasePage {
31 AddressPage({
32 required this.addressListViewModel,
33 required this.dashboardViewModel,
34 required this.receiveOptionViewModel,
35 }) : _cryptoAmountFocus = FocusNode(),
36 _formKey = GlobalKey<FormState>(),
37 _amountController = TextEditingController() {
38 _amountController.addListener(() {
39 if (_formKey.currentState!.validate()) {
40 addressListViewModel.changeAmount(_amountController.text);
41 }
42 });
43 }
44
45 final WalletAddressListViewModel addressListViewModel;
46 final DashboardViewModel dashboardViewModel;
47 final ReceiveOptionViewModel receiveOptionViewModel;
48 final TextEditingController _amountController;
49 final GlobalKey<FormState> _formKey;
50
51 final FocusNode _cryptoAmountFocus;
52
53 @override
54 bool get gradientBackground => true;
55
56 @override
57 bool get resizeToAvoidBottomInset => false;
58
59 bool effectsInstalled = false;
60
61 @override
62 Widget? leading(BuildContext context) {
63 final _backButton = Icon(
64 Icons.arrow_back_ios,
65 color: Theme.of(context).colorScheme.primary,
66 size: 16,
67 );
68 final _closeButton = currentTheme.isDark ? closeButtonImageDarkTheme : closeButtonImage;
69
70 bool isMobileView = responsiveLayoutUtil.shouldRenderMobileUI;
71
72 return MergeSemantics(
73 child: SizedBox(
74 height: isMobileView ? 37 : 45,
75 width: isMobileView ? 37 : 45,
76 child: ButtonTheme(
77 minWidth: double.minPositive,
78 child: Semantics(
79 label: !isMobileView ? S.of(context).close : S.of(context).seed_alert_back,
80 child: TextButton(
81 style: ButtonStyle(
82 overlayColor: WidgetStateColor.resolveWith((states) => Colors.transparent),
83 ),
84 onPressed: () => onClose(context),
85 child: !isMobileView ? _closeButton : _backButton,
86 ),
87 ),
88 ),
89 ),
90 );
91 }
92
93 @override
94 Widget middle(BuildContext context) => PresentReceiveOptionPicker(
95 color: titleColor(context), receiveOptionViewModel: receiveOptionViewModel);
96
97 @override
98 Widget Function(BuildContext, Widget) get rootWrapper =>
99 (BuildContext context, Widget scaffold) => GradientBackground(scaffold: scaffold);
100
101 @override
102 Widget? trailing(BuildContext context) {
103 return Material(
104 color: Colors.transparent,
105 child: Semantics(
106 label: S.of(context).share,
107 child: IconButton(
108 padding: EdgeInsets.zero,
109 constraints: BoxConstraints(),
110 highlightColor: Colors.transparent,
111 splashColor: Colors.transparent,
112 iconSize: 25,
113 onPressed: () {
114 ShareUtil.share(
115 text: addressListViewModel.uri.toString(),
116 context: context,
117 );
118 },
119 icon: Icon(Icons.share, size: 20, color: Theme.of(context).colorScheme.primary),
120 ),
121 ),
122 );
123 }
124
125 @override
126 Widget body(BuildContext context) {
127 _setEffects(context);
128
129 return KeyboardActions(
130 autoScroll: false,
131 disableScroll: true,
132 tapOutsideBehavior: TapOutsideBehavior.translucentDismiss,
133 config: KeyboardActionsConfig(
134 keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
135 keyboardBarColor: Theme.of(context).colorScheme.surface,
136 nextFocus: false,
137 actions: [
138 KeyboardActionsItem(
139 focusNode: _cryptoAmountFocus,
140 toolbarButtons: [(_) => KeyboardDoneButton()],
141 )
142 ],
143 ),
144 child: Container(
145 padding: EdgeInsets.fromLTRB(24, 0, 24, 32),
146 child: Column(
147 children: <Widget>[
148 Expanded(
149 child: QRWidget(
150 formKey: _formKey,
151 addressListViewModel: addressListViewModel,
152 amountTextFieldFocusNode: _cryptoAmountFocus,
153 amountController: _amountController,
154 ),
155 ),
156 SizedBox(height: 16),
157 Observer(
158 builder: (_) {
159 if (addressListViewModel.hasAddressList) {
160 return SelectButton(
161 text: addressListViewModel.buttonTitle,
162 onTap: () => Navigator.of(context).pushNamed(Routes.receive),
163 textColor: Theme.of(context).colorScheme.onSecondaryContainer,
164 color: Theme.of(context).colorScheme.surfaceContainer,
165 arrowColor: Theme.of(context).colorScheme.onSurfaceVariant,
166 textSize: 14,
167 height: 50,
168 );
169 } else {
170 return const SizedBox();
171 }
172 },
173 ),
174 if (addressListViewModel.hasTokensList) ...[
175 Container(
176 padding: EdgeInsets.all(20),
177 decoration: BoxDecoration(
178 border: Border.all(color: Theme.of(context).colorScheme.surfaceContainer),
179 color: Theme.of(context).colorScheme.surfaceContainer,
180 borderRadius: BorderRadius.circular(18),
181 ),
182 child: Column(
183 children: [
184 Row(
185 mainAxisAlignment: MainAxisAlignment.center,
186 children: [
187 CakeImageWidget(
188 imageUrl: addressListViewModel.monoImage,
189 height: 16,
190 width: 16,
191 color: Theme.of(context).colorScheme.onSurface,
192 ),
193 SizedBox(width: 10),
194 Text(
195 '${S.current.your} ${addressListViewModel.walletTypeName} ${S.current.address}',
196 style: Theme.of(context).textTheme.titleMedium?.copyWith(
197 fontWeight: FontWeight.w500,
198 ),
199 ),
200 ],
201 ),
202 SizedBox(height: 10),
203 Text(
204 '${S.current.qr_instruction} ${addressListViewModel.walletTypeName}',
205 textAlign: TextAlign.center,
206 style: Theme.of(context).textTheme.bodyMedium?.copyWith(
207 color: Theme.of(context).colorScheme.onSurfaceVariant,
208 fontWeight: FontWeight.w500,
209 ),
210 ),
211 SizedBox(height: 20),
212 Observer(
213 builder: (_) {
214 final walletImages = addressListViewModel
215 .getWalletImages(addressListViewModel.selectedChainId);
216 return Center(
217 child: SizedBox(
218 height: 40,
219 width: walletImages.length * 32.0,
220 child: Stack(
221 children: [
222 for (int i = walletImages.length - 1; i >= 0; i--)
223 Positioned(
224 left: i * 25.0,
225 child: Container(
226 decoration: BoxDecoration(
227 border: Border.all(
228 color: Theme.of(context).colorScheme.surfaceContainer,
229 width: 3,
230 ),
231 color: Theme.of(context).colorScheme.surfaceContainer,
232 borderRadius: BorderRadius.circular(24),
233 ),
234 child: ClipOval(
235 child: CakeImageWidget(
236 height: 35,
237 width: 35,
238 imageUrl: walletImages[i],
239 color: walletImages.last == walletImages[i]
240 ? Theme.of(context).colorScheme.onSurfaceVariant
241 : null,
242 ),
243 ),
244 ),
245 ),
246 ],
247 ),
248 ),
249 );
250 },
251 ),
252 ],
253 ),
254 ),
255 SizedBox(height: 48),
256 ],
257 ],
258 ),
259 ),
260 );
261 }
262
263 void _setEffects(BuildContext context) {
264 if (effectsInstalled) {
265 return;
266 }
267
268 reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) {
269 if (dashboardViewModel.type == WalletType.bitcoin &&
270 bitcoin!.isBitcoinReceivePageOption(option)) {
271 addressListViewModel.setAddressType(bitcoin!.getOptionToType(option));
272 return;
273 }
274 if (dashboardViewModel.type == WalletType.zcash) {
275 addressListViewModel.setAddressType(zcash!.getOptionToType(option));
276 return;
277 }
278
279 switch (option) {
280 case ReceivePageOption.anonPayInvoice:
281 Navigator.pushNamed(
282 context,
283 Routes.anonPayInvoicePage,
284 arguments: [addressListViewModel.address.address, option],
285 );
286 break;
287 case ReceivePageOption.anonPayDonationLink:
288 final sharedPreferences = getIt.get<SharedPreferences>();
289 final clearnetUrl = sharedPreferences.getString(PreferencesKey.clearnetDonationLink);
290 final onionUrl = sharedPreferences.getString(PreferencesKey.onionDonationLink);
291 final donationWalletName =
292 sharedPreferences.getString(PreferencesKey.donationLinkWalletName);
293
294 if (clearnetUrl != null &&
295 onionUrl != null &&
296 addressListViewModel.wallet.name == donationWalletName) {
297 Navigator.pushNamed(
298 context,
299 Routes.anonPayReceivePage,
300 arguments: AnonPayReceivePageArgs(
301 invoiceInfo: AnonpayDonationLinkInfo(
302 clearnetUrl: clearnetUrl,
303 onionUrl: onionUrl,
304 address: addressListViewModel.address.address,
305 ),
306 qrImage: addressListViewModel.qrImage,
307 ),
308 );
309 } else {
310 Navigator.pushNamed(
311 context,
312 Routes.anonPayInvoicePage,
313 arguments: [addressListViewModel.address.address, option],
314 );
315 }
316 break;
317 default:
318 if ([WalletType.bitcoin, WalletType.litecoin].contains(addressListViewModel.type)) {
319 addressListViewModel.setAddressType(bitcoin!.getBitcoinAddressType(option));
320 }
321 if (addressListViewModel.type == WalletType.zcash) {
322 printV("help me i'll kms if that wont work: ${zcash!.getZcashAddressType(option)}");
323 addressListViewModel.setAddressType(zcash!.getZcashAddressType(option));
324 }
325 }
326 });
327
328 effectsInstalled = true;
329 }
330 }