mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-21 06:18:42 +02:00
Merge branch 'main' into CW-529-Modify-2FA-introduction-screens
This commit is contained in:
+11
-9
@@ -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';
|
||||
@@ -344,7 +345,7 @@ Future<void> setup({
|
||||
getIt.get<SharedPreferences>(),
|
||||
getIt.get<KeyService>(),
|
||||
(WalletType type) => getIt.get<WalletService>(param1: type)));
|
||||
|
||||
|
||||
getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) =>
|
||||
WalletNewVM(getIt.get<AppStore>(),
|
||||
getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
|
||||
@@ -486,6 +487,7 @@ Future<void> setup({
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => BalancePage(
|
||||
nftViewModel: getIt.get<NFTViewModel>(),
|
||||
dashboardViewModel: getIt.get<DashboardViewModel>(),
|
||||
settingsStore: getIt.get<SettingsStore>()));
|
||||
|
||||
@@ -999,11 +1001,10 @@ Future<void> setup({
|
||||
|
||||
getIt.registerFactory(() => YatService());
|
||||
|
||||
getIt.registerFactory(() =>
|
||||
AddressResolver(
|
||||
yatService: getIt.get<YatService>(),
|
||||
wallet: getIt.get<AppStore>().wallet!,
|
||||
settingsStore: getIt.get<SettingsStore>()));
|
||||
getIt.registerFactory(() => AddressResolver(
|
||||
yatService: getIt.get<YatService>(),
|
||||
wallet: getIt.get<AppStore>().wallet!,
|
||||
settingsStore: getIt.get<SettingsStore>()));
|
||||
|
||||
getIt.registerFactoryParam<FullscreenQRPage, QrViewData, void>(
|
||||
(QrViewData viewData, _) => FullscreenQRPage(qrViewData: viewData));
|
||||
@@ -1176,6 +1177,7 @@ Future<void> setup({
|
||||
getIt.registerFactory(
|
||||
() => WalletConnectConnectionsView(web3walletService: getIt.get<Web3WalletService>()));
|
||||
|
||||
getIt.registerFactory(() => NFTViewModel(appStore, getIt.get<BottomSheetService>()));
|
||||
getIt.registerFactory<TorPage>(() => TorPage(getIt.get<AppStore>()));
|
||||
|
||||
_isSetupFinished = true;
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
class WalletNFTsResponseModel {
|
||||
final int? page;
|
||||
final int? pageSize;
|
||||
|
||||
final List<NFTAssetModel>? result;
|
||||
final String? status;
|
||||
|
||||
WalletNFTsResponseModel({this.page, this.pageSize, this.result, this.status});
|
||||
|
||||
factory WalletNFTsResponseModel.fromJson(Map<String, dynamic> 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<String, dynamic>))
|
||||
.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<String, dynamic> 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<String, dynamic>)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NormalizedMetadata {
|
||||
final String? name;
|
||||
final String? description;
|
||||
final String? image;
|
||||
NormalizedMetadata({
|
||||
this.name,
|
||||
this.description,
|
||||
this.image,
|
||||
});
|
||||
|
||||
factory NormalizedMetadata.fromJson(Map<String, dynamic> 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;
|
||||
}
|
||||
}
|
||||
+23
-2
@@ -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<dynamic> createRoute(RouteSettings settings) {
|
||||
@@ -614,6 +619,22 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||
web3walletService: getIt.get<Web3WalletService>(),
|
||||
launchUri: settings.arguments as Uri?,
|
||||
));
|
||||
|
||||
case Routes.nftDetailsPage:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => NFTDetailsPage(
|
||||
nftAsset: settings.arguments as NFTAssetModel,
|
||||
dashboardViewModel: getIt.get<DashboardViewModel>(),
|
||||
),
|
||||
);
|
||||
|
||||
case Routes.importNFTPage:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => ImportNFTPage(
|
||||
nftViewModel: settings.arguments as NFTViewModel,
|
||||
),
|
||||
);
|
||||
|
||||
case Routes.torPage:
|
||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<TorPage>());
|
||||
|
||||
|
||||
+6
-2
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
+23
-25
@@ -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<void>(
|
||||
context: context,
|
||||
builder: (_) => getIt.get<MoneroAccountListPage>())
|
||||
context: context, builder: (_) => getIt.get<MoneroAccountListPage>())
|
||||
: Navigator.of(context).pushNamed(Routes.receive),
|
||||
child: Container(
|
||||
height: 50,
|
||||
@@ -184,26 +180,27 @@ class AddressPage extends BasePage {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
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<SyncIndicatorTheme>()!
|
||||
.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<SyncIndicatorTheme>()!
|
||||
.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(
|
||||
+115
-27
@@ -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<DashboardPageTheme>()!.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<void>(
|
||||
// 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<BalancePageTheme>()!
|
||||
.labelTextColor),
|
||||
color:
|
||||
Theme.of(context).extension<BalancePageTheme>()!.labelTextColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -337,8 +427,6 @@ class BalancePage extends StatelessWidget {
|
||||
}
|
||||
|
||||
void _showBalanceDescription(BuildContext context, String content) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (_) => InformationPage(information: content));
|
||||
showPopUp<void>(context: context, builder: (_) => InformationPage(information: content));
|
||||
}
|
||||
}
|
||||
@@ -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<DashboardPageTheme>()!.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<BalancePageTheme>()!
|
||||
.cardBorderColor,
|
||||
width: 1,
|
||||
),
|
||||
color: Theme.of(context)
|
||||
.extension<SyncIndicatorTheme>()!
|
||||
.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<BalancePageTheme>()!
|
||||
.cardBorderColor,
|
||||
width: 1,
|
||||
),
|
||||
color: Theme.of(context)
|
||||
.extension<SyncIndicatorTheme>()!
|
||||
.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<BalancePageTheme>()!
|
||||
.labelTextColor,
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Text(
|
||||
infoValue,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Lato',
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.extension<BalancePageTheme>()!
|
||||
.assetTitleColor,
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<ImportNFTPage> createState() => _ImportNFTPageState();
|
||||
}
|
||||
|
||||
class _ImportNFTPageState extends State<ImportNFTPage> {
|
||||
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<SeedWidgetTheme>()!.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<CakeTextTheme>()!
|
||||
.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<SeedWidgetTheme>()!.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<CakeTextTheme>()!
|
||||
.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);
|
||||
},
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<SyncIndicatorTheme>()!
|
||||
.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<DashboardPageTheme>()!.textColor,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Theme.of(context)
|
||||
.extension<ExchangePageTheme>()!
|
||||
.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<DashboardPageTheme>()!
|
||||
.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);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
+25
-33
@@ -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,
|
||||
));
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<BalancePageTheme>()!
|
||||
.cardBorderColor,
|
||||
width: 1,
|
||||
),
|
||||
color: Theme.of(context)
|
||||
.extension<SyncIndicatorTheme>()!
|
||||
.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<BalancePageTheme>()!
|
||||
.cardBorderColor,
|
||||
width: 1,
|
||||
),
|
||||
color: Theme.of(context)
|
||||
.extension<SyncIndicatorTheme>()!
|
||||
.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<BalancePageTheme>()!
|
||||
.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<BalancePageTheme>()!
|
||||
.assetTitleColor,
|
||||
height: 1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,13 +67,16 @@ class AddressTextField extends StatelessWidget {
|
||||
enabled: isActive,
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
|
||||
style: textStyle ??
|
||||
TextStyle(
|
||||
fontSize: 16, color: Theme.of(context).extension<CakeTextTheme>()!.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
|
||||
|
||||
@@ -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<void> _addDeviceInfo(File file) async {
|
||||
|
||||
@@ -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<NFTAssetModel> nftAssetByWalletModels;
|
||||
|
||||
@action
|
||||
Future<void> 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<String, dynamic>;
|
||||
|
||||
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<void> 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<String, dynamic>;
|
||||
|
||||
final nftAsset = NFTAssetModel.fromJson(decodedResponse);
|
||||
|
||||
nftAssetByWalletModels.add(nftAsset);
|
||||
|
||||
isImportNFTLoading = false;
|
||||
} catch (e) {
|
||||
isImportNFTLoading = false;
|
||||
bottomSheetService.queueBottomSheet(
|
||||
isModalDismissible: true,
|
||||
widget: BottomSheetMessageDisplayWidget(
|
||||
message: e.toString(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user