1
-import 'package:cake_wallet/buy/buy_amount.dart';
2
-import 'package:cake_wallet/buy/buy_provider.dart';
3
-import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
4
-import 'package:cake_wallet/entities/fiat_currency.dart';
5
-import 'package:cake_wallet/src/widgets/picker.dart';
6
-import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
7
-import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
8
-import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
9
-import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
10
-import 'package:cw_core/wallet_type.dart';
11
-import 'package:cake_wallet/src/screens/buy/widgets/buy_list_item.dart';
12
-import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
13
-import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
14
-import 'package:cake_wallet/utils/show_pop_up.dart';
15
-import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
16
-import 'package:flutter/material.dart';
17
-import 'package:flutter/services.dart';
18
-import 'package:flutter_mobx/flutter_mobx.dart';
19
-import 'package:keyboard_actions/keyboard_actions.dart';
20
-import 'package:cake_wallet/routes.dart';
21
-import 'package:cake_wallet/src/screens/base_page.dart';
22
-import 'package:cake_wallet/src/widgets/primary_button.dart';
23
-import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
24
-import 'package:cake_wallet/generated/i18n.dart';
25
-import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
26
-import 'package:cake_wallet/src/widgets/trail_button.dart';
27
-import 'package:mobx/mobx.dart';
28
-import 'package:url_launcher/url_launcher.dart';
29
-
30
-class PreOrderPage extends BasePage {
31
- PreOrderPage({required this.buyViewModel})
32
- : _amountFocus = FocusNode(),
33
- _amountController = TextEditingController() {
34
- _amountController.addListener(() {
35
- final amount = _amountController.text;
36
-
37
- if (amount != buyViewModel.buyAmountViewModel.amount) {
38
- buyViewModel.buyAmountViewModel.amount = amount;
39
- buyViewModel.selectedProvider = null;
40
- }
41
- });
42
-
43
- reaction((_) => buyViewModel.buyAmountViewModel.amount, (String amount) {
44
- if (_amountController.text != amount) {
45
- _amountController.text = amount;
46
- }
47
- if (amount.isEmpty) {
48
- buyViewModel.selectedProvider = null;
49
- buyViewModel.isShowProviderButtons = false;
50
- } else {
51
- buyViewModel.isShowProviderButtons = true;
52
- }
53
- });
54
- }
55
-
56
- static const _amountPattern = '^([0-9]+([.\,][0-9]{0,2})?|[.\,][0-9]{1,2})\$';
57
-
58
- final BuyViewModel buyViewModel;
59
- final FocusNode _amountFocus;
60
- final TextEditingController _amountController;
61
-
62
- @override
63
- String get title => S.current.buy + ' ' + walletTypeToString(buyViewModel.wallet.type);
64
-
65
- @override
66
- bool get resizeToAvoidBottomInset => false;
67
-
68
- @override
69
- bool get extendBodyBehindAppBar => true;
70
-
71
- @override
72
- AppBarStyle get appBarStyle => AppBarStyle.transparent;
73
-
74
- @override
75
- Widget trailing(context) => TrailButton(
76
- caption: S.of(context).clear,
77
- onPressed: () => buyViewModel.reset());
78
-
79
- @override
80
- Widget body(BuildContext context) {
81
- return KeyboardActions(
82
- config: KeyboardActionsConfig(
83
- keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
84
- keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
85
- nextFocus: false,
86
- actions: [
87
- KeyboardActionsItem(
88
- focusNode: _amountFocus,
89
- toolbarButtons: [(_) => KeyboardDoneButton()],
90
- ),
91
- ]),
92
- child: Container(
93
- height: 0,
94
- color: Theme.of(context).colorScheme.background,
95
- child: ScrollableWithBottomSection(
96
- contentPadding: EdgeInsets.only(bottom: 24),
97
- content: Observer(builder: (_) => Column(
98
- children: [
99
- Container(
100
- decoration: BoxDecoration(
101
- borderRadius: BorderRadius.only(
102
- bottomLeft: Radius.circular(24),
103
- bottomRight: Radius.circular(24)),
104
- gradient: LinearGradient(colors: [
105
- Theme.of(context).extension<SendPageTheme>()!.firstGradientColor,
106
- Theme.of(context).extension<SendPageTheme>()!.secondGradientColor,
107
- ], begin: Alignment.topLeft, end: Alignment.bottomRight),
108
- ),
109
- child: Padding(
110
- padding: EdgeInsets.only(top: 100, bottom: 65),
111
- child: Center(
112
- child: Container(
113
- width: 210,
114
- child: BaseTextFormField(
115
- focusNode: _amountFocus,
116
- controller: _amountController,
117
- keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
118
- inputFormatters: [FilteringTextInputFormatter.allow(RegExp(_amountPattern))],
119
- prefixIcon: GestureDetector(
120
- onTap: () {
121
- showPopUp<void>(
122
- context: context,
123
- builder: (_) => Picker(
124
- hintText: S.current.search_currency,
125
- items: FiatCurrency.currenciesAvailableToBuyWith,
126
- selectedAtIndex:
127
- FiatCurrency.currenciesAvailableToBuyWith.indexOf(buyViewModel.fiatCurrency),
128
- onItemSelected: (FiatCurrency selectedCurrency) {
129
- buyViewModel.buyAmountViewModel.fiatCurrency = selectedCurrency;
130
- },
131
- images: FiatCurrency.currenciesAvailableToBuyWith
132
- .map((e) => Image.asset("assets/images/flags/${e.countryCode}.png"))
133
- .toList(),
134
- isGridView: true,
135
- matchingCriteria: (FiatCurrency currency, String searchText) {
136
- return currency.title.toLowerCase().contains(searchText) ||
137
- currency.fullName.toLowerCase().contains(searchText);
138
- },
139
- ),
140
- );
141
- },
142
- child: Padding(
143
- padding: EdgeInsets.only(top: 2),
144
- child: Row(
145
- mainAxisSize: MainAxisSize.min,
146
- children: [
147
- Icon(Icons.keyboard_arrow_down, color: Colors.white),
148
- Text(
149
- buyViewModel.fiatCurrency.title + ': ',
150
- style: TextStyle(
151
- fontSize: 36,
152
- fontWeight: FontWeight.w600,
153
- color: Colors.white,
154
- ),
155
- ),
156
- ],
157
- ),
158
- ),
159
- ),
160
- hintText: '0.00',
161
- borderColor: Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderBottomPanelColor,
162
- borderWidth: 0.5,
163
- textStyle: TextStyle(fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white),
164
- placeholderTextStyle: TextStyle(
165
- color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor,
166
- fontWeight: FontWeight.w500,
167
- fontSize: 36,
168
- ),
169
- ),
170
- ),
171
- ),
172
- ),
173
- ),
174
- if (buyViewModel.isShowProviderButtons) Padding(
175
- padding: EdgeInsets.only(top: 38, bottom: 18),
176
- child: Text(
177
- S.of(context).buy_with + ':',
178
- textAlign: TextAlign.center,
179
- style: TextStyle(
180
- color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
181
- fontSize: 18,
182
- fontWeight: FontWeight.bold
183
- ),
184
- )
185
- ),
186
- if (buyViewModel.isShowProviderButtons)
187
- ...buyViewModel.items.map(
188
- (item) => Observer(builder: (_) =>
189
- FutureBuilder<BuyAmount>(
190
- future: item.buyAmount,
191
- builder: (context, AsyncSnapshot<BuyAmount> snapshot) {
192
- double sourceAmount;
193
- double destAmount;
194
- double achAmount;
195
- int minAmount;
196
-
197
- if (snapshot.hasData && snapshot.data != null) {
198
- sourceAmount = snapshot.data!.sourceAmount;
199
- destAmount = snapshot.data!.destAmount;
200
- minAmount = snapshot.data!.minAmount;
201
- achAmount = snapshot.data!.achSourceAmount ?? 0;
202
- } else {
203
- sourceAmount = 0.0;
204
- destAmount = 0.0;
205
- minAmount = 0;
206
- achAmount = 0;
207
- }
208
-
209
- return Padding(
210
- padding:
211
- EdgeInsets.only(left: 15, top: 20, right: 15),
212
- child: Observer(builder: (_) {
213
- return BuyListItem(
214
- selectedProvider:
215
- buyViewModel.selectedProvider,
216
- provider: item.provider,
217
- sourceAmount: sourceAmount,
218
- sourceCurrency: buyViewModel.fiatCurrency,
219
- destAmount: destAmount,
220
- destCurrency: buyViewModel.cryptoCurrency,
221
- achSourceAmount: achAmount,
222
- onTap: ((buyViewModel.doubleAmount != 0.0)
223
- && (snapshot.hasData)) ? () =>
224
- onSelectBuyProvider(
225
- context: context,
226
- provider: item.provider,
227
- sourceAmount: sourceAmount,
228
- minAmount: minAmount
229
- ) : null
230
- );
231
- })
232
- );
233
- }
234
- ))
235
- )
236
- ],
237
- )),
238
- bottomSectionPadding:
239
- EdgeInsets.only(left: 24, right: 24, bottom: 24),
240
- bottomSection: Observer(builder: (_) {
241
- return LoadingPrimaryButton(
242
- onPressed: () => onPresentProvider(context: context),
243
- text: buyViewModel.selectedProvider == null
244
- ? S.of(context).buy
245
- : S.of(context).buy_with +
246
- ' ${buyViewModel.selectedProvider!.description.title}',
247
- color: Theme.of(context).primaryColor,
248
- textColor: Colors.white,
249
- isLoading: buyViewModel.isRunning,
250
- isDisabled: (buyViewModel.selectedProvider == null) ||
251
- buyViewModel.isDisabled
252
- );
253
- })
254
- )
255
- )
256
- );
257
- }
258
-
259
- void onSelectBuyProvider({required BuildContext context, required BuyProvider provider,
260
- required double sourceAmount, required int minAmount}) {
261
-
262
- if ((provider is MoonPayBuyProvider)&&
263
- (buyViewModel.buyAmountViewModel.doubleAmount < minAmount)) {
264
- showPopUp<void>(
265
- context: context,
266
- builder: (BuildContext context) {
267
- return AlertWithOneAction(
268
- alertTitle: 'MoonPay',
269
- alertContent: S.of(context).moonpay_alert_text(
270
- minAmount.toString(),
271
- buyViewModel.fiatCurrency.toString()),
272
- buttonText: S.of(context).ok,
273
- buttonAction: () => Navigator.of(context).pop());
274
- });
275
- return;
276
- }
277
- buyViewModel.selectedProvider = provider;
278
- sourceAmount > 0
279
- ? buyViewModel.isDisabled = false
280
- : buyViewModel.isDisabled = true;
281
- }
282
-
283
- Future<void> onPresentProvider({required BuildContext context}) async {
284
- if (buyViewModel.isRunning) {
285
- return;
286
- }
287
-
288
- buyViewModel.isRunning = true;
289
- final url = await buyViewModel.fetchUrl();
290
-
291
- if (url.isNotEmpty) {
292
- if (buyViewModel.selectedProvider is MoonPayBuyProvider) {
293
- if (await canLaunch(url)) await launch(url);
294
- } else {
295
- await Navigator.of(context)
296
- .pushNamed(Routes.buyWebView,
297
- arguments: [url, buyViewModel]);
298
- }
299
- }
300
-
301
- buyViewModel.reset();
302
- buyViewModel.isRunning = false;
303
- }
304
-}