diff --git a/lib/pages/exchange_view/exchange_coin_selection/exchange_currency_selection_view.dart b/lib/pages/exchange_view/exchange_coin_selection/exchange_currency_selection_view.dart index 1b2376b4c..c0d027ff4 100644 --- a/lib/pages/exchange_view/exchange_coin_selection/exchange_currency_selection_view.dart +++ b/lib/pages/exchange_view/exchange_coin_selection/exchange_currency_selection_view.dart @@ -111,26 +111,28 @@ class _ExchangeCurrencySelectionViewState return currencies; } - await showDialog<void>( - context: context, - builder: (context) => StackDialog( - title: "ChangeNOW Error", - message: "Failed to load currency data: ${cn.exception}", - leftButton: SecondaryButton( - label: "Ok", - onPressed: Navigator.of(context, rootNavigator: isDesktop).pop, + if (mounted) { + await showDialog<void>( + context: context, + builder: (context) => StackDialog( + title: "ChangeNOW Error", + message: "Failed to load currency data: ${cn.exception}", + leftButton: SecondaryButton( + label: "Ok", + onPressed: Navigator.of(context, rootNavigator: isDesktop).pop, + ), + rightButton: PrimaryButton( + label: "Retry", + onPressed: () async { + Navigator.of(context, rootNavigator: isDesktop).pop(); + _currencies = await _showUpdatingCurrencies( + whileFuture: _loadCurrencies()); + setState(() {}); + }, + ), ), - rightButton: PrimaryButton( - label: "Retry", - onPressed: () async { - Navigator.of(context, rootNavigator: isDesktop).pop(); - _currencies = - await _showUpdatingCurrencies(whileFuture: _loadCurrencies()); - setState(() {}); - }, - ), - ), - ); + ); + } } else { currencies.addAll(cn.value!); } @@ -180,13 +182,13 @@ class _ExchangeCurrencySelectionViewState .where((e) => e.name.toLowerCase().contains(text.toLowerCase()) || e.ticker.toLowerCase().contains(text.toLowerCase())) - .toList(growable: false); + .toList(); } else { if (text.isEmpty) { return _currencies .where((e) => e.ticker.toLowerCase() != widget.pairedTicker!.toLowerCase()) - .toList(growable: false); + .toList(); } return _currencies @@ -194,7 +196,7 @@ class _ExchangeCurrencySelectionViewState e.ticker.toLowerCase() != widget.pairedTicker!.toLowerCase() && (e.name.toLowerCase().contains(text.toLowerCase()) || e.ticker.toLowerCase().contains(text.toLowerCase()))) - .toList(growable: false); + .toList(); } } @@ -328,181 +330,111 @@ class _ExchangeCurrencySelectionViewState height: 12, ), Flexible( - child: Builder(builder: (context) { - final coins = Coin.values.where((e) => - e.ticker.toLowerCase() != widget.pairedTicker?.toLowerCase()); + child: Builder( + builder: (context) { + final coins = Coin.values.where((e) => + e.ticker.toLowerCase() != + widget.pairedTicker?.toLowerCase()); - final items = filter(_searchString) - .where((e) => coins - .where((coin) => - coin.ticker.toLowerCase() == e.ticker.toLowerCase()) - .isNotEmpty) - .toList(growable: false); - items.sort((a, b) => a.name.compareTo(b.name)); + final items = filter(_searchString); - return RoundedWhiteContainer( - padding: const EdgeInsets.all(0), - child: ListView.builder( - shrinkWrap: true, - primary: isDesktop ? false : null, - itemCount: items.length, - itemBuilder: (builderContext, index) { - final bool hasImageUrl = - items[index].image.startsWith("http"); - return Padding( - padding: const EdgeInsets.symmetric(vertical: 4), - child: GestureDetector( - onTap: () { - Navigator.of(context).pop(items[index]); - }, - child: RoundedWhiteContainer( - child: Row( - children: [ - SizedBox( - width: 24, - height: 24, - child: isStackCoin(items[index].ticker) - ? getIconForTicker( - items[index].ticker, - size: 24, - ) - : hasImageUrl - ? SvgPicture.network( - items[index].image, - width: 24, - height: 24, - placeholderBuilder: (_) => - const LoadingIndicator(), - ) - : const SizedBox( - width: 24, - height: 24, - ), - ), - const SizedBox( - width: 10, - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - items[index].name, - style: STextStyles.largeMedium14(context), - ), - const SizedBox( - height: 2, - ), - Text( - items[index].ticker.toUpperCase(), - style: STextStyles.smallMed12(context) - .copyWith( - color: Theme.of(context) - .extension<StackColors>()! - .textSubtitle1, - ), - ), - ], + final walletCoins = items + .where((currency) => coins + .where((coin) => + coin.ticker.toLowerCase() == + currency.ticker.toLowerCase()) + .isNotEmpty) + .toList(); + + // sort alphabetically by name + items.sort((a, b) => a.name.compareTo(b.name)); + + // reverse sort walletCoins to prepare for next step + walletCoins.sort((a, b) => b.name.compareTo(a.name)); + + // insert wallet coins at beginning + for (final c in walletCoins) { + items.remove(c); + items.insert(0, c); + } + + return RoundedWhiteContainer( + padding: const EdgeInsets.all(0), + child: ListView.builder( + shrinkWrap: true, + primary: isDesktop ? false : null, + itemCount: items.length, + itemBuilder: (builderContext, index) { + final bool hasImageUrl = + items[index].image.startsWith("http"); + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: GestureDetector( + onTap: () { + Navigator.of(context).pop(items[index]); + }, + child: RoundedWhiteContainer( + child: Row( + children: [ + SizedBox( + width: 24, + height: 24, + child: isStackCoin(items[index].ticker) + ? getIconForTicker( + items[index].ticker, + size: 24, + ) + : hasImageUrl + ? SvgPicture.network( + items[index].image, + width: 24, + height: 24, + placeholderBuilder: (_) => + const LoadingIndicator(), + ) + : const SizedBox( + width: 24, + height: 24, + ), ), - ), - ], + const SizedBox( + width: 10, + ), + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + items[index].name, + style: + STextStyles.largeMedium14(context), + ), + const SizedBox( + height: 2, + ), + Text( + items[index].ticker.toUpperCase(), + style: STextStyles.smallMed12(context) + .copyWith( + color: Theme.of(context) + .extension<StackColors>()! + .textSubtitle1, + ), + ), + ], + ), + ), + ], + ), ), ), - ), - ); - }, - ), - ); - }), - ), - const SizedBox( - height: 20, - ), - Text( - "All coins", - style: STextStyles.smallMed12(context), - ), - const SizedBox( - height: 12, - ), - Flexible( - child: Builder(builder: (context) { - final filtered = filter(_searchString); - filtered.sort((a, b) => a.name.compareTo(b.name)); - return RoundedWhiteContainer( - padding: const EdgeInsets.all(0), - child: ListView.builder( - shrinkWrap: true, - primary: isDesktop ? false : null, - itemCount: filtered.length, - itemBuilder: (builderContext, index) { - final bool hasImageUrl = - filtered[index].image.startsWith("http"); - return Padding( - padding: const EdgeInsets.symmetric(vertical: 4), - child: GestureDetector( - onTap: () { - Navigator.of(context).pop(filtered[index]); - }, - child: RoundedWhiteContainer( - child: Row( - children: [ - SizedBox( - width: 24, - height: 24, - child: isStackCoin(filtered[index].ticker) - ? getIconForTicker( - filtered[index].ticker, - size: 24, - ) - : hasImageUrl - ? SvgPicture.network( - filtered[index].image, - width: 24, - height: 24, - placeholderBuilder: (_) => - const LoadingIndicator(), - ) - : const SizedBox( - width: 24, - height: 24, - ), - ), - const SizedBox( - width: 10, - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - filtered[index].name, - style: STextStyles.largeMedium14(context), - ), - const SizedBox( - height: 2, - ), - Text( - filtered[index].ticker.toUpperCase(), - style: STextStyles.smallMed12(context) - .copyWith( - color: Theme.of(context) - .extension<StackColors>()! - .textSubtitle1, - ), - ), - ], - ), - ), - ], - ), - ), - ), - ); - }, - ), - ); - }), + ); + }, + ), + ); + }, + ), ), ], ), diff --git a/lib/pages/exchange_view/exchange_form.dart b/lib/pages/exchange_view/exchange_form.dart index 76bba50ad..3d4dcfcde 100644 --- a/lib/pages/exchange_view/exchange_form.dart +++ b/lib/pages/exchange_view/exchange_form.dart @@ -149,6 +149,8 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> { currency.ticker, caseSensitive: false, ) + .and() + .tokenContractEqualTo(currency.tokenContract) .findAll(); final items = [Tuple2(currency.exchangeName, currency)]; @@ -631,6 +633,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> { .getAggregateCurrency( widget.contract == null ? coin!.ticker : widget.contract!.symbol, ExchangeRateType.estimated, + widget.contract == null ? null : widget.contract!.address, ) .then((value) { if (value != null) { diff --git a/lib/pages/receive_view/receive_view.dart b/lib/pages/receive_view/receive_view.dart index 94cec7c14..87587aa46 100644 --- a/lib/pages/receive_view/receive_view.dart +++ b/lib/pages/receive_view/receive_view.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:qr_flutter/qr_flutter.dart'; +import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/pages/receive_view/addresses/wallet_addresses_view.dart'; import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart'; @@ -25,15 +26,15 @@ import 'package:stackwallet/widgets/rounded_white_container.dart'; class ReceiveView extends ConsumerStatefulWidget { const ReceiveView({ Key? key, - required this.coin, required this.walletId, + this.tokenContract, this.clipboard = const ClipboardWrapper(), }) : super(key: key); static const String routeName = "/receiveView"; - final Coin coin; final String walletId; + final EthContract? tokenContract; final ClipboardInterface clipboard; @override @@ -86,7 +87,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> { @override void initState() { walletId = widget.walletId; - coin = widget.coin; + coin = ref.read(walletsChangeNotifierProvider).getManager(walletId).coin; clipboard = widget.clipboard; WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { @@ -117,6 +118,8 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> { } }); + final ticker = widget.tokenContract?.symbol ?? coin.ticker; + return Background( child: Scaffold( backgroundColor: Theme.of(context).extension<StackColors>()!.background, @@ -127,7 +130,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> { }, ), title: Text( - "Receive ${coin.ticker}", + "Receive $ticker", style: STextStyles.navBarTitle(context), ), actions: [ @@ -245,7 +248,7 @@ class _ReceiveViewState extends ConsumerState<ReceiveView> { Row( children: [ Text( - "Your ${coin.ticker} address", + "Your $ticker address", style: STextStyles.itemSubtitle(context), ), const Spacer(), diff --git a/lib/pages/token_view/sub_widgets/token_summary.dart b/lib/pages/token_view/sub_widgets/token_summary.dart index d9c62ae66..30b60a885 100644 --- a/lib/pages/token_view/sub_widgets/token_summary.dart +++ b/lib/pages/token_view/sub_widgets/token_summary.dart @@ -209,7 +209,7 @@ class TokenWalletOptions extends StatelessWidget { ReceiveView.routeName, arguments: Tuple2( walletId, - Coin.ethereum, + tokenContract, ), ); }, diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 82adc3b6a..941acd719 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -800,10 +800,7 @@ class _WalletViewState extends ConsumerState<WalletView> { unawaited( Navigator.of(context).pushNamed( ReceiveView.routeName, - arguments: Tuple2( - walletId, - coin, - ), + arguments: walletId, ), ); } diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 4031e425e..340b7803d 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -1019,12 +1019,22 @@ class RouteGenerator { return _routeError("${settings.name} invalid args: ${args.toString()}"); case ReceiveView.routeName: - if (args is Tuple2<String, Coin>) { + if (args is String) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => ReceiveView( + walletId: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } else if (args is Tuple2<String, EthContract?>) { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, builder: (_) => ReceiveView( walletId: args.item1, - coin: args.item2, + tokenContract: args.item2, ), settings: RouteSettings( name: settings.name, diff --git a/lib/services/exchange/exchange_data_loading_service.dart b/lib/services/exchange/exchange_data_loading_service.dart index 8e73e46a9..4eed07504 100644 --- a/lib/services/exchange/exchange_data_loading_service.dart +++ b/lib/services/exchange/exchange_data_loading_service.dart @@ -61,10 +61,12 @@ class ExchangeDataLoadingService { final sendCurrency = await getAggregateCurrency( "BTC", state.exchangeRateType, + null, ); final receiveCurrency = await getAggregateCurrency( "XMR", state.exchangeRateType, + null, ); state.setCurrencies(sendCurrency, receiveCurrency); } @@ -72,7 +74,10 @@ class ExchangeDataLoadingService { } Future<AggregateCurrency?> getAggregateCurrency( - String ticker, ExchangeRateType rateType) async { + String ticker, + ExchangeRateType rateType, + String? contract, + ) async { final currencies = await ExchangeDataLoadingService.instance.isar.currencies .filter() .group((q) => rateType == ExchangeRateType.fixed @@ -89,6 +94,8 @@ class ExchangeDataLoadingService { ticker, caseSensitive: false, ) + .and() + .tokenContractEqualTo(contract) .findAll(); final items = currencies