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 {