mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
CW-1138-Add-CakePay-orders-to-the-transaction-history-screen (#2446)
* migrate to new backend API * hide value_type filter * Improve vendor card parsing * Update country.dart * Update lib/cake_pay/src/models/cake_pay_card.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Update lib/cake_pay/src/models/cake_pay_card.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * address review comments * fix min limit value * Update lib/view_model/cake_pay/cake_pay_buy_card_view_model.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * cake pay order row * order provider adapter * add Cake Pay order filtering * Update cake_pay_buy_card_view_model.dart * fix hidden order balance * Update lib/cake_pay/src/models/cake_pay_card.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * Update lib/cake_pay/src/services/cake_pay_api.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> * address review comments --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
@@ -1,6 +1,6 @@
|
||||
import 'package:cake_wallet/buy/buy_amount.dart';
|
||||
import 'package:cake_wallet/buy/buy_quote.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/order/order.dart';
|
||||
import 'package:cake_wallet/buy/pairs_utils.dart';
|
||||
import 'package:cake_wallet/buy/payment_method.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import 'package:cw_core/enumerable_item.dart';
|
||||
|
||||
class BuyProviderDescription extends EnumerableItem<int>
|
||||
with Serializable<int> {
|
||||
const BuyProviderDescription({required String title, required int raw})
|
||||
class BuyProviderDescription extends EnumerableItem<int> with Serializable<int> {
|
||||
const BuyProviderDescription({required String title, required int raw, required this.image})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
static const wyre = BuyProviderDescription(title: 'Wyre', raw: 0);
|
||||
static const moonPay = BuyProviderDescription(title: 'MoonPay', raw: 1);
|
||||
final String image;
|
||||
|
||||
static BuyProviderDescription deserialize({required int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return wyre;
|
||||
case 1:
|
||||
return moonPay;
|
||||
default:
|
||||
throw Exception('Incorrect token $raw for BuyProviderDescription deserialize');
|
||||
}
|
||||
static const wyre = BuyProviderDescription(title: 'Wyre', raw: 0, image: '');
|
||||
static const moonPay = BuyProviderDescription(title: 'MoonPay', raw: 1, image: '');
|
||||
|
||||
static BuyProviderDescription deserialize({required int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return wyre;
|
||||
case 1:
|
||||
return moonPay;
|
||||
default:
|
||||
throw Exception('Incorrect token $raw for BuyProviderDescription deserialize');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import 'package:cake_wallet/buy/buy_exception.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/buy/buy_quote.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/order/order.dart';
|
||||
import 'package:cake_wallet/order/order_source_description.dart';
|
||||
import 'package:cake_wallet/buy/pairs_utils.dart';
|
||||
import 'package:cake_wallet/buy/payment_method.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
@@ -329,7 +330,8 @@ class MoonPayProvider extends BuyProvider {
|
||||
|
||||
return Order(
|
||||
id: id,
|
||||
provider: BuyProviderDescription.moonPay,
|
||||
source: OrderSourceDescription.buy,
|
||||
buyProvider: BuyProviderDescription.moonPay,
|
||||
transferId: id,
|
||||
state: state,
|
||||
createdAt: createdAt,
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cw_core/format_amount.dart';
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
part 'order.g.dart';
|
||||
|
||||
@HiveType(typeId: Order.typeId)
|
||||
class Order extends HiveObject {
|
||||
Order(
|
||||
{required this.id,
|
||||
required this.transferId,
|
||||
required this.createdAt,
|
||||
required this.amount,
|
||||
required this.receiveAddress,
|
||||
required this.walletId,
|
||||
BuyProviderDescription? provider,
|
||||
TradeState? state,
|
||||
this.from,
|
||||
this.to}) {
|
||||
if (provider != null) {
|
||||
providerRaw = provider.raw;
|
||||
}
|
||||
if (state != null) {
|
||||
stateRaw = state.raw;
|
||||
}
|
||||
}
|
||||
|
||||
static const typeId = ORDER_TYPE_ID;
|
||||
static const boxName = 'Orders';
|
||||
static const boxKey = 'ordersBoxKey';
|
||||
|
||||
@HiveField(0, defaultValue: '')
|
||||
String id;
|
||||
|
||||
@HiveField(1, defaultValue: '')
|
||||
String transferId;
|
||||
|
||||
@HiveField(2)
|
||||
String? from;
|
||||
|
||||
@HiveField(3)
|
||||
String? to;
|
||||
|
||||
@HiveField(4, defaultValue: '')
|
||||
late String stateRaw;
|
||||
|
||||
TradeState get state => TradeState.deserialize(raw: stateRaw);
|
||||
|
||||
@HiveField(5)
|
||||
DateTime createdAt;
|
||||
|
||||
@HiveField(6, defaultValue: '')
|
||||
String amount;
|
||||
|
||||
@HiveField(7, defaultValue: '')
|
||||
String receiveAddress;
|
||||
|
||||
@HiveField(8, defaultValue: '')
|
||||
String walletId;
|
||||
|
||||
@HiveField(9, defaultValue: 0)
|
||||
late int providerRaw;
|
||||
|
||||
BuyProviderDescription get provider =>
|
||||
BuyProviderDescription.deserialize(raw: providerRaw);
|
||||
|
||||
String amountFormatted() => formatAmount(amount);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
import 'package:cake_wallet/buy/buy_exception.dart';
|
||||
import 'package:cake_wallet/order/order_source_description.dart';
|
||||
import 'package:cake_wallet/buy/pairs_utils.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
@@ -7,7 +8,7 @@ import 'package:cw_core/utils/proxy_wrapper.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/order/order.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
@@ -157,7 +158,8 @@ class WyreBuyProvider extends BuyProvider {
|
||||
|
||||
return Order(
|
||||
id: id,
|
||||
provider: BuyProviderDescription.wyre,
|
||||
source: OrderSourceDescription.buy,
|
||||
buyProvider: BuyProviderDescription.wyre,
|
||||
transferId: transferId,
|
||||
from: from,
|
||||
to: to,
|
||||
|
||||
@@ -39,6 +39,7 @@ class CakePayCard {
|
||||
final termsAndConditions = stripHtmlIfNeeded(json['terms_and_conditions'] as String? ?? '');
|
||||
final howToUse = stripHtmlIfNeeded(json['how_to_use'] as String? ?? '');
|
||||
final fiatCurrency = FiatCurrency.deserialize(raw: json['currency_code'] as String? ?? '');
|
||||
|
||||
String? minValue = json['min_value'] as String?;
|
||||
|
||||
final parsedMinValueLocal = _toDouble(json['min_value']);
|
||||
|
||||
@@ -15,8 +15,11 @@ class CakePayOrder {
|
||||
final List<OrderCard> cards;
|
||||
final String? externalId;
|
||||
final double amountUsd;
|
||||
final String totalReceiveAmount;
|
||||
final int quantity;
|
||||
final String status;
|
||||
final String? vouchers;
|
||||
final String fiatCurrencyCode;
|
||||
final PaymentData paymentData;
|
||||
|
||||
CakePayOrder({
|
||||
@@ -24,23 +27,68 @@ class CakePayOrder {
|
||||
required this.cards,
|
||||
required this.externalId,
|
||||
required this.amountUsd,
|
||||
required this.totalReceiveAmount,
|
||||
required this.quantity,
|
||||
required this.status,
|
||||
required this.vouchers,
|
||||
required this.fiatCurrencyCode,
|
||||
required this.paymentData,
|
||||
});
|
||||
|
||||
factory CakePayOrder.fromMap(Map<String, dynamic> map) {
|
||||
|
||||
final cards = map['cards'] as List<dynamic>;
|
||||
final firstCard = cards.isNotEmpty ? cards.first as Map<String, dynamic> : {};
|
||||
|
||||
return CakePayOrder(
|
||||
orderId: map['order_id'] as String,
|
||||
cards: (map['cards'] as List<dynamic>)
|
||||
cards: cards
|
||||
.map((x) => OrderCard.fromMap(x as Map<String, dynamic>))
|
||||
.toList(),
|
||||
externalId: map['external_id'] as String?,
|
||||
amountUsd: map['amount_usd'] as double,
|
||||
totalReceiveAmount: firstCard['subtotal'] as String? ?? '',
|
||||
quantity: firstCard['quantity'] as int,
|
||||
status: map['status'] as String,
|
||||
vouchers: map['vouchers'] as String?,
|
||||
fiatCurrencyCode: firstCard['currency_code'] as String? ?? '',
|
||||
paymentData: PaymentData.fromMap(map['payment_data'] as Map<String, dynamic>));
|
||||
}
|
||||
|
||||
static CryptoPaymentData? getPaymentDataFor({CakePayPaymentMethod? method,CakePayOrder? order}) {
|
||||
if (order == null || method == null) return null;
|
||||
|
||||
final data = switch (method) {
|
||||
CakePayPaymentMethod.BTC => order.paymentData.btc,
|
||||
CakePayPaymentMethod.XMR => order.paymentData.xmr,
|
||||
CakePayPaymentMethod.LTC => order.paymentData.ltc,
|
||||
CakePayPaymentMethod.LTC_MWEB => order.paymentData.ltc_mweb,
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (data == null) return null;
|
||||
|
||||
final bip21 = data.paymentUrls?.bip21;
|
||||
if (bip21 != null && bip21.isNotEmpty) {
|
||||
final uri = Uri.parse(bip21);
|
||||
final addr = uri.path;
|
||||
final price = uri.queryParameters['amount'] ?? data.price;
|
||||
|
||||
return CryptoPaymentData(price: price, address: addr, amount: data.amount, paymentUrls: data.paymentUrls);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static String getCurrencyCodeFromPaymentMethod(CakePayPaymentMethod method) {
|
||||
return switch (method) {
|
||||
CakePayPaymentMethod.BTC => 'BTC',
|
||||
CakePayPaymentMethod.BTC_LN => 'BTC',
|
||||
CakePayPaymentMethod.XMR => 'XMR',
|
||||
CakePayPaymentMethod.LTC => 'LTC',
|
||||
CakePayPaymentMethod.LTC_MWEB => 'LTC',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class OrderCard {
|
||||
@@ -112,11 +160,13 @@ class PaymentData {
|
||||
|
||||
class CryptoPaymentData {
|
||||
final String price;
|
||||
final String amount;
|
||||
final PaymentUrl? paymentUrls;
|
||||
final String address;
|
||||
|
||||
CryptoPaymentData({
|
||||
required this.price,
|
||||
required this.amount,
|
||||
this.paymentUrls,
|
||||
required this.address,
|
||||
});
|
||||
@@ -124,6 +174,7 @@ class CryptoPaymentData {
|
||||
factory CryptoPaymentData.fromMap(Map<String, dynamic> map) {
|
||||
return CryptoPaymentData(
|
||||
price: map['price'] as String,
|
||||
amount: (map['amount_from'] as double?)?.toString() ?? '',
|
||||
paymentUrls: PaymentUrl.fromMap(map['paymentUrls'] as Map<String, dynamic>?),
|
||||
address: map['address'] as String,
|
||||
);
|
||||
|
||||
@@ -19,7 +19,7 @@ class CakePayApi {
|
||||
static const authPath = '/api/accounts/auth';
|
||||
static final verifyEmailPath = '/api/accounts/auth/verify';
|
||||
static final logoutPath = '/api/accounts/logout';
|
||||
static final createOrderPath = '/api/orders/order';
|
||||
static final orderPath = '/api/orders/order';
|
||||
static final simulatePaymentPath = '/api/orders/simulate-payment';
|
||||
|
||||
/// AuthenticateUser
|
||||
@@ -104,7 +104,7 @@ class CakePayApi {
|
||||
required bool confirmsVoidedRefund,
|
||||
required bool confirmsTermsAgreed,
|
||||
}) async {
|
||||
final uri = Uri.https(baseCakePayUri, createOrderPath);
|
||||
final uri = Uri.https(baseCakePayUri, orderPath);
|
||||
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
@@ -155,6 +155,28 @@ class CakePayApi {
|
||||
throw Exception(message);
|
||||
}
|
||||
|
||||
/// Get Order by ID
|
||||
Future<CakePayOrder> getOrderById({required String orderId, required String token}) async {
|
||||
final uri = Uri.https(baseCakePayUri, '$orderPath/$orderId');
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Token $token',
|
||||
};
|
||||
final response = await ProxyWrapper().get(
|
||||
clearnetUri: uri,
|
||||
headers: headers,
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception('Unexpected http status: ${response.statusCode}');
|
||||
}
|
||||
|
||||
final bodyJson = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
||||
return CakePayOrder.fromMap(bodyJson);
|
||||
}
|
||||
|
||||
///Simulate Payment
|
||||
Future<String> simulatePayment(
|
||||
{required String CSRFToken,
|
||||
|
||||
@@ -109,6 +109,11 @@ class CakePayService {
|
||||
);
|
||||
}
|
||||
|
||||
Future<CakePayOrder> findOrderById({required String orderId}) async {
|
||||
final token = (await secureStorage.read(key: cakePayUserTokenKey))!;
|
||||
return await cakePayApi.getOrderById(orderId: orderId, token: token);
|
||||
}
|
||||
|
||||
///Simulate Purchase Gift Card
|
||||
Future<String> simulatePayment({required String orderId}) async {
|
||||
final token = (await secureStorage.read(key: cakePayUserTokenKey))!;
|
||||
|
||||
+13
-4
@@ -10,7 +10,7 @@ import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
||||
import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
||||
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/order/order.dart';
|
||||
import 'package:cake_wallet/core/backup_service_v3.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
|
||||
@@ -43,6 +43,7 @@ import 'package:cake_wallet/src/screens/start_tor/start_tor_page.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_connect/services/bottom_sheet_service.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_connect/services/key_service/wallet_connect_key_service.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_connect/services/walletkit_service.dart';
|
||||
import 'package:cake_wallet/store/dashboard/order_filter_store.dart';
|
||||
import 'package:cake_wallet/themes/core/theme_store.dart';
|
||||
import 'package:cake_wallet/view_model/dev/monero_background_sync.dart';
|
||||
import 'package:cake_wallet/view_model/dev/secure_preferences.dart';
|
||||
@@ -374,6 +375,7 @@ Future<void> setup({
|
||||
getIt.registerFactory(() =>
|
||||
PayjoinTransactionsStore(payjoinSessionSource: _payjoinSessionSource));
|
||||
getIt.registerSingleton<TradeFilterStore>(TradeFilterStore());
|
||||
getIt.registerSingleton<OrderFilterStore>(OrderFilterStore());
|
||||
getIt.registerSingleton<TransactionFilterStore>(TransactionFilterStore(getIt.get<AppStore>()));
|
||||
getIt.registerSingleton<FiatConversionStore>(FiatConversionStore());
|
||||
getIt.registerSingleton<SendTemplateStore>(SendTemplateStore(templateSource: _templates));
|
||||
@@ -545,11 +547,12 @@ Future<void> setup({
|
||||
balanceViewModel: getIt.get<BalanceViewModel>(),
|
||||
appStore: getIt.get<AppStore>(),
|
||||
tradesStore: getIt.get<TradesStore>(),
|
||||
ordersStore: getIt.get<OrdersStore>(),
|
||||
tradeFilterStore: getIt.get<TradeFilterStore>(),
|
||||
orderFilterStore: getIt.get<OrderFilterStore>(),
|
||||
transactionFilterStore: getIt.get<TransactionFilterStore>(),
|
||||
settingsStore: settingsStore,
|
||||
yatStore: getIt.get<YatStore>(),
|
||||
ordersStore: getIt.get<OrdersStore>(),
|
||||
anonpayTransactionsStore: getIt.get<AnonpayTransactionsStore>(),
|
||||
payjoinTransactionsStore: getIt.get<PayjoinTransactionsStore>(),
|
||||
sharedPreferences: getIt.get<SharedPreferences>(),
|
||||
@@ -1349,8 +1352,13 @@ Future<void> setup({
|
||||
|
||||
getIt.registerFactoryParam<OrderDetailsViewModel, Order, void>((order, _) {
|
||||
final wallet = getIt.get<AppStore>().wallet;
|
||||
final cakePayService = getIt.get<CakePayService>();
|
||||
|
||||
return OrderDetailsViewModel(wallet: wallet!, orderForDetails: order);
|
||||
return OrderDetailsViewModel(
|
||||
wallet: wallet!,
|
||||
orderForDetails: order,
|
||||
cakePayService: cakePayService,
|
||||
orders: _ordersSource);
|
||||
});
|
||||
|
||||
getIt.registerFactoryParam<OrderDetailsPage, Order, void>(
|
||||
@@ -1423,7 +1431,8 @@ Future<void> setup({
|
||||
(CakePayVendor vendor, _) {
|
||||
return CakePayBuyCardViewModel(vendor: vendor,
|
||||
cakePayService: getIt.get<CakePayService>(),
|
||||
sendViewModel: getIt.get<SendViewModel>());
|
||||
sendViewModel: getIt.get<SendViewModel>(),
|
||||
orders: _ordersSource);
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => CakePayAccountViewModel(cakePayService: getIt.get<CakePayService>()));
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import 'dart:io';
|
||||
import 'dart:ui';
|
||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||
import 'package:cake_wallet/app_scroll_behavior.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/order/order.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/core/background_sync.dart';
|
||||
import 'package:cake_wallet/core/node_switching_service.dart';
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cw_core/format_amount.dart';
|
||||
import 'package:cw_core/hive_type_ids.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
import 'order_provider_description.dart';
|
||||
import 'order_source_description.dart';
|
||||
|
||||
part 'order.g.dart';
|
||||
|
||||
@HiveType(typeId: Order.typeId)
|
||||
class Order extends HiveObject {
|
||||
Order({
|
||||
required this.id,
|
||||
required this.transferId,
|
||||
required this.createdAt,
|
||||
required this.amount,
|
||||
this.receiveAmount,
|
||||
this.quantity,
|
||||
required this.receiveAddress,
|
||||
required this.walletId,
|
||||
this.from,
|
||||
this.to,
|
||||
TradeState? state,
|
||||
OrderSourceDescription source = OrderSourceDescription.buy,
|
||||
BuyProviderDescription? buyProvider,
|
||||
OrderProviderDescription? giftCardProvider,
|
||||
}) {
|
||||
sourceRaw = source.raw;
|
||||
if (state != null) stateRaw = state.raw;
|
||||
if (buyProvider != null) providerRaw = buyProvider.raw;
|
||||
if (giftCardProvider != null) providerRaw = giftCardProvider.raw;
|
||||
}
|
||||
|
||||
static const typeId = ORDER_TYPE_ID;
|
||||
static const boxName = 'Orders';
|
||||
static const boxKey = 'ordersBoxKey';
|
||||
|
||||
@HiveField(0, defaultValue: '')
|
||||
String id;
|
||||
|
||||
@HiveField(1, defaultValue: '')
|
||||
String transferId;
|
||||
|
||||
@HiveField(2)
|
||||
String? from;
|
||||
|
||||
@HiveField(3)
|
||||
String? to;
|
||||
|
||||
@HiveField(4, defaultValue: '')
|
||||
late String stateRaw;
|
||||
|
||||
TradeState get state => TradeState.deserialize(raw: stateRaw);
|
||||
|
||||
@HiveField(5)
|
||||
DateTime createdAt;
|
||||
|
||||
@HiveField(6, defaultValue: '')
|
||||
String amount;
|
||||
|
||||
@HiveField(7, defaultValue: '')
|
||||
String receiveAddress;
|
||||
|
||||
@HiveField(8, defaultValue: '')
|
||||
String walletId;
|
||||
|
||||
@HiveField(9, defaultValue: 0)
|
||||
late int providerRaw;
|
||||
|
||||
@HiveField(10, defaultValue: '')
|
||||
String? receiveAmount;
|
||||
|
||||
@HiveField(11, defaultValue: 0)
|
||||
late int? sourceRaw;
|
||||
|
||||
@HiveField(12, defaultValue: '')
|
||||
String? quantity;
|
||||
|
||||
OrderSourceDescription get source => OrderSourceDescription.deserialize(raw: sourceRaw ?? 0);
|
||||
|
||||
BuyProviderDescription? get buyProvider => source == OrderSourceDescription.buy
|
||||
? BuyProviderDescription.deserialize(raw: providerRaw)
|
||||
: null;
|
||||
|
||||
OrderProviderDescription? get orderProvider => source == OrderSourceDescription.order
|
||||
? OrderProviderDescription.deserialize(raw: providerRaw)
|
||||
: null;
|
||||
|
||||
String get providerTitle {
|
||||
if (source == OrderSourceDescription.buy) {
|
||||
return buyProvider?.title ?? '';
|
||||
} else {
|
||||
return orderProvider?.title ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
String get providerIcon {
|
||||
if (source == OrderSourceDescription.buy) {
|
||||
return buyProvider?.image ?? '';
|
||||
} else {
|
||||
return orderProvider?.image ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
String amountFormatted() => formatAmount(amount) + ' ${from ?? ''}';
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import 'package:cake_wallet/order/order.dart';
|
||||
|
||||
abstract class OrderProvider {
|
||||
Future<(Order, Object?)> findOrderById(String id);
|
||||
|
||||
String get title;
|
||||
|
||||
String get trackUrl;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import 'package:cake_wallet/cake_pay/src/models/cake_pay_order.dart';
|
||||
import 'package:cake_wallet/cake_pay/src/services/cake_pay_service.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/order/order.dart';
|
||||
import 'package:cake_wallet/order/order_provider.dart';
|
||||
import 'package:cake_wallet/order/order_provider_description.dart';
|
||||
import 'package:cake_wallet/order/order_source_description.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
|
||||
class CakePayOrderProviderAdapter implements OrderProvider {
|
||||
CakePayOrderProviderAdapter({
|
||||
required this.wallet,
|
||||
required this.cakePayService,
|
||||
});
|
||||
|
||||
final WalletBase wallet;
|
||||
final CakePayService cakePayService;
|
||||
|
||||
@override
|
||||
String get title => 'Cake Pay';
|
||||
|
||||
@override
|
||||
String get trackUrl => '';
|
||||
|
||||
@override
|
||||
Future<(Order, Object?)> findOrderById(String id) async {
|
||||
final cakePayOrder = await cakePayService.findOrderById(orderId: id);
|
||||
final cards = cakePayOrder.cards;
|
||||
final order = Order(
|
||||
id: cakePayOrder.orderId,
|
||||
state: determineState(cakePayOrder.status),
|
||||
transferId: '',
|
||||
createdAt: DateTime.now(),
|
||||
amount: '',
|
||||
receiveAmount: cakePayOrder.totalReceiveAmount,
|
||||
quantity: cakePayOrder.quantity.toString(),
|
||||
receiveAddress: '',
|
||||
walletId: wallet.id,
|
||||
from: '',
|
||||
to: cakePayOrder.fiatCurrencyCode,
|
||||
source: OrderSourceDescription.order,
|
||||
giftCardProvider: OrderProviderDescription.cakePay,
|
||||
);
|
||||
return (order, cards);
|
||||
}
|
||||
|
||||
TradeState determineState(String state) {
|
||||
final swapState = switch (state) {
|
||||
'new' => TradeState.pending,
|
||||
'expired_but_still_pending' => TradeState.overdue,
|
||||
'expired' => TradeState.expired,
|
||||
'failed' => TradeState.failed,
|
||||
'paid' => TradeState.paid,
|
||||
'paid_partial' => TradeState.underpaid,
|
||||
'pending_purchase' => TradeState.processing,
|
||||
'purchase_processing' => TradeState.processing,
|
||||
'purchased' => TradeState.confirmed,
|
||||
'pending_email' => TradeState.awaiting,
|
||||
'complete' => TradeState.complete,
|
||||
'pending_refund' => TradeState.refund,
|
||||
'refunded' => TradeState.refunded,
|
||||
_ => TradeState.notFound
|
||||
};
|
||||
|
||||
return swapState;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
||||
import 'package:cake_wallet/order/order.dart';
|
||||
import 'package:cake_wallet/order/order_provider.dart';
|
||||
import 'package:cake_wallet/order/order_source_description.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
|
||||
class MoonPayOrderProviderAdapter implements OrderProvider {
|
||||
MoonPayOrderProviderAdapter(
|
||||
{required AppStore appStore, required WalletBase wallet, bool isTestEnvironment = false})
|
||||
: _inner = MoonPayProvider(
|
||||
appStore: appStore,
|
||||
wallet: wallet,
|
||||
isTestEnvironment: isTestEnvironment,
|
||||
),
|
||||
_wallet = wallet;
|
||||
|
||||
final MoonPayProvider _inner;
|
||||
final WalletBase _wallet;
|
||||
|
||||
@override
|
||||
String get title => 'MoonPay';
|
||||
|
||||
@override
|
||||
String get trackUrl => _inner.trackUrl;
|
||||
|
||||
@override
|
||||
Future<(Order, Object?)> findOrderById(String id) async {
|
||||
final buyOrder = await _inner.findOrderById(id);
|
||||
final order = Order(
|
||||
id: buyOrder.id,
|
||||
transferId: buyOrder.transferId,
|
||||
createdAt: buyOrder.createdAt,
|
||||
amount: buyOrder.amount,
|
||||
receiveAmount: buyOrder.receiveAmount,
|
||||
receiveAddress: _wallet.walletAddresses.address,
|
||||
walletId: _wallet.id,
|
||||
from: buyOrder.from,
|
||||
to: buyOrder.to,
|
||||
state: buyOrder.state,
|
||||
source: OrderSourceDescription.buy,
|
||||
buyProvider: BuyProviderDescription.moonPay,
|
||||
);
|
||||
return (order, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
|
||||
import 'package:cake_wallet/order/order.dart';
|
||||
import 'package:cake_wallet/order/order_provider.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
|
||||
class WyreOrderProviderAdapter implements OrderProvider {
|
||||
WyreOrderProviderAdapter({required WalletBase wallet})
|
||||
: _wyreProvider = WyreBuyProvider(wallet: wallet);
|
||||
|
||||
final WyreBuyProvider _wyreProvider;
|
||||
|
||||
@override
|
||||
Future<(Order, Object?)> findOrderById(String id) async {
|
||||
final order = await _wyreProvider.findOrderById(id);
|
||||
return (order, null);
|
||||
}
|
||||
|
||||
@override
|
||||
String get title => _wyreProvider.title;
|
||||
|
||||
@override
|
||||
String get trackUrl => _wyreProvider.trackUrl;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'package:cw_core/enumerable_item.dart';
|
||||
|
||||
class OrderProviderDescription extends EnumerableItem<int> with Serializable<int> {
|
||||
const OrderProviderDescription({
|
||||
required String title,
|
||||
required int raw,
|
||||
required this.image,
|
||||
}) : super(title: title, raw: raw);
|
||||
|
||||
final String image;
|
||||
|
||||
static const cakePay = OrderProviderDescription(
|
||||
title: 'Cake Pay', raw: 0, image: 'assets/images/cake_pay_icon.png');
|
||||
|
||||
static OrderProviderDescription deserialize({required int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return cakePay;
|
||||
default:
|
||||
throw Exception('Invalid OrderProviderDescription $raw');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:cw_core/enumerable_item.dart';
|
||||
|
||||
class OrderSourceDescription extends EnumerableItem<int> with Serializable<int> {
|
||||
const OrderSourceDescription({required String title, required int raw})
|
||||
: super(title: title, raw: raw);
|
||||
|
||||
static const buy = OrderSourceDescription(title: 'Buy', raw: 0);
|
||||
static const order = OrderSourceDescription(title: 'Order', raw: 1);
|
||||
|
||||
static OrderSourceDescription deserialize({required int raw}) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return buy;
|
||||
case 1:
|
||||
return order;
|
||||
default:
|
||||
throw Exception('Invalid OrderSourceDescription raw value: $raw');
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@ import 'dart:io';
|
||||
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
|
||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_arguments.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/order/order.dart';
|
||||
import 'package:cake_wallet/core/new_wallet_type_arguments.dart';
|
||||
import 'package:cake_wallet/core/totp_request_details.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||
import 'package:cake_wallet/order/order_source_description.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/anonpay_transaction_row.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/order_row.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/payjoin_transaction_row.dart';
|
||||
@@ -189,19 +191,32 @@ class TransactionsPage extends StatelessWidget {
|
||||
}
|
||||
if (item is OrderListItem) {
|
||||
final order = item.order;
|
||||
|
||||
return Observer(
|
||||
builder: (_) => OrderRow(
|
||||
builder: (_) {
|
||||
|
||||
// TODO: Refactor Amount Hiding Logic it is not working properly for Orders and Trades
|
||||
final hide = dashboardViewModel.balanceViewModel.displayMode ==
|
||||
BalanceDisplayMode.hiddenBalance;
|
||||
|
||||
final formattedAmount = hide ? '---' : order.amountFormatted();
|
||||
final formattedReceiveAmount = hide ? '---' : order.receiveAmount;
|
||||
|
||||
return OrderRow(
|
||||
key: item.key,
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.orderDetails, arguments: order),
|
||||
provider: order.provider,
|
||||
from: order.from!,
|
||||
to: order.to!,
|
||||
createdAtFormattedDate:
|
||||
DateFormat('HH:mm').format(order.createdAt),
|
||||
formattedAmount: item.orderFormattedAmount,
|
||||
),
|
||||
providerTitle: order.providerTitle,
|
||||
providerIconPath: order.providerIcon,
|
||||
from: order.from ?? '',
|
||||
to: order.to ?? '',
|
||||
createdAtFormattedDate: DateFormat('HH:mm').format(order.createdAt),
|
||||
formattedAmount: formattedAmount,
|
||||
formattedReceiveAmount: dashboardViewModel.balanceViewModel.isFiatDisabled &&
|
||||
order.source == OrderSourceDescription.order
|
||||
? ''
|
||||
: formattedReceiveAmount,
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
return Container(color: Colors.transparent, height: 1);
|
||||
|
||||
@@ -1,77 +1,110 @@
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/buy/get_buy_provider_icon.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/utils/image_utill.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class OrderRow extends StatelessWidget {
|
||||
OrderRow({
|
||||
required this.provider,
|
||||
const OrderRow({
|
||||
Key? key,
|
||||
required this.providerTitle,
|
||||
required this.providerIconPath,
|
||||
required this.from,
|
||||
required this.to,
|
||||
required this.createdAtFormattedDate,
|
||||
this.state,
|
||||
this.onTap,
|
||||
this.formattedAmount,
|
||||
super.key,
|
||||
});
|
||||
this.formattedReceiveAmount,
|
||||
}) : super(key: key);
|
||||
|
||||
final VoidCallback? onTap;
|
||||
final BuyProviderDescription provider;
|
||||
|
||||
final String providerTitle;
|
||||
final String providerIconPath;
|
||||
|
||||
final String from;
|
||||
final String to;
|
||||
final String createdAtFormattedDate;
|
||||
final String? createdAtFormattedDate;
|
||||
final String? formattedAmount;
|
||||
final String? formattedReceiveAmount;
|
||||
final TradeState? state;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final iconColor = Theme.of(context).colorScheme.onSurfaceVariant;
|
||||
|
||||
final providerIcon = getBuyProviderIcon(provider, iconColor: iconColor);
|
||||
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: EdgeInsets.fromLTRB(24, 8, 24, 8),
|
||||
padding: const EdgeInsets.fromLTRB(24, 8, 24, 8),
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (providerIcon != null)
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 12),
|
||||
child: providerIcon,
|
||||
),
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
_ProviderIcon(path: providerIconPath, title: providerTitle),
|
||||
if (state != null)
|
||||
Positioned(
|
||||
right: 0,
|
||||
bottom: 2,
|
||||
child: Container(
|
||||
height: 8,
|
||||
width: 8,
|
||||
decoration: BoxDecoration(
|
||||
color: _statusColor(context, state!),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[
|
||||
Text(
|
||||
'$from → $to',
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
formattedAmount != null
|
||||
? Text(
|
||||
formattedAmount! + ' ' + to,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
)
|
||||
: Container()
|
||||
]),
|
||||
SizedBox(height: 5),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
createdAtFormattedDate,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
'$from → $to',
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
if (formattedAmount != null)
|
||||
Text(
|
||||
formattedAmount!,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
if (createdAtFormattedDate != null)
|
||||
Text(
|
||||
createdAtFormattedDate!,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
if (formattedReceiveAmount != null)
|
||||
Text(
|
||||
formattedReceiveAmount!,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
@@ -82,4 +115,54 @@ class OrderRow extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Color _statusColor(BuildContext context, TradeState status) {
|
||||
switch (status) {
|
||||
case TradeState.complete:
|
||||
case TradeState.completed:
|
||||
case TradeState.finished:
|
||||
case TradeState.success:
|
||||
case TradeState.settled:
|
||||
return PaletteDark.brightGreen;
|
||||
case TradeState.failed:
|
||||
case TradeState.expired:
|
||||
case TradeState.notFound:
|
||||
return Palette.darkRed;
|
||||
default:
|
||||
return const Color(0xffff6600);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _ProviderIcon extends StatelessWidget {
|
||||
const _ProviderIcon({required this.path, required this.title});
|
||||
|
||||
final String path;
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (path.isNotEmpty) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: ImageUtil.getImageFromPath(imagePath: path, height: 36, width: 36),
|
||||
);
|
||||
}
|
||||
return Container(
|
||||
height: 36,
|
||||
width: 36,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
title.isNotEmpty ? title.characters.first.toUpperCase() : '?',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'package:cake_wallet/cake_pay/src/models/cake_pay_order.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CakePayDetailsListCardItem extends StandartListItem {
|
||||
CakePayDetailsListCardItem({
|
||||
required String title,
|
||||
required String value,
|
||||
required this.id,
|
||||
required this.createdAt,
|
||||
this.price,
|
||||
this.quantity,
|
||||
required this.from,
|
||||
required this.to,
|
||||
required this.onTap,
|
||||
required this.cards,
|
||||
}) : super(title: title, value: value);
|
||||
|
||||
final String id;
|
||||
final String createdAt;
|
||||
final String? price;
|
||||
final String? quantity;
|
||||
final String from;
|
||||
final String to;
|
||||
final void Function(BuildContext) onTap;
|
||||
final List<OrderCard> cards;
|
||||
}
|
||||
@@ -1,4 +1,9 @@
|
||||
import 'package:cake_wallet/src/screens/order_details/cake_pay_detail_list_card_item.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/trade_details_status_item.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_pay_order_list_card.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list_status_row.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.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';
|
||||
@@ -19,24 +24,25 @@ class OrderDetailsPage extends BasePage {
|
||||
final OrderDetailsViewModel orderDetailsViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) =>
|
||||
OrderDetailsPageBody(orderDetailsViewModel);
|
||||
Widget body(BuildContext context) => OrderDetailsPageBody(orderDetailsViewModel, currentTheme);
|
||||
}
|
||||
|
||||
class OrderDetailsPageBody extends StatefulWidget {
|
||||
OrderDetailsPageBody(this.orderDetailsViewModel);
|
||||
OrderDetailsPageBody(this.orderDetailsViewModel, this.currentTheme);
|
||||
|
||||
final OrderDetailsViewModel orderDetailsViewModel;
|
||||
final MaterialThemeBase currentTheme;
|
||||
|
||||
@override
|
||||
OrderDetailsPageBodyState createState() =>
|
||||
OrderDetailsPageBodyState(orderDetailsViewModel);
|
||||
OrderDetailsPageBodyState(orderDetailsViewModel, currentTheme);
|
||||
}
|
||||
|
||||
class OrderDetailsPageBodyState extends State<OrderDetailsPageBody> {
|
||||
OrderDetailsPageBodyState(this.orderDetailsViewModel);
|
||||
OrderDetailsPageBodyState(this.orderDetailsViewModel, this.currentTheme);
|
||||
|
||||
final OrderDetailsViewModel orderDetailsViewModel;
|
||||
final MaterialThemeBase currentTheme;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -47,28 +53,45 @@ class OrderDetailsPageBodyState extends State<OrderDetailsPageBody> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Observer(builder: (_) {
|
||||
final itemsCount = orderDetailsViewModel.items.length;
|
||||
return SectionStandardList(
|
||||
sectionCount: 1,
|
||||
itemCounter: (int _) => orderDetailsViewModel.items.length,
|
||||
itemCounter: (int _) => itemsCount,
|
||||
itemBuilder: (__, index) {
|
||||
final item = orderDetailsViewModel.items[index];
|
||||
|
||||
if (item is DetailsListStatusItem) {
|
||||
return StandardListStatusRow(title: item.title, value: item.value);
|
||||
}
|
||||
|
||||
if (item is CakePayDetailsListCardItem) {
|
||||
final cardImagePath = (item.cards.isNotEmpty) ? item.cards.first.cardImagePath : null;
|
||||
|
||||
return CakePayOrderListCard(
|
||||
id: item.id,
|
||||
create: item.createdAt,
|
||||
price: item.price,
|
||||
quantity: item.quantity,
|
||||
pair: '${item.from} → ${item.to}',
|
||||
currentTheme: widget.currentTheme.type,
|
||||
onTap: item.onTap,
|
||||
backgroundImage: cardImagePath,
|
||||
);
|
||||
}
|
||||
|
||||
if (item is TrackTradeListItem) {
|
||||
return GestureDetector(
|
||||
onTap: item.onTap,
|
||||
child: ListRow(
|
||||
title: '${item.title}', value: '${item.value}'));
|
||||
child: ListRow(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: ListRow(
|
||||
title: '${item.title}', value: '${item.value}'));
|
||||
child: ListRow(title: '${item.title}', value: '${item.value}'));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
|
||||
class CakePayOrderListCard extends StatelessWidget {
|
||||
const CakePayOrderListCard({
|
||||
Key? key,
|
||||
required this.id,
|
||||
required this.create,
|
||||
this.quantity,
|
||||
this.price,
|
||||
required this.pair,
|
||||
required this.onTap,
|
||||
required this.currentTheme,
|
||||
this.backgroundImage,
|
||||
this.aspectRatio = 16 / 9,
|
||||
}) : super(key: key);
|
||||
|
||||
final String id;
|
||||
final String create;
|
||||
final String? quantity;
|
||||
final String? price;
|
||||
final String pair;
|
||||
final ThemeType currentTheme;
|
||||
final void Function(BuildContext) onTap;
|
||||
final String? backgroundImage;
|
||||
final double aspectRatio;
|
||||
|
||||
ImageProvider? _imageProviderFor(String? path) {
|
||||
if (path == null || path.isEmpty) return null;
|
||||
if (path.startsWith('http://') || path.startsWith('https://')) return NetworkImage(path);
|
||||
return AssetImage(path);
|
||||
}
|
||||
|
||||
Color _labelBackground(BuildContext context) {
|
||||
final surface = Theme.of(context).colorScheme.surface;
|
||||
return surface.withOpacity(0.75);
|
||||
}
|
||||
|
||||
Color _titleLabelBackground(BuildContext context) {
|
||||
final surface = Theme.of(context).colorScheme.surface;
|
||||
return surface.withOpacity(0.85);
|
||||
}
|
||||
|
||||
Widget _labelBox(
|
||||
BuildContext context, {
|
||||
required String text,
|
||||
EdgeInsets padding = const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
double radius = 10,
|
||||
Color? background,
|
||||
TextStyle? style,
|
||||
}) {
|
||||
return Container(
|
||||
padding: padding,
|
||||
decoration: BoxDecoration(
|
||||
color: background ?? _labelBackground(context),
|
||||
borderRadius: BorderRadius.circular(radius),
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: style ??
|
||||
Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final image = _imageProviderFor(backgroundImage);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
onTap: () => onTap(context),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: AspectRatio(
|
||||
aspectRatio: aspectRatio,
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
if (image != null)
|
||||
Image(image: image, fit: BoxFit.cover)
|
||||
else
|
||||
Container(color: Theme.of(context).colorScheme.surfaceContainerHighest),
|
||||
if (image != null)
|
||||
Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
stops: [0.0, 0.6, 1.0],
|
||||
colors: [Colors.transparent, Colors.black26, Colors.black54],
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(14.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_labelBox(
|
||||
context,
|
||||
text: 'Create • $create',
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||
radius: 8,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 10,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
_labelBox(
|
||||
context,
|
||||
text: 'Qty • $quantity Total • $price',
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||
radius: 8,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 10,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
_labelBox(
|
||||
context,
|
||||
text: pair,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
radius: 12,
|
||||
background: _titleLabelBackground(context),
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
_labelBox(
|
||||
context,
|
||||
text: 'Order • $id',
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
radius: 10,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
IgnorePointer(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withOpacity(currentTheme == ThemeType.dark ? 0.08 : 0.12),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import 'package:cake_wallet/order/order_provider_description.dart';
|
||||
import 'package:cake_wallet/order/order_source_description.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'order_filter_store.g.dart';
|
||||
|
||||
class OrderFilterStore = OrderFilterStoreBase with _$OrderFilterStore;
|
||||
|
||||
abstract class OrderFilterStoreBase with Store {
|
||||
OrderFilterStoreBase() : displayCakePay = true;
|
||||
|
||||
@observable
|
||||
bool displayCakePay;
|
||||
|
||||
@computed
|
||||
bool get displayAllOrders => displayCakePay;
|
||||
|
||||
@action
|
||||
void toggleDisplayOrder(OrderProviderDescription provider) {
|
||||
switch (provider) {
|
||||
case OrderProviderDescription.cakePay:
|
||||
displayCakePay = !displayCakePay;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
List<OrderListItem> filtered({
|
||||
required List<OrderListItem> orders,
|
||||
required WalletBase wallet,
|
||||
}) {
|
||||
final walletOrders =
|
||||
orders.where((item) => item.order.walletId == wallet.id).toList();
|
||||
|
||||
final cakePayOrders = walletOrders.where((item) {
|
||||
final order = item.order;
|
||||
final isOrderSource = order.source == OrderSourceDescription.order;
|
||||
final isCakePay = order.orderProvider == OrderProviderDescription.cakePay;
|
||||
return isOrderSource && isCakePay;
|
||||
}).toList();
|
||||
|
||||
if (!displayCakePay) return <OrderListItem>[];
|
||||
return cakePayOrders;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/order/order.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/order_list_item.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
@@ -8,7 +8,7 @@ import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/view_model/buy/buy_item.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/order/order.dart';
|
||||
import 'package:cake_wallet/store/dashboard/orders_store.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cake_wallet/buy/buy_provider_description.dart';
|
||||
import 'package:cake_wallet/order/order_provider_description.dart';
|
||||
import 'package:cake_wallet/order/order.dart';
|
||||
import 'package:cake_wallet/order/order_source_description.dart';
|
||||
import 'package:cake_wallet/cake_pay/src/models/cake_pay_card.dart';
|
||||
import 'package:cake_wallet/cake_pay/src/models/cake_pay_order.dart';
|
||||
import 'package:cake_wallet/cake_pay/src/models/cake_pay_vendor.dart';
|
||||
import 'package:cake_wallet/cake_pay/src/services/cake_pay_service.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'cake_pay_buy_card_view_model.g.dart';
|
||||
@@ -17,8 +23,12 @@ class CakePayBuyCardViewModel = CakePayBuyCardViewModelBase with _$CakePayBuyCar
|
||||
|
||||
abstract class CakePayBuyCardViewModelBase with Store {
|
||||
CakePayBuyCardViewModelBase(
|
||||
{required this.vendor, required CakePayService cakePayService, required this.sendViewModel})
|
||||
: _cakePayService = cakePayService, walletType = sendViewModel.walletType,
|
||||
{required this.vendor,
|
||||
required CakePayService cakePayService,
|
||||
required this.sendViewModel,
|
||||
required this.orders})
|
||||
: _cakePayService = cakePayService,
|
||||
walletType = sendViewModel.walletType,
|
||||
amount = vendor.card!.denominationItems.isNotEmpty
|
||||
? vendor.card!.denominationItems.first.value
|
||||
: 0,
|
||||
@@ -41,6 +51,7 @@ abstract class CakePayBuyCardViewModelBase with Store {
|
||||
final double min;
|
||||
final CakePayCard card;
|
||||
final WalletType walletType;
|
||||
final Box<Order> orders;
|
||||
|
||||
CakePayOrder? order;
|
||||
Timer? _timer;
|
||||
@@ -56,7 +67,7 @@ abstract class CakePayBuyCardViewModelBase with Store {
|
||||
bool get isDenominationSelected =>
|
||||
card.denominationItems.isNotEmpty &&
|
||||
card.denominationItems.any((item) => item.value == amount);
|
||||
|
||||
|
||||
Future<bool> get isUserLogged async => await _cakePayService.isLogged();
|
||||
|
||||
@observable
|
||||
@@ -99,7 +110,10 @@ abstract class CakePayBuyCardViewModelBase with Store {
|
||||
case WalletType.bitcoin:
|
||||
return [CakePayPaymentMethod.BTC];
|
||||
case WalletType.litecoin:
|
||||
return [CakePayPaymentMethod.LTC,if (sendViewModel.isMwebEnabled) CakePayPaymentMethod.LTC_MWEB];
|
||||
return [
|
||||
CakePayPaymentMethod.LTC,
|
||||
if (sendViewModel.isMwebEnabled) CakePayPaymentMethod.LTC_MWEB
|
||||
];
|
||||
case WalletType.monero:
|
||||
return [CakePayPaymentMethod.XMR];
|
||||
default:
|
||||
@@ -124,31 +138,6 @@ abstract class CakePayBuyCardViewModelBase with Store {
|
||||
amount = double.parse(input.replaceAll(',', '.'));
|
||||
}
|
||||
|
||||
CryptoPaymentData? getPaymentDataFor(CakePayPaymentMethod? method) {
|
||||
if (order == null || method == null) return null;
|
||||
|
||||
final data = switch (method) {
|
||||
CakePayPaymentMethod.BTC => order?.paymentData.btc,
|
||||
CakePayPaymentMethod.XMR => order?.paymentData.xmr,
|
||||
CakePayPaymentMethod.LTC => order?.paymentData.ltc,
|
||||
CakePayPaymentMethod.LTC_MWEB => order?.paymentData.ltc_mweb,
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (data == null) return null;
|
||||
|
||||
final bip21 = data.paymentUrls?.bip21;
|
||||
if (bip21 != null && bip21.isNotEmpty) {
|
||||
final uri = Uri.parse(bip21);
|
||||
final addr = uri.path;
|
||||
final price = uri.queryParameters['amount'] ?? data.price;
|
||||
|
||||
return CryptoPaymentData(price: price, address: addr);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> createOrder() async {
|
||||
if (walletType != WalletType.bitcoin &&
|
||||
@@ -166,8 +155,29 @@ abstract class CakePayBuyCardViewModelBase with Store {
|
||||
confirmsVoidedRefund: confirmsVoidedRefund,
|
||||
confirmsTermsAgreed: confirmsTermsAgreed,
|
||||
);
|
||||
await confirmSending();
|
||||
final paymentData =
|
||||
CakePayOrder.getPaymentDataFor(method: selectedPaymentMethod, order: order); // TODO should hande other currencies if added
|
||||
if (paymentData == null || order == null)
|
||||
throw Exception('Payment data or order is not available.');
|
||||
|
||||
await confirmSending(paymentData);
|
||||
expirationTime = order!.paymentData.expirationTime;
|
||||
|
||||
final orderRecord = Order(
|
||||
id: order!.orderId,
|
||||
state: TradeState.deserialize(raw: order!.status),
|
||||
transferId: order!.externalId ?? '',
|
||||
from: CakePayOrder.getCurrencyCodeFromPaymentMethod(selectedPaymentMethod!),
|
||||
to: order!.fiatCurrencyCode,
|
||||
createdAt: DateTime.now(),
|
||||
amount: paymentData.amount ?? '',
|
||||
receiveAmount: order!.totalReceiveAmount,
|
||||
quantity: order!.quantity.toString(),
|
||||
receiveAddress: paymentData.address ?? '',
|
||||
source: OrderSourceDescription.order,
|
||||
giftCardProvider: OrderProviderDescription.cakePay,
|
||||
walletId: sendViewModel.wallet.id);
|
||||
orders.add(orderRecord);
|
||||
updateRemainingTime();
|
||||
_startExpirationTimer();
|
||||
} catch (e) {
|
||||
@@ -177,15 +187,12 @@ abstract class CakePayBuyCardViewModelBase with Store {
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> confirmSending() async {
|
||||
final cryptoPaymentData = getPaymentDataFor(selectedPaymentMethod);
|
||||
if (order == null || cryptoPaymentData == null) return;
|
||||
|
||||
Future<void> confirmSending(CryptoPaymentData paymentData) async {
|
||||
try {
|
||||
sendViewModel.clearOutputs();
|
||||
final output = sendViewModel.outputs.first;
|
||||
output.address = cryptoPaymentData.address;
|
||||
output.setCryptoAmount(cryptoPaymentData.price);
|
||||
output.address = paymentData.address;
|
||||
output.setCryptoAmount(paymentData.price);
|
||||
|
||||
await sendViewModel.createTransaction();
|
||||
} catch (e) {
|
||||
|
||||
@@ -13,7 +13,9 @@ import 'package:cake_wallet/entities/service_status.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/monero/monero.dart';
|
||||
import 'package:cake_wallet/order/order_provider_description.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/store/dashboard/order_filter_store.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cw_core/utils/proxy_wrapper.dart';
|
||||
@@ -72,6 +74,7 @@ abstract class DashboardViewModelBase with Store {
|
||||
required this.appStore,
|
||||
required this.tradesStore,
|
||||
required this.tradeFilterStore,
|
||||
required this.orderFilterStore,
|
||||
required this.transactionFilterStore,
|
||||
required this.settingsStore,
|
||||
required this.yatStore,
|
||||
@@ -110,6 +113,13 @@ abstract class DashboardViewModelBase with Store {
|
||||
// caption: S.current.transactions_by_date,
|
||||
// onChanged: null),
|
||||
],
|
||||
'Orders': [
|
||||
FilterItem(
|
||||
value: () => orderFilterStore.displayCakePay,
|
||||
caption: 'Cake Pay',
|
||||
onChanged: () =>
|
||||
orderFilterStore.toggleDisplayOrder(OrderProviderDescription.cakePay)),
|
||||
],
|
||||
S.current.trades: [
|
||||
FilterItem(
|
||||
value: () => tradeFilterStore.displayAllTrades,
|
||||
@@ -452,7 +462,7 @@ abstract class DashboardViewModelBase with Store {
|
||||
_items.addAll(
|
||||
transactionFilterStore.filtered(transactions: [...transactions, ...anonpayTransactions]));
|
||||
_items.addAll(tradeFilterStore.filtered(trades: trades, wallet: wallet));
|
||||
_items.addAll(orders);
|
||||
_items.addAll(orderFilterStore.filtered(orders: orders, wallet: wallet));
|
||||
|
||||
if (payjoinTransactions.isNotEmpty) {
|
||||
final _payjoinTransactions = payjoinTransactions;
|
||||
@@ -807,6 +817,8 @@ abstract class DashboardViewModelBase with Store {
|
||||
|
||||
TradeFilterStore tradeFilterStore;
|
||||
|
||||
OrderFilterStore orderFilterStore;
|
||||
|
||||
AnonpayTransactionsStore anonpayTransactionsStore;
|
||||
|
||||
TransactionFilterStore transactionFilterStore;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/order/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';
|
||||
@@ -15,13 +15,11 @@ class OrderListItem extends ActionListItem {
|
||||
|
||||
BalanceDisplayMode get displayMode => settingsStore.balanceDisplayMode;
|
||||
|
||||
String get orderFormattedAmount {
|
||||
return order.amount != null
|
||||
? displayMode == BalanceDisplayMode.hiddenBalance
|
||||
? '---'
|
||||
: order.amountFormatted()
|
||||
: order.amount;
|
||||
}
|
||||
String get orderFormattedAmount =>
|
||||
displayMode == BalanceDisplayMode.hiddenBalance ? '---' : order.amountFormatted();
|
||||
|
||||
String get orderFormattedReceiveAddress =>
|
||||
displayMode == BalanceDisplayMode.hiddenBalance ? '---' : order.receiveAmount ?? '';
|
||||
|
||||
@override
|
||||
DateTime get date => order.createdAt;
|
||||
|
||||
@@ -1,41 +1,61 @@
|
||||
import 'dart:async';
|
||||
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:cw_core/utils/print_verbose.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/cake_pay/src/models/cake_pay_order.dart';
|
||||
import 'package:cake_wallet/cake_pay/src/services/cake_pay_service.dart';
|
||||
import 'package:cake_wallet/core/utilities.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
import 'package:cake_wallet/order/order.dart';
|
||||
import 'package:cake_wallet/order/order_provider.dart';
|
||||
import 'package:cake_wallet/order/order_provider_adapter/cake_pay_order_provider_adapter.dart';
|
||||
import 'package:cake_wallet/order/order_provider_adapter/wyre_order_provider_adapter.dart';
|
||||
import 'package:cake_wallet/order/order_provider_description.dart';
|
||||
import 'package:cake_wallet/order/order_source_description.dart';
|
||||
import 'package:cake_wallet/src/screens/order_details/cake_pay_detail_list_card_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/src/screens/trade_details/trade_details_status_item.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
import 'package:cake_wallet/utils/date_formatter.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart';
|
||||
import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
part 'order_details_view_model.g.dart';
|
||||
|
||||
class OrderDetailsViewModel = OrderDetailsViewModelBase
|
||||
with _$OrderDetailsViewModel;
|
||||
class OrderDetailsViewModel = OrderDetailsViewModelBase with _$OrderDetailsViewModel;
|
||||
|
||||
abstract class OrderDetailsViewModelBase with Store {
|
||||
OrderDetailsViewModelBase({required WalletBase wallet, required Order orderForDetails})
|
||||
: items = ObservableList<StandartListItem>(),
|
||||
order = orderForDetails {
|
||||
if (order.provider != null) {
|
||||
switch (order.provider) {
|
||||
case BuyProviderDescription.wyre:
|
||||
_provider = WyreBuyProvider(wallet: wallet);
|
||||
break;
|
||||
case BuyProviderDescription.moonPay:
|
||||
// _provider = MoonPayProvider(wallet: wallet);// TODO: CW-521
|
||||
break;
|
||||
}
|
||||
OrderDetailsViewModelBase({
|
||||
required WalletBase wallet,
|
||||
required Order orderForDetails,
|
||||
required this.cakePayService,
|
||||
required this.orders,
|
||||
}) : items = ObservableList<StandartListItem>(),
|
||||
order = orderForDetails {
|
||||
switch (order.source) {
|
||||
case OrderSourceDescription.buy:
|
||||
if (order.buyProvider == BuyProviderDescription.wyre) {
|
||||
_provider = WyreOrderProviderAdapter(wallet: wallet);
|
||||
} else if (order.buyProvider == BuyProviderDescription.moonPay) {
|
||||
//_provider = MoonPayOrderProviderAdapter(appStore: null /* provide if needed */, wallet: wallet);
|
||||
}
|
||||
break;
|
||||
|
||||
case OrderSourceDescription.order:
|
||||
if (order.orderProvider == OrderProviderDescription.cakePay) {
|
||||
_provider = CakePayOrderProviderAdapter(wallet: wallet, cakePayService: cakePayService);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
_updateItems();
|
||||
_updateOrder();
|
||||
timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateOrder());
|
||||
timer = Timer.periodic(const Duration(seconds: 20), (_) async => _updateOrder());
|
||||
}
|
||||
|
||||
@observable
|
||||
@@ -44,27 +64,37 @@ abstract class OrderDetailsViewModelBase with Store {
|
||||
@observable
|
||||
ObservableList<StandartListItem> items;
|
||||
|
||||
BuyProvider? _provider;
|
||||
final CakePayService cakePayService;
|
||||
final Box<Order> orders;
|
||||
OrderProvider? _provider;
|
||||
List<OrderCard> cards = [];
|
||||
|
||||
Timer? timer;
|
||||
|
||||
@action
|
||||
Future<void> _updateOrder() async {
|
||||
try {
|
||||
if (_provider != null && (_provider is MoonPayProvider || _provider is WyreBuyProvider)) {
|
||||
final updatedOrder = _provider is MoonPayProvider
|
||||
? await (_provider as MoonPayProvider).findOrderById(order.id)
|
||||
: await (_provider as WyreBuyProvider).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();
|
||||
if (_provider == null) return;
|
||||
|
||||
final updatedOrderObj = await _provider!.findOrderById(order.id);
|
||||
final updatedOrder = updatedOrderObj.$1;
|
||||
|
||||
if (_provider is CakePayOrderProviderAdapter) {
|
||||
cards = updatedOrderObj.$2 as List<OrderCard>? ?? [];
|
||||
}
|
||||
|
||||
final existing = orders.values.firstWhereOrNull((e) => e.id == updatedOrder.id);
|
||||
if (existing != null) {
|
||||
existing.stateRaw = updatedOrder.stateRaw;
|
||||
await existing.save();
|
||||
|
||||
order = existing;
|
||||
} else {
|
||||
await orders.add(updatedOrder);
|
||||
order = updatedOrder;
|
||||
}
|
||||
|
||||
_updateItems();
|
||||
} catch (e) {
|
||||
printV(e.toString());
|
||||
}
|
||||
@@ -73,55 +103,43 @@ abstract class OrderDetailsViewModelBase with Store {
|
||||
void _updateItems() {
|
||||
final dateFormat = DateFormatter.withCurrentLocal();
|
||||
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),
|
||||
]);
|
||||
|
||||
items.add(
|
||||
StandartListItem(
|
||||
title: 'Buy provider',
|
||||
value: order.provider.title)
|
||||
);
|
||||
DetailsListStatusItem(title: S.current.trade_details_state, value: order.state.toString()));
|
||||
|
||||
if (_provider != null && (_provider is MoonPayProvider || _provider is WyreBuyProvider)) {
|
||||
|
||||
final trackUrl = _provider is MoonPayProvider
|
||||
? (_provider as MoonPayProvider).trackUrl
|
||||
: (_provider as WyreBuyProvider).trackUrl;
|
||||
|
||||
if (trackUrl.isNotEmpty ?? false) {
|
||||
final buildURL = trackUrl + '${order.transferId}';
|
||||
items.add(
|
||||
TrackTradeListItem(
|
||||
title: S.current.track,
|
||||
value: buildURL,
|
||||
onTap: () async {
|
||||
try {
|
||||
final uri = Uri.parse(buildURL);
|
||||
if (await canLaunchUrl(uri))
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
} catch (e) {}
|
||||
}));
|
||||
}
|
||||
if (_provider is CakePayOrderProviderAdapter) {
|
||||
items.add(CakePayDetailsListCardItem(
|
||||
title: '',
|
||||
value: '',
|
||||
id: order.id,
|
||||
createdAt: dateFormat.format(order.createdAt),
|
||||
price: order.receiveAmount,
|
||||
quantity: order.quantity,
|
||||
from: order.from ?? '',
|
||||
to: order.to ?? '',
|
||||
cards: cards,
|
||||
onTap: (BuildContext context) {
|
||||
Clipboard.setData(ClipboardData(text: '${order.id}'));
|
||||
showBar<void>(context, S.of(context).copied_to_clipboard);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
items.add(
|
||||
StandartListItem(
|
||||
title: S.current.trade_details_created_at,
|
||||
value: dateFormat.format(order.createdAt).toString())
|
||||
);
|
||||
items.add(StandartListItem(title: 'Order provider', value: _provider?.title ?? ''));
|
||||
|
||||
items.add(
|
||||
StandartListItem(
|
||||
title: S.current.trade_details_pair,
|
||||
value: '${order.from} → ${order.to}')
|
||||
);
|
||||
final trackUrl = _provider?.trackUrl ?? '';
|
||||
if (trackUrl.isNotEmpty) {
|
||||
final buildURL = trackUrl + '${order.transferId}';
|
||||
items.add(TrackTradeListItem(
|
||||
title: S.current.track,
|
||||
value: buildURL,
|
||||
onTap: () async {
|
||||
try {
|
||||
final uri = Uri.parse(buildURL);
|
||||
if (await canLaunchUrl(uri))
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
} catch (e) {}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user