diff --git a/lib/main.dart b/lib/main.dart index d103a0bab8..df6533b8d5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -59,7 +59,14 @@ Future main() async { // is the only/primary API/repository for KDF final KomodoDefiSdk komodoDefiSdk = await mm2.initialize(); final mm2Api = Mm2Api(mm2: mm2, sdk: komodoDefiSdk); - await AppBootstrapper.instance.ensureInitialized(komodoDefiSdk, mm2Api); + // Sparkline is dependent on Hive initialization, so we pass it on to the + // bootstrapper here + final sparklineRepository = SparklineRepository.defaultInstance(); + await AppBootstrapper.instance.ensureInitialized( + komodoDefiSdk, + mm2Api, + sparklineRepository, + ); final coinsRepo = CoinsRepo(kdfSdk: komodoDefiSdk, mm2: mm2); final walletsRepository = WalletsRepository( @@ -77,13 +84,11 @@ Future main() async { path: '$assetsPath/translations', child: MultiRepositoryProvider( providers: [ - RepositoryProvider(create: (_) => komodoDefiSdk), - RepositoryProvider(create: (_) => mm2Api), - RepositoryProvider(create: (_) => coinsRepo), - RepositoryProvider(create: (_) => walletsRepository), - // TODO: Refactor in SDK to avoid use of this global variable. - // This is necessary for now for CoinSparkline. - RepositoryProvider(create: (_) => sparklineRepository), + RepositoryProvider.value(value: komodoDefiSdk), + RepositoryProvider.value(value: mm2Api), + RepositoryProvider.value(value: coinsRepo), + RepositoryProvider.value(value: walletsRepository), + RepositoryProvider.value(value: sparklineRepository), ], child: const MyApp(), ), diff --git a/lib/services/initializer/app_bootstrapper.dart b/lib/services/initializer/app_bootstrapper.dart index 1d79aa6c27..b1a5e13453 100644 --- a/lib/services/initializer/app_bootstrapper.dart +++ b/lib/services/initializer/app_bootstrapper.dart @@ -9,7 +9,11 @@ final class AppBootstrapper { bool _isInitialized = false; - Future ensureInitialized(KomodoDefiSdk kdfSdk, Mm2Api mm2Api) async { + Future ensureInitialized( + KomodoDefiSdk kdfSdk, + Mm2Api mm2Api, + SparklineRepository sparklineRepository, + ) async { if (_isInitialized) return; // Register core services with GetIt @@ -22,8 +26,10 @@ final class AppBootstrapper { log('AppBootstrapper: Log initialized in ${timer.elapsedMilliseconds}ms'); timer.reset(); - await _warmUpInitializers().awaitAll(); - log('AppBootstrapper: Warm-up initializers completed in ${timer.elapsedMilliseconds}ms'); + await _warmUpInitializers(sparklineRepository).awaitAll(); + log( + 'AppBootstrapper: Warm-up initializers completed in ${timer.elapsedMilliseconds}ms', + ); timer.stop(); _isInitialized = true; @@ -38,7 +44,9 @@ final class AppBootstrapper { /// A list of futures that should be completed before the app starts /// ([runApp]) which do not depend on each other. - List> _warmUpInitializers() { + List> _warmUpInitializers( + SparklineRepository sparklineRepository, + ) { return [ app_bloc_root.loadLibrary(), packageInformation.init(), @@ -46,9 +54,10 @@ final class AppBootstrapper { CexMarketData.ensureInitialized(), PlatformTuner.setWindowTitleAndSize(), _initializeSettings(), - _initHive(isWeb: kIsWeb || kIsWasm, appFolder: appFolder).then( - (_) => sparklineRepository.init(), - ), + _initHive( + isWeb: kIsWeb || kIsWasm, + appFolder: appFolder, + ).then((_) => sparklineRepository.init()), ]; } diff --git a/lib/views/common/header/actions/header_actions.dart b/lib/views/common/header/actions/header_actions.dart index 11edff5779..2f69965e39 100644 --- a/lib/views/common/header/actions/header_actions.dart +++ b/lib/views/common/header/actions/header_actions.dart @@ -6,7 +6,6 @@ import 'package:komodo_ui_kit/komodo_ui_kit.dart'; import 'package:web_dex/app_config/app_config.dart'; import 'package:web_dex/bloc/coins_bloc/asset_coin_extension.dart'; import 'package:web_dex/bloc/coins_bloc/coins_bloc.dart'; -import 'package:web_dex/bloc/cex_market_data/portfolio_growth/portfolio_growth_bloc.dart'; import 'package:web_dex/common/screen.dart'; import 'package:web_dex/generated/codegen_loader.g.dart'; import 'package:web_dex/model/coin.dart'; @@ -19,9 +18,7 @@ const EdgeInsets headerActionsPadding = EdgeInsets.fromLTRB(38, 18, 0, 0); final _languageCodes = localeList.map((e) => e.languageCode).toList(); final _langCode2flags = { for (var loc in _languageCodes) - loc: SvgPicture.asset( - '$assetsPath/flags/$loc.svg', - ), + loc: SvgPicture.asset('$assetsPath/flags/$loc.svg'), }; List? getHeaderActions(BuildContext context) { return [ @@ -36,14 +33,12 @@ List? getHeaderActions(BuildContext context) { ), Padding( padding: headerActionsPadding, - child: BlocBuilder( - builder: (context, pgState) { - final coins = context.select>( - (bloc) => bloc.state.walletCoins.values, + child: BlocBuilder( + builder: (context, state) { + final totalBalance = _getTotalBalance( + state.walletCoins.values, + context, ); - final totalBalance = pgState is PortfolioGrowthChartLoadSuccess - ? pgState.totalBalance - : _getTotalBalance(coins, context); return ActionTextButton( text: LocaleKeys.balance.tr(), @@ -53,17 +48,16 @@ List? getHeaderActions(BuildContext context) { }, ), ), - const Padding( - padding: headerActionsPadding, - child: AccountSwitcher(), - ), + const Padding(padding: headerActionsPadding, child: AccountSwitcher()), if (!isWideScreen) const SizedBox(width: mainLayoutPadding), ]; } double _getTotalBalance(Iterable coins, BuildContext context) { - double total = - coins.fold(0, (prev, coin) => prev + (coin.usdBalance(context.sdk) ?? 0)); + double total = coins.fold( + 0, + (prev, coin) => prev + (coin.usdBalance(context.sdk) ?? 0), + ); if (total > 0.01) { return total; diff --git a/lib/views/fiat/fiat_inputs.dart b/lib/views/fiat/fiat_inputs.dart index 2d4f0c38b5..8ed83e5f4b 100644 --- a/lib/views/fiat/fiat_inputs.dart +++ b/lib/views/fiat/fiat_inputs.dart @@ -1,10 +1,11 @@ +import 'package:decimal/decimal.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:komodo_defi_sdk/komodo_defi_sdk.dart' show KomodoDefiSdk; import 'package:komodo_defi_types/komodo_defi_types.dart' show PubkeyInfo, AssetPubkeys; -import 'package:komodo_ui/komodo_ui.dart'; +import 'package:komodo_ui/komodo_ui.dart' show Debouncer, SourceAddressField; import 'package:komodo_ui_kit/komodo_ui_kit.dart'; import 'package:web_dex/bloc/fiat/models/fiat_price_info.dart'; import 'package:web_dex/bloc/fiat/models/i_currency.dart'; @@ -58,17 +59,20 @@ class FiatInputs extends StatefulWidget { class FiatInputsState extends State { TextEditingController fiatController = TextEditingController(); + late final Debouncer _debouncer; + bool _hasUserInput = false; @override void dispose() { fiatController.dispose(); - + _debouncer.dispose(); super.dispose(); } @override void initState() { super.initState(); + _debouncer = Debouncer(duration: const Duration(milliseconds: 300)); fiatController.text = widget.initialFiatAmount?.toString() ?? ''; } @@ -76,14 +80,20 @@ class FiatInputsState extends State { void didUpdateWidget(FiatInputs oldWidget) { super.didUpdateWidget(oldWidget); + // Reset _hasUserInput flag when asset or fiat currency changes + if (oldWidget.selectedAsset != widget.selectedAsset || + oldWidget.initialFiat != widget.initialFiat) { + _hasUserInput = false; + } + final Decimal? newFiatAmount = widget.initialFiatAmount; // Convert the current text to Decimal for comparison final Decimal currentFiatAmount = Decimal.tryParse(fiatController.text) ?? Decimal.zero; - // Compare using Decimal values - if (newFiatAmount != currentFiatAmount) { + // Only update if user hasn't made changes or if amounts are different + if (!_hasUserInput && newFiatAmount != currentFiatAmount) { final newFiatAmountText = newFiatAmount?.toString() ?? ''; fiatController ..text = newFiatAmountText @@ -106,7 +116,16 @@ class FiatInputsState extends State { } void fiatAmountChanged(String? newValue) { - widget.onFiatAmountUpdate(newValue); + // track if user has made inputs to avoid overwriting them + // with stale bloc state updates (e.g. race condition) + _hasUserInput = true; + _debouncer.run(() { + if (mounted) { + widget.onFiatAmountUpdate(newValue); + // Reset flag after API call to allow future bloc state updates + _hasUserInput = false; + } + }); } @override diff --git a/sdk b/sdk index 170aab4128..3f503d2368 160000 --- a/sdk +++ b/sdk @@ -1 +1 @@ -Subproject commit 170aab41285e7e96c01c97525f4bf4d9f2810778 +Subproject commit 3f503d2368c6ddd7a03bb15b587c3f27efa845c5