From 0c77b23ecb0c08e7931d341d2cdf643edc51e7f5 Mon Sep 17 00:00:00 2001 From: Adegoke David <64401859+Blazebrain@users.noreply.github.com> Date: Wed, 22 Nov 2023 18:43:26 +0100 Subject: [PATCH] feat: Implement NFT Tab for Eth (#1166) * feat: Implement NFT Listing and Importing of new NFTs, also display NFTs linked to the wallet address * Adjust UI based on wallet type, display nfts only when an ethereum wallet * fix: Prevent tab bar from scrolling * feat:Add NFT tab: adjust models and add localization * feat:Add NFT tab: adjust models and add localization * chore: Remove unused widget * fix: Adjust UI to reflect more data, display image based on type, either png or svg, adjust theme-a * fix: Update viewmodel * fix: Add missing dependency to fix failing CI * fix: Revert change in inject app script * Delete cw_polygon/pubspec.lock * - Code enhancements - UI fixes - Removing unrelated files --------- Co-authored-by: Omar Hatem --- .github/workflows/pr_test_build.yml | 1 + .../.plugin_symlinks/path_provider_linux | 2 +- .../flutter/ephemeral/.plugin_symlinks/tor | 1 + ios/Podfile.lock | 2 +- lib/di.dart | 29 +-- lib/entities/wallet_nft_response.dart | 95 ++++++++++ lib/router.dart | 25 ++- lib/routes.dart | 8 +- lib/src/screens/dashboard/dashboard_page.dart | 8 +- .../dashboard/desktop_dashboard_page.dart | 5 +- .../desktop_dashboard_actions.dart | 2 +- .../{widgets => pages}/address_page.dart | 48 +++-- .../{widgets => pages}/balance_page.dart | 142 +++++++++++--- .../{widgets => pages}/market_place_page.dart | 0 .../dashboard/pages/nft_details_page.dart | 178 ++++++++++++++++++ .../dashboard/pages/nft_import_page.dart | 168 +++++++++++++++++ .../dashboard/pages/nft_listing_page.dart | 84 +++++++++ .../{widgets => pages}/transactions_page.dart | 58 +++--- .../widgets/nft_image_tile_widget.dart | 37 ++++ .../dashboard/widgets/nft_tile_widget.dart | 95 ++++++++++ lib/src/widgets/address_text_field.dart | 3 + lib/utils/exception_handler.dart | 1 + lib/view_model/dashboard/nft_view_model.dart | 139 ++++++++++++++ pubspec_base.yaml | 1 + res/values/strings_ar.arb | 15 +- res/values/strings_bg.arb | 7 + res/values/strings_cs.arb | 9 +- res/values/strings_de.arb | 7 + res/values/strings_en.arb | 7 + res/values/strings_es.arb | 7 + res/values/strings_fr.arb | 9 + res/values/strings_ha.arb | 7 + res/values/strings_hi.arb | 7 + res/values/strings_hr.arb | 9 +- res/values/strings_id.arb | 7 + res/values/strings_it.arb | 9 +- res/values/strings_ja.arb | 9 +- res/values/strings_ko.arb | 7 + res/values/strings_my.arb | 7 + res/values/strings_nl.arb | 7 + res/values/strings_pl.arb | 9 +- res/values/strings_pt.arb | 7 + res/values/strings_ru.arb | 7 + res/values/strings_th.arb | 7 + res/values/strings_tl.arb | 7 + res/values/strings_tr.arb | 9 +- res/values/strings_uk.arb | 9 +- res/values/strings_ur.arb | 15 +- res/values/strings_yo.arb | 9 +- res/values/strings_zh.arb | 9 +- tool/utils/secret_key.dart | 1 + 51 files changed, 1219 insertions(+), 132 deletions(-) create mode 120000 cw_bitcoin_cash/linux/flutter/ephemeral/.plugin_symlinks/tor create mode 100644 lib/entities/wallet_nft_response.dart rename lib/src/screens/dashboard/{widgets => pages}/address_page.dart (86%) rename lib/src/screens/dashboard/{widgets => pages}/balance_page.dart (77%) rename lib/src/screens/dashboard/{widgets => pages}/market_place_page.dart (100%) create mode 100644 lib/src/screens/dashboard/pages/nft_details_page.dart create mode 100644 lib/src/screens/dashboard/pages/nft_import_page.dart create mode 100644 lib/src/screens/dashboard/pages/nft_listing_page.dart rename lib/src/screens/dashboard/{widgets => pages}/transactions_page.dart (77%) create mode 100644 lib/src/screens/dashboard/widgets/nft_image_tile_widget.dart create mode 100644 lib/src/screens/dashboard/widgets/nft_tile_widget.dart create mode 100644 lib/view_model/dashboard/nft_view_model.dart diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index d74b85ce4..7b2b611d3 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -136,6 +136,7 @@ jobs: echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart echo "const robinhoodCIdApiSecret = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart echo "const walletConnectProjectId = '${{ secrets.WALLET_CONNECT_PROJECT_ID }}';" >> lib/.secrets.g.dart + echo "const moralisApiKey = '${{ secrets.MORALIS_API_KEY }}';" >> 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_bitcoin_cash/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux b/cw_bitcoin_cash/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux index 17553f81e..0ed52b295 120000 --- a/cw_bitcoin_cash/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux +++ b/cw_bitcoin_cash/linux/flutter/ephemeral/.plugin_symlinks/path_provider_linux @@ -1 +1 @@ -/Users/omarhatem/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/ \ No newline at end of file +/Users/blazebrain/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/ \ No newline at end of file diff --git a/cw_bitcoin_cash/linux/flutter/ephemeral/.plugin_symlinks/tor b/cw_bitcoin_cash/linux/flutter/ephemeral/.plugin_symlinks/tor new file mode 120000 index 000000000..7ee2b206d --- /dev/null +++ b/cw_bitcoin_cash/linux/flutter/ephemeral/.plugin_symlinks/tor @@ -0,0 +1 @@ +/Users/blazebrain/.pub-cache/git/tor-09ba92cb11d4e3cacf97256e57863b805f79f2e5/ \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 775419d18..bbb088325 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -302,7 +302,7 @@ SPEC CHECKSUMS: tor: 662a9f5b980b5c86decb8ba611de9bcd4c8286eb uni_links: d97da20c7701486ba192624d99bffaaffcfc298a UnstoppableDomainsResolution: c3c67f4d0a5e2437cb00d4bd50c2e00d6e743841 - url_launcher_ios: 68d46cc9766d0c41dbdc884310529557e3cd7a86 + url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47 workmanager: 0afdcf5628bbde6924c21af7836fed07b42e30e6 diff --git a/lib/di.dart b/lib/di.dart index d6a43030a..0a7097b7b 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -27,7 +27,7 @@ import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sideba import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet_selection_dropdown.dart'; import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart'; import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart'; import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart'; import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart'; import 'package:cake_wallet/src/screens/nano_accounts/nano_account_list_page.dart'; @@ -65,6 +65,7 @@ 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/home_settings_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/nft_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'; @@ -80,7 +81,7 @@ import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dar import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart'; import 'package:cake_wallet/src/screens/ionia/ionia.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/balance_page.dart'; import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart'; @@ -212,7 +213,7 @@ import 'package:cake_wallet/store/templates/exchange_template_store.dart'; import 'package:cake_wallet/entities/template.dart'; import 'package:cake_wallet/exchange/exchange_template.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; -import 'package:cake_wallet/src/screens/dashboard/widgets/address_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart'; import 'package:cake_wallet/anypay/anypay_api.dart'; import 'package:cake_wallet/view_model/ionia/ionia_gift_card_details_view_model.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart'; @@ -342,10 +343,9 @@ Future setup({ getIt.get(), (WalletType type) => getIt.get(param1: type))); - getIt.registerFactoryParam((type, _) => - WalletNewVM(getIt.get(), - getIt.get(param1: type), _walletInfoSource, - type: type)); + getIt.registerFactoryParam((type, _) => WalletNewVM( + getIt.get(), getIt.get(param1: type), _walletInfoSource, + type: type)); getIt.registerFactoryParam((WalletType type, _) { return WalletRestorationFromQRVM(getIt.get(), @@ -483,6 +483,7 @@ Future setup({ }); getIt.registerFactory(() => BalancePage( + nftViewModel: getIt.get(), dashboardViewModel: getIt.get(), settingsStore: getIt.get())); @@ -901,8 +902,8 @@ Future setup({ (param1, isCreate) => NewWalletTypePage(onTypeSelected: param1, isCreate: isCreate ?? true)); getIt.registerFactoryParam( - (WalletType type, AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel) - => PreSeedPage(type, advancedPrivacySettingsViewModel)); + (WalletType type, AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel) => + PreSeedPage(type, advancedPrivacySettingsViewModel)); getIt.registerFactoryParam((trade, _) => TradeDetailsViewModel( @@ -996,11 +997,10 @@ Future setup({ getIt.registerFactory(() => YatService()); - getIt.registerFactory(() => - AddressResolver( - yatService: getIt.get(), - wallet: getIt.get().wallet!, - settingsStore: getIt.get())); + getIt.registerFactory(() => AddressResolver( + yatService: getIt.get(), + wallet: getIt.get().wallet!, + settingsStore: getIt.get())); getIt.registerFactoryParam( (QrViewData viewData, _) => FullscreenQRPage(qrViewData: viewData)); @@ -1176,6 +1176,7 @@ Future setup({ getIt.registerFactory( () => WalletConnectConnectionsView(web3walletService: getIt.get())); + getIt.registerFactory(() => NFTViewModel(appStore, getIt.get())); getIt.registerFactory(() => TorPage(getIt.get())); _isSetupFinished = true; diff --git a/lib/entities/wallet_nft_response.dart b/lib/entities/wallet_nft_response.dart new file mode 100644 index 000000000..74a7508ab --- /dev/null +++ b/lib/entities/wallet_nft_response.dart @@ -0,0 +1,95 @@ +class WalletNFTsResponseModel { + final int? page; + final int? pageSize; + + final List? result; + final String? status; + + WalletNFTsResponseModel({this.page, this.pageSize, this.result, this.status}); + + factory WalletNFTsResponseModel.fromJson(Map json) { + return WalletNFTsResponseModel( + page: json['page'] as int?, + pageSize: json['page_size'] as int?, + result: (json['result'] as List?) + ?.map((x) => NFTAssetModel.fromJson(x as Map)) + .toList(), + status: json['status'] as String?, + ); + } +} + +class NFTAssetModel { + final String? tokenAddress; + final String? tokenId; + final String? contractType; + final String? name; + final String? symbol; + NormalizedMetadata? normalizedMetadata; + + NFTAssetModel( + {this.tokenAddress, + this.tokenId, + this.contractType, + this.name, + this.symbol, + this.normalizedMetadata}); + + factory NFTAssetModel.fromJson(Map json) { + return NFTAssetModel( + tokenAddress: json['token_address'] as String?, + tokenId: json['token_id'] as String?, + contractType: json['contract_type'] as String?, + name: json['name'] as String?, + symbol: json['symbol'] as String?, + normalizedMetadata: json['normalized_metadata'] != null + ? new NormalizedMetadata.fromJson( + json['normalized_metadata'] as Map) + : null, + ); + } +} + +class NormalizedMetadata { + final String? name; + final String? description; + final String? image; + NormalizedMetadata({ + this.name, + this.description, + this.image, + }); + + factory NormalizedMetadata.fromJson(Map json) { + return NormalizedMetadata( + name: json['name'] as String?, + description: json['description'] as String?, + image: json['image'] as String?, + ); + + } + + String? get imageUrl { + if (image == null) return image; + + if (image!.contains('ipfs.io')) return image; + + if (!image!.contains('ipfs')) return image; + + // IPFS public gateway provided by Cloudflare is https://cloudflare-ipfs.com/ipfs/ + // + // Here is an example of an ipfs image link: + // + // [ipfs://bafkreia2i2ctfexpovgzfff66wqhbmwwpvqjvozan7ioifzcnq76jharwu] + + //https://ipfs.io/ipfs/QmTRcRXo6cXByjHYHTVxGpag6vpocrG3rxjPC9PxKAArR9/1620.png + + const String ipfsPublicGateway = 'https://cloudflare-ipfs.com/ipfs/'; + + final ipfsPath = image?.split('//')[1]; + + final imageLink = '$ipfsPublicGateway$ipfsPath'; + + return imageLink; + } +} diff --git a/lib/router.dart b/lib/router.dart index 75f491dfc..fb21ba32c 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -5,6 +5,7 @@ import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart'; import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/entities/qr_view_data.dart'; +import 'package:cake_wallet/entities/wallet_nft_response.dart'; import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart'; import 'package:cake_wallet/src/screens/backup/backup_page.dart'; import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart'; @@ -13,6 +14,7 @@ import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart'; import 'package:cake_wallet/src/screens/buy/webview_page.dart'; import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart'; import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/nft_details_page.dart'; import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart'; import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart'; import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart'; @@ -20,7 +22,7 @@ import 'package:cake_wallet/src/screens/restore/sweeping_wallet_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/dashboard/desktop_widgets/desktop_dashboard_actions.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart'; import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/display_settings_page.dart'; @@ -56,6 +58,7 @@ import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.da import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart'; import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; @@ -102,7 +105,7 @@ import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dar import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart'; import 'package:flutter/services.dart'; import 'package:cake_wallet/wallet_types.g.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/address_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart'; import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart'; import 'package:cake_wallet/src/screens/ionia/ionia.dart'; import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart'; @@ -111,6 +114,8 @@ import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/node.dart'; +import 'src/screens/dashboard/pages/nft_import_page.dart'; + late RouteSettings currentRouteSettings; Route createRoute(RouteSettings settings) { @@ -616,6 +621,22 @@ Route createRoute(RouteSettings settings) { web3walletService: getIt.get(), launchUri: settings.arguments as Uri?, )); + + case Routes.nftDetailsPage: + return MaterialPageRoute( + builder: (_) => NFTDetailsPage( + nftAsset: settings.arguments as NFTAssetModel, + dashboardViewModel: getIt.get(), + ), + ); + + case Routes.importNFTPage: + return MaterialPageRoute( + builder: (_) => ImportNFTPage( + nftViewModel: settings.arguments as NFTViewModel, + ), + ); + case Routes.torPage: return MaterialPageRoute(builder: (_) => getIt.get()); diff --git a/lib/routes.dart b/lib/routes.dart index 4c1a917ab..4511d48e1 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -7,7 +7,8 @@ class Routes { static const restoreOptions = '/restore_options'; static const restoreWalletFromSeedKeys = '/restore_wallet_from_seeds_keys'; static const restoreWalletTypeFromQR = '/restore_wallet_from_qr_code'; - static const restoreWalletChooseDerivation = '/restore_wallet_choose_derivation'; + static const restoreWalletChooseDerivation = + '/restore_wallet_choose_derivation'; static const dashboard = '/dashboard'; static const send = '/send'; static const transactionDetails = '/transaction_info'; @@ -100,6 +101,9 @@ class Routes { static const editToken = '/edit_token'; static const manageNodes = '/manage_nodes'; static const managePowNodes = '/manage_pow_nodes'; - static const walletConnectConnectionsListing = '/wallet-connect-connections-listing'; + static const walletConnectConnectionsListing = + '/wallet-connect-connections-listing'; + static const nftDetailsPage = '/nft_details_page'; + static const importNFTPage = '/import_nft_page'; static const torPage = '/tor_page'; } diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index d4662c625..2552f4a4a 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -1,11 +1,10 @@ import 'dart:async'; import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart'; -import 'package:cake_wallet/core/wallet_connect/web3wallet_service.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/main_actions.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/market_place_page.dart'; import 'package:cake_wallet/src/screens/wallet_connect/widgets/modals/bottom_sheet_listener.dart'; import 'package:cake_wallet/src/widgets/gradient_background.dart'; import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; @@ -15,7 +14,6 @@ import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/yat_emoji_id.dart'; -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; @@ -23,8 +21,8 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/menu_widget.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/action_button.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/balance_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; diff --git a/lib/src/screens/dashboard/desktop_dashboard_page.dart b/lib/src/screens/dashboard/desktop_dashboard_page.dart index ed9fa6912..216ea152d 100644 --- a/lib/src/screens/dashboard/desktop_dashboard_page.dart +++ b/lib/src/screens/dashboard/desktop_dashboard_page.dart @@ -1,17 +1,14 @@ import 'dart:async'; import 'package:cake_wallet/entities/preferences_key.dart'; -import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/release_notes/release_notes_screen.dart'; import 'package:cake_wallet/src/screens/yat_emoji_id.dart'; -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/utils/version_comparator.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/balance_page.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart'; -import 'package:mobx/mobx.dart'; import 'package:cake_wallet/main.dart'; import 'package:cake_wallet/router.dart' as Router; import 'package:shared_preferences/shared_preferences.dart'; diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart index 5e1f8d16a..20ddea361 100644 --- a/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart @@ -1,7 +1,7 @@ import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/main_actions.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_action_button.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/market_place_page.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart'; import 'package:flutter/material.dart'; diff --git a/lib/src/screens/dashboard/widgets/address_page.dart b/lib/src/screens/dashboard/pages/address_page.dart similarity index 86% rename from lib/src/screens/dashboard/widgets/address_page.dart rename to lib/src/screens/dashboard/pages/address_page.dart index c57613fa5..ff21d4aad 100644 --- a/lib/src/screens/dashboard/widgets/address_page.dart +++ b/lib/src/screens/dashboard/pages/address_page.dart @@ -6,10 +6,8 @@ import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/receive_page_option.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option_picker.dart'; -import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/gradient_background.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; -import 'package:cake_wallet/themes/extensions/receive_page_theme.dart'; import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; @@ -26,7 +24,6 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; import 'package:mobx/mobx.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; class AddressPage extends BasePage { @@ -163,8 +160,7 @@ class AddressPage extends BasePage { return GestureDetector( onTap: () async => dashboardViewModel.isAutoGenerateSubaddressesEnabled ? await showPopUp( - context: context, - builder: (_) => getIt.get()) + context: context, builder: (_) => getIt.get()) : Navigator.of(context).pushNamed(Routes.receive), child: Container( height: 50, @@ -184,26 +180,27 @@ class AddressPage extends BasePage { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Observer( - builder: (_) { - String label = addressListViewModel.hasAccounts - ? S.of(context).accounts_subaddresses - : S.of(context).addresses; + builder: (_) { + String label = addressListViewModel.hasAccounts + ? S.of(context).accounts_subaddresses + : S.of(context).addresses; - if (dashboardViewModel.isAutoGenerateSubaddressesEnabled) { - label = addressListViewModel.hasAccounts - ? S.of(context).accounts - : S.of(context).account; - } - return Text( - label, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Theme.of(context) - .extension()! - .textColor), - ); - },), + if (dashboardViewModel.isAutoGenerateSubaddressesEnabled) { + label = addressListViewModel.hasAccounts + ? S.of(context).accounts + : S.of(context).account; + } + return Text( + label, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Theme.of(context) + .extension()! + .textColor), + ); + }, + ), Icon( Icons.arrow_forward_ios, size: 14, @@ -213,7 +210,8 @@ class AddressPage extends BasePage { ), ), ); - } else if (dashboardViewModel.isAutoGenerateSubaddressesEnabled || addressListViewModel.showElectrumAddressDisclaimer) { + } else if (dashboardViewModel.isAutoGenerateSubaddressesEnabled || + addressListViewModel.showElectrumAddressDisclaimer) { return Text(S.of(context).electrum_address_disclaimer, textAlign: TextAlign.center, style: TextStyle( diff --git a/lib/src/screens/dashboard/widgets/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart similarity index 77% rename from lib/src/screens/dashboard/widgets/balance_page.dart rename to lib/src/screens/dashboard/pages/balance_page.dart index 41d4e7bb0..057352456 100644 --- a/lib/src/screens/dashboard/widgets/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -1,6 +1,7 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/nft_listing_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/home_screen_account_widget.dart'; import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart'; import 'package:cake_wallet/src/widgets/introducing_card.dart'; @@ -11,15 +12,80 @@ import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; import 'package:cake_wallet/utils/feature_flag.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; class BalancePage extends StatelessWidget { - BalancePage({required this.dashboardViewModel, required this.settingsStore}); + BalancePage({ + required this.dashboardViewModel, + required this.settingsStore, + required this.nftViewModel, + }); final DashboardViewModel dashboardViewModel; + final NFTViewModel nftViewModel; final SettingsStore settingsStore; + @override + Widget build(BuildContext context) { + return Observer( + builder: (context) { + final isEthereumWallet = dashboardViewModel.type == WalletType.ethereum; + return DefaultTabController( + length: isEthereumWallet ? 2 : 1, + child: Column( + children: [ + if (isEthereumWallet) + Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.only(left: 8), + child: TabBar( + indicatorSize: TabBarIndicatorSize.label, + isScrollable: true, + physics: NeverScrollableScrollPhysics(), + labelStyle: TextStyle( + fontSize: 18, + fontFamily: 'Lato', + fontWeight: FontWeight.w600, + color: + Theme.of(context).extension()!.pageTitleTextColor, + height: 1, + ), + tabs: [ + Tab(text: 'My Crypto'), + Tab(text: 'My NFTs'), + ], + ), + ), + ), + Expanded( + child: TabBarView( + physics: NeverScrollableScrollPhysics(), + children: [ + CryptoBalanceWidget(dashboardViewModel: dashboardViewModel), + if (isEthereumWallet) NFTListingPage(nftViewModel: nftViewModel) + ], + ), + ), + ], + ), + ); + }, + ); + } +} + +class CryptoBalanceWidget extends StatelessWidget { + const CryptoBalanceWidget({ + super.key, + required this.dashboardViewModel, + }); + + final DashboardViewModel dashboardViewModel; + @override Widget build(BuildContext context) { return GestureDetector( @@ -38,7 +104,7 @@ class BalancePage extends StatelessWidget { accountName: dashboardViewModel.subname) : Column( children: [ - SizedBox(height: 56), + SizedBox(height: 16), Container( margin: const EdgeInsets.only(left: 24, bottom: 16), child: Observer( @@ -105,8 +171,7 @@ class BalancePage extends StatelessWidget { itemBuilder: (__, index) { final balance = dashboardViewModel.balanceViewModel.formattedBalances.elementAt(index); - return buildBalanceRow( - context, + return BalanceRowWidget( availableBalanceLabel: '${dashboardViewModel.balanceViewModel.availableBalanceLabel}', availableBalance: balance.availableBalance, @@ -130,20 +195,44 @@ class BalancePage extends StatelessWidget { ), ); } +} - Widget buildBalanceRow( - BuildContext context, { - required String availableBalanceLabel, - required String availableBalance, - required String availableFiatBalance, - required String additionalBalanceLabel, - required String additionalBalance, - required String additionalFiatBalance, - required String frozenBalance, - required String frozenFiatBalance, - required String currency, - required bool hasAdditionalBalance, - }) { +class BalanceRowWidget extends StatelessWidget { + const BalanceRowWidget({ + required this.availableBalanceLabel, + required this.availableBalance, + required this.availableFiatBalance, + required this.additionalBalanceLabel, + required this.additionalBalance, + required this.additionalFiatBalance, + required this.frozenBalance, + required this.frozenFiatBalance, + required this.currency, + required this.hasAdditionalBalance, + super.key, + }); + + final String availableBalanceLabel; + final String availableBalance; + final String availableFiatBalance; + final String additionalBalanceLabel; + final String additionalBalance; + final String additionalFiatBalance; + final String frozenBalance; + final String frozenFiatBalance; + final String currency; + final bool hasAdditionalBalance; + + // void _showBalanceDescription(BuildContext context) { + // showPopUp( + // context: context, + // builder: (_) => + // InformationPage(information: S.current.available_balance_description), + // ); + // } + + @override + Widget build(BuildContext context) { return Container( margin: const EdgeInsets.only(left: 16, right: 16), decoration: BoxDecoration( @@ -165,8 +254,9 @@ class BalancePage extends StatelessWidget { children: [ GestureDetector( behavior: HitTestBehavior.opaque, - onTap: hasAdditionalBalance ? () => - _showBalanceDescription(context, S.current.available_balance_description) + onTap: hasAdditionalBalance + ? () => + _showBalanceDescription(context, S.current.available_balance_description) : null, child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -229,8 +319,9 @@ class BalancePage extends StatelessWidget { if (frozenBalance.isNotEmpty) GestureDetector( behavior: HitTestBehavior.opaque, - onTap: hasAdditionalBalance ? - () => _showBalanceDescription(context, S.current.unavailable_balance_description) + onTap: hasAdditionalBalance + ? () => + _showBalanceDescription(context, S.current.unavailable_balance_description) : null, child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -253,9 +344,8 @@ class BalancePage extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 4), child: Icon(Icons.help_outline, size: 16, - color: Theme.of(context) - .extension()! - .labelTextColor), + color: + Theme.of(context).extension()!.labelTextColor), ), ], ), @@ -337,8 +427,6 @@ class BalancePage extends StatelessWidget { } void _showBalanceDescription(BuildContext context, String content) { - showPopUp( - context: context, - builder: (_) => InformationPage(information: content)); + showPopUp(context: context, builder: (_) => InformationPage(information: content)); } } diff --git a/lib/src/screens/dashboard/widgets/market_place_page.dart b/lib/src/screens/dashboard/pages/market_place_page.dart similarity index 100% rename from lib/src/screens/dashboard/widgets/market_place_page.dart rename to lib/src/screens/dashboard/pages/market_place_page.dart diff --git a/lib/src/screens/dashboard/pages/nft_details_page.dart b/lib/src/screens/dashboard/pages/nft_details_page.dart new file mode 100644 index 000000000..4bddb550b --- /dev/null +++ b/lib/src/screens/dashboard/pages/nft_details_page.dart @@ -0,0 +1,178 @@ +import 'package:cake_wallet/entities/wallet_nft_response.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/widgets/menu_widget.dart'; +import 'package:cake_wallet/src/screens/dashboard/widgets/nft_image_tile_widget.dart'; +import 'package:cake_wallet/src/widgets/gradient_background.dart'; +import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:flutter/material.dart'; + +class NFTDetailsPage extends BasePage { + NFTDetailsPage({required this.dashboardViewModel, required this.nftAsset}); + + final DashboardViewModel dashboardViewModel; + final NFTAssetModel nftAsset; + + @override + bool get gradientBackground => true; + + @override + Widget Function(BuildContext, Widget) get rootWrapper => + (BuildContext context, Widget scaffold) => + GradientBackground(scaffold: scaffold); + + @override + bool get resizeToAvoidBottomInset => false; + + @override + Widget get endDrawer => MenuWidget(dashboardViewModel); + + @override + Widget trailing(BuildContext context) { + final menuButton = Image.asset( + 'assets/images/menu.png', + color: + Theme.of(context).extension()!.pageTitleTextColor, + ); + + return Container( + alignment: Alignment.centerRight, + width: 40, + child: TextButton( + // FIX-ME: Style + //highlightColor: Colors.transparent, + //splashColor: Colors.transparent, + //padding: EdgeInsets.all(0), + onPressed: () => onOpenEndDrawer(), + child: Semantics(label: S.of(context).wallet_menu, child: menuButton), + ), + ); + } + + @override + Widget body(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + Container( + width: double.infinity, + margin: const EdgeInsets.all(16), + padding: const EdgeInsets.symmetric(vertical: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + border: Border.all( + color: Theme.of(context) + .extension()! + .cardBorderColor, + width: 1, + ), + color: Theme.of(context) + .extension()! + .syncedBackgroundColor, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: MediaQuery.sizeOf(context).height / 2.5, + width: double.infinity, + clipBehavior: Clip.hardEdge, + margin: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16.0), + border: Border.all( + color: Theme.of(context) + .extension()! + .cardBorderColor, + width: 1, + ), + color: Theme.of(context) + .extension()! + .syncedBackgroundColor, + + ), + child: NFTImageWidget( + imageUrl: nftAsset.normalizedMetadata?.imageUrl, + ), + ), + SizedBox(height: 16), + _NFTSingleInfoTile( + infoType: S.current.name, + infoValue: nftAsset.normalizedMetadata?.name ?? '', + ), + + if (nftAsset.normalizedMetadata?.description != null) ...[ + SizedBox(height: 16), + _NFTSingleInfoTile( + infoType: 'Description', + infoValue: nftAsset.normalizedMetadata?.description ?? '', + ), + ], + + SizedBox(height: 16), + _NFTSingleInfoTile( + infoType: 'Contract Name', + infoValue: nftAsset.name ?? '', + ), + SizedBox(height: 8), + _NFTSingleInfoTile( + infoType: 'Contract Symbol', + infoValue: nftAsset.symbol ?? '', + ), + ], + ), + ), + ], + ), + ); + } +} + +class _NFTSingleInfoTile extends StatelessWidget { + const _NFTSingleInfoTile({ + required this.infoType, + required this.infoValue, + }); + + final String infoType; + final String infoValue; + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( +infoType, + style: TextStyle( + fontSize: 12, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context) + .extension()! + .labelTextColor, + height: 1, + ), + ), + SizedBox(height: 8), + Text( + infoValue, + style: TextStyle( + fontSize: 16, + fontFamily: 'Lato', + fontWeight: FontWeight.w600, + color: Theme.of(context) + .extension()! + .assetTitleColor, + height: 1, + ), + ), + ], + ), + ); + } +} diff --git a/lib/src/screens/dashboard/pages/nft_import_page.dart b/lib/src/screens/dashboard/pages/nft_import_page.dart new file mode 100644 index 000000000..6b40b46cc --- /dev/null +++ b/lib/src/screens/dashboard/pages/nft_import_page.dart @@ -0,0 +1,168 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/widgets/address_text_field.dart'; +import 'package:cake_wallet/src/widgets/primary_button.dart'; +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; +import 'package:cake_wallet/themes/extensions/seed_widget_theme.dart'; +import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class ImportNFTPage extends StatefulWidget { + const ImportNFTPage({required this.nftViewModel, super.key}); + + final NFTViewModel nftViewModel; + + @override + State createState() => _ImportNFTPageState(); +} + +class _ImportNFTPageState extends State { + late TextEditingController tokenAddressController; + late TextEditingController tokenIDController; + + @override + void initState() { + super.initState(); + tokenAddressController = TextEditingController(); + tokenIDController = TextEditingController(); + } + + @override + void dispose() { + tokenAddressController.dispose(); + tokenIDController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return _ImportNFTPage( + nftViewModel: widget.nftViewModel, + tokenAddressController: tokenAddressController, + tokenIDController: tokenIDController, + ); + } +} + +class _ImportNFTPage extends BasePage { + _ImportNFTPage({ + required this.tokenIDController, + required this.tokenAddressController, + required this.nftViewModel, + }); + + final NFTViewModel nftViewModel; + final TextEditingController tokenIDController; + final TextEditingController tokenAddressController; + + @override + String? get title => S.current.import; + + @override + Widget body(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + + Text( + S.current.address, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontFamily: 'Lato', + fontWeight: FontWeight.w800, + color: + Theme.of(context).extension()!.hintTextColor, + height: 1, + ), + ), + AddressTextField( + controller: tokenAddressController, + options: [AddressTextFieldOption.paste], + onPushPasteButton: (context) async { + final clipboard = await Clipboard.getData('text/plain'); + final tokenAddress = clipboard?.text ?? ''; + + if (tokenAddress.isNotEmpty) { + tokenAddressController.text = tokenAddress; + } + }, + borderColor: Theme.of(context) + .extension()! + .textfieldUnderlineColor, + iconColor: Theme.of(context).primaryColor, + placeholder: '0x...', + textStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: PaletteDark.darkCyanBlue, + ), + hintStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: PaletteDark.darkCyanBlue, + ), + ), + + SizedBox(height: 48), + Text( + S.current.tokenID, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + fontFamily: 'Lato', + fontWeight: FontWeight.w800, + color: Theme.of(context).extension()!.hintTextColor, + height: 1, + ), + ), + AddressTextField( + controller: tokenIDController, + options: [AddressTextFieldOption.paste], + onPushPasteButton: (context) async { + final clipboard = await Clipboard.getData('text/plain'); + final tokenID = clipboard?.text ?? ''; + + if (tokenID.isNotEmpty) { + tokenIDController.text = tokenID; + } + }, + borderColor: Theme.of(context) + .extension()! + .textfieldUnderlineColor, + iconColor: Theme.of(context).primaryColor, + placeholder: S.current.enterTokenID, + textStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: PaletteDark.darkCyanBlue, + ), + hintStyle: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: PaletteDark.darkCyanBlue, + ), + ), + Spacer(), + Observer(builder: (context) { + return LoadingPrimaryButton( + isLoading: nftViewModel.isImportNFTLoading, + text: S.current.import, + color: Theme.of(context).primaryColor, + textColor: Colors.white, + onPressed: () async { + await nftViewModel.importNFT(tokenAddressController.text, tokenIDController.text); + Navigator.pop(context); + }, + ); + }), + ], + ), + ); + } +} diff --git a/lib/src/screens/dashboard/pages/nft_listing_page.dart b/lib/src/screens/dashboard/pages/nft_listing_page.dart new file mode 100644 index 000000000..46690a969 --- /dev/null +++ b/lib/src/screens/dashboard/pages/nft_listing_page.dart @@ -0,0 +1,84 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/dashboard/widgets/nft_tile_widget.dart'; +import 'package:cake_wallet/src/widgets/primary_button.dart'; +import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; +import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class NFTListingPage extends StatelessWidget { + final NFTViewModel nftViewModel; + + const NFTListingPage({super.key, required this.nftViewModel}); + @override + Widget build(BuildContext context) { + return Observer( + builder: (context) { + return Column( + children: [ + SizedBox(height: 16), + Padding( + padding: EdgeInsets.only(left: 16, right: 16, bottom: 16), + child: PrimaryButton( + text: S.current.import, + color: Theme.of(context) + .extension()! + .syncedBackgroundColor, + textColor: Colors.white, + onPressed: () => Navigator.pushNamed( + context, + Routes.importNFTPage, + arguments: nftViewModel, + ), + ), + ), + if (nftViewModel.isLoading) + Expanded( + child: Center( + child: CircularProgressIndicator( + backgroundColor: Theme.of(context).extension()!.textColor, + valueColor: AlwaysStoppedAnimation( + Theme.of(context) + .extension()! + .firstGradientBottomPanelColor, + ), + ), + ), + ), + if (!nftViewModel.isLoading) + Expanded( + child: nftViewModel.nftAssetByWalletModels.isEmpty + ? Center( + child: Text( + S.current.noNFTYet, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 20, + fontFamily: 'Lato', + fontWeight: FontWeight.w600, + color: Theme.of(context) + .extension()! + .pageTitleTextColor, + height: 1, + ), + ), + ) + : ListView.separated( + padding: EdgeInsets.symmetric(horizontal: 4, vertical: 16), + separatorBuilder: (context, index) => SizedBox(height: 8), + itemCount: nftViewModel.nftAssetByWalletModels.length, + itemBuilder: (context, index) { + final nftAsset = nftViewModel.nftAssetByWalletModels[index]; + return NFTTileWidget(nftAsset: nftAsset); + }, + ), + ) + ], + ); + }, + ); + } +} diff --git a/lib/src/screens/dashboard/widgets/transactions_page.dart b/lib/src/screens/dashboard/pages/transactions_page.dart similarity index 77% rename from lib/src/screens/dashboard/widgets/transactions_page.dart rename to lib/src/screens/dashboard/pages/transactions_page.dart index 5c7b78f3a..1be8a2a65 100644 --- a/lib/src/screens/dashboard/widgets/transactions_page.dart +++ b/lib/src/screens/dashboard/pages/transactions_page.dart @@ -46,9 +46,11 @@ class TransactionsPage extends StatelessWidget { return Padding( padding: const EdgeInsets.fromLTRB(24, 0, 24, 8), child: DashBoardRoundedCardWidget( - onTap: () => Navigator.of(context).pushNamed( - Routes.webViewPage, - arguments: ['', Uri.parse('https://guides.cakewallet.com/docs/bugs-service-status/why_are_my_funds_not_appearing/')]), + onTap: () => Navigator.of(context).pushNamed(Routes.webViewPage, arguments: [ + '', + Uri.parse( + 'https://guides.cakewallet.com/docs/bugs-service-status/why_are_my_funds_not_appearing/') + ]), title: S.of(context).syncing_wallet_alert_title, subTitle: S.of(context).syncing_wallet_alert_content, ), @@ -76,40 +78,34 @@ class TransactionsPage extends StatelessWidget { return Observer( builder: (_) => TransactionRow( - onTap: () => Navigator.of(context).pushNamed( - Routes.transactionDetails, - arguments: transaction), + onTap: () => Navigator.of(context) + .pushNamed(Routes.transactionDetails, arguments: transaction), direction: transaction.direction, - formattedDate: DateFormat('HH:mm') - .format(transaction.date), + formattedDate: DateFormat('HH:mm').format(transaction.date), formattedAmount: item.formattedCryptoAmount, - formattedFiatAmount: dashboardViewModel - .balanceViewModel.isFiatDisabled - ? '' - : item.formattedFiatAmount, + formattedFiatAmount: + dashboardViewModel.balanceViewModel.isFiatDisabled + ? '' + : item.formattedFiatAmount, isPending: transaction.isPending, - title: item.formattedTitle + - item.formattedStatus)); + title: item.formattedTitle + item.formattedStatus)); } if (item is AnonpayTransactionListItem) { final transactionInfo = item.transaction; return AnonpayTransactionRow( - onTap: () => Navigator.of(context).pushNamed( - Routes.anonPayDetailsPage, - arguments: transactionInfo), + onTap: () => Navigator.of(context) + .pushNamed(Routes.anonPayDetailsPage, arguments: transactionInfo), currency: transactionInfo.fiatAmount != null ? transactionInfo.fiatEquiv ?? '' - : CryptoCurrency.fromFullName( - transactionInfo.coinTo) + : CryptoCurrency.fromFullName(transactionInfo.coinTo) .name .toUpperCase(), provider: transactionInfo.provider, amount: transactionInfo.fiatAmount?.toString() ?? (transactionInfo.amountTo?.toString() ?? ''), - createdAt: DateFormat('HH:mm') - .format(transactionInfo.createdAt), + createdAt: DateFormat('HH:mm').format(transactionInfo.createdAt), ); } @@ -118,17 +114,14 @@ class TransactionsPage extends StatelessWidget { return Observer( builder: (_) => TradeRow( - onTap: () => Navigator.of(context).pushNamed( - Routes.tradeDetails, - arguments: trade), + onTap: () => Navigator.of(context) + .pushNamed(Routes.tradeDetails, arguments: trade), provider: trade.provider, from: trade.from, to: trade.to, - createdAtFormattedDate: - trade.createdAt != null - ? DateFormat('HH:mm') - .format(trade.createdAt!) - : null, + createdAtFormattedDate: trade.createdAt != null + ? DateFormat('HH:mm').format(trade.createdAt!) + : null, formattedAmount: item.tradeFormattedAmount)); } @@ -138,13 +131,12 @@ class TransactionsPage extends StatelessWidget { return Observer( builder: (_) => OrderRow( onTap: () => Navigator.of(context) - .pushNamed(Routes.orderDetails, - arguments: order), + .pushNamed(Routes.orderDetails, arguments: order), provider: order.provider, from: order.from!, to: order.to!, - createdAtFormattedDate: DateFormat('HH:mm') - .format(order.createdAt), + createdAtFormattedDate: + DateFormat('HH:mm').format(order.createdAt), formattedAmount: item.orderFormattedAmount, )); } diff --git a/lib/src/screens/dashboard/widgets/nft_image_tile_widget.dart b/lib/src/screens/dashboard/widgets/nft_image_tile_widget.dart new file mode 100644 index 000000000..d34ff02cb --- /dev/null +++ b/lib/src/screens/dashboard/widgets/nft_image_tile_widget.dart @@ -0,0 +1,37 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class NFTImageWidget extends StatelessWidget { + const NFTImageWidget({ + required this.imageUrl, + }); + + final String? imageUrl; + + @override + Widget build(BuildContext context) { + try { + if (imageUrl == null) return Icon(Icons.error); + + if (imageUrl!.contains('.svg')) { + return SvgPicture.network(imageUrl!); + } + + return Image.network( + imageUrl!, + fit: BoxFit.cover, + loadingBuilder: (BuildContext _, Widget child, ImageChunkEvent? loadingProgress) { + if (loadingProgress == null) { + return child; + } else { + return CupertinoActivityIndicator(animating: true); + } + }, + errorBuilder: (_, __, ___) => Icon(Icons.error), + ); + } catch (_) { + return Icon(Icons.error); + } + } +} diff --git a/lib/src/screens/dashboard/widgets/nft_tile_widget.dart b/lib/src/screens/dashboard/widgets/nft_tile_widget.dart new file mode 100644 index 000000000..0be027f44 --- /dev/null +++ b/lib/src/screens/dashboard/widgets/nft_tile_widget.dart @@ -0,0 +1,95 @@ +import 'package:cake_wallet/entities/wallet_nft_response.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/dashboard/widgets/nft_image_tile_widget.dart'; +import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; +import 'package:cake_wallet/themes/extensions/sync_indicator_theme.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class NFTTileWidget extends StatelessWidget { + const NFTTileWidget({super.key, required this.nftAsset}); + + final NFTAssetModel nftAsset; + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () => Navigator.pushNamed(context, Routes.nftDetailsPage, + arguments: nftAsset), + child: Container( + width: double.infinity, + margin: const EdgeInsets.only(left: 16, right: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + border: Border.all( + color: Theme.of(context) + .extension()! + .cardBorderColor, + width: 1, + ), + color: Theme.of(context) + .extension()! + .syncedBackgroundColor, + ), + child: Row( + children: [ + Container( + height: 100, + width: 100, + clipBehavior: Clip.hardEdge, + margin: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16.0), + border: Border.all( + color: Theme.of(context) + .extension()! + .cardBorderColor, + width: 1, + ), + color: Theme.of(context) + .extension()! + .syncedBackgroundColor, + ), + child: NFTImageWidget( + imageUrl: nftAsset.normalizedMetadata?.imageUrl, + ), + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${nftAsset.name ?? ''} - ${nftAsset.symbol ?? ''}', + style: TextStyle( + fontSize: 12, + fontFamily: 'Lato', + fontWeight: FontWeight.w400, + color: Theme.of(context) + .extension()! + .labelTextColor, + height: 1, + ), + ), + SizedBox(height: 8), + Text( + nftAsset.normalizedMetadata?.name ?? nftAsset.name ?? "", + style: TextStyle( + fontSize: 20, + fontFamily: 'Lato', + fontWeight: FontWeight.w900, + color: Theme.of(context) + .extension()! + .assetTitleColor, + height: 1, + ), + ), + ], + ), + ) + ], + ), + ), + ); + } +} + diff --git a/lib/src/widgets/address_text_field.dart b/lib/src/widgets/address_text_field.dart index 092a70422..0467b18a2 100644 --- a/lib/src/widgets/address_text_field.dart +++ b/lib/src/widgets/address_text_field.dart @@ -67,13 +67,16 @@ class AddressTextField extends StatelessWidget { enabled: isActive, controller: controller, focusNode: focusNode, + style: textStyle ?? TextStyle( fontSize: 16, color: Theme.of(context).extension()!.titleColor), decoration: InputDecoration( + suffixIcon: SizedBox( width: prefixIconWidth * options.length + (spaceBetweenPrefixIcons * options.length), ), + hintStyle: hintStyle ?? TextStyle(fontSize: 16, color: Theme.of(context).hintColor), hintText: placeholder ?? S.current.widgets_address, focusedBorder: isBorderExist diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index bea43a6c6..36eac4941 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -162,6 +162,7 @@ class ExceptionHandler { "Error while launching http", "OS Error: Network is unreachable", "ClientException: Write failed, uri=http", + "Connection terminated during handshake", ]; static Future _addDeviceInfo(File file) async { diff --git a/lib/view_model/dashboard/nft_view_model.dart b/lib/view_model/dashboard/nft_view_model.dart new file mode 100644 index 000000000..ec5919b7d --- /dev/null +++ b/lib/view_model/dashboard/nft_view_model.dart @@ -0,0 +1,139 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'dart:convert'; +import 'dart:developer'; + +import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart'; +import 'package:cake_wallet/src/screens/wallet_connect/widgets/message_display_widget.dart'; +import 'package:http/http.dart' as http; +import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/.secrets.g.dart' as secrets; + +import 'package:cake_wallet/entities/wallet_nft_response.dart'; +import 'package:cake_wallet/store/app_store.dart'; + +part 'nft_view_model.g.dart'; + +class NFTViewModel = NFTViewModelBase with _$NFTViewModel; + +abstract class NFTViewModelBase with Store { + NFTViewModelBase(this.appStore, this.bottomSheetService) + : isLoading = false, + isImportNFTLoading = false, + nftAssetByWalletModels = ObservableList() { + getNFTAssetByWallet(); + + reaction((_) => appStore.wallet, (_) => getNFTAssetByWallet()); + } + + final AppStore appStore; + final BottomSheetService bottomSheetService; + + @observable + bool isLoading; + + @observable + bool isImportNFTLoading; + + ObservableList nftAssetByWalletModels; + + @action + Future getNFTAssetByWallet() async { + final walletAddress = appStore.wallet!.walletInfo.address; + log('Fetching wallet NFTs for $walletAddress'); + + // the [chain] refers to the chain network that the nft is on + // the [format] refers to the number format type of the responses + // the [normalizedMetadata] field is a boolean that determines if + // the response would include a json string of the NFT Metadata that can be decoded + // and used within the wallet + final uri = Uri.https( + 'deep-index.moralis.io', + '/api/v2.2/$walletAddress/nft', + { + "chain": "eth", + "format": "decimal", + "media_items": "false", + "normalizeMetadata": "true", + }, + ); + + try { + isLoading = true; + + final response = await http.get( + uri, + headers: { + "Accept": "application/json", + "X-API-Key": secrets.moralisApiKey, + }, + ); + + final decodedResponse = jsonDecode(response.body) as Map; + + final result = WalletNFTsResponseModel.fromJson(decodedResponse).result ?? []; + + nftAssetByWalletModels.clear(); + + nftAssetByWalletModels.addAll(result); + + isLoading = false; + } catch (e) { + isLoading = false; + log(e.toString()); + bottomSheetService.queueBottomSheet( + isModalDismissible: true, + widget: BottomSheetMessageDisplayWidget( + message: e.toString(), + ), + ); + } + } + + @action + Future importNFT(String tokenAddress, String tokenId) async { + + // the [chain] refers to the chain network that the nft is on + // the [format] refers to the number format type of the responses + // the [normalizedMetadata] field is a boolean that determines if + // the response would include a json string of the NFT Metadata that can be decoded + // and used within the wallet + final uri = Uri.https( + 'deep-index.moralis.io', + '/api/v2.2/nft/$tokenAddress/$tokenId', + { + "chain": "eth", + "format": "decimal", + "media_items": "false", + "normalizeMetadata": "true", + }, + ); + + try { + isImportNFTLoading = true; + + final response = await http.get( + uri, + headers: { + "Accept": "application/json", + "X-API-Key": secrets.moralisApiKey, + }, + ); + + final decodedResponse = jsonDecode(response.body) as Map; + + final nftAsset = NFTAssetModel.fromJson(decodedResponse); + + nftAssetByWalletModels.add(nftAsset); + + isImportNFTLoading = false; + } catch (e) { + isImportNFTLoading = false; + bottomSheetService.queueBottomSheet( + isModalDismissible: true, + widget: BottomSheetMessageDisplayWidget( + message: e.toString(), + ), + ); + } + } +} diff --git a/pubspec_base.yaml b/pubspec_base.yaml index c6101d28c..edfc64890 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -103,6 +103,7 @@ dependencies: url: https://github.com/cake-tech/tor.git ref: main socks5_proxy: ^1.0.4 + flutter_svg: ^2.0.9 dev_dependencies: flutter_test: diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 84a0a72f5..fb883eb13 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -383,7 +383,7 @@ "change_backup_password_alert": "لن تكون ملفات النسخ الاحتياطي السابقة متاحة للاستيراد بكلمة مرور نسخ احتياطي جديدة. سيتم استخدام كلمة مرور النسخ الاحتياطي الجديدة لملفات النسخ الاحتياطي الجديدة فقط. هل أنت متأكد أنك تريد تغيير كلمة المرور الاحتياطية؟", "enter_backup_password": "أدخل كلمة المرور الاحتياطية هنا", "select_backup_file": "حدد ملف النسخ الاحتياطي", - "import": "اختيار ملف", + "import": "ﺩﺭﻮﺘﺴﻳ", "please_select_backup_file": "الرجاء تحديد ملف النسخ الاحتياطي وإدخال كلمة مرور النسخ الاحتياطي.", "fixed_rate": "السعر الثابت", "fixed_rate_alert": "ستتمكن من إدخال مبلغ الاستلام عند تشغيل وضع السعر الثابت. هل تريد التبديل إلى وضع السعر الثابت؟", @@ -727,9 +727,16 @@ "require_for_exchanges_to_external_wallets": "ﺔﻴﺟﺭﺎﺧ ﻆﻓﺎﺤﻣ ﻰﻟﺇ ﺕﻻﺩﺎﺒﺘﻟﺍ ﺐﻠﻄﺘﺗ", "camera_permission_is_required": ".ﺍﺮﻴﻣﺎﻜﻟﺍ ﻥﺫﺇ ﺏﻮﻠﻄﻣ", "switchToETHWallet": "ﻯﺮﺧﺃ ﺓﺮﻣ ﺔﻟﻭﺎﺤﻤﻟﺍﻭ Ethereum ﺔﻈﻔﺤﻣ ﻰﻟﺇ ﻞﻳﺪﺒﺘﻟﺍ ﻰﺟﺮﻳ", - "seed_phrase_length": " ﺭﻭﺬﺒﻟﺍ ﺓﺭﺎﺒﻌﻟﺍ ﻝﻮﻃ", - "unavailable_balance": " ﺮﻓﻮﺘﻣ ﺮﻴﻏ ﺪﻴﺻﺭ", + "importNFTs": "NFTs ﺩﺍﺮﻴﺘﺳﺍ", + "noNFTYet": "ﻥﻵﺍ ﻰﺘﺣ NFTs ﺪﺟﻮﻳ ﻻ", + "address": " ﻥﺍﻮﻨﻋ", + "enterTokenID": "ﺰﻴﻤﻤﻟﺍ ﺰﻣﺮﻟﺍ ﻑﺮﻌﻣ ﻞﺧﺩﺃ", + "tokenID": "ﻒﻳﺮﻌﺗ ﺔﻗﺎﻄﺑ", + "name": "ﻢﺳﺍ", + "symbol": "ﺰﻣﺭ", + "seed_phrase_length": "ﺭﻭﺬﺒﻟﺍ ﺓﺭﺎﺒﻌﻟﺍ ﻝﻮﻃ", + "unavailable_balance": "ﺮﻓﻮﺘﻣ ﺮﻴﻏ ﺪﻴﺻﺭ", "unavailable_balance_description": ".ﺎﻫﺪﻴﻤﺠﺗ ءﺎﻐﻟﺇ ﺭﺮﻘﺗ ﻰﺘﺣ ﺕﻼﻣﺎﻌﻤﻠﻟ ﻝﻮﺻﻮﻠﻟ ﺔﻠﺑﺎﻗ ﺮﻴﻏ ﺓﺪﻤﺠﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﻞﻈﺗ ﺎﻤﻨﻴﺑ ،ﺎﻬﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻣﺎﻌﻤﻟﺍ ﻝﺎﻤﺘﻛﺍ ﺩﺮﺠﻤﺑ ﺔﺣﺎﺘﻣ ﺔﻠﻔﻘﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﺢﺒﺼﺘﺳ .ﻚﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻤﻌﻟﺍ ﻲﻓ ﻢﻜﺤﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻂﺸﻧ ﻞﻜﺸﺑ ﺎﻫﺪﻴﻤﺠﺘﺑ ﺖﻤﻗ", "unspent_change": "يتغير", - "tor_connection": " ﺭﻮﺗ ﻝﺎﺼﺗﺍ" + "tor_connection": "ﺭﻮﺗ ﻝﺎﺼﺗﺍ" } diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index b1851156c..1b3ded5d6 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -723,6 +723,13 @@ "require_for_exchanges_to_external_wallets": "Изискване за обмен към външни портфейли", "camera_permission_is_required": "Изисква се разрешение за камерата.\nМоля, активирайте го от настройките на приложението.", "switchToETHWallet": "Моля, преминете към портфейл Ethereum и опитайте отново", + "importNFTs": "Импортирайте NFT", + "noNFTYet": "Все още няма NFT", + "address": "Адрес", + "enterTokenID": "Въведете идентификатора на токена", + "tokenID": "документ за самоличност", + "name": "Име", + "symbol": "Символ", "seed_phrase_length": "Дължина на началната фраза", "unavailable_balance": "Неналично салдо", "unavailable_balance_description": "Неналично салдо: Тази обща сума включва средства, които са заключени в чакащи транзакции и тези, които сте замразили активно в настройките за контрол на монетите. Заключените баланси ще станат достъпни, след като съответните им транзакции бъдат завършени, докато замразените баланси остават недостъпни за транзакции, докато не решите да ги размразите.", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 2e59f5fc5..4f0e89bd3 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -383,7 +383,7 @@ "change_backup_password_alert": "Vaše předchozí soubory se zálohami nebude možné naimportovat s novým heslem. Nové heslo bude použito pouze pro nové zálohy. Opravdu chcete změnit heslo pro zálohy?", "enter_backup_password": "Zde zadejte své heslo pro zálohy", "select_backup_file": "Vybrat soubor se zálohou", - "import": "Importovat", + "import": "Import", "please_select_backup_file": "Prosím vyberte soubor se zálohou a zadejte heslo pro zálohy.", "fixed_rate": "Pevný kurz", "fixed_rate_alert": "Když je zvolený pevný kurz, můžete zadat konkrétní částku, kterou chcete dostat. Chcete se přepnout do režimu s pevným kurzem?", @@ -723,6 +723,13 @@ "require_for_exchanges_to_external_wallets": "Vyžadovat pro výměny do externích peněženek", "camera_permission_is_required": "Vyžaduje se povolení fotoaparátu.\nPovolte jej v nastavení aplikace.", "switchToETHWallet": "Přejděte na peněženku Ethereum a zkuste to znovu", + "importNFTs": "Importujte NFT", + "noNFTYet": "Zatím žádné NFT", + "address": "Adresa", + "enterTokenID": "Zadejte ID tokenu", + "tokenID": "ID", + "name": "název", + "symbol": "Symbol", "seed_phrase_length": "Délka fráze semene", "unavailable_balance": "Nedostupný zůstatek", "unavailable_balance_description": "Nedostupný zůstatek: Tento součet zahrnuje prostředky, které jsou uzamčeny v nevyřízených transakcích a ty, které jste aktivně zmrazili v nastavení kontroly mincí. Uzamčené zůstatky budou k dispozici po dokončení příslušných transakcí, zatímco zmrazené zůstatky zůstanou pro transakce nepřístupné, dokud se nerozhodnete je uvolnit.", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 7868553be..360e621db 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -731,6 +731,13 @@ "require_for_exchanges_to_external_wallets": "Erforderlich für den Umtausch in externe Wallets", "camera_permission_is_required": "Eine Kameraerlaubnis ist erforderlich.\nBitte aktivieren Sie es in den App-Einstellungen.", "switchToETHWallet": "Bitte wechseln Sie zu einem Ethereum-Wallet und versuchen Sie es erneut", + "importNFTs": "NFTs importieren", + "noNFTYet": "Noch keine NFTs", + "address": "Adresse", + "enterTokenID": "Geben Sie die Token-ID ein", + "tokenID": "AUSWEIS", + "name": "Name", + "symbol": "Symbol", "seed_phrase_length": "Länge der Seed-Phrase", "unavailable_balance": "Nicht verfügbares Guthaben", "unavailable_balance_description": "Nicht verfügbares Guthaben: Diese Summe umfasst Gelder, die in ausstehenden Transaktionen gesperrt sind, und solche, die Sie in Ihren Münzkontrolleinstellungen aktiv eingefroren haben. Gesperrte Guthaben werden verfügbar, sobald die entsprechenden Transaktionen abgeschlossen sind, während eingefrorene Guthaben für Transaktionen nicht zugänglich bleiben, bis Sie sich dazu entschließen, sie wieder freizugeben.", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index c3cd256d9..0f059759d 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -732,6 +732,13 @@ "require_for_exchanges_to_external_wallets": "Require for exchanges to external wallets", "camera_permission_is_required": "Camera permission is required. \nPlease enable it from app settings.", "switchToETHWallet": "Please switch to an Ethereum wallet and try again", + "importNFTs": "Import NFTs", + "noNFTYet": "No NFTs yet", + "address": "Address", + "enterTokenID": "Enter the token ID", + "tokenID": "ID", + "name": "Name", + "symbol": "Symbol", "seed_phrase_length": "Seed phrase length", "unavailable_balance": "Unavailable balance", "unavailable_balance_description": "Unavailable Balance: This total includes funds that are locked in pending transactions and those you have actively frozen in your coin control settings. Locked balances will become available once their respective transactions are completed, while frozen balances remain inaccessible for transactions until you decide to unfreeze them.", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 4e624a9b4..8a4f180f6 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -731,6 +731,13 @@ "require_for_exchanges_to_external_wallets": "Requerido para intercambios a billeteras externas", "camera_permission_is_required": "Se requiere permiso de la cámara.\nHabilítelo desde la configuración de la aplicación.", "switchToETHWallet": "Cambie a una billetera Ethereum e inténtelo nuevamente.", + "importNFTs": "Importar NFT", + "noNFTYet": "Aún no hay NFT", + "address": "DIRECCIÓN", + "enterTokenID": "Ingrese el ID del token", + "tokenID": "IDENTIFICACIÓN", + "name": "Nombre", + "symbol": "Símbolo", "seed_phrase_length": "Longitud de la frase inicial", "unavailable_balance": "Saldo no disponible", "unavailable_balance_description": "Saldo no disponible: este total incluye fondos que están bloqueados en transacciones pendientes y aquellos que usted ha congelado activamente en su configuración de control de monedas. Los saldos bloqueados estarán disponibles una vez que se completen sus respectivas transacciones, mientras que los saldos congelados permanecerán inaccesibles para las transacciones hasta que usted decida descongelarlos.", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index d4d414029..158aaadb4 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -729,6 +729,15 @@ "exchange_provider_unsupported": "${providerName} n'est plus pris en charge !", "domain_looks_up": "Résolution de nom", "require_for_exchanges_to_external_wallets": "Exiger pour les échanges vers des portefeuilles externes", + "camera_permission_is_required": "L'autorisation de la caméra est requise.\nVeuillez l'activer à partir des paramètres de l'application.", + "switchToETHWallet": "Veuillez passer à un portefeuille (wallet) Ethereum et réessayer", + "importNFTs": "Importer des NFT", + "noNFTYet": "Pas encore de NFT", + "address": "Adresse", + "enterTokenID": "Entrez l'ID du jeton", + "tokenID": "IDENTIFIANT", + "name": "Nom", + "symbol": "Symbole", "seed_phrase_length": "Longueur de la phrase de départ", "unavailable_balance": "Solde indisponible", "unavailable_balance_description": "Solde indisponible : ce total comprend les fonds bloqués dans les transactions en attente et ceux que vous avez activement gelés dans vos paramètres de contrôle des pièces. Les soldes bloqués deviendront disponibles une fois leurs transactions respectives terminées, tandis que les soldes gelés resteront inaccessibles aux transactions jusqu'à ce que vous décidiez de les débloquer.", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 0e2589b6a..8b6f38d05 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -709,6 +709,13 @@ "require_for_exchanges_to_external_wallets": "Bukatar musanya zuwa wallet na waje", "camera_permission_is_required": "Ana buƙatar izinin kyamara.\nDa fatan za a kunna shi daga saitunan app.", "switchToETHWallet": "Da fatan za a canza zuwa walat ɗin Ethereum kuma a sake gwadawa", + "importNFTs": "Shigo da NFTs", + "noNFTYet": "Babu NFTs tukuna", + "address": "Adireshi", + "enterTokenID": "Shigar da alamar alama", + "tokenID": "ID", + "name": "Suna", + "symbol": "Alama", "seed_phrase_length": "Tsawon jimlar iri", "unavailable_balance": "Ma'aunin da ba ya samuwa", "unavailable_balance_description": "Ma'auni Babu: Wannan jimlar ya haɗa da kuɗi waɗanda ke kulle a cikin ma'amaloli da ke jiran aiki da waɗanda kuka daskare sosai a cikin saitunan sarrafa kuɗin ku. Ma'auni da aka kulle za su kasance da zarar an kammala ma'amalolinsu, yayin da daskararrun ma'auni ba za su iya samun damar yin ciniki ba har sai kun yanke shawarar cire su.", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 19467af09..9afc420a2 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -731,6 +731,13 @@ "require_for_exchanges_to_external_wallets": "बाहरी वॉलेट में एक्सचेंज की आवश्यकता है", "camera_permission_is_required": "कैमरे की अनुमति आवश्यक है.\nकृपया इसे ऐप सेटिंग से सक्षम करें।", "switchToETHWallet": "कृपया एथेरियम वॉलेट पर स्विच करें और पुनः प्रयास करें", + "importNFTs": "एनएफटी आयात करें", + "noNFTYet": "अभी तक कोई एनएफटी नहीं", + "address": "पता", + "enterTokenID": "टोकन आईडी दर्ज करें", + "tokenID": "पहचान", + "name": "नाम", + "symbol": "प्रतीक", "seed_phrase_length": "बीज वाक्यांश की लंबाई", "unavailable_balance": "अनुपलब्ध शेष", "unavailable_balance_description": "अनुपलब्ध शेष राशि: इस कुल में वे धनराशि शामिल हैं जो लंबित लेनदेन में बंद हैं और जिन्हें आपने अपनी सिक्का नियंत्रण सेटिंग्स में सक्रिय रूप से जमा कर रखा है। लॉक किए गए शेष उनके संबंधित लेन-देन पूरे होने के बाद उपलब्ध हो जाएंगे, जबकि जमे हुए शेष लेन-देन के लिए अप्राप्य रहेंगे जब तक कि आप उन्हें अनफ्रीज करने का निर्णय नहीं लेते।", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 30eea10dc..fd4efa97e 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -383,7 +383,7 @@ "change_backup_password_alert": "Nećemo moći uvesti Vaše prethodne datoteke sigurnosne kopije s novom lozinkom za sigurnosnu kopiju. Novu lozinku za sigurnosnu kopiju moći ćete koristiti samo za nove datoteke sigurnosne kopije. Jeste li sigurni da želite promijeniti lozinku za sigurnosnu kopiju?", "enter_backup_password": "Unesite svoju lozinku za sigurnosnu kopiju ovdje", "select_backup_file": "Odaberite datoteku sigurnosne kopije", - "import": "Uvezi", + "import": "Uvoz", "please_select_backup_file": "Molimo odaberite datoteku sigurnosne kopije i unesite lozinku za sigurnosnu kopiju.", "fixed_rate": "Fiksna stopa", "fixed_rate_alert": "Moći ćete unijeti iznos koji želite primiti nakon što označite način rada fiksne stope. Želite li se prebaciti na način rada fiksne stope?", @@ -729,6 +729,13 @@ "require_for_exchanges_to_external_wallets": "Zahtijeva razmjene na vanjske novčanike", "camera_permission_is_required": "Potrebno je dopuštenje kamere.\nOmogućite ga u postavkama aplikacije.", "switchToETHWallet": "Prijeđite na Ethereum novčanik i pokušajte ponovno", + "importNFTs": "Uvoz NFT-ova", + "noNFTYet": "Još nema NFT-ova", + "address": "Adresa", + "enterTokenID": "Unesite ID tokena", + "tokenID": "iskaznica", + "name": "Ime", + "symbol": "Simbol", "seed_phrase_length": "Duljina početne fraze", "unavailable_balance": "Nedostupno stanje", "unavailable_balance_description": "Nedostupno stanje: Ovaj ukupni iznos uključuje sredstva koja su zaključana u transakcijama na čekanju i ona koja ste aktivno zamrznuli u postavkama kontrole novčića. Zaključani saldi postat će dostupni kada se dovrše njihove transakcije, dok zamrznuti saldi ostaju nedostupni za transakcije sve dok ih ne odlučite odmrznuti.", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index c10feffbc..4de382641 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -719,6 +719,13 @@ "require_for_exchanges_to_external_wallets": "Memerlukan pertukaran ke dompet eksternal", "camera_permission_is_required": "Izin kamera diperlukan.\nSilakan aktifkan dari pengaturan aplikasi.", "switchToETHWallet": "Silakan beralih ke dompet Ethereum dan coba lagi", + "importNFTs": "Impor NFT", + "noNFTYet": "Belum ada NFT", + "address": "Alamat", + "enterTokenID": "Masukkan ID tokennya", + "tokenID": "PENGENAL", + "name": "Nama", + "symbol": "Simbol", "seed_phrase_length": "Panjang frase benih", "unavailable_balance": "Saldo tidak tersedia", "unavailable_balance_description": "Saldo Tidak Tersedia: Total ini termasuk dana yang terkunci dalam transaksi yang tertunda dan dana yang telah Anda bekukan secara aktif di pengaturan kontrol koin Anda. Saldo yang terkunci akan tersedia setelah transaksi masing-masing selesai, sedangkan saldo yang dibekukan tetap tidak dapat diakses untuk transaksi sampai Anda memutuskan untuk mencairkannya.", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index a13930666..4fc35b3e3 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -383,7 +383,7 @@ "change_backup_password_alert": "I precedenti file di backup non potranno essere importati con la nuova password di backup. La nuova password di backup verrà usata soltanto per i nuovi file di backup. Sei sicuro di voler cambiare la tua password di backup?", "enter_backup_password": "Inserisci la password di backup qui", "select_backup_file": "Seleziona file di backup", - "import": "Importa", + "import": "Importare", "please_select_backup_file": "Gentilmente seleziona il file di backup e inserisci la password di backup.", "fixed_rate": "Tasso fisso", "fixed_rate_alert": "Potrai inserire l'ammontare da ricevere quando il tasso è fisso. Vuoi cambiare alla modalità tasso fisso?", @@ -731,6 +731,13 @@ "require_for_exchanges_to_external_wallets": "Richiede scambi con portafogli esterni", "camera_permission_is_required": "È richiesta l'autorizzazione della fotocamera.\nAbilitalo dalle impostazioni dell'app.", "switchToETHWallet": "Passa a un portafoglio Ethereum e riprova", + "importNFTs": "Importa NFT", + "noNFTYet": "Nessun NFT ancora", + "address": "Indirizzo", + "enterTokenID": "Inserisci l'ID del token", + "tokenID": "ID", + "name": "Nome", + "symbol": "Simbolo", "seed_phrase_length": "Lunghezza della frase seed", "unavailable_balance": "Saldo non disponibile", "unavailable_balance_description": "Saldo non disponibile: questo totale include i fondi bloccati nelle transazioni in sospeso e quelli che hai congelato attivamente nelle impostazioni di controllo delle monete. I saldi bloccati diventeranno disponibili una volta completate le rispettive transazioni, mentre i saldi congelati rimarranno inaccessibili per le transazioni finché non deciderai di sbloccarli.", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 3e61dbd9d..a4fe8b46f 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -383,7 +383,7 @@ "change_backup_password_alert": "以前のバックアップファイルは、新しいバックアップパスワードでインポートできなくなります。 新しいバックアップパスワードは、新しいバックアップファイルにのみ使用されます。 バックアップパスワードを変更してもよろしいですか?", "enter_backup_password": "ここにバックアップパスワードを入力してください", "select_backup_file": "バックアップファイルを選択", - "import": "インポート", + "import": "輸入", "please_select_backup_file": "バックアップファイルを選択し、バックアップパスワードを入力してください。", "fixed_rate": "固定金利", "fixed_rate_alert": "固定金利モードにチェックを入れると、受取額を入力できるようになります。 固定金利モードに切り替えますか?", @@ -731,6 +731,13 @@ "require_for_exchanges_to_external_wallets": "外部ウォレットへの交換に必要", "camera_permission_is_required": "カメラの許可が必要です。\nアプリの設定から有効にしてください。", "switchToETHWallet": "イーサリアムウォレットに切り替えてもう一度お試しください", + "importNFTs": "NFTのインポート", + "noNFTYet": "NFTはまだありません", + "address": "住所", + "enterTokenID": "トークンIDを入力してください", + "tokenID": "ID", + "name": "名前", + "symbol": "シンボル", "seed_phrase_length": "シードフレーズの長さ", "unavailable_balance": "利用できない残高", "unavailable_balance_description": "利用不可能な残高: この合計には、保留中のトランザクションにロックされている資金と、コイン管理設定でアクティブに凍結した資金が含まれます。ロックされた残高は、それぞれの取引が完了すると利用可能になりますが、凍結された残高は、凍結を解除するまで取引にアクセスできません。", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index de09c65d1..2b638e9d5 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -729,6 +729,13 @@ "require_for_exchanges_to_external_wallets": "외부 지갑으로의 교환을 위해 필요", "camera_permission_is_required": "카메라 권한이 필요합니다.\n앱 설정에서 활성화해 주세요.", "switchToETHWallet": "이더리움 지갑으로 전환한 후 다시 시도해 주세요.", + "importNFTs": "NFT 가져오기", + "noNFTYet": "아직 NFT가 없습니다", + "address": "주소", + "enterTokenID": "토큰 ID를 입력하세요", + "tokenID": "ID", + "name": "이름", + "symbol": "상징", "seed_phrase_length": "시드 문구 길이", "unavailable_balance": "사용할 수 없는 잔액", "unavailable_balance_description": "사용할 수 없는 잔액: 이 총계에는 보류 중인 거래에 잠겨 있는 자금과 코인 관리 설정에서 적극적으로 동결된 자금이 포함됩니다. 잠긴 잔액은 해당 거래가 완료되면 사용할 수 있게 되며, 동결된 잔액은 동결을 해제하기 전까지 거래에 액세스할 수 없습니다.", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 1853c0eff..19ee9c051 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -729,6 +729,13 @@ "require_for_exchanges_to_external_wallets": "ပြင်ပပိုက်ဆံအိတ်များသို့ လဲလှယ်ရန် လိုအပ်သည်။", "camera_permission_is_required": "ကင်မရာခွင့်ပြုချက် လိုအပ်ပါသည်။\nအက်ပ်ဆက်တင်များမှ ၎င်းကိုဖွင့်ပါ။", "switchToETHWallet": "ကျေးဇူးပြု၍ Ethereum ပိုက်ဆံအိတ်သို့ ပြောင်းပြီး ထပ်စမ်းကြည့်ပါ။", + "importNFTs": "NFTs များကို တင်သွင်းပါ။", + "noNFTYet": "NFTs မရှိသေးပါ။", + "address": "လိပ်စာ", + "enterTokenID": "တိုကင် ID ကိုထည့်ပါ။", + "tokenID": "အမှတ်သညာ", + "name": "နာမည်", + "symbol": "သင်္ကေတ", "seed_phrase_length": "မျိုးစေ့စာပိုဒ်တိုအရှည်", "unavailable_balance": "လက်ကျန်ငွေ မရရှိနိုင်ပါ။", "unavailable_balance_description": "မရရှိနိုင်သော လက်ကျန်ငွေ- ဤစုစုပေါင်းတွင် ဆိုင်းငံ့ထားသော ငွေပေးငွေယူများတွင် သော့ခတ်ထားသော ငွေကြေးများနှင့် သင်၏ coin ထိန်းချုပ်မှုဆက်တင်များတွင် သင် တက်ကြွစွာ အေးခဲထားသော ငွေများ ပါဝင်သည်။ သော့ခတ်ထားသော လက်ကျန်ငွေများကို ၎င်းတို့၏ သက်ဆိုင်ရာ ငွေပေးငွေယူများ ပြီးမြောက်သည်နှင့် တပြိုင်နက် ရရှိနိုင်မည်ဖြစ်ပြီး၊ အေးခဲထားသော လက်ကျန်များကို ၎င်းတို့အား ပြန်ဖြုတ်ရန် သင်ဆုံးဖြတ်သည်အထိ ငွေပေးငွေယူများအတွက် ဆက်လက်၍မရနိုင်ပါ။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index a497c20ab..838fec0c1 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -731,6 +731,13 @@ "require_for_exchanges_to_external_wallets": "Vereist voor uitwisselingen naar externe portemonnees", "camera_permission_is_required": "Cameratoestemming is vereist.\nSchakel dit in via de app-instellingen.", "switchToETHWallet": "Schakel over naar een Ethereum-portemonnee en probeer het opnieuw", + "importNFTs": "NFT's importeren", + "noNFTYet": "Nog geen NFT's", + "address": "Adres", + "enterTokenID": "Voer de token-ID in", + "tokenID": "ID kaart", + "name": "Naam", + "symbol": "Symbool", "seed_phrase_length": "Lengte van de zaadzin", "unavailable_balance": "Onbeschikbaar saldo", "unavailable_balance_description": "Niet-beschikbaar saldo: Dit totaal omvat het geld dat is vergrendeld in lopende transacties en het geld dat u actief hebt bevroren in uw muntcontrole-instellingen. Vergrendelde saldi komen beschikbaar zodra de betreffende transacties zijn voltooid, terwijl bevroren saldi ontoegankelijk blijven voor transacties totdat u besluit ze weer vrij te geven.", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index dc69468bd..309f20613 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -383,7 +383,7 @@ "change_backup_password_alert": "Twoje poprzednie pliki kopii zapasowej nie będą dostępne do zaimportowania z nowym hasłem kopii zapasowej. Nowe hasło kopii zapasowej będzie używane tylko dla nowych plików kopii zapasowych. Czy na pewno chcesz zmienić hasło zapasowe?", "enter_backup_password": "Wprowadź tutaj hasło kopii zapasowej", "select_backup_file": "Wybierz plik kopii zapasowej", - "import": "Zaimportuj", + "import": "Import", "please_select_backup_file": "Wybierz plik kopii zapasowej i wprowadź hasło.", "fixed_rate": "Stała stawka", "fixed_rate_alert": "Będziesz mógł wprowadzić kwotę do otrzymania, gdy wybrany bedzie tryb stałego przeliczenia. Czy chcesz przejść do trybu stałej stawki?", @@ -731,6 +731,13 @@ "require_for_exchanges_to_external_wallets": "Wymagaj wymiany na portfele zewnętrzne", "camera_permission_is_required": "Wymagane jest pozwolenie na korzystanie z aparatu.\nWłącz tę funkcję w ustawieniach aplikacji.", "switchToETHWallet": "Przejdź na portfel Ethereum i spróbuj ponownie", + "importNFTs": "Importuj NFT", + "noNFTYet": "Nie ma jeszcze NFT", + "address": "Adres", + "enterTokenID": "Wprowadź identyfikator tokena", + "tokenID": "ID", + "name": "Nazwa", + "symbol": "Symbol", "seed_phrase_length": "Długość frazy początkowej", "unavailable_balance": "Niedostępne saldo", "unavailable_balance_description": "Niedostępne saldo: Suma ta obejmuje środki zablokowane w transakcjach oczekujących oraz te, które aktywnie zamroziłeś w ustawieniach kontroli monet. Zablokowane salda staną się dostępne po zakończeniu odpowiednich transakcji, natomiast zamrożone salda pozostaną niedostępne dla transakcji, dopóki nie zdecydujesz się ich odblokować.", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index c6d22ae23..8feeb732c 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -730,6 +730,13 @@ "require_for_exchanges_to_external_wallets": "Exigir trocas para carteiras externas", "camera_permission_is_required": "É necessária permissão da câmera.\nAtive-o nas configurações do aplicativo.", "switchToETHWallet": "Mude para uma carteira Ethereum e tente novamente", + "importNFTs": "Importar NFTs", + "noNFTYet": "Ainda não há NFT", + "address": "Endereço", + "enterTokenID": "Insira o ID do token", + "tokenID": "EU IA", + "name": "Nome", + "symbol": "Símbolo", "seed_phrase_length": "Comprimento da frase-semente", "unavailable_balance": "Saldo indisponível", "unavailable_balance_description": "Saldo Indisponível: Este total inclui fundos bloqueados em transações pendentes e aqueles que você congelou ativamente nas configurações de controle de moedas. Os saldos bloqueados ficarão disponíveis assim que suas respectivas transações forem concluídas, enquanto os saldos congelados permanecerão inacessíveis para transações até que você decida descongelá-los.", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 50bef718d..98eddcae7 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -731,6 +731,13 @@ "require_for_exchanges_to_external_wallets": "Требовать обмена на внешние кошельки", "camera_permission_is_required": "Требуется разрешение камеры.\nПожалуйста, включите его в настройках приложения.", "switchToETHWallet": "Пожалуйста, переключитесь на кошелек Ethereum и повторите попытку.", + "importNFTs": "Импортировать NFT", + "noNFTYet": "NFT пока нет", + "address": "Адрес", + "enterTokenID": "Введите идентификатор токена", + "tokenID": "ИДЕНТИФИКАТОР", + "name": "Имя", + "symbol": "Символ", "seed_phrase_length": "Длина исходной фразы", "unavailable_balance": "Недоступный баланс", "unavailable_balance_description": "Недоступный баланс: в эту сумму входят средства, заблокированные в ожидающих транзакциях, и средства, которые вы активно заморозили в настройках управления монетами. Заблокированные балансы станут доступны после завершения соответствующих транзакций, а замороженные балансы останутся недоступными для транзакций, пока вы не решите их разморозить.", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 95a1cd406..c9cfca47c 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -729,6 +729,13 @@ "require_for_exchanges_to_external_wallets": "จำเป็นต้องแลกเปลี่ยนกับกระเป๋าเงินภายนอก", "camera_permission_is_required": "ต้องได้รับอนุญาตจากกล้อง\nโปรดเปิดใช้งานจากการตั้งค่าแอป", "switchToETHWallet": "โปรดเปลี่ยนไปใช้กระเป๋าเงิน Ethereum แล้วลองอีกครั้ง", + "importNFTs": "นำเข้า NFT", + "noNFTYet": "ยังไม่มี NFT", + "address": "ที่อยู่", + "enterTokenID": "ป้อนรหัสโทเค็น", + "tokenID": "บัตรประจำตัวประชาชน", + "name": "ชื่อ", + "symbol": "เครื่องหมาย", "seed_phrase_length": "ความยาววลีของเมล็ด", "unavailable_balance": "ยอดคงเหลือไม่พร้อมใช้งาน", "unavailable_balance_description": "ยอดคงเหลือที่ไม่พร้อมใช้งาน: ยอดรวมนี้รวมถึงเงินทุนที่ถูกล็อคในการทำธุรกรรมที่รอดำเนินการและที่คุณได้แช่แข็งไว้ในการตั้งค่าการควบคุมเหรียญของคุณ ยอดคงเหลือที่ถูกล็อคจะพร้อมใช้งานเมื่อธุรกรรมที่เกี่ยวข้องเสร็จสมบูรณ์ ในขณะที่ยอดคงเหลือที่แช่แข็งจะไม่สามารถเข้าถึงได้สำหรับธุรกรรมจนกว่าคุณจะตัดสินใจยกเลิกการแช่แข็ง", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 960432a1b..d68dbc656 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -726,6 +726,13 @@ "require_for_exchanges_to_external_wallets": "Kinakailangan para sa mga palitan sa mga panlabas na wallet", "camera_permission_is_required": "Kinakailangan ang pahintulot sa camera.\nMangyaring paganahin ito mula sa mga setting ng app.", "switchToETHWallet": "Mangyaring lumipat sa isang Ethereum wallet at subukang muli", + "importNFTs": "Mag-import ng mga NFT", + "noNFTYet": "Wala pang NFT", + "address": "Address", + "enterTokenID": "Ilagay ang token ID", + "tokenID": "ID", + "name": "Pangalan", + "symbol": "Simbolo", "seed_phrase_length": "Haba ng parirala ng binhi", "unavailable_balance": "Hindi available na balanse", "unavailable_balance_description": "Hindi Available na Balanse: Kasama sa kabuuang ito ang mga pondong naka-lock sa mga nakabinbing transaksyon at ang mga aktibong na-freeze mo sa iyong mga setting ng kontrol ng coin. Magiging available ang mga naka-lock na balanse kapag nakumpleto na ang kani-kanilang mga transaksyon, habang ang mga nakapirming balanse ay nananatiling hindi naa-access para sa mga transaksyon hanggang sa magpasya kang i-unfreeze ang mga ito.", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 1b7982504..c0fc58feb 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -383,7 +383,7 @@ "change_backup_password_alert": "Önceki yedekleme dosyaların yeni yedek parolası ile içe aktarılamayacaktır. Yeni yedekleme parolası yalnızca yeni yedekleme dosyaları için kullanılabilir olacak. Yedekleme parolasını değiştirmek istediğinden emin misin?", "enter_backup_password": "Yedekleme parolasını buraya gir", "select_backup_file": "Yedek dosyası seç", - "import": "İçe aktar", + "import": "İçe aktarmak", "please_select_backup_file": "Lütfen yedekleme dosyasını seç ve yedekleme parolasını gir.", "fixed_rate": "Sabit oran", "fixed_rate_alert": "Sabit oran modunu işaretlersen alım tutarını girebilirsin. Sabit oran moduna geçmek ister misin?", @@ -729,6 +729,13 @@ "require_for_exchanges_to_external_wallets": "Harici cüzdanlara geçiş yapılmasını zorunlu kılın", "camera_permission_is_required": "Kamera izni gereklidir.\nLütfen uygulama ayarlarından etkinleştirin.", "switchToETHWallet": "Lütfen bir Ethereum cüzdanına geçin ve tekrar deneyin", + "importNFTs": "NFT'leri içe aktar", + "noNFTYet": "Henüz NFT yok", + "address": "Adres", + "enterTokenID": "Belirteç kimliğini girin", + "tokenID": "İD", + "name": "İsim", + "symbol": "Sembol", "seed_phrase_length": "Çekirdek cümle uzunluğu", "unavailable_balance": "Kullanılamayan bakiye", "unavailable_balance_description": "Kullanılamayan Bakiye: Bu toplam, bekleyen işlemlerde kilitlenen fonları ve jeton kontrol ayarlarınızda aktif olarak dondurduğunuz fonları içerir. Kilitli bakiyeler, ilgili işlemleri tamamlandıktan sonra kullanılabilir hale gelir; dondurulmuş bakiyeler ise siz onları dondurmaya karar verene kadar işlemler için erişilemez durumda kalır.", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 849542e6f..0ad5fa97a 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -383,7 +383,7 @@ "change_backup_password_alert": "Ваші попередні файли резервних копій будуть недоступні для імпорту з новим паролем резервної копії. Новий пароль резервної копії буде використовуватися тільки для нових файлів резервних копій. Ви впевнені, що хочете змінити пароль резервної копії?", "enter_backup_password": "Введіть пароль резервної копії", "select_backup_file": "Виберіть файл резервної копії", - "import": "Імпортувати", + "import": "Імпорт", "please_select_backup_file": "Виберіть файл резервної копії та введіть пароль резервної копії.", "fixed_rate": "Фіксована ставка", "fixed_rate_alert": "Ви зможете ввести суму отримання тоді, коли буде встановлений режим фіксованої ставки. Ви хочете перейти в режим фіксованої ставки?", @@ -731,6 +731,13 @@ "require_for_exchanges_to_external_wallets": "Потрібен для обміну на зовнішні гаманці", "camera_permission_is_required": "Потрібен дозвіл камери.\nУвімкніть його в налаштуваннях програми.", "switchToETHWallet": "Перейдіть на гаманець Ethereum і повторіть спробу", + "importNFTs": "Імпорт NFT", + "noNFTYet": "NFT ще немає", + "address": "Адреса", + "enterTokenID": "Введіть ідентифікатор токена", + "tokenID": "ID", + "name": "Ім'я", + "symbol": "символ", "seed_phrase_length": "Довжина початкової фрази", "unavailable_balance": "Недоступний баланс", "unavailable_balance_description": "Недоступний баланс: ця сума включає кошти, заблоковані в незавершених транзакціях, і ті, які ви активно заморозили в налаштуваннях контролю монет. Заблоковані баланси стануть доступними після завершення відповідних транзакцій, тоді як заморожені баланси залишаються недоступними для транзакцій, доки ви не вирішите їх розморозити.", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 0b7e4c0a7..e26c3b4d4 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -384,7 +384,7 @@ "change_backup_password_alert": "آپ کی پچھلی بیک اپ فائلیں نئے بیک اپ پاس ورڈ کے ساتھ درآمد کرنے کے لیے دستیاب نہیں ہوں گی۔ نیا بیک اپ پاس ورڈ صرف نئی بیک اپ فائلوں کے لیے استعمال کیا جائے گا۔ کیا آپ واقعی بیک اپ پاس ورڈ تبدیل کرنا چاہتے ہیں؟", "enter_backup_password": "یہاں بیک اپ پاس ورڈ درج کریں۔", "select_backup_file": "بیک اپ فائل کو منتخب کریں۔", - "import": "درآمد کریں۔", + "import": " ۔ﮟﯾﺮﮐ ﺪﻣﺁﺭﺩ", "please_select_backup_file": "براہ کرم بیک اپ فائل منتخب کریں اور بیک اپ پاس ورڈ درج کریں۔", "fixed_rate": "مقررہ شرح", "fixed_rate_alert": "فکسڈ ریٹ موڈ چیک ہونے پر آپ وصولی رقم درج کر سکیں گے۔ کیا آپ فکسڈ ریٹ موڈ پر سوئچ کرنا چاہتے ہیں؟", @@ -723,9 +723,16 @@ "require_for_exchanges_to_external_wallets": "۔ﮯﮨ ﺕﺭﻭﺮﺿ ﯽﮐ ﮯﻟﺩﺎﺒﺗ ﮟﯿﻣ ﮮﻮﭩﺑ ﯽﻧﻭﺮﯿﺑ", "camera_permission_is_required": "۔ﮯﮨ ﺭﺎﮐﺭﺩ ﺕﺯﺎﺟﺍ ﯽﮐ ﮮﺮﻤﯿﮐ", "switchToETHWallet": "۔ﮟﯾﺮﮐ ﺶﺷﻮﮐ ﮦﺭﺎﺑﻭﺩ ﺭﻭﺍ ﮟﯾﺮﮐ ﭻﺋﻮﺳ ﺮﭘ ﭧﯿﻟﺍﻭ Ethereum ﻡﺮﮐ ﮦﺍﺮﺑ", - "seed_phrase_length": " ﯽﺋﺎﺒﻤﻟ ﯽﮐ ﮯﻠﻤﺟ ﮯﮐ ﺞﯿﺑ", - "unavailable_balance": " ﺲﻨﻠﯿﺑ ﺏﺎﯿﺘﺳﺩ ﺮﯿﻏ", + "importNFTs": "NFTs ۔ﮟﯾﺮﮐ ﺪﻣﺁﺭﺩ", + "noNFTYet": "۔ﮟﯿﮨ ﮟﯿﮩﻧ NFTs ﯽﺋﻮﮐ ﮏﺗ ﯽﮭﺑﺍ", + "address": "ﮧﺘﭘ", + "enterTokenID": " ۔ﮟﯾﺮﮐ ﺝﺭﺩ ID ﻦﮐﻮﭨ", + "tokenID": "ID", + "name": "ﻡﺎﻧ", + "symbol": "ﺖﻣﻼﻋ", + "seed_phrase_length": "ﯽﺋﺎﺒﻤﻟ ﯽﮐ ﮯﻠﻤﺟ ﮯﮐ ﺞﯿﺑ", + "unavailable_balance": "ﺲﻨﻠﯿﺑ ﺏﺎﯿﺘﺳﺩ ﺮﯿﻏ", "unavailable_balance_description": "۔ﮯﺗﺮﮐ ﮟﯿﮩﻧ ﮧﻠﺼﯿﻓ ﺎﮐ ﮯﻧﺮﮐ ﺪﻤﺠﻨﻣ ﻥﺍ ﮟﯿﮩﻧﺍ ﭖﺁ ﮧﮐ ﮏﺗ ﺐﺟ ﮟﯿﮨ ﮯﺘﮨﺭ ﯽﺋﺎﺳﺭ ﻞﺑﺎﻗﺎﻧ ﮏﺗ ﺖﻗﻭ ﺱﺍ ﮯﯿﻟ ﮯﮐ ﻦﯾﺩ ﻦﯿﻟ ﺲﻨﻠﯿﺑ ﺪﻤﺠﻨﻣ ﮧﮐ ﺐﺟ ،ﮯﮔ ﮟﯿﺋﺎﺟ ﻮﮨ ﺏﺎﯿﺘﺳﺩ ﺲﻨﻠﯿﺑ ﻞﻔﻘﻣ ﺪﻌﺑ ﮯﮐ ﮯﻧﻮﮨ ﻞﻤﮑﻣ ﻦﯾﺩ ﻦﯿﻟ ﮧﻘﻠﻌﺘﻣ ﮯﮐ ﻥﺍ ۔ﮯﮨ ﺎﮭﮐﺭ ﺮ", "unspent_change": "تبدیل کریں", - "tor_connection": " ﻦﺸﮑﻨﮐ ﺭﻮﭨ" + "tor_connection": "ﻦﺸﮑﻨﮐ ﺭﻮﭨ" } diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 57962b38e..d512f5e8f 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -381,7 +381,7 @@ "change_backup_password_alert": "Ẹ kò lè fi ọ̀rọ̀ aṣínà títun ti ẹ̀dà nípamọ́ ṣí àwọn àkọsílẹ̀ nípamọ́ tẹ́lẹ̀tẹ́lẹ̀ yín. Ẹ máa fi ọ̀rọ̀ aṣínà ti ẹ̀dà nípamọ́ títun ṣí àwọn àkọsílẹ̀ nípamọ́ títun nìkan. Ṣé ó dá ẹ lójú pé ẹ fẹ́ pààrọ̀ aṣínà ti ẹ̀dà nípamọ́?", "enter_backup_password": "Tẹ̀ ọ̀rọ̀ aṣínà ti ẹ̀dà ḿbí", "select_backup_file": "Select backup file", - "import": "Gba wọlé", + "import": "gbe wọle", "please_select_backup_file": "Ẹ jọ̀wọ́ yan àkọsílẹ̀ nípamọ́ àti tẹ̀ ọ̀rọ̀ aṣínà ti ẹ̀dà.", "fixed_rate": "Iye t'á ṣẹ́ owó sí ò ní pààrọ̀", "fixed_rate_alert": "Ẹ lè tẹ̀ iye owó tó ń bọ̀ tí iye t'a ṣẹ́ owó sí bá is checked. Ṣé ẹ fẹ́ sún ipò ti iye t'á ṣẹ́ owó sí ò ní pààrọ̀ mọ́?", @@ -725,6 +725,13 @@ "require_for_exchanges_to_external_wallets": "Beere fun awọn paṣipaarọ si awọn apamọwọ ita", "camera_permission_is_required": "A nilo igbanilaaye kamẹra.\nJọwọ jeki o lati app eto.", "switchToETHWallet": "Jọwọ yipada si apamọwọ Ethereum ki o tun gbiyanju lẹẹkansi", + "importNFTs": "Gbe awọn NFT wọle", + "noNFTYet": "Ko si awọn NFT sibẹsibẹ", + "address": "Adirẹsi", + "enterTokenID": "Tẹ ID ami sii", + "tokenID": "ID", + "name": "Oruko", + "symbol": "Aami", "seed_phrase_length": "Gigun gbolohun irugbin", "unavailable_balance": "Iwontunwonsi ti ko si", "unavailable_balance_description": "Iwontunws.funfun ti ko si: Lapapọ yii pẹlu awọn owo ti o wa ni titiipa ni awọn iṣowo isunmọ ati awọn ti o ti didi ni itara ninu awọn eto iṣakoso owo rẹ. Awọn iwọntunwọnsi titiipa yoo wa ni kete ti awọn iṣowo oniwun wọn ba ti pari, lakoko ti awọn iwọntunwọnsi tio tutunini ko ni iraye si fun awọn iṣowo titi iwọ o fi pinnu lati mu wọn kuro.", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 0862f20e2..974008594 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -382,7 +382,7 @@ "change_backup_password_alert": "您以前的备份文件将无法使用新的备份密码導入。 新的备份密码将仅用于新的备份文件。 您确定要更改备份密码吗?", "enter_backup_password": "在此处输入備用密码", "select_backup_file": "选择备份文件", - "import": "导入", + "import": "进口", "please_select_backup_file": "请选择备份文件,然后输入备份密码。", "fixed_rate": "固定汇率", "fixed_rate_alert": "选中固定汇率模式后,您将可以输入接收金额。 您要切换到固定汇率模式吗?", @@ -730,6 +730,13 @@ "require_for_exchanges_to_external_wallets": "需要兑换到外部钱包", "camera_permission_is_required": "需要相机许可。\n请从应用程序设置中启用它。", "switchToETHWallet": "请切换到以太坊钱包并重试", + "importNFTs": "导入 NFT", + "noNFTYet": "还没有 NFT", + "address": "地址", + "enterTokenID": "输入令牌 ID", + "tokenID": "ID", + "name": "姓名", + "symbol": "象征", "seed_phrase_length": "种子短语长度", "unavailable_balance": "不可用余额", "unavailable_balance_description": "不可用余额:此总额包括锁定在待处理交易中的资金以及您在硬币控制设置中主动冻结的资金。一旦各自的交易完成,锁定的余额将变得可用,而冻结的余额在您决定解冻之前仍然无法进行交易。", diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index a8c6a6166..163b80135 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -36,6 +36,7 @@ class SecretKey { SecretKey('robinhoodApplicationId', () => ''), SecretKey('robinhoodCIdApiSecret', () => ''), SecretKey('walletConnectProjectId', () => ''), + SecretKey('moralisApiKey', () => '') ]; static final ethereumSecrets = [