diff --git a/lib/bloc/nfts/nft_main_bloc.dart b/lib/bloc/nfts/nft_main_bloc.dart index 604fbb508f..caf53ca5a4 100644 --- a/lib/bloc/nfts/nft_main_bloc.dart +++ b/lib/bloc/nfts/nft_main_bloc.dart @@ -214,9 +214,12 @@ class NftMainBloc extends Bloc { ) { final Map countMap = {}; + // Only include chains that have NFTs (which means their parent coins are activated) for (final NftBlockchains chain in NftBlockchains.values) { - final count = nfts[chain]?.length ?? 0; - countMap[chain] = count; + if (nfts.containsKey(chain)) { + final count = nfts[chain]?.length ?? 0; + countMap[chain] = count; + } } final sorted = countMap.entries.toList()..sort((a, b) => b.value - a.value); diff --git a/lib/bloc/nfts/nft_main_repo.dart b/lib/bloc/nfts/nft_main_repo.dart index 50a0300d44..5b3a14ee85 100644 --- a/lib/bloc/nfts/nft_main_repo.dart +++ b/lib/bloc/nfts/nft_main_repo.dart @@ -20,10 +20,14 @@ class NftsRepo { final Mm2ApiNft _api; Future updateNft(List chains) async { - // Ensure that the parent coins for the NFT chains are activated. - await _activateParentCoins(chains); - await _api.enableNftChains(chains); - final json = await _api.updateNftList(chains); + // Filter to only chains whose parent coins are already activated + final activatedChains = await _getActivatedChains(chains); + if (activatedChains.isEmpty) { + _log.info('No NFT chains with activated parent coins'); + return; + } + await _api.enableNftChains(activatedChains); + final json = await _api.updateNftList(activatedChains); if (json['error'] != null) { _log.severe(json['error'] as String); throw ApiError(message: json['error'] as String); @@ -31,10 +35,14 @@ class NftsRepo { } Future> getNfts(List chains) async { - // Ensure that the parent coins for the NFT chains are activated. - await _activateParentCoins(chains); - await _api.enableNftChains(chains); - final json = await _api.getNftList(chains); + // Filter to only chains whose parent coins are already activated + final activatedChains = await _getActivatedChains(chains); + if (activatedChains.isEmpty) { + _log.info('No NFT chains with activated parent coins'); + return []; + } + await _api.enableNftChains(activatedChains); + final json = await _api.getNftList(activatedChains); final jsonError = json['error'] as String?; if (jsonError != null) { _log.severe(jsonError); @@ -65,34 +73,27 @@ class NftsRepo { } } - /// Ensures that the parent coins for the provided NFT chains are activated. + /// Checks if the parent coins for the provided NFT chains are activated. + /// Returns only the chains whose parent coins are already activated. /// - /// TODO: Migrate NFT functionality to the SDK. This is a temporary measure - /// during the transition period. - Future _activateParentCoins(List chains) async { + /// Note: We no longer automatically activate parent coins to avoid unnecessary + /// overhead. Users must manually enable the parent chain assets before using + /// NFT functionality for that chain. + Future> _getActivatedChains( + List chains, + ) async { final List knownCoins = _coinsRepo.getKnownCoins(); - final List parentCoins = chains - .map((NftBlockchains chain) { - return knownCoins.firstWhereOrNull( - (Coin coin) => coin.id.id == chain.coinAbbr(), - ); - }) - .whereType() - .toList(); - - if (parentCoins.isEmpty) { - return; - } - - try { - await _coinsRepo.activateCoinsSync( - parentCoins, - notify: false, - addToWalletMetadata: false, - maxRetryAttempts: 10, + final List activeCoins = await _coinsRepo.getWalletCoins(); + + return chains.where((NftBlockchains chain) { + final parentCoin = knownCoins.firstWhereOrNull( + (Coin coin) => coin.id.id == chain.coinAbbr(), ); - } catch (e, s) { - _log.shout('Failed to activate parent coins', e, s); - } + + if (parentCoin == null) return false; + + // Check if the parent coin is already activated + return activeCoins.any((coin) => coin.id.id == parentCoin.id.id); + }).toList(); } } diff --git a/lib/views/nfts/common/widgets/nft_no_chains_enabled.dart b/lib/views/nfts/common/widgets/nft_no_chains_enabled.dart new file mode 100644 index 0000000000..cf28546d76 --- /dev/null +++ b/lib/views/nfts/common/widgets/nft_no_chains_enabled.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:web_dex/views/nfts/common/widgets/nft_no_login.dart'; + +class NftNoChainsEnabled extends StatelessWidget { + const NftNoChainsEnabled({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 300), + child: const NftNoLogin( + text: 'Please enable NFT protocol assets in the wallet. Enable chains like ETH, BNB, AVAX, MATIC, or FTM to view your NFTs.', + ), + ), + ], + ); + } +} diff --git a/lib/views/nfts/nft_main/nft_main.dart b/lib/views/nfts/nft_main/nft_main.dart index 456b3c7738..8bb64a287d 100644 --- a/lib/views/nfts/nft_main/nft_main.dart +++ b/lib/views/nfts/nft_main/nft_main.dart @@ -10,6 +10,7 @@ import 'package:web_dex/mm2/mm2_api/rpc/base.dart'; import 'package:web_dex/model/authorize_mode.dart'; import 'package:web_dex/model/nft.dart'; import 'package:web_dex/views/nfts/common/widgets/nft_connect_wallet.dart'; +import 'package:web_dex/views/nfts/common/widgets/nft_no_chains_enabled.dart'; import 'package:web_dex/views/nfts/nft_list/nft_list.dart'; import 'package:web_dex/views/nfts/nft_main/nft_main_controls.dart'; import 'package:web_dex/views/nfts/nft_main/nft_main_failure.dart'; @@ -25,10 +26,11 @@ class NftMain extends StatelessWidget { (bloc) => bloc.state.mode == AuthorizeMode.logIn); final bool isInitial = context.select((bloc) => !bloc.state.isInitialized); - - final bool hasLoaded = context.select( + final bool hasEnabledChains = context.select( (bloc) => bloc.state.sortedChains.isNotEmpty); - if (isLoggedIn && (isInitial || !hasLoaded)) { + + // Only show loading screen if we're still initializing AND there are chains to load + if (isLoggedIn && isInitial && hasEnabledChains) { return const NftMainLoading(); } final ColorSchemeExtension colorScheme = @@ -37,6 +39,7 @@ class NftMain extends StatelessWidget { final List tabs = context.select>( (bloc) => bloc.state.sortedChains); + return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -51,20 +54,21 @@ class NftMain extends StatelessWidget { style: textTheme.bodyMBold.copyWith(color: colorScheme.secondary), ), ), - Container( - width: double.infinity, - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: colorScheme.surfContHighest, + if (hasEnabledChains) + Container( + width: double.infinity, + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: colorScheme.surfContHighest, + ), ), ), + child: NftTabs(tabs: tabs), ), - child: NftTabs(tabs: tabs), - ), - const SizedBox(height: 20), - const NftMainControls(), const SizedBox(height: 20), + if (hasEnabledChains) const NftMainControls(), + if (hasEnabledChains) const SizedBox(height: 20), Flexible( child: Builder(builder: (context) { final mode = context @@ -79,6 +83,17 @@ class NftMain extends StatelessWidget { ); } + // Show placeholder if no NFT chains are enabled + if (!hasEnabledChains) { + return isMobile + ? const Center(child: NftNoChainsEnabled()) + : const Row( + mainAxisAlignment: MainAxisAlignment.center, + key: Key('msg-no-chains-enabled'), + children: [NftNoChainsEnabled()], + ); + } + final BaseError? error = context .select((bloc) => bloc.state.error); if (error != null) { diff --git a/lib/views/wallet/coin_details/coin_details.dart b/lib/views/wallet/coin_details/coin_details.dart index 11dc0e9095..2a25491541 100644 --- a/lib/views/wallet/coin_details/coin_details.dart +++ b/lib/views/wallet/coin_details/coin_details.dart @@ -65,7 +65,18 @@ class _CoinDetailsState extends State { Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { - return _buildContent(); + return GestureDetector( + onHorizontalDragEnd: (details) { + // Detect swipe-back gesture (swipe from left to right) + if (details.primaryVelocity != null && details.primaryVelocity! > 0) { + // Only trigger back navigation if we're on the info page + if (_selectedPageType == CoinPageType.info) { + widget.onBackButtonPressed(); + } + } + }, + child: _buildContent(), + ); }, ); }