From 3006679560ea83c11deb2e101c316eca28680bb8 Mon Sep 17 00:00:00 2001 From: Godwin Asuquo <41484542+godilite@users.noreply.github.com> Date: Fri, 24 Mar 2023 17:26:42 +0200 Subject: [PATCH] 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 --- .github/workflows/pr_test_build.yml | 1 + cw_core/lib/crypto_currency.dart | 21 +- cw_core/lib/currency.dart | 6 + lib/anonpay/anonpay_api.dart | 211 +++++++++++++++++ lib/anonpay/anonpay_donation_link_info.dart | 13 + lib/anonpay/anonpay_info_base.dart | 5 + lib/anonpay/anonpay_invoice_info.dart | 57 +++++ lib/anonpay/anonpay_request.dart | 21 ++ lib/anonpay/anonpay_status_response.dart | 17 ++ lib/di.dart | 75 +++++- lib/entities/fiat_currency.dart | 12 +- lib/entities/preferences_key.dart | 2 + lib/entities/receive_page_option.dart | 23 ++ lib/main.dart | 9 + lib/router.dart | 22 +- lib/routes.dart | 3 + .../anonpay_details/anonpay_details_page.dart | 56 +++++ lib/src/screens/contact/contact_page.dart | 5 +- .../dashboard/widgets/address_page.dart | 203 +++++++++------- .../widgets/anonpay_transaction_row.dart | 64 +++++ .../present_receive_option_picker.dart | 140 +++++++++++ .../dashboard/widgets/transactions_page.dart | 173 +++++++------- .../exchange/widgets/currency_picker.dart | 17 +- .../widgets/currency_picker_item_widget.dart | 4 +- .../widgets/currency_picker_widget.dart | 9 +- .../exchange/widgets/exchange_card.dart | 5 +- .../screens/receive/anonpay_invoice_page.dart | 222 ++++++++++++++++++ .../screens/receive/anonpay_receive_page.dart | 181 ++++++++++++++ .../screens/receive/fullscreen_qr_page.dart | 6 +- .../widgets/anonpay_currency_input_field.dart | 153 ++++++++++++ .../receive/widgets/anonpay_input_form.dart | 132 +++++++++++ .../widgets/anonpay_status_section.dart | 87 +++++++ .../receive/widgets/copy_link_item.dart | 56 +++++ lib/src/screens/receive/widgets/qr_image.dart | 6 +- .../screens/receive/widgets/qr_widget.dart | 137 +++++------ .../screens/wallet_keys/wallet_keys_page.dart | 1 - .../anonpay/anonpay_transactions_store.dart | 34 +++ .../dashboard/transaction_filter_store.dart | 18 +- .../anon_invoice_page_view_model.dart | 186 +++++++++++++++ .../anonpay_details_view_model.dart | 78 ++++++ .../anonpay_transaction_list_item.dart | 11 + .../dashboard/dashboard_view_model.dart | 15 +- .../dashboard/receive_option_view_model.dart | 34 +++ res/values/strings_ar.arb | 12 +- res/values/strings_bg.arb | 12 +- res/values/strings_cs.arb | 12 +- res/values/strings_de.arb | 12 +- res/values/strings_en.arb | 10 + res/values/strings_es.arb | 12 +- res/values/strings_fr.arb | 12 +- res/values/strings_hi.arb | 12 +- res/values/strings_hr.arb | 12 +- res/values/strings_id.arb | 12 +- res/values/strings_it.arb | 12 +- res/values/strings_ja.arb | 12 +- res/values/strings_ko.arb | 12 +- res/values/strings_my.arb | 12 +- res/values/strings_nl.arb | 12 +- res/values/strings_pl.arb | 12 +- res/values/strings_pt.arb | 12 +- res/values/strings_ru.arb | 12 +- res/values/strings_th.arb | 12 +- res/values/strings_tr.arb | 12 +- res/values/strings_uk.arb | 12 +- res/values/strings_ur.arb | 12 +- res/values/strings_zh.arb | 12 +- tool/utils/secret_key.dart | 1 + 67 files changed, 2510 insertions(+), 296 deletions(-) create mode 100644 cw_core/lib/currency.dart create mode 100644 lib/anonpay/anonpay_api.dart create mode 100644 lib/anonpay/anonpay_donation_link_info.dart create mode 100644 lib/anonpay/anonpay_info_base.dart create mode 100644 lib/anonpay/anonpay_invoice_info.dart create mode 100644 lib/anonpay/anonpay_request.dart create mode 100644 lib/anonpay/anonpay_status_response.dart create mode 100644 lib/entities/receive_page_option.dart create mode 100644 lib/src/screens/anonpay_details/anonpay_details_page.dart create mode 100644 lib/src/screens/dashboard/widgets/anonpay_transaction_row.dart create mode 100644 lib/src/screens/dashboard/widgets/present_receive_option_picker.dart create mode 100644 lib/src/screens/receive/anonpay_invoice_page.dart create mode 100644 lib/src/screens/receive/anonpay_receive_page.dart create mode 100644 lib/src/screens/receive/widgets/anonpay_currency_input_field.dart create mode 100644 lib/src/screens/receive/widgets/anonpay_input_form.dart create mode 100644 lib/src/screens/receive/widgets/anonpay_status_section.dart create mode 100644 lib/src/screens/receive/widgets/copy_link_item.dart create mode 100644 lib/store/anonpay/anonpay_transactions_store.dart create mode 100644 lib/view_model/anon_invoice_page_view_model.dart create mode 100644 lib/view_model/anonpay_details_view_model.dart create mode 100644 lib/view_model/dashboard/anonpay_transaction_list_item.dart create mode 100644 lib/view_model/dashboard/receive_option_view_model.dart diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 53008ed52..0ea93f4f3 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -113,6 +113,7 @@ jobs: echo "const twitterBearerToken = '${{ secrets.TWITTER_BEARER_TOKEN }}';" >> lib/.secrets.g.dart echo "const trocadorApiKey = '${{ secrets.TROCADOR_API_KEY }}';" >> lib/.secrets.g.dart echo "const trocadorExchangeMarkup = '${{ secrets.TROCADOR_EXCHANGE_MARKUP }}';" >> lib/.secrets.g.dart + echo "const anonPayReferralCode = '${{ secrets.ANON_PAY_REFERRAL_CODE }}';" >> lib/.secrets.g.dart - name: Rename app run: echo -e "id=com.cakewallet.test\nname=$GITHUB_HEAD_REF" > /opt/android/cake_wallet/android/app.properties diff --git a/cw_core/lib/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index 925f39de0..3d076f6e9 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -1,6 +1,7 @@ +import 'package:cw_core/currency.dart'; import 'package:cw_core/enumerable_item.dart'; -class CryptoCurrency extends EnumerableItem with Serializable { +class CryptoCurrency extends EnumerableItem with Serializable implements Currency { const CryptoCurrency({ String title = '', int raw = -1, @@ -162,6 +163,14 @@ class CryptoCurrency extends EnumerableItem with Serializable { return acc; }); + static final Map _fullNameCurrencyMap = + [...all, ...havenCurrencies].fold>({}, (acc, item) { + if(item.fullName != null){ + acc.addAll({item.fullName!.toLowerCase(): item}); + } + return acc; + }); + static CryptoCurrency deserialize({required int raw}) { if (CryptoCurrency._rawCurrencyMap[raw] == null) { @@ -180,6 +189,16 @@ class CryptoCurrency extends EnumerableItem with Serializable { return CryptoCurrency._nameCurrencyMap[name.toLowerCase()]!; } + static CryptoCurrency fromFullName(String name) { + + if (CryptoCurrency._fullNameCurrencyMap[name.toLowerCase()] == null) { + final s = 'Unexpected token: $name for CryptoCurrency fromFullName'; + throw ArgumentError.value(name, 'Fullname', s); + } + return CryptoCurrency._fullNameCurrencyMap[name.toLowerCase()]!; + } + + @override String toString() => title; } diff --git a/cw_core/lib/currency.dart b/cw_core/lib/currency.dart new file mode 100644 index 000000000..4f67c4e28 --- /dev/null +++ b/cw_core/lib/currency.dart @@ -0,0 +1,6 @@ +abstract class Currency { + String get name; + String? get tag; + String? get fullName; + String? get iconPath; +} \ No newline at end of file diff --git a/lib/anonpay/anonpay_api.dart b/lib/anonpay/anonpay_api.dart new file mode 100644 index 000000000..bc6abc6e2 --- /dev/null +++ b/lib/anonpay/anonpay_api.dart @@ -0,0 +1,211 @@ +import 'dart:convert'; +import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart'; +import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart'; +import 'package:cake_wallet/anonpay/anonpay_request.dart'; +import 'package:cake_wallet/anonpay/anonpay_status_response.dart'; +import 'package:cake_wallet/core/fiat_conversion_service.dart'; +import 'package:cake_wallet/entities/fiat_currency.dart'; +import 'package:cake_wallet/exchange/limits.dart'; +import 'package:cw_core/wallet_base.dart'; +import 'package:http/http.dart'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cake_wallet/.secrets.g.dart' as secrets; + +class AnonPayApi { + const AnonPayApi({ + this.useTorOnly = false, + required this.wallet, + }); + final bool useTorOnly; + final WalletBase wallet; + + static const anonpayRef = secrets.anonPayReferralCode; + static const onionApiAuthority = 'trocadorfyhlu27aefre5u7zri66gudtzdyelymftvr4yjwcxhfaqsid.onion'; + static const clearNetAuthority = 'trocador.app'; + static const markup = secrets.trocadorExchangeMarkup; + static const anonPayPath = '/anonpay'; + static const anonPayStatus = '/anonpay/status'; + static const coinPath = 'api/coin'; + static const apiKey = secrets.trocadorApiKey; + + Future paymentStatus(String id) async { + final authority = await _getAuthority(); + final response = await get(Uri.https(authority, "$anonPayStatus/$id")); + final responseJSON = json.decode(response.body) as Map; + final status = responseJSON['Status'] as String; + final fiatAmount = responseJSON['Fiat_Amount'] as double?; + final fiatEquiv = responseJSON['Fiat_Equiv'] as String?; + final amountTo = responseJSON['AmountTo'] as double?; + final coinTo = responseJSON['CoinTo'] as String; + final address = responseJSON['Address'] as String; + + return AnonpayStatusResponse( + status: status, + fiatAmount: fiatAmount, + amountTo: amountTo, + coinTo: coinTo, + address: address, + fiatEquiv: fiatEquiv, + ); + } + + Future createInvoice(AnonPayRequest request) async { + final description = Uri.encodeComponent(request.description); + final body = { + 'ticker_to': request.cryptoCurrency.title.toLowerCase(), + 'network_to': _networkFor(request.cryptoCurrency), + 'address': request.address, + 'name': request.name, + 'description': description, + 'email': request.email, + 'ref': anonpayRef, + 'markup': markup, + 'direct': 'False', + }; + + if (request.amount != null) { + body['amount'] = request.amount; + } + if (request.fiatEquivalent != null) { + body['fiat_equiv'] = request.fiatEquivalent; + } + final authority = await _getAuthority(); + + final response = await get(Uri.https(authority, anonPayPath, body)); + + final responseJSON = json.decode(response.body) as Map; + final id = responseJSON['ID'] as String; + final url = responseJSON['url'] as String; + final urlOnion = responseJSON['url_onion'] as String; + final statusUrl = responseJSON['status_url'] as String; + final statusUrlOnion = responseJSON['status_url_onion'] as String; + + final statusInfo = await paymentStatus(id); + + return AnonpayInvoiceInfo( + invoiceId: id, + clearnetUrl: url, + onionUrl: urlOnion, + status: statusInfo.status, + fiatAmount: statusInfo.fiatAmount, + fiatEquiv: statusInfo.fiatEquiv, + amountTo: statusInfo.amountTo, + coinTo: statusInfo.coinTo, + address: statusInfo.address, + clearnetStatusUrl: statusUrl, + onionStatusUrl: statusUrlOnion, + walletId: wallet.id, + createdAt: DateTime.now(), + provider: 'Trocador AnonPay invoice', + ); + } + + Future generateDonationLink(AnonPayRequest request) async { + final body = { + 'ticker_to': request.cryptoCurrency.title.toLowerCase(), + 'network_to': _networkFor(request.cryptoCurrency), + 'address': request.address, + 'ref': anonpayRef, + 'direct': 'True', + }; + if (request.name.isNotEmpty) { + body['name'] = request.name; + } + if (request.description.isNotEmpty) { + body['description'] = request.description; + } + if (request.email.isNotEmpty) { + body['email'] = request.email; + } + + final clearnetUrl = Uri.https(clearNetAuthority, anonPayPath, body); + final onionUrl = Uri.https(onionApiAuthority, anonPayPath, body); + return AnonpayDonationLinkInfo( + clearnetUrl: clearnetUrl.toString(), + onionUrl: onionUrl.toString(), + address: request.address, + ); + } + + Future fetchLimits({ + FiatCurrency? fiatCurrency, + required CryptoCurrency cryptoCurrency, + }) async { + double fiatRate = 0.0; + if (fiatCurrency != null) { + fiatRate = await FiatConversionService.fetchPrice( + crypto: cryptoCurrency, + fiat: fiatCurrency, + torOnly: useTorOnly, + ); + } + + final params = { + 'api_key': apiKey, + 'ticker': cryptoCurrency.title.toLowerCase(), + 'name': cryptoCurrency.name, + }; + + final String apiAuthority = await _getAuthority(); + final uri = Uri.https(apiAuthority, coinPath, params); + + final response = await get(uri); + + if (response.statusCode != 200) { + throw Exception('Unexpected http status: ${response.statusCode}'); + } + + final responseJSON = json.decode(response.body) as List; + + if (responseJSON.isEmpty) { + throw Exception('No data'); + } + + final coinJson = responseJSON.first as Map; + final minimum = coinJson['minimum'] as double; + final maximum = coinJson['maximum'] as double; + + if (fiatCurrency != null) { + return Limits( + min: double.tryParse((minimum * fiatRate).toStringAsFixed(2)), + max: double.tryParse((maximum * fiatRate).toStringAsFixed(2)), + ); + } + + return Limits( + min: minimum, + max: maximum, + ); + } + + String _networkFor(CryptoCurrency currency) { + switch (currency) { + case CryptoCurrency.usdt: + return CryptoCurrency.btc.title.toLowerCase(); + default: + return currency.tag != null ? _normalizeTag(currency.tag!) : 'Mainnet'; + } + } + + String _normalizeTag(String tag) { + switch (tag) { + case 'ETH': + return 'ERC20'; + default: + return tag.toLowerCase(); + } + } + + Future _getAuthority() async { + try { + if (useTorOnly) { + return onionApiAuthority; + } + final uri = Uri.https(onionApiAuthority, '/anonpay'); + await get(uri); + return onionApiAuthority; + } catch (e) { + return clearNetAuthority; + } + } +} diff --git a/lib/anonpay/anonpay_donation_link_info.dart b/lib/anonpay/anonpay_donation_link_info.dart new file mode 100644 index 000000000..9c4d3dda7 --- /dev/null +++ b/lib/anonpay/anonpay_donation_link_info.dart @@ -0,0 +1,13 @@ +import 'package:cake_wallet/anonpay/anonpay_info_base.dart'; + +class AnonpayDonationLinkInfo implements AnonpayInfoBase{ + final String clearnetUrl; + final String onionUrl; + final String address; + + AnonpayDonationLinkInfo({ + required this.clearnetUrl, + required this.onionUrl, + required this.address, + }); +} \ No newline at end of file diff --git a/lib/anonpay/anonpay_info_base.dart b/lib/anonpay/anonpay_info_base.dart new file mode 100644 index 000000000..d53dd0d7f --- /dev/null +++ b/lib/anonpay/anonpay_info_base.dart @@ -0,0 +1,5 @@ +abstract class AnonpayInfoBase { + String get clearnetUrl; + String get onionUrl; + String get address; +} \ No newline at end of file diff --git a/lib/anonpay/anonpay_invoice_info.dart b/lib/anonpay/anonpay_invoice_info.dart new file mode 100644 index 000000000..89613224e --- /dev/null +++ b/lib/anonpay/anonpay_invoice_info.dart @@ -0,0 +1,57 @@ +import 'package:cake_wallet/anonpay/anonpay_info_base.dart'; +import 'package:cw_core/keyable.dart'; +import 'package:hive/hive.dart'; + +part 'anonpay_invoice_info.g.dart'; + +@HiveType(typeId: AnonpayInvoiceInfo.typeId) +class AnonpayInvoiceInfo extends HiveObject with Keyable implements AnonpayInfoBase { + @HiveField(0) + final String invoiceId; + @HiveField(1) + String status; + @HiveField(2) + final double? fiatAmount; + @HiveField(3) + final String? fiatEquiv; + @HiveField(4) + final double? amountTo; + @HiveField(5) + final String coinTo; + @HiveField(6) + final String address; + @HiveField(7) + final String clearnetUrl; + @HiveField(8) + final String onionUrl; + @HiveField(9) + final String clearnetStatusUrl; + @HiveField(10) + final String onionStatusUrl; + @HiveField(11) + final DateTime createdAt; + @HiveField(12) + final String walletId; + @HiveField(13) + final String provider; + + static const typeId = 10; + static const boxName = 'AnonpayInvoiceInfo'; + + AnonpayInvoiceInfo({ + required this.invoiceId, + required this.clearnetUrl, + required this.onionUrl, + required this.clearnetStatusUrl, + required this.onionStatusUrl, + required this.status, + this.fiatAmount, + this.fiatEquiv, + this.amountTo, + required this.coinTo, + required this.address, + required this.createdAt, + required this.walletId, + required this.provider, + }); +} diff --git a/lib/anonpay/anonpay_request.dart b/lib/anonpay/anonpay_request.dart new file mode 100644 index 000000000..f548ea16e --- /dev/null +++ b/lib/anonpay/anonpay_request.dart @@ -0,0 +1,21 @@ +import 'package:cw_core/crypto_currency.dart'; + +class AnonPayRequest { + CryptoCurrency cryptoCurrency; + String address; + String name; + String? amount; + String email; + String description; + String? fiatEquivalent; + + AnonPayRequest({ + required this.cryptoCurrency, + required this.address, + required this.name, + required this.email, + this.amount, + required this.description, + this.fiatEquivalent, + }); +} diff --git a/lib/anonpay/anonpay_status_response.dart b/lib/anonpay/anonpay_status_response.dart new file mode 100644 index 000000000..53e28e9b8 --- /dev/null +++ b/lib/anonpay/anonpay_status_response.dart @@ -0,0 +1,17 @@ +class AnonpayStatusResponse { + final String status; + final double? fiatAmount; + final String? fiatEquiv; + final double? amountTo; + final String coinTo; + final String address; + + const AnonpayStatusResponse({ + required this.status, + this.fiatAmount, + this.fiatEquiv, + this.amountTo, + required this.coinTo, + required this.address, + }); +} diff --git a/lib/di.dart b/lib/di.dart index 2f20d2ce2..0b8407cf5 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1,10 +1,18 @@ +import 'package:cake_wallet/anonpay/anonpay_api.dart'; +import 'package:cake_wallet/anonpay/anonpay_info_base.dart'; +import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart'; import 'package:cake_wallet/core/yat_service.dart'; +import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/parse_address_from_domain.dart'; +import 'package:cake_wallet/entities/receive_page_option.dart'; import 'package:cake_wallet/entities/wake_lock.dart'; import 'package:cake_wallet/ionia/ionia_anypay.dart'; import 'package:cake_wallet/ionia/ionia_gift_card.dart'; import 'package:cake_wallet/ionia/ionia_tip.dart'; +import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart'; import 'package:cake_wallet/src/screens/buy/onramper_page.dart'; +import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart'; +import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart'; import 'package:cake_wallet/src/screens/settings/display_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/other_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/privacy_page.dart'; @@ -13,7 +21,11 @@ import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dar import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart'; import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart'; +import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart'; import 'package:cake_wallet/utils/payment_request.dart'; +import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart'; +import 'package:cake_wallet/view_model/anonpay_details_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_buy_card_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_custom_tip_view_model.dart'; @@ -176,6 +188,7 @@ late Box _exchangeTemplates; late Box _transactionDescriptionBox; late Box _ordersSource; late Box? _unspentCoinsInfoSource; +late Box _anonpayInvoiceInfoSource; Future setup( {required Box walletInfoSource, @@ -186,7 +199,9 @@ Future setup( required Box exchangeTemplates, required Box transactionDescriptionBox, required Box ordersSource, - Box? unspentCoinsInfoSource}) async { + Box? unspentCoinsInfoSource, + required Box anonpayInvoiceInfoSource + }) async { _walletInfoSource = walletInfoSource; _nodeSource = nodeSource; _contactSource = contactSource; @@ -196,6 +211,7 @@ Future setup( _transactionDescriptionBox = transactionDescriptionBox; _ordersSource = ordersSource; _unspentCoinsInfoSource = unspentCoinsInfoSource; + _anonpayInvoiceInfoSource = anonpayInvoiceInfoSource; if (!_isSetupFinished) { getIt.registerSingletonAsync( @@ -240,6 +256,8 @@ Future setup( appStore: getIt.get(), secureStorage: getIt.get()) ..init()); + getIt.registerSingleton(AnonpayTransactionsStore( + anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource)); final secretStore = await SecretStoreBase.load(getIt.get()); @@ -306,7 +324,9 @@ Future setup( transactionFilterStore: getIt.get(), settingsStore: settingsStore, yatStore: getIt.get(), - ordersStore: getIt.get())); + ordersStore: getIt.get(), + anonpayTransactionsStore: getIt.get()) + ); getIt.registerFactory(() => AuthService( secureStorage: getIt.get(), @@ -360,11 +380,37 @@ Future setup( BalancePage(dashboardViewModel: getIt.get(), settingsStore: getIt.get())); getIt.registerFactory(() => DashboardPage( balancePage: getIt.get(), walletViewModel: getIt.get(), addressListViewModel: getIt.get())); + + getIt.registerFactoryParam((pageOption, _) => ReceiveOptionViewModel( + getIt.get().wallet!, pageOption)); + + getIt.registerFactoryParam, void>((args, _) { + final address = args.first as String; + final pageOption = args.last as ReceivePageOption; + return AnonInvoicePageViewModel( + getIt.get(), + address, + getIt.get(), + getIt.get().wallet!, + _anonpayInvoiceInfoSource, + getIt.get(), + pageOption, + ); + }); + + getIt.registerFactoryParam, void>((List args, _) { + final pageOption = args.last as ReceivePageOption; + return AnonPayInvoicePage( + getIt.get(param1: args), + getIt.get(param1: pageOption)); + }); + getIt.registerFactory(() => ReceivePage( addressListViewModel: getIt.get())); getIt.registerFactory(() => AddressPage( addressListViewModel: getIt.get(), - walletViewModel: getIt.get())); + walletViewModel: getIt.get(), + receiveOptionViewModel: getIt.get())); getIt.registerFactoryParam( (WalletAddressListItem? item, _) => WalletAddressEditOrCreateViewModel( @@ -716,8 +762,8 @@ Future setup( getIt.registerFactory(() => AddressResolver(yatService: getIt.get(), walletType: getIt.get().wallet!.type)); - getIt.registerFactoryParam( - (String qrData, bool isLight) => FullscreenQRPage(qrData: qrData, isLight: isLight,)); + getIt.registerFactoryParam( + (String qrData, int? version) => FullscreenQRPage(qrData: qrData, version: version,)); getIt.registerFactory(() => IoniaApi()); @@ -823,6 +869,25 @@ Future setup( getIt.registerFactory(() => IoniaAccountPage(getIt.get())); getIt.registerFactory(() => IoniaAccountCardsPage(getIt.get())); + + getIt.registerFactory(() => AnonPayApi(useTorOnly: getIt.get().exchangeStatus == ExchangeApiMode.torOnly, + wallet: getIt.get().wallet!) + ); + + getIt.registerFactoryParam( + (AnonpayInvoiceInfo anonpayInvoiceInfo, _) + => AnonpayDetailsViewModel( + anonPayApi: getIt.get(), + anonpayInvoiceInfo: anonpayInvoiceInfo, + settingsStore: getIt.get(), + )); + + getIt.registerFactoryParam( + (AnonpayInfoBase anonpayInvoiceInfo, _) => AnonPayReceivePage(invoiceInfo: anonpayInvoiceInfo)); + + getIt.registerFactoryParam( + (AnonpayInvoiceInfo anonpayInvoiceInfo, _) + => AnonpayDetailsPage(anonpayDetailsViewModel: getIt.get(param1: anonpayInvoiceInfo))); getIt.registerFactoryParam( (IoniaAnyPayPaymentInfo paymentInfo, AnyPayPaymentCommittedInfo committedInfo) diff --git a/lib/entities/fiat_currency.dart b/lib/entities/fiat_currency.dart index 12ceff92a..8310eb18f 100644 --- a/lib/entities/fiat_currency.dart +++ b/lib/entities/fiat_currency.dart @@ -1,6 +1,7 @@ +import 'package:cw_core/currency.dart'; import 'package:cw_core/enumerable_item.dart'; -class FiatCurrency extends EnumerableItem with Serializable { +class FiatCurrency extends EnumerableItem with Serializable implements Currency { const FiatCurrency({required String symbol, required this.countryCode, required this.fullName}) : super(title: symbol, raw: symbol); final String countryCode; @@ -118,4 +119,13 @@ class FiatCurrency extends EnumerableItem with Serializable { @override int get hashCode => raw.hashCode ^ title.hashCode; + + @override + String get name => raw; + + @override + String? get tag => null; + + @override + String get iconPath => "assets/images/flags/$countryCode.png"; } diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index e237b0691..8ea30a6f2 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -37,4 +37,6 @@ class PreferencesKey { => '${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}'; static const exchangeProvidersSelection = 'exchange-providers-selection'; + static const clearnetDonationLink = 'clearnet_donation_link'; + static const onionDonationLink = 'onion_donation_link'; } diff --git a/lib/entities/receive_page_option.dart b/lib/entities/receive_page_option.dart new file mode 100644 index 000000000..3ee9abe96 --- /dev/null +++ b/lib/entities/receive_page_option.dart @@ -0,0 +1,23 @@ + +enum ReceivePageOption { + mainnet, + anonPayInvoice, + anonPayDonationLink; + + @override + String toString() { + String label = ''; + switch (this) { + case ReceivePageOption.mainnet: + label = 'Mainnet'; + break; + case ReceivePageOption.anonPayInvoice: + label = 'Trocador AnonPay Invoice'; + break; + case ReceivePageOption.anonPayDonationLink: + label = 'Trocador AnonPay Donation Link'; + break; + } + return label; + } +} diff --git a/lib/main.dart b/lib/main.dart index 7ba1f204a..8cc2629c9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart'; import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/entities/language_service.dart'; import 'package:cake_wallet/buy/order.dart'; @@ -100,6 +101,10 @@ Future main() async { Hive.registerAdapter(UnspentCoinsInfoAdapter()); } + if (!Hive.isAdapterRegistered(AnonpayInvoiceInfo.typeId)) { + Hive.registerAdapter(AnonpayInvoiceInfoAdapter()); + } + final secureStorage = FlutterSecureStorage(); final transactionDescriptionsBoxKey = await getEncryptionKey( secureStorage: secureStorage, forKey: TransactionDescription.boxKey); @@ -120,6 +125,7 @@ Future main() async { final templates = await Hive.openBox