mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-03-12 09:32:33 +00:00
Merge pull request #96 from cake-tech/CAKE-279-integrate-wyre
Cake 279 integrate wyre
This commit is contained in:
commit
21ae0ec0da
21 changed files with 759 additions and 15 deletions
|
@ -7,9 +7,9 @@ import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||||
|
|
||||||
|
|
||||||
public class MainActivity extends FlutterFragmentActivity {
|
public class MainActivity extends FlutterFragmentActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
|
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
|
||||||
GeneratedPluginRegistrant.registerWith(flutterEngine);
|
GeneratedPluginRegistrant.registerWith(flutterEngine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
assets/images/wyre-icon.png
Normal file
BIN
assets/images/wyre-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
45
lib/di.dart
45
lib/di.dart
|
@ -4,8 +4,10 @@ import 'package:cake_wallet/core/wallet_service.dart';
|
||||||
import 'package:cake_wallet/entities/biometric_auth.dart';
|
import 'package:cake_wallet/entities/biometric_auth.dart';
|
||||||
import 'package:cake_wallet/entities/contact_record.dart';
|
import 'package:cake_wallet/entities/contact_record.dart';
|
||||||
import 'package:cake_wallet/entities/load_current_wallet.dart';
|
import 'package:cake_wallet/entities/load_current_wallet.dart';
|
||||||
|
import 'package:cake_wallet/entities/order.dart';
|
||||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
import 'package:cake_wallet/entities/transaction_description.dart';
|
||||||
import 'package:cake_wallet/entities/transaction_info.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/monero/monero_wallet_service.dart';
|
||||||
import 'package:cake_wallet/entities/contact.dart';
|
import 'package:cake_wallet/entities/contact.dart';
|
||||||
import 'package:cake_wallet/entities/node.dart';
|
import 'package:cake_wallet/entities/node.dart';
|
||||||
|
@ -22,6 +24,7 @@ import 'package:cake_wallet/src/screens/faq/faq_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
|
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
|
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/nodes/nodes_list_page.dart';
|
import 'package:cake_wallet/src/screens/nodes/nodes_list_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/pin_code/pin_code_widget.dart';
|
||||||
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
|
import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
|
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
|
||||||
|
@ -39,6 +42,8 @@ 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/wallet_keys/wallet_keys_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/exchange/exchange_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/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/node_list_store.dart';
|
||||||
import 'package:cake_wallet/store/secret_store.dart';
|
import 'package:cake_wallet/store/secret_store.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
|
@ -63,6 +68,7 @@ import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
|
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
|
||||||
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
|
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/order_details_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/rescan_view_model.dart';
|
import 'package:cake_wallet/view_model/rescan_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/restore_from_backup_view_model.dart';
|
import 'package:cake_wallet/view_model/restore_from_backup_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart';
|
import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart';
|
||||||
|
@ -83,6 +89,7 @@ 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_restore_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_seed_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/exchange/exchange_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wyre_view_model.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
@ -115,6 +122,7 @@ Box<Trade> _tradesSource;
|
||||||
Box<Template> _templates;
|
Box<Template> _templates;
|
||||||
Box<ExchangeTemplate> _exchangeTemplates;
|
Box<ExchangeTemplate> _exchangeTemplates;
|
||||||
Box<TransactionDescription> _transactionDescriptionBox;
|
Box<TransactionDescription> _transactionDescriptionBox;
|
||||||
|
Box<Order> _ordersSource;
|
||||||
|
|
||||||
Future setup(
|
Future setup(
|
||||||
{Box<WalletInfo> walletInfoSource,
|
{Box<WalletInfo> walletInfoSource,
|
||||||
|
@ -123,7 +131,8 @@ Future setup(
|
||||||
Box<Trade> tradesSource,
|
Box<Trade> tradesSource,
|
||||||
Box<Template> templates,
|
Box<Template> templates,
|
||||||
Box<ExchangeTemplate> exchangeTemplates,
|
Box<ExchangeTemplate> exchangeTemplates,
|
||||||
Box<TransactionDescription> transactionDescriptionBox}) async {
|
Box<TransactionDescription> transactionDescriptionBox,
|
||||||
|
Box<Order> ordersSource}) async {
|
||||||
_walletInfoSource = walletInfoSource;
|
_walletInfoSource = walletInfoSource;
|
||||||
_nodeSource = nodeSource;
|
_nodeSource = nodeSource;
|
||||||
_contactSource = contactSource;
|
_contactSource = contactSource;
|
||||||
|
@ -131,10 +140,11 @@ Future setup(
|
||||||
_templates = templates;
|
_templates = templates;
|
||||||
_exchangeTemplates = exchangeTemplates;
|
_exchangeTemplates = exchangeTemplates;
|
||||||
_transactionDescriptionBox = transactionDescriptionBox;
|
_transactionDescriptionBox = transactionDescriptionBox;
|
||||||
|
_ordersSource = ordersSource;
|
||||||
|
|
||||||
if (!_isSetupFinished) {
|
if (!_isSetupFinished) {
|
||||||
getIt.registerSingletonAsync<SharedPreferences>(
|
getIt.registerSingletonAsync<SharedPreferences>(
|
||||||
() => SharedPreferences.getInstance());
|
() => SharedPreferences.getInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
final settingsStore = await SettingsStoreBase.load(nodeSource: _nodeSource);
|
final settingsStore = await SettingsStoreBase.load(nodeSource: _nodeSource);
|
||||||
|
@ -157,6 +167,8 @@ Future setup(
|
||||||
nodeListStore: getIt.get<NodeListStore>()));
|
nodeListStore: getIt.get<NodeListStore>()));
|
||||||
getIt.registerSingleton<TradesStore>(TradesStore(
|
getIt.registerSingleton<TradesStore>(TradesStore(
|
||||||
tradesSource: _tradesSource, settingsStore: getIt.get<SettingsStore>()));
|
tradesSource: _tradesSource, settingsStore: getIt.get<SettingsStore>()));
|
||||||
|
getIt.registerSingleton<OrdersStore>(OrdersStore(
|
||||||
|
ordersSource: _ordersSource, settingsStore: getIt.get<SettingsStore>()));
|
||||||
getIt.registerSingleton<TradeFilterStore>(TradeFilterStore());
|
getIt.registerSingleton<TradeFilterStore>(TradeFilterStore());
|
||||||
getIt.registerSingleton<TransactionFilterStore>(TransactionFilterStore());
|
getIt.registerSingleton<TransactionFilterStore>(TransactionFilterStore());
|
||||||
getIt.registerSingleton<FiatConversionStore>(FiatConversionStore());
|
getIt.registerSingleton<FiatConversionStore>(FiatConversionStore());
|
||||||
|
@ -219,7 +231,10 @@ Future setup(
|
||||||
appStore: getIt.get<AppStore>(),
|
appStore: getIt.get<AppStore>(),
|
||||||
tradesStore: getIt.get<TradesStore>(),
|
tradesStore: getIt.get<TradesStore>(),
|
||||||
tradeFilterStore: getIt.get<TradeFilterStore>(),
|
tradeFilterStore: getIt.get<TradeFilterStore>(),
|
||||||
transactionFilterStore: getIt.get<TransactionFilterStore>()));
|
transactionFilterStore: getIt.get<TransactionFilterStore>(),
|
||||||
|
ordersSource: _ordersSource,
|
||||||
|
ordersStore: getIt.get<OrdersStore>(),
|
||||||
|
wyreViewModel: getIt.get<WyreViewModel>()));
|
||||||
|
|
||||||
getIt.registerFactory<AuthService>(() => AuthService(
|
getIt.registerFactory<AuthService>(() => AuthService(
|
||||||
secureStorage: getIt.get<FlutterSecureStorage>(),
|
secureStorage: getIt.get<FlutterSecureStorage>(),
|
||||||
|
@ -511,6 +526,30 @@ Future setup(
|
||||||
getIt.registerFactoryParam<TradeDetailsPage, Trade, void>((Trade trade, _) =>
|
getIt.registerFactoryParam<TradeDetailsPage, Trade, void>((Trade trade, _) =>
|
||||||
TradeDetailsPage(getIt.get<TradeDetailsViewModel>(param1: trade)));
|
TradeDetailsPage(getIt.get<TradeDetailsViewModel>(param1: trade)));
|
||||||
|
|
||||||
|
getIt.registerFactory(() {
|
||||||
|
final wallet = getIt.get<AppStore>().wallet;
|
||||||
|
return WyreService(walletType: wallet.type, walletAddress: wallet.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
getIt.registerFactory(() {
|
||||||
|
final wallet = getIt.get<AppStore>().wallet;
|
||||||
|
return WyreViewModel(ordersSource, getIt.get<OrdersStore>(),
|
||||||
|
walletId: wallet.id, address: wallet.address, type: wallet.type,
|
||||||
|
wyreService: getIt.get<WyreService>());
|
||||||
|
});
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<WyrePage, String, void>((String url, _) =>
|
||||||
|
WyrePage(getIt.get<WyreViewModel>(),
|
||||||
|
ordersStore: getIt.get<OrdersStore>(), url: url));
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<OrderDetailsViewModel, Order, void>(
|
||||||
|
(order, _) => OrderDetailsViewModel(
|
||||||
|
wyreViewModel: getIt.get<WyreViewModel>(),
|
||||||
|
orderForDetails: order));
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<OrderDetailsPage, Order, void>((Order order, _) =>
|
||||||
|
OrderDetailsPage(getIt.get<OrderDetailsViewModel>(param1: order)));
|
||||||
|
|
||||||
getIt.registerFactory(() => SupportViewModel());
|
getIt.registerFactory(() => SupportViewModel());
|
||||||
|
|
||||||
getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>()));
|
getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>()));
|
||||||
|
|
55
lib/entities/order.dart
Normal file
55
lib/entities/order.dart
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||||
|
import 'package:cake_wallet/entities/format_amount.dart';
|
||||||
|
|
||||||
|
part 'order.g.dart';
|
||||||
|
|
||||||
|
@HiveType(typeId: Order.typeId)
|
||||||
|
class Order extends HiveObject {
|
||||||
|
Order(
|
||||||
|
{this.id,
|
||||||
|
this.transferId,
|
||||||
|
this.from,
|
||||||
|
this.to,
|
||||||
|
TradeState state,
|
||||||
|
this.createdAt,
|
||||||
|
this.amount,
|
||||||
|
this.receiveAddress,
|
||||||
|
this.walletId})
|
||||||
|
: stateRaw = state?.raw;
|
||||||
|
|
||||||
|
static const typeId = 8;
|
||||||
|
static const boxName = 'Orders';
|
||||||
|
static const boxKey = 'ordersBoxKey';
|
||||||
|
|
||||||
|
@HiveField(0)
|
||||||
|
String id;
|
||||||
|
|
||||||
|
@HiveField(1)
|
||||||
|
String transferId;
|
||||||
|
|
||||||
|
@HiveField(2)
|
||||||
|
String from;
|
||||||
|
|
||||||
|
@HiveField(3)
|
||||||
|
String to;
|
||||||
|
|
||||||
|
@HiveField(4)
|
||||||
|
String stateRaw;
|
||||||
|
|
||||||
|
TradeState get state => TradeState.deserialize(raw: stateRaw);
|
||||||
|
|
||||||
|
@HiveField(5)
|
||||||
|
DateTime createdAt;
|
||||||
|
|
||||||
|
@HiveField(6)
|
||||||
|
String amount;
|
||||||
|
|
||||||
|
@HiveField(7)
|
||||||
|
String receiveAddress;
|
||||||
|
|
||||||
|
@HiveField(8)
|
||||||
|
String walletId;
|
||||||
|
|
||||||
|
String amountFormatted() => formatAmount(amount);
|
||||||
|
}
|
8
lib/entities/wyre_exception.dart
Normal file
8
lib/entities/wyre_exception.dart
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
class WyreException implements Exception {
|
||||||
|
WyreException(this.description);
|
||||||
|
|
||||||
|
String description;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => description;
|
||||||
|
}
|
111
lib/entities/wyre_service.dart
Normal file
111
lib/entities/wyre_service.dart
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:cake_wallet/entities/wyre_exception.dart';
|
||||||
|
import 'package:cake_wallet/exchange/trade_state.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.walletType,
|
||||||
|
@required this.walletAddress,
|
||||||
|
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 WalletType walletType;
|
||||||
|
final String walletAddress;
|
||||||
|
|
||||||
|
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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/entities/order.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
@ -69,11 +70,17 @@ Future<void> main() async {
|
||||||
Hive.registerAdapter(ExchangeTemplateAdapter());
|
Hive.registerAdapter(ExchangeTemplateAdapter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Hive.isAdapterRegistered(Order.typeId)) {
|
||||||
|
Hive.registerAdapter(OrderAdapter());
|
||||||
|
}
|
||||||
|
|
||||||
final secureStorage = FlutterSecureStorage();
|
final secureStorage = FlutterSecureStorage();
|
||||||
final transactionDescriptionsBoxKey = await getEncryptionKey(
|
final transactionDescriptionsBoxKey = await getEncryptionKey(
|
||||||
secureStorage: secureStorage, forKey: TransactionDescription.boxKey);
|
secureStorage: secureStorage, forKey: TransactionDescription.boxKey);
|
||||||
final tradesBoxKey = await getEncryptionKey(
|
final tradesBoxKey = await getEncryptionKey(
|
||||||
secureStorage: secureStorage, forKey: Trade.boxKey);
|
secureStorage: secureStorage, forKey: Trade.boxKey);
|
||||||
|
final ordersBoxKey = await getEncryptionKey(
|
||||||
|
secureStorage: secureStorage, forKey: Order.boxKey);
|
||||||
final contacts = await Hive.openBox<Contact>(Contact.boxName);
|
final contacts = await Hive.openBox<Contact>(Contact.boxName);
|
||||||
final nodes = await Hive.openBox<Node>(Node.boxName);
|
final nodes = await Hive.openBox<Node>(Node.boxName);
|
||||||
final transactionDescriptions = await Hive.openBox<TransactionDescription>(
|
final transactionDescriptions = await Hive.openBox<TransactionDescription>(
|
||||||
|
@ -81,6 +88,8 @@ Future<void> main() async {
|
||||||
encryptionKey: transactionDescriptionsBoxKey);
|
encryptionKey: transactionDescriptionsBoxKey);
|
||||||
final trades =
|
final trades =
|
||||||
await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
|
await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
|
||||||
|
final orders =
|
||||||
|
await Hive.openBox<Order>(Order.boxName, encryptionKey: ordersBoxKey);
|
||||||
final walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName);
|
final walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName);
|
||||||
final templates = await Hive.openBox<Template>(Template.boxName);
|
final templates = await Hive.openBox<Template>(Template.boxName);
|
||||||
final exchangeTemplates =
|
final exchangeTemplates =
|
||||||
|
@ -91,6 +100,7 @@ Future<void> main() async {
|
||||||
walletInfoSource: walletInfoSource,
|
walletInfoSource: walletInfoSource,
|
||||||
contactSource: contacts,
|
contactSource: contacts,
|
||||||
tradesSource: trades,
|
tradesSource: trades,
|
||||||
|
ordersSource: orders,
|
||||||
// fiatConvertationService: fiatConvertationService,
|
// fiatConvertationService: fiatConvertationService,
|
||||||
templates: templates,
|
templates: templates,
|
||||||
exchangeTemplates: exchangeTemplates,
|
exchangeTemplates: exchangeTemplates,
|
||||||
|
@ -118,6 +128,7 @@ Future<void> initialSetup(
|
||||||
@required Box<WalletInfo> walletInfoSource,
|
@required Box<WalletInfo> walletInfoSource,
|
||||||
@required Box<Contact> contactSource,
|
@required Box<Contact> contactSource,
|
||||||
@required Box<Trade> tradesSource,
|
@required Box<Trade> tradesSource,
|
||||||
|
@required Box<Order> ordersSource,
|
||||||
// @required FiatConvertationService fiatConvertationService,
|
// @required FiatConvertationService fiatConvertationService,
|
||||||
@required Box<Template> templates,
|
@required Box<Template> templates,
|
||||||
@required Box<ExchangeTemplate> exchangeTemplates,
|
@required Box<ExchangeTemplate> exchangeTemplates,
|
||||||
|
@ -139,7 +150,8 @@ Future<void> initialSetup(
|
||||||
tradesSource: tradesSource,
|
tradesSource: tradesSource,
|
||||||
templates: templates,
|
templates: templates,
|
||||||
exchangeTemplates: exchangeTemplates,
|
exchangeTemplates: exchangeTemplates,
|
||||||
transactionDescriptionBox: transactionDescriptions);
|
transactionDescriptionBox: transactionDescriptions,
|
||||||
|
ordersSource: ordersSource);
|
||||||
await bootstrap(navigatorKey);
|
await bootstrap(navigatorKey);
|
||||||
monero_wallet.onStartup();
|
monero_wallet.onStartup();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
import 'package:cake_wallet/entities/contact_record.dart';
|
import 'package:cake_wallet/entities/contact_record.dart';
|
||||||
|
import 'package:cake_wallet/entities/order.dart';
|
||||||
import 'package:cake_wallet/entities/transaction_description.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/backup_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
|
import 'package:cake_wallet/src/screens/backup/edit_backup_password_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/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/restore_from_backup_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/restore/wallet_restore_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/seed/pre_seed_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/support/support_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/store/settings_store.dart';
|
||||||
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
|
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
@ -285,6 +288,16 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
builder: (_) =>
|
builder: (_) =>
|
||||||
getIt.get<TradeDetailsPage>(param1: settings.arguments as Trade));
|
getIt.get<TradeDetailsPage>(param1: settings.arguments as Trade));
|
||||||
|
|
||||||
|
case Routes.orderDetails:
|
||||||
|
return MaterialPageRoute<void>(
|
||||||
|
builder: (_) =>
|
||||||
|
getIt.get<OrderDetailsPage>(param1: settings.arguments as Order));
|
||||||
|
|
||||||
|
case Routes.wyre:
|
||||||
|
return MaterialPageRoute<void>(
|
||||||
|
builder: (_) =>
|
||||||
|
getIt.get<WyrePage>(param1: settings.arguments as String));
|
||||||
|
|
||||||
case Routes.restoreWalletFromSeedDetails:
|
case Routes.restoreWalletFromSeedDetails:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
final walletRestorationFromSeedVM =
|
final walletRestorationFromSeedVM =
|
||||||
|
|
|
@ -52,4 +52,6 @@ class Routes {
|
||||||
static const editBackupPassword = '/edit_backup_passowrd';
|
static const editBackupPassword = '/edit_backup_passowrd';
|
||||||
static const restoreFromBackup = '/restore_from_backup';
|
static const restoreFromBackup = '/restore_from_backup';
|
||||||
static const support = '/support';
|
static const support = '/support';
|
||||||
|
static const orderDetails = '/order_details';
|
||||||
|
static const wyre = '/wyre';
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/themes/theme_base.dart';
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
|
@ -12,7 +13,9 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/address_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
|
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator.dart';
|
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||||
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
|
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
|
||||||
|
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||||
|
|
||||||
class DashboardPage extends BasePage {
|
class DashboardPage extends BasePage {
|
||||||
DashboardPage({
|
DashboardPage({
|
||||||
|
@ -81,7 +84,7 @@ class DashboardPage extends BasePage {
|
||||||
final exchangeImage = Image.asset('assets/images/transfer.png',
|
final exchangeImage = Image.asset('assets/images/transfer.png',
|
||||||
height: 24.27, width: 22.25,
|
height: 24.27, width: 22.25,
|
||||||
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
|
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
|
||||||
final receiveImage = Image.asset('assets/images/download.png',
|
final buyImage = Image.asset('assets/images/coins.png',
|
||||||
height: 22.24, width: 24,
|
height: 22.24, width: 24,
|
||||||
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
|
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
|
||||||
_setEffects();
|
_setEffects();
|
||||||
|
@ -111,7 +114,7 @@ class DashboardPage extends BasePage {
|
||||||
)),
|
)),
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.only(left: 45, right: 45, bottom: 24),
|
padding: EdgeInsets.only(left: 45, right: 45, bottom: 24),
|
||||||
child: Row(
|
child: Observer(builder: (_) => Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ActionButton(
|
ActionButton(
|
||||||
|
@ -122,8 +125,41 @@ class DashboardPage extends BasePage {
|
||||||
image: exchangeImage,
|
image: exchangeImage,
|
||||||
title: S.of(context).exchange,
|
title: S.of(context).exchange,
|
||||||
route: Routes.exchange),
|
route: Routes.exchange),
|
||||||
|
if (walletViewModel.type == WalletType.bitcoin) 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 {
|
||||||
|
try {
|
||||||
|
walletViewModel.isRunningWebView = true;
|
||||||
|
final url =
|
||||||
|
await walletViewModel.wyreViewModel.wyreUrl;
|
||||||
|
await Navigator.of(context)
|
||||||
|
.pushNamed(Routes.wyre, arguments: url);
|
||||||
|
walletViewModel.isRunningWebView = false;
|
||||||
|
} catch(e) {
|
||||||
|
print(e.toString());
|
||||||
|
walletViewModel.isRunningWebView = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
),
|
)),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
|
|
@ -3,14 +3,16 @@ import 'package:flutter/material.dart';
|
||||||
class ActionButton extends StatelessWidget {
|
class ActionButton extends StatelessWidget {
|
||||||
ActionButton(
|
ActionButton(
|
||||||
{@required this.image,
|
{@required this.image,
|
||||||
@required this.title,
|
@required this.title,
|
||||||
@required this.route,
|
this.route,
|
||||||
this.alignment = Alignment.center});
|
this.onClick,
|
||||||
|
this.alignment = Alignment.center});
|
||||||
|
|
||||||
final Image image;
|
final Image image;
|
||||||
final String title;
|
final String title;
|
||||||
final String route;
|
final String route;
|
||||||
final Alignment alignment;
|
final Alignment alignment;
|
||||||
|
final void Function() onClick;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -23,8 +25,10 @@ class ActionButton extends StatelessWidget {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (route.isNotEmpty) {
|
if (route?.isNotEmpty ?? false) {
|
||||||
Navigator.of(context, rootNavigator: true).pushNamed(route);
|
Navigator.of(context, rootNavigator: true).pushNamed(route);
|
||||||
|
} else {
|
||||||
|
onClick?.call();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
|
@ -48,4 +52,4 @@ class ActionButton extends StatelessWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
72
lib/src/screens/dashboard/widgets/order_row.dart
Normal file
72
lib/src/screens/dashboard/widgets/order_row.dart
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class OrderRow extends StatelessWidget {
|
||||||
|
OrderRow({
|
||||||
|
@required this.onTap,
|
||||||
|
this.from,
|
||||||
|
this.to,
|
||||||
|
this.createdAtFormattedDate,
|
||||||
|
this.formattedAmount});
|
||||||
|
final VoidCallback onTap;
|
||||||
|
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) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(24, 8, 24, 8),
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
wyreImage,
|
||||||
|
SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: <Widget>[
|
||||||
|
Text('$from → $to',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context).accentTextTheme.
|
||||||
|
display3.backgroundColor
|
||||||
|
)),
|
||||||
|
formattedAmount != null
|
||||||
|
? Text(formattedAmount + ' ' + to,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context).accentTextTheme.
|
||||||
|
display3.backgroundColor
|
||||||
|
))
|
||||||
|
: Container()
|
||||||
|
]),
|
||||||
|
SizedBox(height: 5),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(createdAtFormattedDate,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Theme.of(context).textTheme
|
||||||
|
.overline.backgroundColor))
|
||||||
|
])
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'package:cake_wallet/src/screens/dashboard/widgets/order_row.dart';
|
||||||
|
import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
@ -75,6 +77,21 @@ class TransactionsPage extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item is OrderListItem) {
|
||||||
|
final order = item.order;
|
||||||
|
|
||||||
|
return OrderRow(
|
||||||
|
onTap: () => Navigator.of(context).pushNamed(
|
||||||
|
Routes.orderDetails,
|
||||||
|
arguments: order),
|
||||||
|
from: order.from,
|
||||||
|
to: order.to,
|
||||||
|
createdAtFormattedDate:
|
||||||
|
DateFormat('HH:mm').format(order.createdAt),
|
||||||
|
formattedAmount: item.orderFormattedAmount,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
height: 1);
|
height: 1);
|
||||||
|
|
47
lib/src/screens/order_details/order_details_page.dart
Normal file
47
lib/src/screens/order_details/order_details_page.dart
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||||
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
|
import 'package:cake_wallet/view_model/order_details_view_model.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
|
||||||
|
|
||||||
|
class OrderDetailsPage extends BasePage {
|
||||||
|
OrderDetailsPage(this.orderDetailsViewModel);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get title => 'Order Details';
|
||||||
|
|
||||||
|
final OrderDetailsViewModel orderDetailsViewModel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget body(BuildContext context) {
|
||||||
|
return Observer(builder: (_) {
|
||||||
|
return SectionStandardList(
|
||||||
|
sectionCount: 1,
|
||||||
|
itemCounter: (int _) => orderDetailsViewModel.items.length,
|
||||||
|
itemBuilder: (_, __, index) {
|
||||||
|
final item = orderDetailsViewModel.items[index];
|
||||||
|
|
||||||
|
if (item is TrackTradeListItem) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: item.onTap,
|
||||||
|
child: StandartListRow(
|
||||||
|
title: '${item.title}', value: '${item.value}'));
|
||||||
|
} else {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: '${item.value}'));
|
||||||
|
showBar<void>(context, S.of(context).copied_to_clipboard);
|
||||||
|
},
|
||||||
|
child: StandartListRow(
|
||||||
|
title: '${item.title}', value: '${item.value}'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
97
lib/src/screens/wyre/wyre_page.dart
Normal file
97
lib/src/screens/wyre/wyre_page.dart
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
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:flutter/material.dart';
|
||||||
|
import 'package:webview_flutter/webview_flutter.dart';
|
||||||
|
|
||||||
|
class WyrePage extends BasePage {
|
||||||
|
WyrePage(this.wyreViewModel,
|
||||||
|
{@required this.ordersStore, @required this.url});
|
||||||
|
|
||||||
|
final OrdersStore ordersStore;
|
||||||
|
final String url;
|
||||||
|
final WyreViewModel wyreViewModel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get title => S.current.buy;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Color get backgroundDarkColor => Colors.white;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Color get titleColor => Palette.darkBlueCraiola;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget body(BuildContext context) =>
|
||||||
|
WyrePageBody(wyreViewModel, ordersStore: ordersStore, url: url);
|
||||||
|
}
|
||||||
|
|
||||||
|
class WyrePageBody extends StatefulWidget {
|
||||||
|
WyrePageBody(this.wyreViewModel, {this.ordersStore, this.url});
|
||||||
|
|
||||||
|
final OrdersStore ordersStore;
|
||||||
|
final String url;
|
||||||
|
final WyreViewModel wyreViewModel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
WyrePageBodyState createState() => WyrePageBodyState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class WyrePageBodyState extends State<WyrePageBody> {
|
||||||
|
String orderId;
|
||||||
|
WebViewController _webViewController;
|
||||||
|
GlobalKey _webViewkey;
|
||||||
|
Timer _timer;
|
||||||
|
bool _isSaving;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_webViewkey = GlobalKey();
|
||||||
|
_isSaving = false;
|
||||||
|
widget.ordersStore.orderId = '';
|
||||||
|
|
||||||
|
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
|
||||||
|
|
||||||
|
_timer?.cancel();
|
||||||
|
_timer = Timer.periodic(Duration(seconds: 1), (timer) async {
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return WebView(
|
||||||
|
key: _webViewkey,
|
||||||
|
initialUrl: widget.url,
|
||||||
|
javascriptMode: JavascriptMode.unrestricted,
|
||||||
|
onWebViewCreated: (WebViewController controller) =>
|
||||||
|
setState(() => _webViewController = controller));
|
||||||
|
}
|
||||||
|
}
|
45
lib/store/dashboard/orders_store.dart
Normal file
45
lib/store/dashboard/orders_store.dart
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:cake_wallet/entities/order.dart';
|
||||||
|
import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
|
|
||||||
|
part 'orders_store.g.dart';
|
||||||
|
|
||||||
|
class OrdersStore = OrdersStoreBase with _$OrdersStore;
|
||||||
|
|
||||||
|
abstract class OrdersStoreBase with Store {
|
||||||
|
OrdersStoreBase({this.ordersSource, this.settingsStore}) {
|
||||||
|
orders = <OrderListItem>[];
|
||||||
|
|
||||||
|
orderId = '';
|
||||||
|
|
||||||
|
_onOrdersChanged =
|
||||||
|
ordersSource.watch().listen((_) async => await updateOrderList());
|
||||||
|
|
||||||
|
updateOrderList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Box<Order> ordersSource;
|
||||||
|
StreamSubscription<BoxEvent> _onOrdersChanged;
|
||||||
|
SettingsStore settingsStore;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
List<OrderListItem> orders;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
Order order;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
String orderId;
|
||||||
|
|
||||||
|
@action
|
||||||
|
void setOrder(Order order) => this.order = order;
|
||||||
|
|
||||||
|
@action
|
||||||
|
Future updateOrderList() async => orders =
|
||||||
|
ordersSource.values.map((order) => OrderListItem(
|
||||||
|
order: order,
|
||||||
|
displayMode: settingsStore.balanceDisplayMode)).toList();
|
||||||
|
}
|
|
@ -1,7 +1,12 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
|
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
|
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
|
||||||
import 'package:cake_wallet/entities/balance.dart';
|
import 'package:cake_wallet/entities/balance.dart';
|
||||||
|
import 'package:cake_wallet/entities/order.dart';
|
||||||
import 'package:cake_wallet/entities/transaction_history.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/account.dart';
|
||||||
import 'package:cake_wallet/monero/monero_balance.dart';
|
import 'package:cake_wallet/monero/monero_balance.dart';
|
||||||
import 'package:cake_wallet/monero/monero_transaction_history.dart';
|
import 'package:cake_wallet/monero/monero_transaction_history.dart';
|
||||||
|
@ -13,13 +18,20 @@ import 'package:cake_wallet/entities/transaction_direction.dart';
|
||||||
import 'package:cake_wallet/entities/transaction_info.dart';
|
import 'package:cake_wallet/entities/transaction_info.dart';
|
||||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||||
import 'package:cake_wallet/exchange/trade.dart';
|
import 'package:cake_wallet/exchange/trade.dart';
|
||||||
|
import 'package:cake_wallet/store/dashboard/orders_store.dart';
|
||||||
import 'package:cake_wallet/utils/mobx.dart';
|
import 'package:cake_wallet/utils/mobx.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/filter_item.dart';
|
import 'package:cake_wallet/view_model/dashboard/filter_item.dart';
|
||||||
|
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/trade_list_item.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/transaction_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/dashboard/action_list_item.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/action_list_display_mode.dart';
|
import 'package:cake_wallet/view_model/dashboard/action_list_display_mode.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wyre_view_model.dart';
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:cake_wallet/core/wallet_base.dart';
|
import 'package:cake_wallet/core/wallet_base.dart';
|
||||||
import 'package:cake_wallet/entities/sync_status.dart';
|
import 'package:cake_wallet/entities/sync_status.dart';
|
||||||
|
@ -30,6 +42,8 @@ import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||||
import 'package:cake_wallet/store/dashboard/trade_filter_store.dart';
|
import 'package:cake_wallet/store/dashboard/trade_filter_store.dart';
|
||||||
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
|
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/formatted_item_list.dart';
|
import 'package:cake_wallet/view_model/dashboard/formatted_item_list.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
import 'package:convert/convert.dart';
|
||||||
|
|
||||||
part 'dashboard_view_model.g.dart';
|
part 'dashboard_view_model.g.dart';
|
||||||
|
|
||||||
|
@ -41,7 +55,10 @@ abstract class DashboardViewModelBase with Store {
|
||||||
this.appStore,
|
this.appStore,
|
||||||
this.tradesStore,
|
this.tradesStore,
|
||||||
this.tradeFilterStore,
|
this.tradeFilterStore,
|
||||||
this.transactionFilterStore}) {
|
this.transactionFilterStore,
|
||||||
|
this.ordersSource,
|
||||||
|
this.ordersStore,
|
||||||
|
this.wyreViewModel}) {
|
||||||
filterItems = {
|
filterItems = {
|
||||||
S.current.transactions: [
|
S.current.transactions: [
|
||||||
FilterItem(
|
FilterItem(
|
||||||
|
@ -76,6 +93,8 @@ abstract class DashboardViewModelBase with Store {
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
isRunningWebView = false;
|
||||||
|
|
||||||
name = appStore.wallet?.name;
|
name = appStore.wallet?.name;
|
||||||
wallet ??= appStore.wallet;
|
wallet ??= appStore.wallet;
|
||||||
type = wallet.type;
|
type = wallet.type;
|
||||||
|
@ -141,6 +160,9 @@ abstract class DashboardViewModelBase with Store {
|
||||||
@observable
|
@observable
|
||||||
String subname;
|
String subname;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
bool isRunningWebView;
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
String get address => wallet.address;
|
String get address => wallet.address;
|
||||||
|
|
||||||
|
@ -171,6 +193,11 @@ abstract class DashboardViewModelBase with Store {
|
||||||
.where((trade) => trade.trade.walletId == wallet.id)
|
.where((trade) => trade.trade.walletId == wallet.id)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
@computed
|
||||||
|
List<OrderListItem> get orders => ordersStore.orders
|
||||||
|
.where((item) => item.order.walletId == wallet.id)
|
||||||
|
.toList();
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
double get price => balanceViewModel.price;
|
double get price => balanceViewModel.price;
|
||||||
|
|
||||||
|
@ -180,6 +207,7 @@ abstract class DashboardViewModelBase with Store {
|
||||||
|
|
||||||
_items.addAll(transactionFilterStore.filtered(transactions: transactions));
|
_items.addAll(transactionFilterStore.filtered(transactions: transactions));
|
||||||
_items.addAll(tradeFilterStore.filtered(trades: trades, wallet: wallet));
|
_items.addAll(tradeFilterStore.filtered(trades: trades, wallet: wallet));
|
||||||
|
_items.addAll(orders);
|
||||||
|
|
||||||
return formattedItemsList(_items);
|
return formattedItemsList(_items);
|
||||||
}
|
}
|
||||||
|
@ -189,16 +217,22 @@ abstract class DashboardViewModelBase with Store {
|
||||||
|
|
||||||
bool get hasRescan => wallet.type == WalletType.monero;
|
bool get hasRescan => wallet.type == WalletType.monero;
|
||||||
|
|
||||||
|
Box<Order> ordersSource;
|
||||||
|
|
||||||
BalanceViewModel balanceViewModel;
|
BalanceViewModel balanceViewModel;
|
||||||
|
|
||||||
AppStore appStore;
|
AppStore appStore;
|
||||||
|
|
||||||
TradesStore tradesStore;
|
TradesStore tradesStore;
|
||||||
|
|
||||||
|
OrdersStore ordersStore;
|
||||||
|
|
||||||
TradeFilterStore tradeFilterStore;
|
TradeFilterStore tradeFilterStore;
|
||||||
|
|
||||||
TransactionFilterStore transactionFilterStore;
|
TransactionFilterStore transactionFilterStore;
|
||||||
|
|
||||||
|
WyreViewModel wyreViewModel;
|
||||||
|
|
||||||
Map<String, List<FilterItem>> filterItems;
|
Map<String, List<FilterItem>> filterItems;
|
||||||
|
|
||||||
ReactionDisposer _reaction;
|
ReactionDisposer _reaction;
|
||||||
|
@ -279,4 +313,6 @@ abstract class DashboardViewModelBase with Store {
|
||||||
balanceViewModel: balanceViewModel,
|
balanceViewModel: balanceViewModel,
|
||||||
settingsStore: appStore.settingsStore)));
|
settingsStore: appStore.settingsStore)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
21
lib/view_model/dashboard/order_list_item.dart
Normal file
21
lib/view_model/dashboard/order_list_item.dart
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:cake_wallet/entities/order.dart';
|
||||||
|
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
|
||||||
|
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||||
|
|
||||||
|
class OrderListItem extends ActionListItem {
|
||||||
|
OrderListItem({this.order, this.displayMode});
|
||||||
|
|
||||||
|
final Order order;
|
||||||
|
final BalanceDisplayMode displayMode;
|
||||||
|
|
||||||
|
String get orderFormattedAmount {
|
||||||
|
return order.amount != null
|
||||||
|
? displayMode == BalanceDisplayMode.hiddenBalance
|
||||||
|
? '---'
|
||||||
|
: order.amountFormatted()
|
||||||
|
: order.amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
DateTime get date => order.createdAt;
|
||||||
|
}
|
85
lib/view_model/order_details_view_model.dart
Normal file
85
lib/view_model/order_details_view_model.dart
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:cake_wallet/entities/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';
|
||||||
|
|
||||||
|
part 'order_details_view_model.g.dart';
|
||||||
|
|
||||||
|
class OrderDetailsViewModel = OrderDetailsViewModelBase
|
||||||
|
with _$OrderDetailsViewModel;
|
||||||
|
|
||||||
|
abstract class OrderDetailsViewModelBase with Store {
|
||||||
|
OrderDetailsViewModelBase({this.wyreViewModel, Order orderForDetails}) {
|
||||||
|
order = orderForDetails;
|
||||||
|
|
||||||
|
items = ObservableList<StandartListItem>();
|
||||||
|
|
||||||
|
_updateItems();
|
||||||
|
|
||||||
|
_updateOrder();
|
||||||
|
|
||||||
|
_timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
@observable
|
||||||
|
Order order;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
ObservableList<StandartListItem> items;
|
||||||
|
|
||||||
|
WyreViewModel wyreViewModel;
|
||||||
|
|
||||||
|
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();
|
||||||
|
} catch (e) {
|
||||||
|
print(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateItems() {
|
||||||
|
final dateFormat = DateFormatter.withCurrentLocal();
|
||||||
|
final buildURL =
|
||||||
|
wyreViewModel.trackUrl + '${order.transferId}';
|
||||||
|
|
||||||
|
items?.clear();
|
||||||
|
|
||||||
|
items.addAll([
|
||||||
|
StandartListItem(
|
||||||
|
title: 'Transfer ID',
|
||||||
|
value: order.transferId),
|
||||||
|
StandartListItem(
|
||||||
|
title: S.current.trade_details_state,
|
||||||
|
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}')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
42
lib/view_model/wyre_view_model.dart
Normal file
42
lib/view_model/wyre_view_model.dart
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
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/entities/wallet_type.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.walletId, @required this.address, @required this.type,
|
||||||
|
@required this.wyreService});
|
||||||
|
|
||||||
|
Future<String> get wyreUrl => wyreService.getWyreUrl();
|
||||||
|
|
||||||
|
String get trackUrl => wyreService.trackUrl;
|
||||||
|
|
||||||
|
final Box<Order> ordersSource;
|
||||||
|
final OrdersStore ordersStore;
|
||||||
|
|
||||||
|
final String walletId;
|
||||||
|
final WalletType type;
|
||||||
|
final String address;
|
||||||
|
|
||||||
|
final WyreService wyreService;
|
||||||
|
|
||||||
|
Future<void> saveOrder(String orderId) async {
|
||||||
|
try {
|
||||||
|
final order = await wyreService.findOrderById(orderId);
|
||||||
|
order.receiveAddress = address;
|
||||||
|
order.walletId = walletId;
|
||||||
|
await ordersSource.add(order);
|
||||||
|
ordersStore.setOrder(order);
|
||||||
|
} catch (e) {
|
||||||
|
print(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -55,6 +55,8 @@ dependencies:
|
||||||
auto_size_text: ^2.1.0
|
auto_size_text: ^2.1.0
|
||||||
dotted_border: ^1.0.5
|
dotted_border: ^1.0.5
|
||||||
smooth_page_indicator: ^0.2.0
|
smooth_page_indicator: ^0.2.0
|
||||||
|
webview_flutter: ^1.0.7
|
||||||
|
flutter_spinkit: ^4.1.2
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
|
|
Loading…
Reference in a new issue