mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-11-17 01:37:40 +00:00
Merge pull request #142 from cake-tech/CAKE-306-add-moonpay-option-for-btc-buying
Cake 306 add moonpay option for btc buying
This commit is contained in:
commit
f946037ea9
55 changed files with 1334 additions and 307 deletions
BIN
assets/images/moonpay-icon.png
Normal file
BIN
assets/images/moonpay-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
12
lib/buy/buy_amount.dart
Normal file
12
lib/buy/buy_amount.dart
Normal file
|
@ -0,0 +1,12 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class BuyAmount {
|
||||
BuyAmount({
|
||||
@required this.sourceAmount,
|
||||
@required this.destAmount,
|
||||
this.minAmount = 0});
|
||||
|
||||
final double sourceAmount;
|
||||
final double destAmount;
|
||||
final int minAmount;
|
||||
}
|
12
lib/buy/buy_exception.dart
Normal file
12
lib/buy/buy_exception.dart
Normal file
|
@ -0,0 +1,12 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
|
||||
class BuyException implements Exception {
|
||||
BuyException({@required this.description, @required this.text});
|
||||
|
||||
final BuyProviderDescription description;
|
||||
final String text;
|
||||
|
||||
@override
|
||||
String toString() => '${description.title}: $text';
|
||||
}
|
27
lib/buy/buy_provider.dart
Normal file
27
lib/buy/buy_provider.dart
Normal file
|
@ -0,0 +1,27 @@
|
|||
import 'package:cake_wallet/buy/buy_amount.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
|
||||
abstract class BuyProvider {
|
||||
BuyProvider({this.wallet, this.isTestEnvironment});
|
||||
|
||||
final WalletBase wallet;
|
||||
final bool isTestEnvironment;
|
||||
|
||||
String get title;
|
||||
BuyProviderDescription get description;
|
||||
String get trackUrl;
|
||||
|
||||
WalletType get walletType => wallet.type;
|
||||
String get walletAddress => wallet.address;
|
||||
String get walletId => wallet.id;
|
||||
|
||||
@override
|
||||
String toString() => title;
|
||||
|
||||
Future<String> requestUrl(String amount, String sourceCurrency);
|
||||
Future<Order> findOrderById(String id);
|
||||
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency);
|
||||
}
|
21
lib/buy/buy_provider_description.dart
Normal file
21
lib/buy/buy_provider_description.dart
Normal file
|
@ -0,0 +1,21 @@
|
|||
import 'package:cake_wallet/entities/enumerable_item.dart';
|
||||
|
||||
class BuyProviderDescription extends EnumerableItem<int>
|
||||
with Serializable<int> {
|
||||
const BuyProviderDescription({String title, int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
static const wyre = BuyProviderDescription(title: 'Wyre', raw: 0);
|
||||
static const moonPay = BuyProviderDescription(title: 'MoonPay', raw: 1);
|
||||
|
||||
static BuyProviderDescription deserialize({int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return wyre;
|
||||
case 1:
|
||||
return moonPay;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
28
lib/buy/get_buy_provider_icon.dart
Normal file
28
lib/buy/get_buy_provider_icon.dart
Normal file
|
@ -0,0 +1,28 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
|
||||
Image getBuyProviderIcon(BuyProviderDescription providerDescription,
|
||||
{bool isWhiteIconColor = false}) {
|
||||
|
||||
final _wyreIcon =
|
||||
Image.asset('assets/images/wyre-icon.png', width: 36, height: 36);
|
||||
final _moonPayWhiteIcon =
|
||||
Image.asset('assets/images/moonpay-icon.png', color: Colors.white,
|
||||
width: 36, height: 34);
|
||||
final _moonPayBlackIcon =
|
||||
Image.asset('assets/images/moonpay-icon.png', color: Colors.black,
|
||||
width: 36, height: 34);
|
||||
|
||||
if (providerDescription != null) {
|
||||
switch (providerDescription) {
|
||||
case BuyProviderDescription.wyre:
|
||||
return _wyreIcon;
|
||||
case BuyProviderDescription.moonPay:
|
||||
return isWhiteIconColor ? _moonPayWhiteIcon : _moonPayBlackIcon;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
144
lib/buy/moonpay/moonpay_buy_provider.dart
Normal file
144
lib/buy/moonpay/moonpay_buy_provider.dart
Normal file
|
@ -0,0 +1,144 @@
|
|||
import 'dart:convert';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:cake_wallet/buy/buy_exception.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:cake_wallet/buy/buy_amount.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
|
||||
class MoonPayBuyProvider extends BuyProvider {
|
||||
MoonPayBuyProvider({WalletBase wallet, bool isTestEnvironment = false})
|
||||
: super(wallet: wallet, isTestEnvironment: isTestEnvironment) {
|
||||
baseUrl = isTestEnvironment ? _baseTestUrl : _baseProductUrl;
|
||||
}
|
||||
|
||||
static const _baseTestUrl = 'https://buy-staging.moonpay.com';
|
||||
static const _baseProductUrl = 'https://buy.moonpay.com';
|
||||
static const _apiUrl = 'https://api.moonpay.com';
|
||||
static const _currenciesSuffix = '/v3/currencies';
|
||||
static const _quoteSuffix = '/buy_quote';
|
||||
static const _transactionsSuffix = '/v1/transactions';
|
||||
static const _ipAddressSuffix = '/v4/ip_address';
|
||||
static const _apiKey = secrets.moonPayApiKey;
|
||||
static const _secretKey = secrets.moonPaySecretKey;
|
||||
|
||||
@override
|
||||
String get title => 'MoonPay';
|
||||
|
||||
@override
|
||||
BuyProviderDescription get description => BuyProviderDescription.moonPay;
|
||||
|
||||
String get currencyCode =>
|
||||
walletTypeToCryptoCurrency(walletType).title.toLowerCase();
|
||||
|
||||
@override
|
||||
String get trackUrl => baseUrl + '/transaction_receipt?transactionId=';
|
||||
|
||||
String baseUrl;
|
||||
|
||||
@override
|
||||
Future<String> requestUrl(String amount, String sourceCurrency) async {
|
||||
final enabledPaymentMethods =
|
||||
'credit_debit_card%2Capple_pay%2Cgoogle_pay%2Csamsung_pay'
|
||||
'%2Csepa_bank_transfer%2Cgbp_bank_transfer%2Cgbp_open_banking_payment';
|
||||
|
||||
final suffix = '?apiKey=' + _apiKey + '¤cyCode=' +
|
||||
currencyCode + '&enabledPaymentMethods=' + enabledPaymentMethods +
|
||||
'&walletAddress=' + walletAddress +
|
||||
'&baseCurrencyCode=' + sourceCurrency.toLowerCase() +
|
||||
'&baseCurrencyAmount=' + amount + '&lockAmount=true' +
|
||||
'&showAllCurrencies=false' + '&showWalletAddressForm=false';
|
||||
|
||||
final originalUrl = baseUrl + suffix;
|
||||
|
||||
final messageBytes = utf8.encode(suffix);
|
||||
final key = utf8.encode(_secretKey);
|
||||
final hmac = Hmac(sha256, key);
|
||||
final digest = hmac.convert(messageBytes);
|
||||
final signature = base64.encode(digest.bytes);
|
||||
final urlWithSignature = originalUrl +
|
||||
'&signature=${Uri.encodeComponent(signature)}';
|
||||
|
||||
return isTestEnvironment ? originalUrl : urlWithSignature;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) async {
|
||||
final url = _apiUrl + _currenciesSuffix + '/$currencyCode' +
|
||||
_quoteSuffix + '/?apiKey=' + _apiKey +
|
||||
'&baseCurrencyAmount=' + amount +
|
||||
'&baseCurrencyCode=' + sourceCurrency.toLowerCase();
|
||||
|
||||
final response = await get(url);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw BuyException(
|
||||
description: description,
|
||||
text: 'Quote is not found!');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final sourceAmount = responseJSON['totalAmount'] as double;
|
||||
final destAmount = responseJSON['quoteCurrencyAmount'] as double;
|
||||
final minSourceAmount = responseJSON['baseCurrency']['minAmount'] as int;
|
||||
|
||||
return BuyAmount(
|
||||
sourceAmount: sourceAmount,
|
||||
destAmount: destAmount,
|
||||
minAmount: minSourceAmount);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Order> findOrderById(String id) async {
|
||||
final url = _apiUrl + _transactionsSuffix + '/$id' +
|
||||
'?apiKey=' + _apiKey;
|
||||
|
||||
final response = await get(url);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw BuyException(
|
||||
description: description,
|
||||
text: 'Transaction $id is not found!');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final status = responseJSON['status'] as String;
|
||||
final state = TradeState.deserialize(raw: status);
|
||||
final createdAtRaw = responseJSON['createdAt'] as String;
|
||||
final createdAt = DateTime.parse(createdAtRaw).toLocal();
|
||||
final amount = responseJSON['quoteCurrencyAmount'] as double;
|
||||
|
||||
return Order(
|
||||
id: id,
|
||||
provider: description,
|
||||
transferId: id,
|
||||
state: state,
|
||||
createdAt: createdAt,
|
||||
amount: amount.toString(),
|
||||
receiveAddress: walletAddress,
|
||||
walletId: walletId
|
||||
);
|
||||
}
|
||||
|
||||
static Future<bool> onEnabled() async {
|
||||
final url = _apiUrl + _ipAddressSuffix + '?apiKey=' + _apiKey;
|
||||
var isBuyEnable = false;
|
||||
|
||||
final response = await get(url);
|
||||
|
||||
try {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
isBuyEnable = responseJSON['isBuyAllowed'] as bool;
|
||||
} catch (e) {
|
||||
isBuyEnable = false;
|
||||
print(e.toString());
|
||||
}
|
||||
|
||||
return isBuyEnable;
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/entities/format_amount.dart';
|
||||
|
@ -8,6 +9,7 @@ part 'order.g.dart';
|
|||
class Order extends HiveObject {
|
||||
Order(
|
||||
{this.id,
|
||||
BuyProviderDescription provider,
|
||||
this.transferId,
|
||||
this.from,
|
||||
this.to,
|
||||
|
@ -16,7 +18,8 @@ class Order extends HiveObject {
|
|||
this.amount,
|
||||
this.receiveAddress,
|
||||
this.walletId})
|
||||
: stateRaw = state?.raw;
|
||||
: providerRaw = provider?.raw,
|
||||
stateRaw = state?.raw;
|
||||
|
||||
static const typeId = 8;
|
||||
static const boxName = 'Orders';
|
||||
|
@ -51,5 +54,11 @@ class Order extends HiveObject {
|
|||
@HiveField(8)
|
||||
String walletId;
|
||||
|
||||
@HiveField(9)
|
||||
int providerRaw;
|
||||
|
||||
BuyProviderDescription get provider =>
|
||||
BuyProviderDescription.deserialize(raw: providerRaw);
|
||||
|
||||
String amountFormatted() => formatAmount(amount);
|
||||
}
|
163
lib/buy/wyre/wyre_buy_provider.dart
Normal file
163
lib/buy/wyre/wyre_buy_provider.dart
Normal file
|
@ -0,0 +1,163 @@
|
|||
import 'dart:convert';
|
||||
import 'package:cake_wallet/buy/buy_exception.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:cake_wallet/buy/buy_amount.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
|
||||
class WyreBuyProvider extends BuyProvider {
|
||||
WyreBuyProvider({WalletBase wallet, bool isTestEnvironment = false})
|
||||
: super(wallet: wallet, isTestEnvironment: isTestEnvironment) {
|
||||
baseApiUrl = isTestEnvironment
|
||||
? _baseTestApiUrl
|
||||
: _baseProductApiUrl;
|
||||
}
|
||||
|
||||
static const _baseTestApiUrl = 'https://api.testwyre.com';
|
||||
static const _baseProductApiUrl = 'https://api.sendwyre.com';
|
||||
static const _trackTestUrl = 'https://dash.testwyre.com/track/';
|
||||
static const _trackProductUrl = 'https://dash.sendwyre.com/track/';
|
||||
static const _ordersSuffix = '/v3/orders';
|
||||
static const _reserveSuffix = '/reserve';
|
||||
static const _quoteSuffix = '/quote/partner';
|
||||
static const _timeStampSuffix = '?timestamp=';
|
||||
static const _transferSuffix = '/v2/transfer/';
|
||||
static const _trackSuffix = '/track';
|
||||
static const _countryCode = 'US';
|
||||
static const _secretKey = secrets.wyreSecretKey;
|
||||
static const _accountId = secrets.wyreAccountId;
|
||||
|
||||
@override
|
||||
String get title => 'Wyre';
|
||||
|
||||
@override
|
||||
BuyProviderDescription get description => BuyProviderDescription.wyre;
|
||||
|
||||
@override
|
||||
String get trackUrl => isTestEnvironment
|
||||
? _trackTestUrl
|
||||
: _trackProductUrl;
|
||||
|
||||
String baseApiUrl;
|
||||
|
||||
@override
|
||||
Future<String> requestUrl(String amount, String sourceCurrency) async {
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
final url = baseApiUrl + _ordersSuffix + _reserveSuffix +
|
||||
_timeStampSuffix + timestamp;
|
||||
final body = {
|
||||
'amount': amount,
|
||||
'sourceCurrency': sourceCurrency,
|
||||
'destCurrency': walletTypeToCryptoCurrency(walletType).title,
|
||||
'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress,
|
||||
'referrerAccountId': _accountId,
|
||||
'lockFields': ['amount', 'sourceCurrency', 'destCurrency', 'dest']
|
||||
};
|
||||
|
||||
final response = await post(url,
|
||||
headers: {
|
||||
'Authorization': 'Bearer $_secretKey',
|
||||
'Content-Type': 'application/json',
|
||||
'cache-control': 'no-cache'
|
||||
},
|
||||
body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw BuyException(
|
||||
description: description,
|
||||
text: 'Url $url is not found!');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final urlFromResponse = responseJSON['url'] as String;
|
||||
return urlFromResponse;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BuyAmount> calculateAmount(String amount, String sourceCurrency) async {
|
||||
final quoteUrl = _baseProductApiUrl + _ordersSuffix + _quoteSuffix;
|
||||
final body = {
|
||||
'amount': amount,
|
||||
'sourceCurrency': sourceCurrency,
|
||||
'destCurrency': walletTypeToCryptoCurrency(walletType).title,
|
||||
'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress,
|
||||
'accountId': _accountId,
|
||||
'country': _countryCode
|
||||
};
|
||||
|
||||
final response = await post(quoteUrl,
|
||||
headers: {
|
||||
'Authorization': 'Bearer $_secretKey',
|
||||
'Content-Type': 'application/json',
|
||||
'cache-control': 'no-cache'
|
||||
},
|
||||
body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw BuyException(
|
||||
description: description,
|
||||
text: 'Quote is not found!');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final sourceAmount = responseJSON['sourceAmount'] as double;
|
||||
final destAmount = responseJSON['destAmount'] as double;
|
||||
|
||||
return BuyAmount(sourceAmount: sourceAmount, destAmount: destAmount);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Order> findOrderById(String id) async {
|
||||
final orderUrl = baseApiUrl + _ordersSuffix + '/$id';
|
||||
final orderResponse = await get(orderUrl);
|
||||
|
||||
if (orderResponse.statusCode != 200) {
|
||||
throw BuyException(
|
||||
description: description,
|
||||
text: 'Order $id is not found!');
|
||||
}
|
||||
|
||||
final orderResponseJSON =
|
||||
json.decode(orderResponse.body) as Map<String, dynamic>;
|
||||
final transferId = orderResponseJSON['transferId'] as String;
|
||||
final from = orderResponseJSON['sourceCurrency'] as String;
|
||||
final to = orderResponseJSON['destCurrency'] as String;
|
||||
final status = orderResponseJSON['status'] as String;
|
||||
final state = TradeState.deserialize(raw: status.toLowerCase());
|
||||
final createdAtRaw = orderResponseJSON['createdAt'] as int;
|
||||
final createdAt =
|
||||
DateTime.fromMillisecondsSinceEpoch(createdAtRaw).toLocal();
|
||||
|
||||
final transferUrl =
|
||||
baseApiUrl + _transferSuffix + transferId + _trackSuffix;
|
||||
final transferResponse = await get(transferUrl);
|
||||
|
||||
if (transferResponse.statusCode != 200) {
|
||||
throw BuyException(
|
||||
description: description,
|
||||
text: 'Transfer $transferId is not found!');
|
||||
}
|
||||
|
||||
final transferResponseJSON =
|
||||
json.decode(transferResponse.body) as Map<String, dynamic>;
|
||||
final amount = transferResponseJSON['destAmount'] as double;
|
||||
|
||||
return Order(
|
||||
id: id,
|
||||
provider: description,
|
||||
transferId: transferId,
|
||||
from: from,
|
||||
to: to,
|
||||
state: state,
|
||||
createdAt: createdAt,
|
||||
amount: amount.toString(),
|
||||
receiveAddress: walletAddress,
|
||||
walletId: walletId
|
||||
);
|
||||
}
|
||||
}
|
47
lib/di.dart
47
lib/di.dart
|
@ -4,11 +4,9 @@ import 'package:cake_wallet/core/backup_service.dart';
|
|||
import 'package:cake_wallet/core/wallet_service.dart';
|
||||
import 'package:cake_wallet/entities/biometric_auth.dart';
|
||||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/entities/load_current_wallet.dart';
|
||||
import 'package:cake_wallet/entities/order.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||
import 'package:cake_wallet/entities/transaction_info.dart';
|
||||
import 'package:cake_wallet/entities/wyre_service.dart';
|
||||
import 'package:cake_wallet/monero/monero_wallet_service.dart';
|
||||
import 'package:cake_wallet/entities/contact.dart';
|
||||
import 'package:cake_wallet/entities/node.dart';
|
||||
|
@ -16,7 +14,8 @@ import 'package:cake_wallet/exchange/trade.dart';
|
|||
import 'package:cake_wallet/reactions/on_authentication_state_change.dart';
|
||||
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
|
||||
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
|
||||
|
||||
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
|
||||
import 'package:cake_wallet/src/screens/buy/pre_order_page.dart';
|
||||
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
|
||||
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
|
||||
|
@ -43,7 +42,6 @@ import 'package:cake_wallet/src/screens/transaction_details/transaction_details_
|
|||
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
|
||||
import 'package:cake_wallet/src/screens/wyre/wyre_page.dart';
|
||||
import 'package:cake_wallet/store/dashboard/orders_store.dart';
|
||||
import 'package:cake_wallet/store/node_list_store.dart';
|
||||
import 'package:cake_wallet/store/secret_store.dart';
|
||||
|
@ -62,6 +60,8 @@ import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.d
|
|||
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
|
||||
import 'package:cake_wallet/store/wallet_list_store.dart';
|
||||
import 'package:cake_wallet/view_model/backup_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/edit_backup_password_view_model.dart';
|
||||
|
@ -90,7 +90,6 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
|||
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wyre_view_model.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
@ -153,6 +152,7 @@ Future setup(
|
|||
final isBitcoinBuyEnabled = (secrets.wyreSecretKey?.isNotEmpty ?? false) &&
|
||||
(secrets.wyreApiKey?.isNotEmpty ?? false) &&
|
||||
(secrets.wyreAccountId?.isNotEmpty ?? false);
|
||||
|
||||
final settingsStore = await SettingsStoreBase.load(
|
||||
nodeSource: _nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled);
|
||||
|
||||
|
@ -243,9 +243,7 @@ Future setup(
|
|||
tradeFilterStore: getIt.get<TradeFilterStore>(),
|
||||
transactionFilterStore: getIt.get<TransactionFilterStore>(),
|
||||
settingsStore: settingsStore,
|
||||
ordersSource: _ordersSource,
|
||||
ordersStore: getIt.get<OrdersStore>(),
|
||||
wyreViewModel: getIt.get<WyreViewModel>(),
|
||||
pushNotificationsService: getIt.get<PushNotificationsService>()));
|
||||
|
||||
getIt.registerFactory<AuthService>(() => AuthService(
|
||||
|
@ -542,22 +540,37 @@ Future setup(
|
|||
getIt.registerFactoryParam<TradeDetailsPage, Trade, void>((Trade trade, _) =>
|
||||
TradeDetailsPage(getIt.get<TradeDetailsViewModel>(param1: trade)));
|
||||
|
||||
getIt.registerFactory(() => BuyAmountViewModel());
|
||||
|
||||
getIt.registerFactory(() {
|
||||
return WyreService(appStore: getIt.get<AppStore>());
|
||||
final wallet = getIt.get<AppStore>().wallet;
|
||||
|
||||
return BuyViewModel(_ordersSource, getIt.get<OrdersStore>(),
|
||||
getIt.get<SettingsStore>(), getIt.get<BuyAmountViewModel>(),
|
||||
wallet: wallet);
|
||||
});
|
||||
|
||||
getIt.registerFactory(() {
|
||||
return WyreViewModel(ordersSource, getIt.get<OrdersStore>(),
|
||||
wyreService: getIt.get<WyreService>());
|
||||
return PreOrderPage(buyViewModel: getIt.get<BuyViewModel>());
|
||||
});
|
||||
|
||||
getIt.registerFactoryParam<WyrePage, String, void>((String url, _) =>
|
||||
WyrePage(getIt.get<WyreViewModel>(),
|
||||
ordersStore: getIt.get<OrdersStore>(), url: url));
|
||||
getIt.registerFactoryParam<BuyWebViewPage, List, void>(
|
||||
(List args, _) {
|
||||
final url = args.first as String;
|
||||
final buyViewModel = args[1] as BuyViewModel;
|
||||
|
||||
getIt.registerFactoryParam<OrderDetailsViewModel, Order, void>((order, _) =>
|
||||
OrderDetailsViewModel(
|
||||
wyreViewModel: getIt.get<WyreViewModel>(), orderForDetails: order));
|
||||
return BuyWebViewPage(buyViewModel: buyViewModel,
|
||||
ordersStore: getIt.get<OrdersStore>(), url: url);
|
||||
});
|
||||
|
||||
getIt.registerFactoryParam<OrderDetailsViewModel, Order, void>(
|
||||
(order, _) {
|
||||
final wallet = getIt.get<AppStore>().wallet;
|
||||
|
||||
return OrderDetailsViewModel(
|
||||
wallet: wallet,
|
||||
orderForDetails: order);
|
||||
});
|
||||
|
||||
getIt.registerFactoryParam<OrderDetailsPage, Order, void>((Order order, _) =>
|
||||
OrderDetailsPage(getIt.get<OrderDetailsViewModel>(param1: order)));
|
||||
|
|
|
@ -21,7 +21,7 @@ class PushNotificationsService {
|
|||
_firebaseMessaging.requestNotificationPermissions();
|
||||
_firebaseMessaging.configure(
|
||||
onMessage: (message) async {
|
||||
Map<dynamic, dynamic> alert = {};
|
||||
Map<dynamic, dynamic> alert = <dynamic, dynamic>{};
|
||||
String msg = '';
|
||||
String title = '';
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
class WyreException implements Exception {
|
||||
WyreException(this.description);
|
||||
|
||||
String description;
|
||||
|
||||
@override
|
||||
String toString() => description;
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'package:cake_wallet/entities/wyre_exception.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
import 'package:cake_wallet/entities/order.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
|
||||
class WyreService {
|
||||
WyreService({@required this.appStore, this.isTestEnvironment = false}) {
|
||||
baseApiUrl = isTestEnvironment ? _baseTestApiUrl : _baseProductApiUrl;
|
||||
trackUrl = isTestEnvironment ? _trackTestUrl : _trackProductUrl;
|
||||
}
|
||||
|
||||
static const _baseTestApiUrl = 'https://api.testwyre.com';
|
||||
static const _baseProductApiUrl = 'https://api.sendwyre.com';
|
||||
static const _trackTestUrl = 'https://dash.testwyre.com/track/';
|
||||
static const _trackProductUrl = 'https://dash.sendwyre.com/track/';
|
||||
static const _ordersSuffix = '/v3/orders';
|
||||
static const _reserveSuffix = '/reserve';
|
||||
static const _timeStampSuffix = '?timestamp=';
|
||||
static const _transferSuffix = '/v2/transfer/';
|
||||
static const _trackSuffix = '/track';
|
||||
|
||||
final bool isTestEnvironment;
|
||||
final AppStore appStore;
|
||||
|
||||
WalletType get walletType => appStore.wallet.type;
|
||||
String get walletAddress => appStore.wallet.address;
|
||||
String get walletId => appStore.wallet.id;
|
||||
|
||||
String baseApiUrl;
|
||||
String trackUrl;
|
||||
|
||||
Future<String> getWyreUrl() async {
|
||||
final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
final url = baseApiUrl +
|
||||
_ordersSuffix +
|
||||
_reserveSuffix +
|
||||
_timeStampSuffix +
|
||||
timestamp;
|
||||
final secretKey = secrets.wyreSecretKey;
|
||||
final accountId = secrets.wyreAccountId;
|
||||
final body = {
|
||||
'destCurrency': walletTypeToCryptoCurrency(walletType).title,
|
||||
'dest':
|
||||
walletTypeToString(walletType).toLowerCase() + ':' + walletAddress,
|
||||
'referrerAccountId': accountId,
|
||||
'lockFields': ['destCurrency', 'dest']
|
||||
};
|
||||
|
||||
final response = await post(url,
|
||||
headers: {
|
||||
'Authorization': 'Bearer $secretKey',
|
||||
'Content-Type': 'application/json',
|
||||
'cache-control': 'no-cache'
|
||||
},
|
||||
body: json.encode(body));
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw WyreException('Url $url is not found!');
|
||||
}
|
||||
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final urlFromResponse = responseJSON['url'] as String;
|
||||
return urlFromResponse;
|
||||
}
|
||||
|
||||
Future<Order> findOrderById(String id) async {
|
||||
final orderUrl = baseApiUrl + _ordersSuffix + '/$id';
|
||||
final orderResponse = await get(orderUrl);
|
||||
|
||||
if (orderResponse.statusCode != 200) {
|
||||
throw WyreException('Order $id is not found!');
|
||||
}
|
||||
|
||||
final orderResponseJSON =
|
||||
json.decode(orderResponse.body) as Map<String, dynamic>;
|
||||
final transferId = orderResponseJSON['transferId'] as String;
|
||||
final from = orderResponseJSON['sourceCurrency'] as String;
|
||||
final to = orderResponseJSON['destCurrency'] as String;
|
||||
final status = orderResponseJSON['status'] as String;
|
||||
final state = TradeState.deserialize(raw: status.toLowerCase());
|
||||
final createdAtRaw = orderResponseJSON['createdAt'] as int;
|
||||
final createdAt =
|
||||
DateTime.fromMillisecondsSinceEpoch(createdAtRaw).toLocal();
|
||||
|
||||
final transferUrl =
|
||||
baseApiUrl + _transferSuffix + transferId + _trackSuffix;
|
||||
final transferResponse = await get(transferUrl);
|
||||
|
||||
if (transferResponse.statusCode != 200) {
|
||||
throw WyreException('Transfer $transferId is not found!');
|
||||
}
|
||||
|
||||
final transferResponseJSON =
|
||||
json.decode(transferResponse.body) as Map<String, dynamic>;
|
||||
final amount = transferResponseJSON['destAmount'] as double;
|
||||
|
||||
return Order(
|
||||
id: id,
|
||||
transferId: transferId,
|
||||
from: from,
|
||||
to: to,
|
||||
state: state,
|
||||
createdAt: createdAt,
|
||||
amount: amount.toString(),
|
||||
receiveAddress: walletAddress,
|
||||
walletId: walletId);
|
||||
}
|
||||
}
|
|
@ -27,6 +27,12 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
|
|||
static const finished = TradeState(raw: 'finished', title: 'Finished');
|
||||
static const waiting = TradeState(raw: 'waiting', title: 'Waiting');
|
||||
static const processing = TradeState(raw: 'processing', title: 'Processing');
|
||||
static const waitingPayment =
|
||||
TradeState(raw: 'waitingPayment', title: 'Waiting payment');
|
||||
static const waitingAuthorization =
|
||||
TradeState(raw: 'waitingAuthorization', title: 'Waiting authorization');
|
||||
static const failed = TradeState(raw: 'failed', title: 'Failed');
|
||||
static const completed = TradeState(raw: 'completed', title: 'Completed');
|
||||
|
||||
static TradeState deserialize({String raw}) {
|
||||
switch (raw) {
|
||||
|
@ -62,6 +68,14 @@ class TradeState extends EnumerableItem<String> with Serializable<String> {
|
|||
return waiting;
|
||||
case 'processing':
|
||||
return processing;
|
||||
case 'waitingPayment':
|
||||
return waitingPayment;
|
||||
case 'waitingAuthorization':
|
||||
return waitingAuthorization;
|
||||
case 'failed':
|
||||
return failed;
|
||||
case 'completed':
|
||||
return completed;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:cake_wallet/entities/language_service.dart';
|
||||
import 'package:cake_wallet/entities/order.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
|
|
@ -23,6 +23,7 @@ class Palette {
|
|||
static const Color persianRed = Color.fromRGBO(206, 55, 55, 1.0);
|
||||
static const Color blueCraiola = Color.fromRGBO(69, 110, 255, 1.0);
|
||||
static const Color blueGreyCraiola = Color.fromRGBO(106, 177, 207, 1.0);
|
||||
static const Color greyBlueCraiola = Color.fromRGBO(116, 139, 219, 1.0);
|
||||
static const Color darkBlueCraiola = Color.fromRGBO(53, 86, 136, 1.0);
|
||||
static const Color pinkFlamingo = Color.fromRGBO(240, 60, 243, 1.0);
|
||||
static const Color redHat = Color.fromRGBO(209, 68, 37, 1.0);
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/entities/order.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
|
||||
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
|
||||
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
|
||||
import 'package:cake_wallet/src/screens/buy/pre_order_page.dart';
|
||||
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
|
||||
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
|
||||
import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
|
||||
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
|
||||
import 'package:cake_wallet/src/screens/support/support_page.dart';
|
||||
import 'package:cake_wallet/src/screens/wyre/wyre_page.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -299,10 +301,17 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
builder: (_) =>
|
||||
getIt.get<OrderDetailsPage>(param1: settings.arguments as Order));
|
||||
|
||||
case Routes.wyre:
|
||||
case Routes.preOrder:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) =>
|
||||
getIt.get<WyrePage>(param1: settings.arguments as String));
|
||||
getIt.get<PreOrderPage>());
|
||||
|
||||
case Routes.buyWebView:
|
||||
final args = settings.arguments as List;
|
||||
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) =>
|
||||
getIt.get<BuyWebViewPage>(param1: args));
|
||||
|
||||
case Routes.restoreWalletFromSeedDetails:
|
||||
final args = settings.arguments as List;
|
||||
|
|
|
@ -53,5 +53,6 @@ class Routes {
|
|||
static const restoreFromBackup = '/restore_from_backup';
|
||||
static const support = '/support';
|
||||
static const orderDetails = '/order_details';
|
||||
static const wyre = '/wyre';
|
||||
static const preOrder = '/pre_order';
|
||||
static const buyWebView = '/buy_web_view';
|
||||
}
|
|
@ -1,20 +1,23 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/store/dashboard/orders_store.dart';
|
||||
import 'package:cake_wallet/view_model/wyre_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class WyrePage extends BasePage {
|
||||
WyrePage(this.wyreViewModel,
|
||||
{@required this.ordersStore, @required this.url});
|
||||
class BuyWebViewPage extends BasePage {
|
||||
BuyWebViewPage({@required this.buyViewModel,
|
||||
@required this.ordersStore, @required this.url});
|
||||
|
||||
final OrdersStore ordersStore;
|
||||
final String url;
|
||||
final WyreViewModel wyreViewModel;
|
||||
final BuyViewModel buyViewModel;
|
||||
|
||||
@override
|
||||
String get title => S.current.buy;
|
||||
|
@ -27,26 +30,27 @@ class WyrePage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) =>
|
||||
WyrePageBody(wyreViewModel, ordersStore: ordersStore, url: url);
|
||||
BuyWebViewPageBody(buyViewModel, ordersStore: ordersStore, url: url);
|
||||
}
|
||||
|
||||
class WyrePageBody extends StatefulWidget {
|
||||
WyrePageBody(this.wyreViewModel, {this.ordersStore, this.url});
|
||||
class BuyWebViewPageBody extends StatefulWidget {
|
||||
BuyWebViewPageBody(this.buyViewModel, {this.ordersStore, this.url});
|
||||
|
||||
final OrdersStore ordersStore;
|
||||
final String url;
|
||||
final WyreViewModel wyreViewModel;
|
||||
final BuyViewModel buyViewModel;
|
||||
|
||||
@override
|
||||
WyrePageBodyState createState() => WyrePageBodyState();
|
||||
BuyWebViewPageBodyState createState() => BuyWebViewPageBodyState();
|
||||
}
|
||||
|
||||
class WyrePageBodyState extends State<WyrePageBody> {
|
||||
class BuyWebViewPageBodyState extends State<BuyWebViewPageBody> {
|
||||
String orderId;
|
||||
WebViewController _webViewController;
|
||||
GlobalKey _webViewkey;
|
||||
Timer _timer;
|
||||
bool _isSaving;
|
||||
BuyProvider _provider;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -54,35 +58,17 @@ class WyrePageBodyState extends State<WyrePageBody> {
|
|||
_webViewkey = GlobalKey();
|
||||
_isSaving = false;
|
||||
widget.ordersStore.orderId = '';
|
||||
_provider = widget.buyViewModel.selectedProvider;
|
||||
|
||||
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
|
||||
|
||||
_timer?.cancel();
|
||||
_timer = Timer.periodic(Duration(seconds: 1), (timer) async {
|
||||
if (_provider is WyreBuyProvider) {
|
||||
_saveOrder(keyword: 'completed', splitSymbol: '/');
|
||||
}
|
||||
|
||||
try {
|
||||
if (_webViewController == null || _isSaving) {
|
||||
return;
|
||||
}
|
||||
|
||||
final url = await _webViewController.currentUrl();
|
||||
|
||||
if (url.contains('completed')) {
|
||||
final urlParts = url.split('/');
|
||||
orderId = urlParts.last;
|
||||
widget.ordersStore.orderId = orderId;
|
||||
|
||||
if (orderId.isNotEmpty) {
|
||||
_isSaving = true;
|
||||
await widget.wyreViewModel.saveOrder(orderId);
|
||||
timer.cancel();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
_isSaving = false;
|
||||
print(e);
|
||||
}
|
||||
});
|
||||
if (_provider is MoonPayBuyProvider) {
|
||||
_saveOrder(keyword: 'transactionId', splitSymbol: '=');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -94,4 +80,33 @@ class WyrePageBodyState extends State<WyrePageBody> {
|
|||
onWebViewCreated: (WebViewController controller) =>
|
||||
setState(() => _webViewController = controller));
|
||||
}
|
||||
|
||||
void _saveOrder({String keyword, String splitSymbol}) {
|
||||
_timer?.cancel();
|
||||
_timer = Timer.periodic(Duration(seconds: 1), (timer) async {
|
||||
|
||||
try {
|
||||
if (_webViewController == null || _isSaving) {
|
||||
return;
|
||||
}
|
||||
|
||||
final url = await _webViewController.currentUrl();
|
||||
|
||||
if (url.contains(keyword)) {
|
||||
final urlParts = url.split(splitSymbol);
|
||||
orderId = urlParts.last;
|
||||
widget.ordersStore.orderId = orderId;
|
||||
|
||||
if (orderId.isNotEmpty) {
|
||||
_isSaving = true;
|
||||
await widget.buyViewModel.saveOrder(orderId);
|
||||
timer.cancel();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
_isSaving = false;
|
||||
print(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
288
lib/src/screens/buy/pre_order_page.dart
Normal file
288
lib/src/screens/buy/pre_order_page.dart
Normal file
|
@ -0,0 +1,288 @@
|
|||
import 'dart:ui';
|
||||
import 'package:cake_wallet/buy/buy_amount.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
|
||||
import 'package:cake_wallet/src/screens/buy/widgets/buy_list_item.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/trail_button.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class PreOrderPage extends BasePage {
|
||||
PreOrderPage({@required this.buyViewModel})
|
||||
: _amountFocus = FocusNode(),
|
||||
_amountController = TextEditingController() {
|
||||
|
||||
_amountController.addListener(() {
|
||||
final amount = _amountController.text;
|
||||
|
||||
if (amount != buyViewModel.buyAmountViewModel.amount) {
|
||||
buyViewModel.buyAmountViewModel.amount = amount;
|
||||
buyViewModel.selectedProvider = null;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => buyViewModel.buyAmountViewModel.amount, (String amount) {
|
||||
if (_amountController.text != amount) {
|
||||
_amountController.text = amount;
|
||||
}
|
||||
if (amount.isEmpty) {
|
||||
buyViewModel.selectedProvider = null;
|
||||
buyViewModel.isShowProviderButtons = false;
|
||||
} else {
|
||||
buyViewModel.isShowProviderButtons = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static const _amountPattern = '^([0-9]+([.\,][0-9]{0,2})?|[.\,][0-9]{1,2})\$';
|
||||
|
||||
final BuyViewModel buyViewModel;
|
||||
final FocusNode _amountFocus;
|
||||
final TextEditingController _amountController;
|
||||
|
||||
@override
|
||||
String get title => S.current.buy_bitcoin;
|
||||
|
||||
@override
|
||||
Color get titleColor => Colors.white;
|
||||
|
||||
@override
|
||||
bool get resizeToAvoidBottomInset => false;
|
||||
|
||||
@override
|
||||
bool get extendBodyBehindAppBar => true;
|
||||
|
||||
@override
|
||||
AppBarStyle get appBarStyle => AppBarStyle.transparent;
|
||||
|
||||
@override
|
||||
Widget trailing(context) => TrailButton(
|
||||
caption: S.of(context).clear,
|
||||
onPressed: () {
|
||||
buyViewModel.reset();
|
||||
});
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return KeyboardActions(
|
||||
config: KeyboardActionsConfig(
|
||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||
keyboardBarColor: Theme.of(context).accentTextTheme.body2
|
||||
.backgroundColor,
|
||||
nextFocus: false,
|
||||
actions: [
|
||||
KeyboardActionsItem(
|
||||
focusNode: _amountFocus,
|
||||
toolbarButtons: [(_) => KeyboardDoneButton()],
|
||||
),
|
||||
]),
|
||||
child: Container(
|
||||
height: 0,
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 24),
|
||||
content: Observer(builder: (_) => Column(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(24),
|
||||
bottomRight: Radius.circular(24)),
|
||||
gradient: LinearGradient(colors: [
|
||||
Theme.of(context).primaryTextTheme.subhead.color,
|
||||
Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.subhead
|
||||
.decorationColor,
|
||||
], begin: Alignment.topLeft, end: Alignment.bottomRight),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 100, bottom: 65),
|
||||
child: Center(
|
||||
child: Container(
|
||||
width: 210,
|
||||
child: BaseTextFormField(
|
||||
focusNode: _amountFocus,
|
||||
controller: _amountController,
|
||||
keyboardType:
|
||||
TextInputType.numberWithOptions(
|
||||
signed: false, decimal: true),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter
|
||||
.allow(RegExp(_amountPattern))
|
||||
],
|
||||
prefixIcon: Padding(
|
||||
padding: EdgeInsets.only(top: 2),
|
||||
child:
|
||||
Text(buyViewModel.fiatCurrency.title + ': ',
|
||||
style: TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
)),
|
||||
),
|
||||
hintText: '0.00',
|
||||
borderColor: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.body2
|
||||
.decorationColor,
|
||||
borderWidth: 0.5,
|
||||
textStyle: TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white),
|
||||
placeholderTextStyle: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.headline
|
||||
.decorationColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 36),
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
if (buyViewModel.isShowProviderButtons) Padding(
|
||||
padding: EdgeInsets.only(top: 38, bottom: 18),
|
||||
child: Text(
|
||||
S.of(context).buy_with + ':',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
)
|
||||
),
|
||||
if (buyViewModel.isShowProviderButtons)
|
||||
...buyViewModel.items.map(
|
||||
(item) => Observer(builder: (_) =>
|
||||
FutureBuilder<BuyAmount>(
|
||||
future: item.buyAmount,
|
||||
builder: (context, AsyncSnapshot<BuyAmount> snapshot) {
|
||||
double sourceAmount;
|
||||
double destAmount;
|
||||
int minAmount;
|
||||
|
||||
if (snapshot.hasData) {
|
||||
sourceAmount = snapshot.data.sourceAmount;
|
||||
destAmount = snapshot.data.destAmount;
|
||||
minAmount = snapshot.data.minAmount;
|
||||
} else {
|
||||
sourceAmount = 0.0;
|
||||
destAmount = 0.0;
|
||||
minAmount = 0;
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding:
|
||||
EdgeInsets.only(left: 15, top: 20, right: 15),
|
||||
child: Observer(builder: (_) {
|
||||
return BuyListItem(
|
||||
selectedProvider:
|
||||
buyViewModel.selectedProvider,
|
||||
provider: item.provider,
|
||||
sourceAmount: sourceAmount,
|
||||
sourceCurrency: buyViewModel.fiatCurrency,
|
||||
destAmount: destAmount,
|
||||
destCurrency: buyViewModel.cryptoCurrency,
|
||||
onTap: ((buyViewModel.doubleAmount != 0.0)
|
||||
&& (snapshot.hasData)) ? () =>
|
||||
onSelectBuyProvider(
|
||||
context: context,
|
||||
provider: item.provider,
|
||||
sourceAmount: sourceAmount,
|
||||
minAmount: minAmount
|
||||
) : null
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
))
|
||||
)
|
||||
],
|
||||
)),
|
||||
bottomSectionPadding:
|
||||
EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
bottomSection: Observer(builder: (_) {
|
||||
return LoadingPrimaryButton(
|
||||
onPressed: () => onPresentProvider(context: context),
|
||||
text: buyViewModel.selectedProvider == null
|
||||
? S.of(context).buy
|
||||
: S.of(context).buy_with +
|
||||
' ${buyViewModel.selectedProvider
|
||||
.description.title}',
|
||||
color: Theme.of(context).accentTextTheme.body2.color,
|
||||
textColor: Colors.white,
|
||||
isLoading: buyViewModel.isRunning,
|
||||
isDisabled: (buyViewModel.selectedProvider == null) ||
|
||||
buyViewModel.isDisabled
|
||||
);
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void onSelectBuyProvider({BuildContext context, BuyProvider provider,
|
||||
double sourceAmount, int minAmount}) {
|
||||
|
||||
if ((provider is MoonPayBuyProvider)&&
|
||||
(buyViewModel.buyAmountViewModel.doubleAmount < minAmount)) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: 'MoonPay',
|
||||
alertContent: S.of(context).moonpay_alert_text(
|
||||
minAmount.toString(),
|
||||
buyViewModel.fiatCurrency.toString()),
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
return;
|
||||
}
|
||||
buyViewModel.selectedProvider = provider;
|
||||
sourceAmount > 0
|
||||
? buyViewModel.isDisabled = false
|
||||
: buyViewModel.isDisabled = true;
|
||||
}
|
||||
|
||||
Future<void> onPresentProvider({BuildContext context}) async {
|
||||
if (buyViewModel.isRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
buyViewModel.isRunning = true;
|
||||
final url = await buyViewModel.fetchUrl();
|
||||
|
||||
if (url.isNotEmpty) {
|
||||
if (buyViewModel.selectedProvider is MoonPayBuyProvider) {
|
||||
if (await canLaunch(url)) await launch(url);
|
||||
} else {
|
||||
await Navigator.of(context)
|
||||
.pushNamed(Routes.buyWebView,
|
||||
arguments: [url, buyViewModel]);
|
||||
}
|
||||
}
|
||||
|
||||
buyViewModel.reset();
|
||||
buyViewModel.isRunning = false;
|
||||
}
|
||||
}
|
116
lib/src/screens/buy/widgets/buy_list_item.dart
Normal file
116
lib/src/screens/buy/widgets/buy_list_item.dart
Normal file
|
@ -0,0 +1,116 @@
|
|||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/get_buy_provider_icon.dart';
|
||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BuyListItem extends StatelessWidget {
|
||||
BuyListItem({
|
||||
@required this.selectedProvider,
|
||||
@required this.provider,
|
||||
@required this.sourceAmount,
|
||||
@required this.sourceCurrency,
|
||||
@required this.destAmount,
|
||||
@required this.destCurrency,
|
||||
@required this.onTap
|
||||
});
|
||||
|
||||
final BuyProvider selectedProvider;
|
||||
final BuyProvider provider;
|
||||
final double sourceAmount;
|
||||
final FiatCurrency sourceCurrency;
|
||||
final double destAmount;
|
||||
final CryptoCurrency destCurrency;
|
||||
final void Function() onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isSelected = selectedProvider?.description == provider.description;
|
||||
|
||||
final providerIcon = getBuyProviderIcon(provider.description,
|
||||
isWhiteIconColor: isSelected);
|
||||
|
||||
final backgroundColor = isSelected
|
||||
? Palette.greyBlueCraiola
|
||||
: Palette.shadowWhite;
|
||||
|
||||
final primaryTextColor = isSelected
|
||||
? Colors.white
|
||||
: Palette.darkGray;
|
||||
|
||||
final secondaryTextColor = isSelected
|
||||
? Colors.white
|
||||
: Palette.darkBlueCraiola;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => onTap?.call(),
|
||||
child: Container(
|
||||
height: 102,
|
||||
padding: EdgeInsets.only(
|
||||
left: 20,
|
||||
//top: 33,
|
||||
right: 20
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(25)),
|
||||
color: backgroundColor
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: 33,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (providerIcon != null) Padding(
|
||||
padding: EdgeInsets.only(right: 10),
|
||||
child: providerIcon
|
||||
),
|
||||
Text(
|
||||
provider.description.title,
|
||||
style: TextStyle(
|
||||
color: secondaryTextColor,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Text(
|
||||
'${destAmount?.toString()} ${destCurrency.title}',
|
||||
style: TextStyle(
|
||||
color: secondaryTextColor,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
Positioned(
|
||||
top: 65,
|
||||
right: 0,
|
||||
child: Text(
|
||||
'${sourceAmount?.toString()} ${sourceCurrency.title}',
|
||||
style: TextStyle(
|
||||
color: primaryTextColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -132,29 +132,12 @@ class DashboardPage extends BasePage {
|
|||
image: exchangeImage,
|
||||
title: S.of(context).exchange,
|
||||
route: Routes.exchange),
|
||||
Observer(
|
||||
builder: (_) => Stack(
|
||||
clipBehavior: Clip.none,
|
||||
alignment: Alignment.topCenter,
|
||||
children: [
|
||||
if (walletViewModel.isRunningWebView)
|
||||
Positioned(
|
||||
top: -5,
|
||||
child: SpinKitRing(
|
||||
color: Theme.of(context).buttonColor,
|
||||
lineWidth: 3,
|
||||
size: 70.0,
|
||||
),
|
||||
),
|
||||
ActionButton(
|
||||
image: buyImage,
|
||||
title: S.of(context).buy,
|
||||
onClick: walletViewModel.isRunningWebView
|
||||
? null
|
||||
: () async =>
|
||||
await _onClickBuyButton(context))
|
||||
],
|
||||
)),
|
||||
ActionButton(
|
||||
image: buyImage,
|
||||
title: S.of(context).buy,
|
||||
onClick: () async =>
|
||||
await _onClickBuyButton(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
@ -198,14 +181,7 @@ class DashboardPage extends BasePage {
|
|||
|
||||
switch (walletType) {
|
||||
case WalletType.bitcoin:
|
||||
try {
|
||||
walletViewModel.isRunningWebView = true;
|
||||
final url = await walletViewModel.wyreViewModel.wyreUrl;
|
||||
await Navigator.of(context).pushNamed(Routes.wyre, arguments: url);
|
||||
walletViewModel.isRunningWebView = false;
|
||||
} catch (_) {
|
||||
walletViewModel.isRunningWebView = false;
|
||||
}
|
||||
Navigator.of(context).pushNamed(Routes.preOrder);
|
||||
break;
|
||||
default:
|
||||
await showPopUp<void>(
|
||||
|
|
|
@ -1,22 +1,33 @@
|
|||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/buy/get_buy_provider_icon.dart';
|
||||
import 'package:cake_wallet/themes/theme_base.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
|
||||
class OrderRow extends StatelessWidget {
|
||||
OrderRow({
|
||||
@required this.onTap,
|
||||
@required this.provider,
|
||||
this.from,
|
||||
this.to,
|
||||
this.createdAtFormattedDate,
|
||||
this.formattedAmount});
|
||||
final VoidCallback onTap;
|
||||
final BuyProviderDescription provider;
|
||||
final String from;
|
||||
final String to;
|
||||
final String createdAtFormattedDate;
|
||||
final String formattedAmount;
|
||||
final wyreImage =
|
||||
Image.asset('assets/images/wyre-icon.png', width: 36, height: 36);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final currentTheme = getIt.get<SettingsStore>().currentTheme;
|
||||
final isWhiteIconColor = currentTheme.type != ThemeType.light;
|
||||
|
||||
final providerIcon = getBuyProviderIcon(provider,
|
||||
isWhiteIconColor: isWhiteIconColor);
|
||||
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
|
@ -26,8 +37,10 @@ class OrderRow extends StatelessWidget {
|
|||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
wyreImage,
|
||||
SizedBox(width: 12),
|
||||
if (providerIcon != null) Padding(
|
||||
padding: EdgeInsets.only(right: 12),
|
||||
child: providerIcon,
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
|
@ -84,6 +84,7 @@ class TransactionsPage extends StatelessWidget {
|
|||
onTap: () => Navigator.of(context).pushNamed(
|
||||
Routes.orderDetails,
|
||||
arguments: order),
|
||||
provider: order.provider,
|
||||
from: order.from,
|
||||
to: order.to,
|
||||
createdAtFormattedDate:
|
||||
|
|
|
@ -98,6 +98,12 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
|||
showInformation(widget.exchangeTradeViewModel, context);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
widget.exchangeTradeViewModel.timer?.cancel();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final copyImage = Image.asset('assets/images/copy_content.png',
|
||||
|
|
|
@ -19,7 +19,33 @@ class OrderDetailsPage extends BasePage {
|
|||
final OrderDetailsViewModel orderDetailsViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
Widget body(BuildContext context) =>
|
||||
OrderDetailsPageBody(orderDetailsViewModel);
|
||||
}
|
||||
|
||||
class OrderDetailsPageBody extends StatefulWidget {
|
||||
OrderDetailsPageBody(this.orderDetailsViewModel);
|
||||
|
||||
final OrderDetailsViewModel orderDetailsViewModel;
|
||||
|
||||
@override
|
||||
OrderDetailsPageBodyState createState() =>
|
||||
OrderDetailsPageBodyState(orderDetailsViewModel);
|
||||
}
|
||||
|
||||
class OrderDetailsPageBodyState extends State<OrderDetailsPageBody> {
|
||||
OrderDetailsPageBodyState(this.orderDetailsViewModel);
|
||||
|
||||
final OrderDetailsViewModel orderDetailsViewModel;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
orderDetailsViewModel.timer?.cancel();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Observer(builder: (_) {
|
||||
return SectionStandardList(
|
||||
sectionCount: 1,
|
||||
|
@ -44,4 +70,5 @@ class OrderDetailsPage extends BasePage {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -67,7 +67,7 @@ class SendPage extends BasePage {
|
|||
Color get titleColor => Colors.white;
|
||||
|
||||
@override
|
||||
bool get resizeToAvoidBottomPadding => false;
|
||||
bool get resizeToAvoidBottomInset => false;
|
||||
|
||||
@override
|
||||
bool get extendBodyBehindAppBar => true;
|
||||
|
|
|
@ -19,7 +19,33 @@ class TradeDetailsPage extends BasePage {
|
|||
final TradeDetailsViewModel tradeDetailsViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
Widget body(BuildContext context) =>
|
||||
TradeDetailsPageBody(tradeDetailsViewModel);
|
||||
}
|
||||
|
||||
class TradeDetailsPageBody extends StatefulWidget {
|
||||
TradeDetailsPageBody(this.tradeDetailsViewModel);
|
||||
|
||||
final TradeDetailsViewModel tradeDetailsViewModel;
|
||||
|
||||
@override
|
||||
TradeDetailsPageBodyState createState() =>
|
||||
TradeDetailsPageBodyState(tradeDetailsViewModel);
|
||||
}
|
||||
|
||||
class TradeDetailsPageBodyState extends State<TradeDetailsPageBody> {
|
||||
TradeDetailsPageBodyState(this.tradeDetailsViewModel);
|
||||
|
||||
final TradeDetailsViewModel tradeDetailsViewModel;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
tradeDetailsViewModel.timer?.cancel();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Observer(builder: (_) {
|
||||
return SectionStandardList(
|
||||
sectionCount: 1,
|
||||
|
@ -44,4 +70,5 @@ class TradeDetailsPage extends BasePage {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,7 +26,8 @@ class BaseTextFormField extends StatelessWidget {
|
|||
this.placeholderTextStyle,
|
||||
this.maxLength,
|
||||
this.focusNode,
|
||||
this.initialValue});
|
||||
this.initialValue,
|
||||
this.borderWidth = 1.0});
|
||||
|
||||
final TextEditingController controller;
|
||||
final TextInputType keyboardType;
|
||||
|
@ -52,6 +53,7 @@ class BaseTextFormField extends StatelessWidget {
|
|||
final bool readOnly;
|
||||
final bool enableInteractiveSelection;
|
||||
final String initialValue;
|
||||
final double borderWidth;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -88,17 +90,17 @@ class BaseTextFormField extends StatelessWidget {
|
|||
borderSide: BorderSide(
|
||||
color: borderColor ??
|
||||
Theme.of(context).primaryTextTheme.title.backgroundColor,
|
||||
width: 1.0)),
|
||||
width: borderWidth)),
|
||||
disabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: borderColor ??
|
||||
Theme.of(context).primaryTextTheme.title.backgroundColor,
|
||||
width: 1.0)),
|
||||
width: borderWidth)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: borderColor ??
|
||||
Theme.of(context).primaryTextTheme.title.backgroundColor,
|
||||
width: 1.0))),
|
||||
width: borderWidth))),
|
||||
validator: validator,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'package:cake_wallet/entities/order.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
|
|
@ -8,7 +8,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:package_info/package_info.dart';
|
||||
import 'package:devicelocale/devicelocale.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
@ -18,6 +17,7 @@ import 'package:cake_wallet/entities/fiat_currency.dart';
|
|||
import 'package:cake_wallet/entities/node.dart';
|
||||
import 'package:cake_wallet/entities/monero_transaction_priority.dart';
|
||||
import 'package:cake_wallet/entities/action_list_display_mode.dart';
|
||||
import 'package:cake_wallet/.secrets.g.dart' as secrets;
|
||||
|
||||
part 'settings_store.g.dart';
|
||||
|
||||
|
@ -248,8 +248,13 @@ abstract class SettingsStoreBase with Store {
|
|||
BitcoinTransactionPriority.medium,
|
||||
BalanceDisplayMode initialBalanceDisplayMode =
|
||||
BalanceDisplayMode.availableBalance}) async {
|
||||
final isBitcoinBuyEnabled = (secrets.wyreSecretKey?.isNotEmpty ?? false) &&
|
||||
(secrets.wyreApiKey?.isNotEmpty ?? false) &&
|
||||
(secrets.wyreAccountId?.isNotEmpty ?? false);
|
||||
|
||||
final settings = await SettingsStoreBase.load(
|
||||
nodeSource: nodeSource,
|
||||
isBitcoinBuyEnabled: isBitcoinBuyEnabled,
|
||||
initialBalanceDisplayMode: initialBalanceDisplayMode,
|
||||
initialFiatCurrency: initialFiatCurrency,
|
||||
initialMoneroTransactionPriority: initialMoneroTransactionPriority,
|
||||
|
|
28
lib/view_model/buy/buy_amount_view_model.dart
Normal file
28
lib/view_model/buy/buy_amount_view_model.dart
Normal file
|
@ -0,0 +1,28 @@
|
|||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
|
||||
part 'buy_amount_view_model.g.dart';
|
||||
|
||||
class BuyAmountViewModel = BuyAmountViewModelBase with _$BuyAmountViewModel;
|
||||
|
||||
abstract class BuyAmountViewModelBase with Store {
|
||||
BuyAmountViewModelBase() : amount = '';
|
||||
|
||||
@observable
|
||||
String amount;
|
||||
|
||||
FiatCurrency get fiatCurrency => FiatCurrency.usd;
|
||||
|
||||
@computed
|
||||
double get doubleAmount {
|
||||
double _amount;
|
||||
|
||||
try {
|
||||
_amount = double.parse(amount.replaceAll(',', '.')) ?? 0.0;
|
||||
} catch (e) {
|
||||
_amount = 0.0;
|
||||
}
|
||||
|
||||
return _amount;
|
||||
}
|
||||
}
|
29
lib/view_model/buy/buy_item.dart
Normal file
29
lib/view_model/buy/buy_item.dart
Normal file
|
@ -0,0 +1,29 @@
|
|||
import 'package:cake_wallet/buy/buy_amount.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart';
|
||||
|
||||
class BuyItem {
|
||||
BuyItem({this.provider, this.buyAmountViewModel});
|
||||
|
||||
final BuyProvider provider;
|
||||
final BuyAmountViewModel buyAmountViewModel;
|
||||
|
||||
double get amount => buyAmountViewModel.doubleAmount;
|
||||
|
||||
FiatCurrency get fiatCurrency => buyAmountViewModel.fiatCurrency;
|
||||
|
||||
Future<BuyAmount> get buyAmount async {
|
||||
BuyAmount _buyAmount;
|
||||
|
||||
try {
|
||||
_buyAmount = await provider
|
||||
.calculateAmount(amount?.toString(), fiatCurrency.title);
|
||||
} catch (e) {
|
||||
_buyAmount = BuyAmount(sourceAmount: 0.0, destAmount: 0.0);
|
||||
print(e.toString());
|
||||
}
|
||||
|
||||
return _buyAmount;
|
||||
}
|
||||
}
|
110
lib/view_model/buy/buy_view_model.dart
Normal file
110
lib/view_model/buy/buy_view_model.dart
Normal file
|
@ -0,0 +1,110 @@
|
|||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
|
||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/view_model/buy/buy_item.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/store/dashboard/orders_store.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'buy_amount_view_model.dart';
|
||||
|
||||
part 'buy_view_model.g.dart';
|
||||
|
||||
class BuyViewModel = BuyViewModelBase with _$BuyViewModel;
|
||||
|
||||
abstract class BuyViewModelBase with Store {
|
||||
BuyViewModelBase(this.ordersSource, this.ordersStore, this.settingsStore,
|
||||
this.buyAmountViewModel, {@required this.wallet}) {
|
||||
|
||||
_fetchBuyItems();
|
||||
|
||||
isRunning = false;
|
||||
isDisabled = true;
|
||||
isShowProviderButtons = false;
|
||||
}
|
||||
|
||||
final Box<Order> ordersSource;
|
||||
final OrdersStore ordersStore;
|
||||
final SettingsStore settingsStore;
|
||||
final BuyAmountViewModel buyAmountViewModel;
|
||||
final WalletBase wallet;
|
||||
|
||||
@observable
|
||||
BuyProvider selectedProvider;
|
||||
|
||||
@observable
|
||||
List<BuyItem> items;
|
||||
|
||||
@observable
|
||||
bool isRunning;
|
||||
|
||||
@observable
|
||||
bool isDisabled;
|
||||
|
||||
@observable
|
||||
bool isShowProviderButtons;
|
||||
|
||||
WalletType get type => wallet.type;
|
||||
|
||||
double get doubleAmount => buyAmountViewModel.doubleAmount;
|
||||
|
||||
FiatCurrency get fiatCurrency => buyAmountViewModel.fiatCurrency;
|
||||
|
||||
CryptoCurrency get cryptoCurrency => walletTypeToCryptoCurrency(type);
|
||||
|
||||
Future <String> fetchUrl() async {
|
||||
String _url = '';
|
||||
|
||||
try {
|
||||
_url = await selectedProvider
|
||||
?.requestUrl(doubleAmount?.toString(), fiatCurrency.title);
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
|
||||
return _url;
|
||||
}
|
||||
|
||||
Future<void> saveOrder(String orderId) async {
|
||||
try {
|
||||
final order = await selectedProvider?.findOrderById(orderId);
|
||||
order.from = fiatCurrency.title;
|
||||
order.to = cryptoCurrency.title;
|
||||
await ordersSource.add(order);
|
||||
ordersStore.setOrder(order);
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
buyAmountViewModel.amount = '';
|
||||
selectedProvider = null;
|
||||
}
|
||||
|
||||
Future<void> _fetchBuyItems() async {
|
||||
final List<BuyProvider> _providerList = [WyreBuyProvider(wallet: wallet)];
|
||||
|
||||
var isMoonPayEnabled = false;
|
||||
try {
|
||||
isMoonPayEnabled = await MoonPayBuyProvider.onEnabled();
|
||||
} catch (e) {
|
||||
isMoonPayEnabled = false;
|
||||
print(e.toString());
|
||||
}
|
||||
|
||||
if (isMoonPayEnabled) {
|
||||
_providerList.add(MoonPayBuyProvider(wallet: wallet));
|
||||
}
|
||||
|
||||
items = _providerList.map((provider) =>
|
||||
BuyItem(provider: provider, buyAmountViewModel: buyAmountViewModel))
|
||||
.toList();
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
import 'package:cake_wallet/core/transaction_history.dart';
|
||||
import 'package:cake_wallet/entities/balance.dart';
|
||||
import 'package:cake_wallet/entities/order.dart';
|
||||
import 'package:cake_wallet/entities/push_notifications_service.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/entities/transaction_history.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/monero/account.dart';
|
||||
import 'package:cake_wallet/monero/monero_balance.dart';
|
||||
import 'package:cake_wallet/monero/monero_transaction_info.dart';
|
||||
|
@ -18,7 +20,9 @@ import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
|
|||
import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wyre_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/action_list_display_mode.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
|
@ -43,9 +47,7 @@ abstract class DashboardViewModelBase with Store {
|
|||
this.tradeFilterStore,
|
||||
this.transactionFilterStore,
|
||||
this.settingsStore,
|
||||
this.ordersSource,
|
||||
this.ordersStore,
|
||||
this.wyreViewModel,
|
||||
PushNotificationsService pushNotificationsService}) {
|
||||
_pushNotificationsService = pushNotificationsService;
|
||||
filterItems = {
|
||||
|
@ -82,8 +84,6 @@ abstract class DashboardViewModelBase with Store {
|
|||
]
|
||||
};
|
||||
|
||||
isRunningWebView = false;
|
||||
|
||||
name = appStore.wallet?.name;
|
||||
wallet ??= appStore.wallet;
|
||||
type = wallet.type;
|
||||
|
@ -152,9 +152,6 @@ abstract class DashboardViewModelBase with Store {
|
|||
@observable
|
||||
String subname;
|
||||
|
||||
@observable
|
||||
bool isRunningWebView;
|
||||
|
||||
@computed
|
||||
String get address => wallet.address;
|
||||
|
||||
|
@ -210,8 +207,6 @@ abstract class DashboardViewModelBase with Store {
|
|||
|
||||
bool get hasRescan => wallet.type == WalletType.monero;
|
||||
|
||||
Box<Order> ordersSource;
|
||||
|
||||
BalanceViewModel balanceViewModel;
|
||||
|
||||
AppStore appStore;
|
||||
|
@ -226,8 +221,6 @@ abstract class DashboardViewModelBase with Store {
|
|||
|
||||
TransactionFilterStore transactionFilterStore;
|
||||
|
||||
WyreViewModel wyreViewModel;
|
||||
|
||||
Map<String, List<FilterItem>> filterItems;
|
||||
|
||||
bool get isBuyEnabled => settingsStore.isBitcoinBuyEnabled;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:cake_wallet/entities/order.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
|
||||
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||
|
|
|
@ -45,7 +45,7 @@ abstract class ExchangeTradeViewModelBase with Store {
|
|||
|
||||
_updateTrade();
|
||||
|
||||
_timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
|
||||
timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
|
||||
}
|
||||
|
||||
final WalletBase wallet;
|
||||
|
@ -71,7 +71,7 @@ abstract class ExchangeTradeViewModelBase with Store {
|
|||
|
||||
ExchangeProvider _provider;
|
||||
|
||||
Timer _timer;
|
||||
Timer timer;
|
||||
|
||||
@action
|
||||
Future confirmSending() async {
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
import 'dart:async';
|
||||
import 'package:cake_wallet/entities/order.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/utils/date_formatter.dart';
|
||||
import 'package:cake_wallet/view_model/wyre_view_model.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
|
||||
|
||||
part 'order_details_view_model.g.dart';
|
||||
|
||||
|
@ -14,16 +18,27 @@ class OrderDetailsViewModel = OrderDetailsViewModelBase
|
|||
with _$OrderDetailsViewModel;
|
||||
|
||||
abstract class OrderDetailsViewModelBase with Store {
|
||||
OrderDetailsViewModelBase({this.wyreViewModel, Order orderForDetails}) {
|
||||
OrderDetailsViewModelBase({WalletBase wallet, Order orderForDetails}) {
|
||||
order = orderForDetails;
|
||||
|
||||
if (order.provider != null) {
|
||||
switch (order.provider) {
|
||||
case BuyProviderDescription.wyre:
|
||||
_provider = WyreBuyProvider(wallet: wallet);
|
||||
break;
|
||||
case BuyProviderDescription.moonPay:
|
||||
_provider = MoonPayBuyProvider(wallet: wallet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
items = ObservableList<StandartListItem>();
|
||||
|
||||
_updateItems();
|
||||
|
||||
_updateOrder();
|
||||
|
||||
_timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateOrder());
|
||||
timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateOrder());
|
||||
}
|
||||
|
||||
@observable
|
||||
|
@ -32,21 +47,25 @@ abstract class OrderDetailsViewModelBase with Store {
|
|||
@observable
|
||||
ObservableList<StandartListItem> items;
|
||||
|
||||
WyreViewModel wyreViewModel;
|
||||
BuyProvider _provider;
|
||||
|
||||
Timer _timer;
|
||||
Timer timer;
|
||||
|
||||
@action
|
||||
Future<void> _updateOrder() async {
|
||||
try {
|
||||
final updatedOrder =
|
||||
await wyreViewModel.wyreService.findOrderById(order.id);
|
||||
|
||||
updatedOrder.receiveAddress = order.receiveAddress;
|
||||
updatedOrder.walletId = order.walletId;
|
||||
order = updatedOrder;
|
||||
|
||||
_updateItems();
|
||||
if (_provider != null) {
|
||||
final updatedOrder = await _provider.findOrderById(order.id);
|
||||
updatedOrder.from = order.from;
|
||||
updatedOrder.to = order.to;
|
||||
updatedOrder.receiveAddress = order.receiveAddress;
|
||||
updatedOrder.walletId = order.walletId;
|
||||
if (order.provider != null) {
|
||||
updatedOrder.providerRaw = order.provider.raw;
|
||||
}
|
||||
order = updatedOrder;
|
||||
_updateItems();
|
||||
}
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
|
@ -54,8 +73,6 @@ abstract class OrderDetailsViewModelBase with Store {
|
|||
|
||||
void _updateItems() {
|
||||
final dateFormat = DateFormatter.withCurrentLocal();
|
||||
final buildURL =
|
||||
wyreViewModel.trackUrl + '${order.transferId}';
|
||||
|
||||
items?.clear();
|
||||
|
||||
|
@ -68,18 +85,37 @@ abstract class OrderDetailsViewModelBase with Store {
|
|||
value: order.state != null
|
||||
? order.state.toString()
|
||||
: S.current.trade_details_fetching),
|
||||
TrackTradeListItem(
|
||||
title: 'Track',
|
||||
value: buildURL,
|
||||
onTap: () {
|
||||
launch(buildURL);
|
||||
}),
|
||||
StandartListItem(
|
||||
title: S.current.trade_details_created_at,
|
||||
value: dateFormat.format(order.createdAt).toString()),
|
||||
StandartListItem(
|
||||
title: S.current.trade_details_pair,
|
||||
value: '${order.from} → ${order.to}')
|
||||
]);
|
||||
|
||||
if (order.provider != null) {
|
||||
items.add(
|
||||
StandartListItem(
|
||||
title: 'Buy provider',
|
||||
value: order.provider.title)
|
||||
);
|
||||
}
|
||||
|
||||
if (_provider?.trackUrl?.isNotEmpty ?? false) {
|
||||
final buildURL = _provider.trackUrl + '${order.transferId}';
|
||||
items.add(
|
||||
TrackTradeListItem(
|
||||
title: 'Track',
|
||||
value: buildURL,
|
||||
onTap: () => launch(buildURL)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
items.add(
|
||||
StandartListItem(
|
||||
title: S.current.trade_details_created_at,
|
||||
value: dateFormat.format(order.createdAt).toString())
|
||||
);
|
||||
|
||||
items.add(
|
||||
StandartListItem(
|
||||
title: S.current.trade_details_pair,
|
||||
value: '${order.from} → ${order.to}')
|
||||
);
|
||||
}
|
||||
}
|
|
@ -39,7 +39,7 @@ abstract class TradeDetailsViewModelBase with Store {
|
|||
|
||||
_updateTrade();
|
||||
|
||||
_timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
|
||||
timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
|
||||
}
|
||||
|
||||
final Box<Trade> trades;
|
||||
|
@ -52,7 +52,7 @@ abstract class TradeDetailsViewModelBase with Store {
|
|||
|
||||
ExchangeProvider _provider;
|
||||
|
||||
Timer _timer;
|
||||
Timer timer;
|
||||
|
||||
@action
|
||||
Future<void> _updateTrade() async {
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
import 'package:cake_wallet/entities/wyre_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/entities/order.dart';
|
||||
import 'package:cake_wallet/store/dashboard/orders_store.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'wyre_view_model.g.dart';
|
||||
|
||||
class WyreViewModel = WyreViewModelBase with _$WyreViewModel;
|
||||
|
||||
abstract class WyreViewModelBase with Store {
|
||||
WyreViewModelBase(this.ordersSource, this.ordersStore,
|
||||
{@required this.wyreService});
|
||||
|
||||
Future<String> get wyreUrl => wyreService.getWyreUrl();
|
||||
|
||||
String get trackUrl => wyreService.trackUrl;
|
||||
|
||||
final Box<Order> ordersSource;
|
||||
final OrdersStore ordersStore;
|
||||
|
||||
final WyreService wyreService;
|
||||
|
||||
Future<void> saveOrder(String orderId) async {
|
||||
try {
|
||||
final order = await wyreService.findOrderById(orderId);
|
||||
await ordersSource.add(order);
|
||||
ordersStore.setOrder(order);
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -478,6 +478,10 @@
|
|||
|
||||
"apk_update" : "APK Update",
|
||||
|
||||
"buy_bitcoin" : "Bitcoin kaufen",
|
||||
"buy_with" : "Kaufen mit",
|
||||
"moonpay_alert_text" : "Der Wert des Betrags muss größer oder gleich sein ${minAmount} ${fiatCurrency}",
|
||||
|
||||
"outdated_electrum_wallet_receive_warning": "Wenn diese Brieftasche einen 12-Wort-Seed hat und in Cake erstellt wurde, zahlen Sie KEINE Bitcoins in diese Brieftasche ein. Alle auf diese Wallet übertragenen BTC können verloren gehen. Erstellen Sie eine neue 24-Wort-Wallet (tippen Sie auf das Menü oben rechts, wählen Sie Wallets, wählen Sie Create New Wallet und dann Bitcoin) und verschieben Sie Ihre BTC SOFORT dorthin. Neue (24 Wörter) BTC-Wallets von Cake sind sicher",
|
||||
"do_not_show_me": "Zeig mir das nicht noch einmal"
|
||||
}
|
|
@ -478,6 +478,10 @@
|
|||
|
||||
"apk_update" : "APK update",
|
||||
|
||||
"buy_bitcoin" : "Buy Bitcoin",
|
||||
"buy_with" : "Buy with",
|
||||
"moonpay_alert_text" : "Value of the amount must be more or equal to ${minAmount} ${fiatCurrency}",
|
||||
|
||||
"outdated_electrum_wallet_receive_warning": "If this wallet has a 12-word seed and was created in Cake, DO NOT deposit Bitcoin into this wallet. Any BTC transferred to this wallet may be lost. Create a new 24-word wallet (tap the menu at the top right, select Wallets, choose Create New Wallet, then select Bitcoin) and IMMEDIATELY move your BTC there. New (24-word) BTC wallets from Cake are secure",
|
||||
"do_not_show_me": "Do not show me this again"
|
||||
}
|
|
@ -478,6 +478,10 @@
|
|||
|
||||
"apk_update" : "Actualización de APK",
|
||||
|
||||
"buy_bitcoin" : "Comprar Bitcoin",
|
||||
"buy_with" : "Compra con",
|
||||
"moonpay_alert_text" : "El valor de la cantidad debe ser mayor o igual a ${minAmount} ${fiatCurrency}",
|
||||
|
||||
"outdated_electrum_wallet_receive_warning": "Si esta billetera tiene una semilla de 12 palabras y se creó en Cake, NO deposite Bitcoin en esta billetera. Cualquier BTC transferido a esta billetera se puede perder. Cree una nueva billetera de 24 palabras (toque el menú en la parte superior derecha, seleccione Monederos, elija Crear nueva billetera, luego seleccione Bitcoin) e INMEDIATAMENTE mueva su BTC allí. Las nuevas carteras BTC (24 palabras) de Cake son seguras",
|
||||
"do_not_show_me": "no me muestres esto otra vez"
|
||||
}
|
|
@ -478,6 +478,10 @@
|
|||
|
||||
"apk_update" : "APK अद्यतन",
|
||||
|
||||
"buy_bitcoin" : "बिटकॉइन खरीदें",
|
||||
"buy_with" : "के साथ खरीदें",
|
||||
"moonpay_alert_text" : "राशि का मूल्य अधिक है या करने के लिए बराबर होना चाहिए ${minAmount} ${fiatCurrency}",
|
||||
|
||||
"outdated_electrum_wallet_receive_warning": "अगर इस वॉलेट में 12 शब्दों का बीज है और इसे केक में बनाया गया है, तो इस वॉलेट में बिटकॉइन जमा न करें। इस वॉलेट में स्थानांतरित किया गया कोई भी बीटीसी खो सकता है। एक नया 24-शब्द वॉलेट बनाएं (ऊपर दाईं ओर स्थित मेनू पर टैप करें, वॉलेट चुनें, नया वॉलेट बनाएं चुनें, फिर बिटकॉइन चुनें) और तुरंत अपना बीटीसी वहां ले जाएं। केक से नए (24-शब्द) बीटीसी वॉलेट सुरक्षित हैं",
|
||||
"do_not_show_me": "मुझे यह फिर न दिखाएं"
|
||||
}
|
|
@ -478,6 +478,10 @@
|
|||
|
||||
"apk_update" : "APK ažuriranje",
|
||||
|
||||
"buy_bitcoin" : "Kupite Bitcoin",
|
||||
"buy_with" : "Kupite s",
|
||||
"moonpay_alert_text" : "Vrijednost iznosa mora biti veća ili jednaka ${minAmount} ${fiatCurrency}",
|
||||
|
||||
"outdated_electrum_wallet_receive_warning": "Ako ovaj novčanik sadrži sjeme od 12 riječi i stvoren je u Torti, NEMOJTE polagati Bitcoin u ovaj novčanik. Bilo koji BTC prebačen u ovaj novčanik može se izgubiti. Stvorite novi novčanik od 24 riječi (taknite izbornik u gornjem desnom dijelu, odaberite Novčanici, odaberite Stvori novi novčanik, a zatim odaberite Bitcoin) i ODMAH premjestite svoj BTC tamo. Novi BTC novčanici (s 24 riječi) tvrtke Cake sigurni su",
|
||||
"do_not_show_me": "Ne pokazuj mi ovo više"
|
||||
}
|
|
@ -478,6 +478,10 @@
|
|||
|
||||
"apk_update" : "Aggiornamento APK",
|
||||
|
||||
"buy_bitcoin" : "Acquista Bitcoin",
|
||||
"buy_with" : "Acquista con",
|
||||
"moonpay_alert_text" : "Il valore dell'importo deve essere maggiore o uguale a ${minAmount} ${fiatCurrency}",
|
||||
|
||||
"outdated_electrum_wallet_receive_warning": "Se questo portafoglio ha un seme di 12 parole ed è stato creato in Cake, NON depositare Bitcoin in questo portafoglio. Qualsiasi BTC trasferito su questo portafoglio potrebbe andare perso. Crea un nuovo portafoglio di 24 parole (tocca il menu in alto a destra, seleziona Portafogli, scegli Crea nuovo portafoglio, quindi seleziona Bitcoin) e sposta IMMEDIATAMENTE lì il tuo BTC. I nuovi portafogli BTC (24 parole) di Cake sono sicuri",
|
||||
"do_not_show_me": "Non mostrarmelo di nuovo"
|
||||
}
|
|
@ -478,6 +478,10 @@
|
|||
|
||||
"apk_update" : "APKアップデート",
|
||||
|
||||
"buy_bitcoin" : "ビットコインを購入する",
|
||||
"buy_with" : "で購入",
|
||||
"moonpay_alert_text" : "金額の値は以上でなければなりません ${minAmount} ${fiatCurrency}",
|
||||
|
||||
"outdated_electrum_wallet_receive_warning": "このウォレットに 12 ワードのシードがあり、Cake で作成された場合、このウォレットにビットコインを入金しないでください。 このウォレットに転送された BTC は失われる可能性があります。 新しい 24 ワードのウォレットを作成し (右上のメニューをタップし、[ウォレット]、[新しいウォレットの作成]、[ビットコイン] の順に選択)、すぐに BTC をそこに移動します。 Cake の新しい (24 ワード) BTC ウォレットは安全です",
|
||||
"do_not_show_me": "また僕にこれを見せないでください"
|
||||
}
|
|
@ -478,6 +478,10 @@
|
|||
|
||||
"apk_update" : "APK 업데이트",
|
||||
|
||||
"buy_bitcoin" : "비트 코인 구매",
|
||||
"buy_with" : "구매",
|
||||
"moonpay_alert_text" : "금액은 다음보다 크거나 같아야합니다 ${minAmount} ${fiatCurrency}",
|
||||
|
||||
"outdated_electrum_wallet_receive_warning": "이 지갑에 12 단어 시드가 있고 Cake에서 생성 된 경우이 지갑에 비트 코인을 입금하지 마십시오. 이 지갑으로 전송 된 모든 BTC는 손실 될 수 있습니다. 새로운 24 단어 지갑을 생성하고 (오른쪽 상단의 메뉴를 탭하고 지갑을 선택한 다음 새 지갑 생성을 선택한 다음 비트 코인을 선택하십시오) 즉시 BTC를 그곳으로 이동하십시오. Cake의 새로운 (24 단어) BTC 지갑은 안전합니다",
|
||||
"do_not_show_me": "나를 다시 표시하지 않음"
|
||||
}
|
|
@ -478,6 +478,10 @@
|
|||
|
||||
"apk_update" : "APK-update",
|
||||
|
||||
"buy_bitcoin" : "Koop Bitcoin",
|
||||
"buy_with" : "Koop met",
|
||||
"moonpay_alert_text" : "Waarde van het bedrag moet meer of gelijk zijn aan ${minAmount} ${fiatCurrency}",
|
||||
|
||||
"outdated_electrum_wallet_receive_warning": "Als deze portemonnee een seed van 12 woorden heeft en is gemaakt in Cake, stort dan GEEN Bitcoin in deze portemonnee. Elke BTC die naar deze portemonnee is overgebracht, kan verloren gaan. Maak een nieuwe portemonnee van 24 woorden (tik op het menu rechtsboven, selecteer Portefeuilles, kies Nieuwe portemonnee maken en selecteer vervolgens Bitcoin) en verplaats je BTC ONMIDDELLIJK daar. Nieuwe (24-woorden) BTC-portefeuilles van Cake zijn veilig",
|
||||
"do_not_show_me": "laat me dit niet opnieuw zien"
|
||||
}
|
|
@ -478,6 +478,10 @@
|
|||
|
||||
"apk_update" : "Aktualizacja APK",
|
||||
|
||||
"buy_bitcoin" : "Kup Bitcoin",
|
||||
"buy_with" : "Kup za pomocą",
|
||||
"moonpay_alert_text" : "Wartość kwoty musi być większa lub równa ${minAmount} ${fiatCurrency}",
|
||||
|
||||
"outdated_electrum_wallet_receive_warning": "Jeśli ten portfel ma 12-wyrazowy seed i został utworzony w Cake, NIE Wpłacaj Bitcoina do tego portfela. Wszelkie BTC przeniesione do tego portfela mogą zostać utracone. Utwórz nowy portfel z 24 słowami (dotknij menu w prawym górnym rogu, wybierz Portfele, wybierz Utwórz nowy portfel, a następnie Bitcoin) i NATYCHMIAST przenieś tam swoje BTC. Nowe (24 słowa) portfele BTC firmy Cake są bezpieczne",
|
||||
"do_not_show_me": "Nie pokazuj mi tego ponownie"
|
||||
}
|
|
@ -478,6 +478,10 @@
|
|||
|
||||
"apk_update" : "Atualização de APK",
|
||||
|
||||
"buy_bitcoin" : "Compre Bitcoin",
|
||||
"buy_with" : "Compre com",
|
||||
"moonpay_alert_text" : "O valor do montante deve ser maior ou igual a ${minAmount} ${fiatCurrency}",
|
||||
|
||||
"outdated_electrum_wallet_receive_warning": "Se esta carteira tiver uma semente de 12 palavras e foi criada no Cake, NÃO deposite Bitcoin nesta carteira. Qualquer BTC transferido para esta carteira pode ser perdido. Crie uma nova carteira de 24 palavras (toque no menu no canto superior direito, selecione Carteiras, escolha Criar Nova Carteira e selecione Bitcoin) e mova IMEDIATAMENTE seu BTC para lá. As novas carteiras BTC (24 palavras) da Cake são seguras",
|
||||
"do_not_show_me": "não me mostre isso novamente"
|
||||
}
|
|
@ -478,6 +478,10 @@
|
|||
|
||||
"apk_update" : "Обновление APK",
|
||||
|
||||
"buy_bitcoin" : "Купить Bitcoin",
|
||||
"buy_with" : "Купить с помощью",
|
||||
"moonpay_alert_text" : "Сумма должна быть больше или равна ${minAmount} ${fiatCurrency}",
|
||||
|
||||
"outdated_electrum_wallet_receive_warning": "Если этот кошелек имеет мнемоническую фразу из 12 слов и был создан в Cake, НЕ переводите биткойны на этот кошелек. Любые BTC, переведенные на этот кошелек, могут быть потеряны. Создайте новый кошелек с мнемоническои фразы из 24 слов (коснитесь меню в правом верхнем углу, выберите «Кошельки», выберите «Создать новый кошелек», затем выберите «Bitcoin») и НЕМЕДЛЕННО переведите туда свои BTC. Новые (24 слова) кошельки BTC от Cake безопасны",
|
||||
"do_not_show_me": "Не показывай мне это больше"
|
||||
}
|
|
@ -478,6 +478,10 @@
|
|||
|
||||
"apk_update" : "Оновлення APK",
|
||||
|
||||
"buy_bitcoin" : "Купити Bitcoin",
|
||||
"buy_with" : "Купити за допомогою",
|
||||
"moonpay_alert_text" : "Значення суми має бути більшим або дорівнювати ${minAmount} ${fiatCurrency}",
|
||||
|
||||
"outdated_electrum_wallet_receive_warning": "Якщо цей гаманець має мнемонічну фразу з 12 слів і був створений у Cake, НЕ переводьте біткойни на цей гаманець. Будь-які BTC, переведений на цей гаманець, можуть бути втраченими. Створіть новий гаманець з мнемонічною фразою з 24 слів (торкніться меню у верхньому правому куті, виберіть Гаманці, виберіть Створити новий гаманець, потім виберіть Bitcoin) і НЕГАЙНО переведіть туди свії BTC. Нові (з мнемонічною фразою з 24 слів) гаманці BTC від Cake надійно захищені",
|
||||
"do_not_show_me": "Не показуй мені це знову"
|
||||
}
|
|
@ -478,6 +478,10 @@
|
|||
|
||||
"apk_update" : "APK更新",
|
||||
|
||||
"buy_bitcoin" : "購買比特幣",
|
||||
"buy_with" : "與一起購買",
|
||||
"moonpay_alert_text" : "金額的價值必須大於或等於 ${minAmount} ${fiatCurrency}",
|
||||
|
||||
"outdated_electrum_wallet_receive_warning": "如果这个钱包有一个 12 字的种子并且是在 Cake 中创建的,不要将比特币存入这个钱包。 任何转移到此钱包的 BTC 都可能丢失。 创建一个新的 24 字钱包(点击右上角的菜单,选择钱包,选择创建新钱包,然后选择比特币)并立即将您的 BTC 移到那里。 Cake 的新(24 字)BTC 钱包是安全的",
|
||||
"do_not_show_me": "不要再让我看这个了"
|
||||
}
|
Loading…
Reference in a new issue