Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions lib/bloc/nfts/nft_main_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,12 @@ class NftMainBloc extends Bloc<NftMainEvent, NftMainState> {
) {
final Map<NftBlockchains, int> 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);
Expand Down
69 changes: 35 additions & 34 deletions lib/bloc/nfts/nft_main_repo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,29 @@ class NftsRepo {
final Mm2ApiNft _api;

Future<void> updateNft(List<NftBlockchains> 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);
}
}

Future<List<NftToken>> getNfts(List<NftBlockchains> 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);
Expand Down Expand Up @@ -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<void> _activateParentCoins(List<NftBlockchains> 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<List<NftBlockchains>> _getActivatedChains(
List<NftBlockchains> chains,
) async {
final List<Coin> knownCoins = _coinsRepo.getKnownCoins();
final List<Coin> parentCoins = chains
.map((NftBlockchains chain) {
return knownCoins.firstWhereOrNull(
(Coin coin) => coin.id.id == chain.coinAbbr(),
);
})
.whereType<Coin>()
.toList();

if (parentCoins.isEmpty) {
return;
}

try {
await _coinsRepo.activateCoinsSync(
parentCoins,
notify: false,
addToWalletMetadata: false,
maxRetryAttempts: 10,
final List<Coin> 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();
}
}
21 changes: 21 additions & 0 deletions lib/views/nfts/common/widgets/nft_no_chains_enabled.dart
Original file line number Diff line number Diff line change
@@ -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.',
),
),
],
);
}
}
41 changes: 28 additions & 13 deletions lib/views/nfts/nft_main/nft_main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -25,10 +26,11 @@ class NftMain extends StatelessWidget {
(bloc) => bloc.state.mode == AuthorizeMode.logIn);
final bool isInitial =
context.select<NftMainBloc, bool>((bloc) => !bloc.state.isInitialized);

final bool hasLoaded = context.select<NftMainBloc, bool>(
final bool hasEnabledChains = context.select<NftMainBloc, bool>(
(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 =
Expand All @@ -37,6 +39,7 @@ class NftMain extends StatelessWidget {
final List<NftBlockchains> tabs =
context.select<NftMainBloc, List<NftBlockchains>>(
(bloc) => bloc.state.sortedChains);

return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
Expand All @@ -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
Expand All @@ -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<NftMainBloc, BaseError?>((bloc) => bloc.state.error);
if (error != null) {
Expand Down
13 changes: 12 additions & 1 deletion lib/views/wallet/coin_details/coin_details.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,18 @@ class _CoinDetailsState extends State<CoinDetails> {
Widget build(BuildContext context) {
return BlocBuilder<CoinsBloc, CoinsState>(
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(),
);
},
);
}
Expand Down
Loading