CAKE-306 | applied _saveOrder() to buy_webview_page.dart; fixed moonpay_buy_provider.dart and wyre_buy_provider.dart; canceled timers on exchange_trade_page.dart, order_details_page.dart and trade_details_page.dart; reworked pre_order_page.dart

OleksandrSobol committed Apr 15, 2021 at 20:10 UTC 1c976bfaa12e6868b0773e7585992d014626a4a9
12 files changed +238 -171
lib/buy/moonpay/moonpay_buy_provider.dart
+4 -5
@@ -23,6 +23,7 @@ class MoonPayBuyProvider extends BuyProvider {
23 static const _currenciesSuffix = '/v3/currencies';
24 static const _quoteSuffix = '/buy_quote';
25 static const _transactionsSuffix = '/v1/transactions';
26 + static const _fiatCurrency = 'USD';
27 static const _apiKey = secrets.moonPayApiKey;
28
29 @override
@@ -35,9 +36,7 @@ class MoonPayBuyProvider extends BuyProvider {
36 walletTypeToCryptoCurrency(walletType).title.toLowerCase();
37
38 @override
38 - String get trackUrl => isTestEnvironment
39 - ? ''
40 - : ''; // FIXME
39 + String get trackUrl => baseApiUrl + '/transaction_receipt?transactionId=';
40
41 String baseApiUrl;
42
@@ -103,8 +102,8 @@ class MoonPayBuyProvider extends BuyProvider {
102 id: id,
103 provider: description,
104 transferId: id,
106 - from: 'USD', //FIXME
107 - to: 'BTC', //FIXME
105 + from: _fiatCurrency,
106 + to: currencyCode.toUpperCase(),
107 state: state,
108 createdAt: createdAt,
109 amount: amount.toString(),
lib/buy/wyre/wyre_buy_provider.dart
+2 -1
@@ -28,6 +28,7 @@ class WyreBuyProvider extends BuyProvider {
28 static const _timeStampSuffix = '?timestamp=';
29 static const _transferSuffix = '/v2/transfer/';
30 static const _trackSuffix = '/track';
31 + static const _countryCode = 'US';
32 static const _secretKey = secrets.wyreSecretKey;
33 static const _accountId = secrets.wyreAccountId;
34
@@ -86,7 +87,7 @@ class WyreBuyProvider extends BuyProvider {
87 'destCurrency': walletTypeToCryptoCurrency(walletType).title,
88 'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress,
89 'accountId': _accountId,
89 - 'country': 'US' //FIXME
90 + 'country': _countryCode
91 };
92
93 final response = await post(quoteUrl,
lib/di.dart
+8 -3
@@ -544,9 +544,14 @@ Future setup(
544 return PreOrderPage(buyViewModel: getIt.get<BuyViewModel>());
545 });
546
547 - getIt.registerFactoryParam<BuyWebViewPage, String, void>((String url, _) =>
548 - BuyWebViewPage(getIt.get<BuyViewModel>(),
549 - ordersStore: getIt.get<OrdersStore>(), url: url));
547 + getIt.registerFactoryParam<BuyWebViewPage, List, void>(
548 + (List args, _) {
549 + final url = args.first as String;
550 + final buyViewModel = args[1] as BuyViewModel;
551 +
552 + return BuyWebViewPage(buyViewModel: buyViewModel,
553 + ordersStore: getIt.get<OrdersStore>(), url: url);
554 + });
555
556 getIt.registerFactoryParam<OrderDetailsViewModel, Order, void>(
557 (order, _) {
lib/router.dart
+4 -1
@@ -12,6 +12,7 @@ import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
12 import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
13 import 'package:cake_wallet/src/screens/support/support_page.dart';
14 import 'package:cake_wallet/store/settings_store.dart';
15 +import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
16 import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
17 import 'package:flutter/cupertino.dart';
18 import 'package:flutter/material.dart';
@@ -304,9 +305,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
305 getIt.get<PreOrderPage>());
306
307 case Routes.buyWebView:
308 + final args = settings.arguments as List;
309 +
310 return MaterialPageRoute<void>(
311 builder: (_) =>
309 - getIt.get<BuyWebViewPage>(param1: settings.arguments as String));
312 + getIt.get<BuyWebViewPage>(param1: args));
313
314 case Routes.restoreWalletFromSeedDetails:
315 final args = settings.arguments as List;
lib/src/screens/buy/buy_webview_page.dart
+33 -45
@@ -12,8 +12,8 @@ import 'package:flutter/material.dart';
12 import 'package:webview_flutter/webview_flutter.dart';
13
14 class BuyWebViewPage extends BasePage {
15 - BuyWebViewPage(this.buyViewModel,
16 - {@required this.ordersStore, @required this.url});
15 + BuyWebViewPage({@required this.buyViewModel,
16 + @required this.ordersStore, @required this.url});
17
18 final OrdersStore ordersStore;
19 final String url;
@@ -63,52 +63,11 @@ class BuyWebViewPageBodyState extends State<BuyWebViewPageBody> {
63 if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
64
65 if (_provider is WyreBuyProvider) {
66 - _timer?.cancel();
67 - _timer = Timer.periodic(Duration(seconds: 1), (timer) async {
68 -
69 - try {
70 - if (_webViewController == null || _isSaving) {
71 - return;
72 - }
73 -
74 - final url = await _webViewController.currentUrl();
75 -
76 - if (url.contains('completed')) {
77 - final urlParts = url.split('/');
78 - orderId = urlParts.last;
79 - widget.ordersStore.orderId = orderId;
80 -
81 - if (orderId.isNotEmpty) {
82 - _isSaving = true;
83 - await widget.buyViewModel.saveOrder(orderId);
84 - timer.cancel();
85 - }
86 - }
87 - } catch (e) {
88 - _isSaving = false;
89 - print(e);
90 - }
91 - });
66 + _saveOrder(keyword: 'completed', splitSymbol: '/');
67 }
68
69 if (_provider is MoonPayBuyProvider) {
95 - /*_timer?.cancel();
96 - _timer = Timer.periodic(Duration(seconds: 1), (timer) async {
97 -
98 - try {
99 - if (_webViewController == null || _isSaving) {
100 - return;
101 - }
102 -
103 - final url = await _webViewController.currentUrl();
104 - print('MoonPay Url = $url');
105 -
106 - timer.cancel();
107 - } catch (e) {
108 - _isSaving = false;
109 - print(e);
110 - }
111 - });*/
70 + _saveOrder(keyword: 'transactionId', splitSymbol: '=');
71 }
72 }
73
@@ -121,4 +80,33 @@ class BuyWebViewPageBodyState extends State<BuyWebViewPageBody> {
80 onWebViewCreated: (WebViewController controller) =>
81 setState(() => _webViewController = controller));
82 }
83 +
84 + void _saveOrder({String keyword, String splitSymbol}) {
85 + _timer?.cancel();
86 + _timer = Timer.periodic(Duration(seconds: 1), (timer) async {
87 +
88 + try {
89 + if (_webViewController == null || _isSaving) {
90 + return;
91 + }
92 +
93 + final url = await _webViewController.currentUrl();
94 +
95 + if (url.contains(keyword)) {
96 + final urlParts = url.split(splitSymbol);
97 + orderId = urlParts.last;
98 + widget.ordersStore.orderId = orderId;
99 +
100 + if (orderId.isNotEmpty) {
101 + _isSaving = true;
102 + await widget.buyViewModel.saveOrder(orderId);
103 + timer.cancel();
104 + }
105 + }
106 + } catch (e) {
107 + _isSaving = false;
108 + print(e);
109 + }
110 + });
111 + }
112 }
lib/src/screens/buy/pre_order_page.dart
+117 -110
@@ -27,9 +27,6 @@ class PreOrderPage extends BasePage {
27
28 if (amount != buyViewModel.buyAmountViewModel.amount) {
29 buyViewModel.buyAmountViewModel.amount = amount;
30 - }
31 -
32 - if (buyViewModel.buyAmountViewModel.doubleAmount == 0.0) {
30 buyViewModel.selectedProvider = null;
31 }
32 });
@@ -38,6 +35,12 @@ class PreOrderPage extends BasePage {
35 if (_amountController.text != amount) {
36 _amountController.text = amount;
37 }
38 + if (amount.isEmpty) {
39 + buyViewModel.selectedProvider = null;
40 + buyViewModel.isShowProviderButtons = false;
41 + } else {
42 + buyViewModel.isShowProviderButtons = true;
43 + }
44 });
45 }
46
@@ -88,120 +91,123 @@ class PreOrderPage extends BasePage {
91 color: Theme.of(context).backgroundColor,
92 child: ScrollableWithBottomSection(
93 contentPadding: EdgeInsets.only(bottom: 24),
91 - content: Column(
94 + content: Observer(builder: (_) => Column(
95 children: [
96 Container(
94 - decoration: BoxDecoration(
95 - borderRadius: BorderRadius.only(
96 - bottomLeft: Radius.circular(24),
97 - bottomRight: Radius.circular(24)),
98 - gradient: LinearGradient(colors: [
99 - Theme.of(context).primaryTextTheme.subhead.color,
100 - Theme.of(context)
101 - .primaryTextTheme
102 - .subhead
103 - .decorationColor,
104 - ], begin: Alignment.topLeft, end: Alignment.bottomRight),
105 - ),
106 - child: Padding(
107 - padding: EdgeInsets.only(top: 100, bottom: 65),
108 - child: Center(
109 - child: Container(
110 - width: 165,
111 - child: BaseTextFormField(
112 - focusNode: _amountFocus,
113 - controller: _amountController,
114 - keyboardType:
115 - TextInputType.numberWithOptions(
116 - signed: false, decimal: true),
117 - inputFormatters: [
118 - FilteringTextInputFormatter
119 - .allow(RegExp(_amountPattern))
120 - ],
121 - prefixIcon: Padding(
122 - padding: EdgeInsets.only(top: 2),
123 - child:
124 - Text(buyViewModel.fiatCurrency.title + ': ',
125 - style: TextStyle(
126 - fontSize: 36,
127 - fontWeight: FontWeight.w600,
128 - color: Colors.white,
129 - )),
130 - ),
131 - hintText: '0.00',
132 - borderColor: Theme.of(context)
133 - .primaryTextTheme
134 - .body2
135 - .decorationColor,
136 - borderWidth: 0.5,
137 - textStyle: TextStyle(
138 - fontSize: 36,
139 - fontWeight: FontWeight.w500,
140 - color: Colors.white),
141 - placeholderTextStyle: TextStyle(
142 - color: Theme.of(context)
143 - .primaryTextTheme
144 - .headline
145 - .decorationColor,
146 - fontWeight: FontWeight.w500,
147 - fontSize: 36),
148 - )
97 + decoration: BoxDecoration(
98 + borderRadius: BorderRadius.only(
99 + bottomLeft: Radius.circular(24),
100 + bottomRight: Radius.circular(24)),
101 + gradient: LinearGradient(colors: [
102 + Theme.of(context).primaryTextTheme.subhead.color,
103 + Theme.of(context)
104 + .primaryTextTheme
105 + .subhead
106 + .decorationColor,
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: 165,
114 + child: BaseTextFormField(
115 + focusNode: _amountFocus,
116 + controller: _amountController,
117 + keyboardType:
118 + TextInputType.numberWithOptions(
119 + signed: false, decimal: true),
120 + inputFormatters: [
121 + FilteringTextInputFormatter
122 + .allow(RegExp(_amountPattern))
123 + ],
124 + prefixIcon: Padding(
125 + padding: EdgeInsets.only(top: 2),
126 + child:
127 + Text(buyViewModel.fiatCurrency.title + ': ',
128 + style: TextStyle(
129 + fontSize: 36,
130 + fontWeight: FontWeight.w600,
131 + color: Colors.white,
132 + )),
133 + ),
134 + hintText: '0.00',
135 + borderColor: Theme.of(context)
136 + .primaryTextTheme
137 + .body2
138 + .decorationColor,
139 + borderWidth: 0.5,
140 + textStyle: TextStyle(
141 + fontSize: 36,
142 + fontWeight: FontWeight.w500,
143 + color: Colors.white),
144 + placeholderTextStyle: TextStyle(
145 + color: Theme.of(context)
146 + .primaryTextTheme
147 + .headline
148 + .decorationColor,
149 + fontWeight: FontWeight.w500,
150 + fontSize: 36),
151 + )
152 + )
153 )
150 - )
151 - )
154 + )
155 ),
153 - Padding(
154 - padding: EdgeInsets.only(top: 38, bottom: 18),
155 - child: Text(
156 - S.of(context).buy_with + ':',
157 - textAlign: TextAlign.center,
158 - style: TextStyle(
159 - color: Theme.of(context).primaryTextTheme.title.color,
160 - fontSize: 18,
161 - fontWeight: FontWeight.bold
162 - ),
163 - )
156 + if (buyViewModel.isShowProviderButtons) Padding(
157 + padding: EdgeInsets.only(top: 38, bottom: 18),
158 + child: Text(
159 + S.of(context).buy_with + ':',
160 + textAlign: TextAlign.center,
161 + style: TextStyle(
162 + color: Theme.of(context).primaryTextTheme.title.color,
163 + fontSize: 18,
164 + fontWeight: FontWeight.bold
165 + ),
166 + )
167 ),
165 - ...buyViewModel.items.map(
166 - (item) => Observer(builder: (_) => FutureBuilder<BuyAmount>(
167 - future: item.buyAmount,
168 - builder: (context, AsyncSnapshot<BuyAmount> snapshot) {
169 - double sourceAmount;
170 - double destAmount;
171 -
172 - if (snapshot.hasData) {
173 - sourceAmount = snapshot.data.sourceAmount;
174 - destAmount = snapshot.data.destAmount;
175 - } else {
176 - sourceAmount = 0.0;
177 - destAmount = 0.0;
168 + if (buyViewModel.isShowProviderButtons)
169 + ...buyViewModel.items.map(
170 + (item) => Observer(builder: (_) => FutureBuilder<BuyAmount>(
171 + future: item.buyAmount,
172 + builder: (context, AsyncSnapshot<BuyAmount> snapshot) {
173 + double sourceAmount;
174 + double destAmount;
175 +
176 + if (snapshot.hasData) {
177 + sourceAmount = snapshot.data.sourceAmount;
178 + destAmount = snapshot.data.destAmount;
179 + } else {
180 + sourceAmount = 0.0;
181 + destAmount = 0.0;
182 + }
183 +
184 + return Padding(
185 + padding:
186 + EdgeInsets.only(left: 15, top: 20, right: 15),
187 + child: Observer(builder: (_) {
188 + return BuyListItem(
189 + selectedProvider: buyViewModel.selectedProvider,
190 + provider: item.provider,
191 + sourceAmount: sourceAmount,
192 + sourceCurrency: buyViewModel.fiatCurrency,
193 + destAmount: destAmount,
194 + destCurrency: buyViewModel.cryptoCurrency,
195 + onTap:
196 + buyViewModel.buyAmountViewModel
197 + .doubleAmount == 0.0 ? null : () {
198 + buyViewModel.selectedProvider = item.provider;
199 + sourceAmount > 0
200 + ? buyViewModel.isDisabled = false
201 + : buyViewModel.isDisabled = true;
202 + }
203 + );
204 + })
205 + );
206 }
179 -
180 - return Padding(
181 - padding:
182 - EdgeInsets.only(left: 15, top: 20, right: 15),
183 - child: Observer(builder: (_) => BuyListItem(
184 - selectedProvider: buyViewModel.selectedProvider,
185 - provider: item.provider,
186 - sourceAmount: sourceAmount,
187 - sourceCurrency: buyViewModel.fiatCurrency,
188 - destAmount: destAmount,
189 - destCurrency: buyViewModel.cryptoCurrency,
190 - onTap:
191 - buyViewModel.buyAmountViewModel
192 - .doubleAmount == 0.0 ? null : () {
193 - buyViewModel.selectedProvider = item.provider;
194 - sourceAmount > 0
195 - ? buyViewModel.isDisabled = false
196 - : buyViewModel.isDisabled = true;
197 - }
198 - ))
199 - );
200 - }
201 - ),)
207 + ))
208 )
209 ],
204 - ),
210 + )),
211 bottomSectionPadding:
212 EdgeInsets.only(left: 24, right: 24, bottom: 24),
213 bottomSection: Observer(builder: (_) {
@@ -214,7 +220,8 @@ class PreOrderPage extends BasePage {
220 await buyViewModel.fetchUrl();
221 if (url.isNotEmpty) {
222 await Navigator.of(context)
217 - .pushNamed(Routes.buyWebView, arguments: url);
223 + .pushNamed(Routes.buyWebView,
224 + arguments: [url, buyViewModel]);
225 buyViewModel.reset();
226 }
227 buyViewModel.isRunning = false;
lib/src/screens/exchange_trade/exchange_trade_page.dart
+6
@@ -98,6 +98,12 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
98 showInformation(widget.exchangeTradeViewModel, context);
99 }
100
101 + @override
102 + void dispose() {
103 + super.dispose();
104 + widget.exchangeTradeViewModel.timer?.cancel();
105 + }
106 +
107 @override
108 Widget build(BuildContext context) {
109 final copyImage = Image.asset('assets/images/copy_content.png',
lib/src/screens/order_details/order_details_page.dart
+28 -1
@@ -19,7 +19,33 @@ class OrderDetailsPage extends BasePage {
19 final OrderDetailsViewModel orderDetailsViewModel;
20
21 @override
22 - Widget body(BuildContext context) {
22 + Widget body(BuildContext context) =>
23 + OrderDetailsPageBody(orderDetailsViewModel);
24 +}
25 +
26 +class OrderDetailsPageBody extends StatefulWidget {
27 + OrderDetailsPageBody(this.orderDetailsViewModel);
28 +
29 + final OrderDetailsViewModel orderDetailsViewModel;
30 +
31 + @override
32 + OrderDetailsPageBodyState createState() =>
33 + OrderDetailsPageBodyState(orderDetailsViewModel);
34 +}
35 +
36 +class OrderDetailsPageBodyState extends State<OrderDetailsPageBody> {
37 + OrderDetailsPageBodyState(this.orderDetailsViewModel);
38 +
39 + final OrderDetailsViewModel orderDetailsViewModel;
40 +
41 + @override
42 + void dispose() {
43 + super.dispose();
44 + orderDetailsViewModel.timer?.cancel();
45 + }
46 +
47 + @override
48 + Widget build(BuildContext context) {
49 return Observer(builder: (_) {
50 return SectionStandardList(
51 sectionCount: 1,
@@ -44,4 +70,5 @@ class OrderDetailsPage extends BasePage {
70 });
71 });
72 }
73 +
74 }
\ No newline at end of file
lib/src/screens/trade_details/trade_details_page.dart
+28 -1
@@ -19,7 +19,33 @@ class TradeDetailsPage extends BasePage {
19 final TradeDetailsViewModel tradeDetailsViewModel;
20
21 @override
22 - Widget body(BuildContext context) {
22 + Widget body(BuildContext context) =>
23 + TradeDetailsPageBody(tradeDetailsViewModel);
24 +}
25 +
26 +class TradeDetailsPageBody extends StatefulWidget {
27 + TradeDetailsPageBody(this.tradeDetailsViewModel);
28 +
29 + final TradeDetailsViewModel tradeDetailsViewModel;
30 +
31 + @override
32 + TradeDetailsPageBodyState createState() =>
33 + TradeDetailsPageBodyState(tradeDetailsViewModel);
34 +}
35 +
36 +class TradeDetailsPageBodyState extends State<TradeDetailsPageBody> {
37 + TradeDetailsPageBodyState(this.tradeDetailsViewModel);
38 +
39 + final TradeDetailsViewModel tradeDetailsViewModel;
40 +
41 + @override
42 + void dispose() {
43 + super.dispose();
44 + tradeDetailsViewModel.timer?.cancel();
45 + }
46 +
47 + @override
48 + Widget build(BuildContext context) {
49 return Observer(builder: (_) {
50 return SectionStandardList(
51 sectionCount: 1,
@@ -44,4 +70,5 @@ class TradeDetailsPage extends BasePage {
70 });
71 });
72 }
73 +
74 }
lib/view_model/buy/buy_view_model.dart
+4
@@ -29,6 +29,7 @@ abstract class BuyViewModelBase with Store {
29 .toList();
30 isRunning = false;
31 isDisabled = true;
32 + isShowProviderButtons = false;
33 }
34
35 final Box<Order> ordersSource;
@@ -51,6 +52,9 @@ abstract class BuyViewModelBase with Store {
52 @observable
53 bool isDisabled;
54
55 + @observable
56 + bool isShowProviderButtons;
57 +
58 WalletType get type => wallet.type;
59
60 double get doubleAmount => buyAmountViewModel.doubleAmount;
lib/view_model/exchange/exchange_trade_view_model.dart
+2 -2
@@ -45,7 +45,7 @@ abstract class ExchangeTradeViewModelBase with Store {
45
46 _updateTrade();
47
48 - _timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
48 + timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
49 }
50
51 final WalletBase wallet;
@@ -71,7 +71,7 @@ abstract class ExchangeTradeViewModelBase with Store {
71
72 ExchangeProvider _provider;
73
74 - Timer _timer;
74 + Timer timer;
75
76 @action
77 Future confirmSending() async {
lib/view_model/trade_details_view_model.dart
+2 -2
@@ -39,7 +39,7 @@ abstract class TradeDetailsViewModelBase with Store {
39
40 _updateTrade();
41
42 - _timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
42 + timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
43 }
44
45 final Box<Trade> trades;
@@ -52,7 +52,7 @@ abstract class TradeDetailsViewModelBase with Store {
52
53 ExchangeProvider _provider;
54
55 - Timer _timer;
55 + Timer timer;
56
57 @action
58 Future<void> _updateTrade() async {