mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-23 06:38:35 +02:00
Cw 314 trocador receive screen update (#823)
* Change receive screen ui * Upgrade flutter packages * revert Upgrade flutter packages * revert Upgrade flutter packages * Adjust flow for anon invoice page navigation * Add receive screen ui * Implement anonpay invoice * Add invoice detail to transactions page * Implement donation link * Fix transaction filter and details view * Save donation link * Fix transaction display issues * Fix formatting * Fix merge conflict * Fix localization * Fix transaction amount display * Fix transaction limit for fiat * Update fix from code review * Fix issues from code review * Make amountTo nullable to avoid potential * Remove encoding for description in donation link * Remove optional params from request * Fix QR image version * Refactor QRCode, fix issues from code review * Pass version to QRCode full page --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
This commit is contained in:
co-authored by
OmarHatem
parent
893a267de6
commit
3006679560
@@ -0,0 +1,186 @@
|
||||
import 'package:cake_wallet/anonpay/anonpay_api.dart';
|
||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||
import 'package:cake_wallet/anonpay/anonpay_request.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cake_wallet/entities/receive_page_option.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/currency.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
part 'anon_invoice_page_view_model.g.dart';
|
||||
|
||||
class AnonInvoicePageViewModel = AnonInvoicePageViewModelBase with _$AnonInvoicePageViewModel;
|
||||
|
||||
abstract class AnonInvoicePageViewModelBase with Store {
|
||||
AnonInvoicePageViewModelBase(
|
||||
this.anonPayApi,
|
||||
this.address,
|
||||
this.settingsStore,
|
||||
this._wallet,
|
||||
this._anonpayInvoiceInfoSource,
|
||||
this.sharedPreferences,
|
||||
this.pageOption,
|
||||
) : receipientEmail = '',
|
||||
receipientName = '',
|
||||
description = '',
|
||||
amount = '',
|
||||
state = InitialExecutionState(),
|
||||
selectedCurrency = walletTypeToCryptoCurrency(_wallet.type),
|
||||
cryptoCurrency = walletTypeToCryptoCurrency(_wallet.type) {
|
||||
_getPreviousDonationLink();
|
||||
_fetchLimits();
|
||||
}
|
||||
|
||||
List<Currency> get currencies => [walletTypeToCryptoCurrency(_wallet.type), ...FiatCurrency.all];
|
||||
final AnonPayApi anonPayApi;
|
||||
final String address;
|
||||
final SettingsStore settingsStore;
|
||||
final WalletBase _wallet;
|
||||
final Box<AnonpayInvoiceInfo> _anonpayInvoiceInfoSource;
|
||||
final SharedPreferences sharedPreferences;
|
||||
final ReceivePageOption pageOption;
|
||||
|
||||
@observable
|
||||
Currency selectedCurrency;
|
||||
|
||||
CryptoCurrency cryptoCurrency;
|
||||
|
||||
@observable
|
||||
String receipientEmail;
|
||||
|
||||
@observable
|
||||
String receipientName;
|
||||
|
||||
@observable
|
||||
String description;
|
||||
|
||||
@observable
|
||||
String amount;
|
||||
|
||||
@observable
|
||||
ExecutionState state;
|
||||
|
||||
@computed
|
||||
int get selectedCurrencyIndex => currencies.indexOf(selectedCurrency);
|
||||
|
||||
@observable
|
||||
double? minimum;
|
||||
|
||||
@observable
|
||||
double? maximum;
|
||||
|
||||
@action
|
||||
void selectCurrency(Currency currency) {
|
||||
selectedCurrency = currency;
|
||||
maximum = minimum = null;
|
||||
if (currency is CryptoCurrency) {
|
||||
cryptoCurrency = currency;
|
||||
} else {
|
||||
cryptoCurrency = walletTypeToCryptoCurrency(_wallet.type);
|
||||
}
|
||||
|
||||
_fetchLimits();
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> createInvoice() async {
|
||||
state = IsExecutingState();
|
||||
if (amount.isNotEmpty) {
|
||||
final amountInCrypto = double.parse(amount);
|
||||
if (minimum != null && amountInCrypto < minimum!) {
|
||||
state = FailureState('Amount is too small');
|
||||
return;
|
||||
}
|
||||
if (maximum != null && amountInCrypto > maximum!) {
|
||||
state = FailureState('Amount is too big');
|
||||
return;
|
||||
}
|
||||
}
|
||||
final result = await anonPayApi.createInvoice(AnonPayRequest(
|
||||
cryptoCurrency: cryptoCurrency,
|
||||
address: address,
|
||||
amount: amount.isEmpty ? null : amount,
|
||||
description: description,
|
||||
email: receipientEmail,
|
||||
name: receipientName,
|
||||
fiatEquivalent:
|
||||
selectedCurrency is FiatCurrency ? (selectedCurrency as FiatCurrency).raw : null,
|
||||
));
|
||||
|
||||
_anonpayInvoiceInfoSource.add(result);
|
||||
|
||||
state = ExecutedSuccessfullyState(payload: result);
|
||||
}
|
||||
|
||||
@action
|
||||
void setRequestParams({
|
||||
required String inputAmount,
|
||||
required String inputName,
|
||||
required String inputEmail,
|
||||
required String inputDescription,
|
||||
}) {
|
||||
receipientName = inputName;
|
||||
receipientEmail = inputEmail;
|
||||
description = inputDescription;
|
||||
amount = inputAmount;
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> generateDonationLink() async {
|
||||
state = IsExecutingState();
|
||||
|
||||
final result = await anonPayApi.generateDonationLink(AnonPayRequest(
|
||||
cryptoCurrency: cryptoCurrency,
|
||||
address: address,
|
||||
description: description,
|
||||
email: receipientEmail,
|
||||
name: receipientName,
|
||||
));
|
||||
|
||||
await sharedPreferences.setString(PreferencesKey.clearnetDonationLink, result.clearnetUrl);
|
||||
await sharedPreferences.setString(PreferencesKey.onionDonationLink, result.onionUrl);
|
||||
|
||||
state = ExecutedSuccessfullyState(payload: result);
|
||||
}
|
||||
|
||||
Future<void> _fetchLimits() async {
|
||||
final limit = await anonPayApi.fetchLimits(
|
||||
cryptoCurrency: cryptoCurrency,
|
||||
fiatCurrency: selectedCurrency is FiatCurrency ? selectedCurrency as FiatCurrency : null,
|
||||
);
|
||||
minimum = limit.min;
|
||||
maximum = limit.max != null ? limit.max! / 4 : null;
|
||||
}
|
||||
|
||||
@action
|
||||
void reset() {
|
||||
selectedCurrency = walletTypeToCryptoCurrency(_wallet.type);
|
||||
cryptoCurrency = walletTypeToCryptoCurrency(_wallet.type);
|
||||
receipientEmail = '';
|
||||
receipientName = '';
|
||||
description = '';
|
||||
amount = '';
|
||||
_fetchLimits();
|
||||
}
|
||||
|
||||
Future<void> _getPreviousDonationLink() async {
|
||||
if (pageOption == ReceivePageOption.anonPayDonationLink) {
|
||||
final donationLink = sharedPreferences.getString(PreferencesKey.clearnetDonationLink);
|
||||
if (donationLink != null) {
|
||||
final url = Uri.parse(donationLink);
|
||||
url.queryParameters.forEach((key, value) {
|
||||
if (key == 'name') receipientName = value;
|
||||
if (key == 'email') receipientEmail = value;
|
||||
if (key == 'description') description = Uri.decodeComponent(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cake_wallet/anonpay/anonpay_api.dart';
|
||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/trade_details/trade_details_list_card.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/store/settings_store.dart';
|
||||
import 'package:cake_wallet/utils/date_formatter.dart';
|
||||
import 'package:cake_wallet/utils/show_bar.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
part 'anonpay_details_view_model.g.dart';
|
||||
|
||||
class AnonpayDetailsViewModel = AnonpayDetailsViewModelBase with _$AnonpayDetailsViewModel;
|
||||
|
||||
abstract class AnonpayDetailsViewModelBase with Store {
|
||||
AnonpayDetailsViewModelBase(
|
||||
{required this.anonPayApi,
|
||||
required AnonpayInvoiceInfo anonpayInvoiceInfo,
|
||||
required this.settingsStore})
|
||||
: items = ObservableList<StandartListItem>(),
|
||||
invoiceDetail = anonpayInvoiceInfo {
|
||||
_updateItems();
|
||||
_updateInvoiceDetail();
|
||||
timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateInvoiceDetail());
|
||||
}
|
||||
|
||||
final AnonPayApi anonPayApi;
|
||||
final SettingsStore settingsStore;
|
||||
final AnonpayInvoiceInfo invoiceDetail;
|
||||
|
||||
final ObservableList<StandartListItem> items;
|
||||
|
||||
Timer? timer;
|
||||
|
||||
@action
|
||||
Future<void> _updateInvoiceDetail() async {
|
||||
try {
|
||||
final data = await anonPayApi.paymentStatus(invoiceDetail.invoiceId);
|
||||
invoiceDetail.status = data.status;
|
||||
_updateItems();
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void _updateItems() {
|
||||
final dateFormat = DateFormatter.withCurrentLocal();
|
||||
items.clear();
|
||||
items.addAll([
|
||||
DetailsListStatusItem(title: S.current.status, value: invoiceDetail.status),
|
||||
TradeDetailsListCardItem(
|
||||
id: invoiceDetail.invoiceId,
|
||||
createdAt: dateFormat.format(invoiceDetail.createdAt).toString(),
|
||||
pair: (invoiceDetail.fiatAmount != null)
|
||||
? "→ ${invoiceDetail.fiatAmount} ${invoiceDetail.fiatEquiv ?? ''}"
|
||||
: '→ ${invoiceDetail.amountTo ?? ''} ${CryptoCurrency.fromFullName(invoiceDetail.coinTo).name.toUpperCase()}',
|
||||
onTap: (BuildContext context) {
|
||||
Clipboard.setData(ClipboardData(text: '${invoiceDetail.invoiceId}'));
|
||||
showBar<void>(context, S.of(context).copied_to_clipboard);
|
||||
},
|
||||
),
|
||||
StandartListItem(title: S.current.trade_details_provider, value: invoiceDetail.provider)
|
||||
]);
|
||||
|
||||
items.add(TrackTradeListItem(
|
||||
title: 'Track',
|
||||
value: invoiceDetail.clearnetStatusUrl,
|
||||
onTap: () => launchUrlString(invoiceDetail.clearnetStatusUrl)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
|
||||
|
||||
class AnonpayTransactionListItem extends ActionListItem {
|
||||
AnonpayTransactionListItem({required this.transaction});
|
||||
|
||||
final AnonpayInvoiceInfo transaction;
|
||||
|
||||
@override
|
||||
DateTime get date => transaction.createdAt;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/entities/fiat_api_mode.dart';
|
||||
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
|
||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
@@ -48,7 +50,9 @@ abstract class DashboardViewModelBase with Store {
|
||||
required this.transactionFilterStore,
|
||||
required this.settingsStore,
|
||||
required this.yatStore,
|
||||
required this.ordersStore})
|
||||
required this.ordersStore,
|
||||
required this.anonpayTransactionsStore,
|
||||
})
|
||||
: isOutdatedElectrumWallet = false,
|
||||
hasSellAction = false,
|
||||
isEnabledSellAction = false,
|
||||
@@ -227,6 +231,11 @@ abstract class DashboardViewModelBase with Store {
|
||||
List<OrderListItem> get orders => ordersStore.orders
|
||||
.where((item) => item.order.walletId == wallet.id)
|
||||
.toList();
|
||||
|
||||
@computed
|
||||
List<AnonpayTransactionListItem> get anonpayTransactons => anonpayTransactionsStore.transactions
|
||||
.where((item) => item.transaction.walletId == wallet.id)
|
||||
.toList();
|
||||
|
||||
@computed
|
||||
double get price => balanceViewModel.price;
|
||||
@@ -235,7 +244,7 @@ abstract class DashboardViewModelBase with Store {
|
||||
List<ActionListItem> get items {
|
||||
final _items = <ActionListItem>[];
|
||||
|
||||
_items.addAll(transactionFilterStore.filtered(transactions: transactions));
|
||||
_items.addAll(transactionFilterStore.filtered(transactions: [...transactions, ...anonpayTransactons]));
|
||||
_items.addAll(tradeFilterStore.filtered(trades: trades, wallet: wallet));
|
||||
_items.addAll(orders);
|
||||
|
||||
@@ -262,6 +271,8 @@ abstract class DashboardViewModelBase with Store {
|
||||
|
||||
TradeFilterStore tradeFilterStore;
|
||||
|
||||
AnonpayTransactionsStore anonpayTransactionsStore;
|
||||
|
||||
TransactionFilterStore transactionFilterStore;
|
||||
|
||||
Map<String, List<FilterItem>> filterItems;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import 'package:cake_wallet/entities/receive_page_option.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'receive_option_view_model.g.dart';
|
||||
|
||||
class ReceiveOptionViewModel = ReceiveOptionViewModelBase with _$ReceiveOptionViewModel;
|
||||
|
||||
abstract class ReceiveOptionViewModelBase with Store {
|
||||
ReceiveOptionViewModelBase(this._wallet, this.initialPageOption)
|
||||
: selectedReceiveOption = initialPageOption ?? ReceivePageOption.mainnet,
|
||||
_options = [] {
|
||||
final walletType = _wallet.type;
|
||||
_options =
|
||||
walletType == WalletType.haven ? [ReceivePageOption.mainnet] : ReceivePageOption.values;
|
||||
}
|
||||
|
||||
final WalletBase _wallet;
|
||||
|
||||
final ReceivePageOption? initialPageOption;
|
||||
|
||||
List<ReceivePageOption> _options;
|
||||
|
||||
@observable
|
||||
ReceivePageOption selectedReceiveOption;
|
||||
|
||||
List<ReceivePageOption> get options => _options;
|
||||
|
||||
@action
|
||||
void selectReceiveOption(ReceivePageOption option) {
|
||||
selectedReceiveOption = option;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user