diff --git a/lib/views/market_maker_bot/buy_coin_select_dropdown.dart b/lib/views/market_maker_bot/buy_coin_select_dropdown.dart index b02554f7bd..34d60e056b 100644 --- a/lib/views/market_maker_bot/buy_coin_select_dropdown.dart +++ b/lib/views/market_maker_bot/buy_coin_select_dropdown.dart @@ -8,6 +8,7 @@ import 'package:web_dex/model/forms/coin_trade_amount_input.dart'; import 'package:web_dex/views/market_maker_bot/coin_selection_and_amount_input.dart'; import 'package:web_dex/views/market_maker_bot/coin_trade_amount_form_field.dart'; import 'package:web_dex/views/market_maker_bot/market_maker_form_error_message_extensions.dart'; +import 'package:web_dex/shared/utils/utils.dart'; class BuyCoinSelectDropdown extends StatelessWidget { const BuyCoinSelectDropdown({ @@ -26,6 +27,29 @@ class BuyCoinSelectDropdown extends StatelessWidget { Widget build(BuildContext context) { return CoinSelectionAndAmountInput( coins: coins, + refine: (list) { + // Order: active with balance (top), active without balance (middle), inactive (bottom) + final sdk = context.sdk; + int rank(Coin c) { + final hasBalance = (c.lastKnownUsdBalance(sdk) ?? 0) > 0; + if (c.isActive && hasBalance) return 0; + if (c.isActive) return 1; + return 2; + } + final sorted = List.from(list); + sorted.sort((a, b) { + final ra = rank(a); + final rb = rank(b); + if (ra != rb) return ra - rb; + // Within rank, keep existing priority/balance sorting from upstream by + // comparing their current position heuristically via usd balance desc then abbr + final ba = a.lastKnownUsdBalance(sdk) ?? 0.0; + final bb = b.lastKnownUsdBalance(sdk) ?? 0.0; + if (ba != bb) return bb.compareTo(ba); + return a.abbr.compareTo(b.abbr); + }); + return sorted; + }, title: LocaleKeys.buy.tr(), selectedCoin: buyCoin.value, onItemSelected: onItemSelected, diff --git a/lib/views/market_maker_bot/coin_search_dropdown.dart b/lib/views/market_maker_bot/coin_search_dropdown.dart index 5c8e1a5138..13bd905a19 100644 --- a/lib/views/market_maker_bot/coin_search_dropdown.dart +++ b/lib/views/market_maker_bot/coin_search_dropdown.dart @@ -5,6 +5,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:komodo_ui/komodo_ui.dart'; import 'package:web_dex/bloc/coins_bloc/coins_repo.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:web_dex/bloc/coins_manager/coins_manager_bloc.dart'; +import 'package:web_dex/router/state/routing_state.dart'; +import 'package:web_dex/router/state/wallet_state.dart'; +import 'package:web_dex/generated/codegen_loader.g.dart'; +import 'package:web_dex/model/main_menu_value.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; import 'package:web_dex/shared/widgets/coin_item/coin_item_body.dart'; @@ -133,6 +139,10 @@ class _CoinDropdownState extends State { _overlayEntry = null; }, maxHeight: dropdownHeight, + onClose: () { + _overlayEntry?.remove(); + _overlayEntry = null; + }, ), ), ], @@ -168,11 +178,13 @@ class _SearchableDropdown extends StatefulWidget { final List> items; final ValueChanged onItemSelected; final double maxHeight; + final VoidCallback? onClose; const _SearchableDropdown({ required this.items, required this.onItemSelected, this.maxHeight = 300, + this.onClose, }); @override @@ -252,6 +264,39 @@ class _SearchableDropdownState extends State<_SearchableDropdown> { padding: const EdgeInsets.all(16), child: Text(LocaleKeys.nothingFound.tr()), ), + const Divider(height: 1), + _AddAssetsFooter(onClose: widget.onClose), + ], + ), + ), + ); + } +} + +class _AddAssetsFooter extends StatelessWidget { + const _AddAssetsFooter({this.onClose}); + final VoidCallback? onClose; + + @override + Widget build(BuildContext context) { + return InkWell( + key: const Key('add-assets-dropdown-footer'), + onTap: () { + context + .read() + .add(const CoinsManagerCoinsListReset(CoinsManagerAction.add)); + routingState.selectedMenu = MainMenuValue.wallet; + routingState.walletState.action = coinsManagerRouteAction.addAssets; + onClose?.call(); + }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.add_circle_outline), + const SizedBox(width: 8), + Text(LocaleKeys.addAssets.tr()), ], ), ), diff --git a/lib/views/market_maker_bot/coin_selection_and_amount_input.dart b/lib/views/market_maker_bot/coin_selection_and_amount_input.dart index b363b65451..aa46906ca6 100644 --- a/lib/views/market_maker_bot/coin_selection_and_amount_input.dart +++ b/lib/views/market_maker_bot/coin_selection_and_amount_input.dart @@ -24,6 +24,7 @@ class CoinSelectionAndAmountInput extends StatefulWidget { this.trailing, this.onItemSelected, this.useFrontPlate = true, + this.refine, }); final Coin? selectedCoin; @@ -32,6 +33,9 @@ class CoinSelectionAndAmountInput extends StatefulWidget { final Widget? trailing; final Function(Coin?)? onItemSelected; final bool useFrontPlate; + /// Optional refiner applied after default filtering/sorting to customize + /// ordering (e.g., activation/balance grouping for buy list). + final List Function(List)? refine; @override State createState() => @@ -63,31 +67,34 @@ class _CoinSelectionAndAmountInputState late final _sdk = context.read(); void _prepareItems() { - _items = - prepareCoinsForTable( - context, - widget.coins, - null, - testCoinsEnabled: context - .read() - .state - .testCoinsEnabled, - ) - .map( - (coin) => DropdownMenuItem( - value: coin.abbr, - child: CoinSelectItemWidget( - name: coin.displayName, - coinId: coin.abbr, - trailing: AssetBalanceText( - coin.toSdkAsset(_sdk).id, - activateIfNeeded: false, - ), - title: CoinItemBody(coin: coin, size: CoinItemSize.large), - ), + List list = prepareCoinsForTable( + context, + widget.coins, + null, + testCoinsEnabled: + context.read().state.testCoinsEnabled, + ); + + if (widget.refine != null) { + list = widget.refine!(list); + } + + _items = list + .map( + (coin) => DropdownMenuItem( + value: coin.abbr, + child: CoinSelectItemWidget( + name: coin.displayName, + coinId: coin.abbr, + trailing: AssetBalanceText( + coin.toSdkAsset(_sdk).id, + activateIfNeeded: false, ), - ) - .toList(); + title: CoinItemBody(coin: coin, size: CoinItemSize.large), + ), + ), + ) + .toList(); } @override diff --git a/lib/views/market_maker_bot/market_maker_bot_form.dart b/lib/views/market_maker_bot/market_maker_bot_form.dart index 3f27e1190c..c64c4cfa06 100644 --- a/lib/views/market_maker_bot/market_maker_bot_form.dart +++ b/lib/views/market_maker_bot/market_maker_bot_form.dart @@ -172,7 +172,7 @@ class _MakerFormMobileLayoutState extends State<_MakerFormMobileLayout> { children: [ BlocBuilder( builder: (context, state) { - final coins = state.walletCoins.values + final coins = state.coins.values .where((e) { final usdPrice = e.usdPrice?.price?.toDouble() ?? 0.0; return usdPrice > 0; diff --git a/lib/views/market_maker_bot/market_maker_bot_form_content.dart b/lib/views/market_maker_bot/market_maker_bot_form_content.dart index d3abe49c99..e8ee94d7e0 100644 --- a/lib/views/market_maker_bot/market_maker_bot_form_content.dart +++ b/lib/views/market_maker_bot/market_maker_bot_form_content.dart @@ -77,7 +77,7 @@ class _MarketMakerBotFormContentState extends State { key: const Key('$keyPrefix-sell-select'), sellCoin: state.sellCoin, sellAmount: state.sellAmount, - coins: _coinsWithUsdBalance(widget.coins), + coins: _sellableCoins(widget.coins), minimumTradeVolume: state.minimumTradeVolume, maximumTradeVolume: state.maximumTradeVolume, onItemSelected: _onSelectSellCoin, @@ -174,10 +174,13 @@ class _MarketMakerBotFormContentState extends State { ); } - List _coinsWithUsdBalance(List coins) { - return coins - .where((coin) => (coin.lastKnownUsdBalance(context.sdk) ?? 0) > 0) - .toList(); + List _sellableCoins(List coins) { + // Sell list: must be active, have balance, and have fiat price + return coins.where((coin) { + final hasUsdPrice = (coin.usdPrice?.price?.toDouble() ?? 0.0) > 0.0; + final hasBalance = (coin.lastKnownUsdBalance(context.sdk) ?? 0) > 0.0; + return coin.isActive && hasUsdPrice && hasBalance; + }).toList(); } void _onMakeOrderPressed() { @@ -208,7 +211,11 @@ class _MarketMakerBotFormContentState extends State { } List _filteredCoinsList(Coin? coin) { - return widget.coins.where((e) => e.abbr != coin?.abbr).toList(); + // Buy list: include all compatible coins with price data; exclude currently selected sell coin + return widget.coins + .where((e) => e.abbr != coin?.abbr) + .where((e) => (e.usdPrice?.price?.toDouble() ?? 0.0) > 0.0) + .toList(); } void _onTradeMarginChanged(String value) {