From 8824d7cef9542ba4bae733d6f6cb155989089e2c Mon Sep 17 00:00:00 2001 From: Francois Date: Wed, 9 Jul 2025 13:31:43 +0200 Subject: [PATCH 1/2] fix(auth): skip lifecycle check for Trezor and watch auth changes --- lib/bloc/auth_bloc/auth_bloc.dart | 10 ++- lib/bloc/auth_bloc/trezor_auth_mixin.dart | 86 ++++++++++++++++------- 2 files changed, 67 insertions(+), 29 deletions(-) diff --git a/lib/bloc/auth_bloc/auth_bloc.dart b/lib/bloc/auth_bloc/auth_bloc.dart index 19e81a0ab6..1201d1bab0 100644 --- a/lib/bloc/auth_bloc/auth_bloc.dart +++ b/lib/bloc/auth_bloc/auth_bloc.dart @@ -2,11 +2,11 @@ import 'dart:async'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart' + show PrivateKeyPolicy; import 'package:komodo_defi_sdk/komodo_defi_sdk.dart'; import 'package:komodo_defi_types/komodo_defi_types.dart'; import 'package:logging/logging.dart'; -import 'package:komodo_defi_rpc_methods/komodo_defi_rpc_methods.dart' - show PrivateKeyPolicy; import 'package:web_dex/app_config/app_config.dart'; import 'package:web_dex/bloc/settings/settings_repository.dart'; import 'package:web_dex/blocs/wallets_repository.dart'; @@ -327,12 +327,16 @@ class AuthBloc extends Bloc with TrezorAuthMixin { Emitter emit, ) async { final currentUser = await _kdfSdk.auth.currentUser; - if (currentUser != null) { + final isTrezorWallet = currentUser?.walletId.authOptions.privKeyPolicy == + const PrivateKeyPolicy.trezor(); + + if (currentUser != null && !isTrezorWallet) { emit(AuthBlocState.loggedIn(currentUser)); _listenToAuthStateChanges(); } } + @override void _listenToAuthStateChanges() { _authChangesSubscription?.cancel(); _authChangesSubscription = _kdfSdk.auth.watchCurrentUser().listen((user) { diff --git a/lib/bloc/auth_bloc/trezor_auth_mixin.dart b/lib/bloc/auth_bloc/trezor_auth_mixin.dart index e419c93c54..735354365e 100644 --- a/lib/bloc/auth_bloc/trezor_auth_mixin.dart +++ b/lib/bloc/auth_bloc/trezor_auth_mixin.dart @@ -13,14 +13,18 @@ mixin TrezorAuthMixin on Bloc { on(_onTrezorCancel); } + /// Abstract method overriden in [AuthBloc] to start listening + /// to authentication state changes. + void _listenToAuthStateChanges(); + Future _onTrezorInitAndAuth( AuthTrezorInitAndAuthStarted event, Emitter emit, ) async { try { - final authOptions = AuthOptions( + const authOptions = AuthOptions( derivationMethod: DerivationMethod.hdWallet, - privKeyPolicy: const PrivateKeyPolicy.trezor(), + privKeyPolicy: PrivateKeyPolicy.trezor(), ); final Stream authStream = _sdk.auth.signInStream( @@ -35,6 +39,7 @@ mixin TrezorAuthMixin on Bloc { if (authState.status == AuthenticationStatus.completed || authState.status == AuthenticationStatus.error || authState.status == AuthenticationStatus.cancelled) { + _listenToAuthStateChanges(); break; } } @@ -52,7 +57,8 @@ mixin TrezorAuthMixin on Bloc { } Future _handleAuthenticationState( - AuthenticationState authState) async { + AuthenticationState authState, + ) async { switch (authState.status) { case AuthenticationStatus.initializing: return AuthBlocState.trezorInitializing( @@ -84,17 +90,21 @@ mixin TrezorAuthMixin on Bloc { case AuthenticationStatus.authenticating: return AuthBlocState.loading(); case AuthenticationStatus.completed: - return await _setupTrezorWallet(authState); + return _setupTrezorWallet(authState); case AuthenticationStatus.error: - return AuthBlocState.error(AuthException( - authState.error ?? 'Trezor authentication failed', - type: AuthExceptionType.generalAuthError, - )); + return AuthBlocState.error( + AuthException( + authState.error ?? 'Trezor authentication failed', + type: AuthExceptionType.generalAuthError, + ), + ); case AuthenticationStatus.cancelled: - return AuthBlocState.error(AuthException( - 'Trezor authentication was cancelled', - type: AuthExceptionType.generalAuthError, - )); + return AuthBlocState.error( + AuthException( + 'Trezor authentication was cancelled', + type: AuthExceptionType.generalAuthError, + ), + ); } } @@ -107,10 +117,12 @@ mixin TrezorAuthMixin on Bloc { // This should not happen, but if it does then trezor initialization failed // and we should not proceed. if (authState.user == null) { - return AuthBlocState.error(AuthException( - 'Trezor initialization failed', - type: AuthExceptionType.generalAuthError, - )); + return AuthBlocState.error( + AuthException( + 'Trezor initialization failed', + type: AuthExceptionType.generalAuthError, + ), + ); } await _sdk.setWalletType(WalletType.trezor); @@ -134,16 +146,28 @@ mixin TrezorAuthMixin on Bloc { try { final taskId = state.authenticationState?.taskId; if (taskId == null) { - emit(AuthBlocState.error(AuthException('No task ID found', - type: AuthExceptionType.generalAuthError))); + emit( + AuthBlocState.error( + AuthException( + 'No task ID found', + type: AuthExceptionType.generalAuthError, + ), + ), + ); return; } await _sdk.auth.setHardwareDevicePin(taskId, event.pin); } catch (e) { _log.shout('Failed to provide PIN', e); - emit(AuthBlocState.error(AuthException('Failed to provide PIN', - type: AuthExceptionType.generalAuthError))); + emit( + AuthBlocState.error( + AuthException( + 'Failed to provide PIN', + type: AuthExceptionType.generalAuthError, + ), + ), + ); } } @@ -154,8 +178,14 @@ mixin TrezorAuthMixin on Bloc { try { final taskId = state.authenticationState?.taskId; if (taskId == null) { - emit(AuthBlocState.error(AuthException('No task ID found', - type: AuthExceptionType.generalAuthError))); + emit( + AuthBlocState.error( + AuthException( + 'No task ID found', + type: AuthExceptionType.generalAuthError, + ), + ), + ); return; } @@ -165,10 +195,14 @@ mixin TrezorAuthMixin on Bloc { ); } catch (e) { _log.shout('Failed to provide passphrase', e); - emit(AuthBlocState.error(AuthException( - 'Failed to provide passphrase', - type: AuthExceptionType.generalAuthError, - ))); + emit( + AuthBlocState.error( + AuthException( + 'Failed to provide passphrase', + type: AuthExceptionType.generalAuthError, + ), + ), + ); } } From a0565e900b910dfdc6e45c5261897c8cbe8df8c7 Mon Sep 17 00:00:00 2001 From: Francois Date: Wed, 9 Jul 2025 15:13:27 +0200 Subject: [PATCH 2/2] fix(wallet-main): setState in postFrameCallback and allow multiple ticks --- .../wallet_page/wallet_main/wallet_main.dart | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/lib/views/wallet/wallet_page/wallet_main/wallet_main.dart b/lib/views/wallet/wallet_page/wallet_main/wallet_main.dart index da65c19800..15f54339c1 100644 --- a/lib/views/wallet/wallet_page/wallet_main/wallet_main.dart +++ b/lib/views/wallet/wallet_page/wallet_main/wallet_main.dart @@ -1,18 +1,24 @@ import 'dart:async'; -import 'package:flutter/gestures.dart'; import 'package:app_theme/app_theme.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:komodo_defi_sdk/komodo_defi_sdk.dart'; +import 'package:komodo_defi_types/komodo_defi_types.dart'; import 'package:komodo_ui_kit/komodo_ui_kit.dart'; +import 'package:web_dex/analytics/events.dart'; +import 'package:web_dex/analytics/events/misc_events.dart'; import 'package:web_dex/app_config/app_config.dart'; +import 'package:web_dex/bloc/analytics/analytics_bloc.dart'; import 'package:web_dex/bloc/assets_overview/bloc/asset_overview_bloc.dart'; import 'package:web_dex/bloc/auth_bloc/auth_bloc.dart'; import 'package:web_dex/bloc/bridge_form/bridge_bloc.dart'; import 'package:web_dex/bloc/bridge_form/bridge_event.dart'; import 'package:web_dex/bloc/cex_market_data/portfolio_growth/portfolio_growth_bloc.dart'; +import 'package:web_dex/bloc/cex_market_data/price_chart/price_chart_bloc.dart'; +import 'package:web_dex/bloc/cex_market_data/price_chart/price_chart_event.dart'; import 'package:web_dex/bloc/cex_market_data/profit_loss/profit_loss_bloc.dart'; import 'package:web_dex/bloc/coins_bloc/coins_bloc.dart'; import 'package:web_dex/bloc/taker_form/taker_bloc.dart'; @@ -29,15 +35,9 @@ import 'package:web_dex/router/state/wallet_state.dart'; import 'package:web_dex/views/common/page_header/page_header.dart'; import 'package:web_dex/views/common/pages/page_layout.dart'; import 'package:web_dex/views/dex/dex_helpers.dart'; -import 'package:web_dex/bloc/analytics/analytics_bloc.dart'; -import 'package:web_dex/analytics/events.dart'; -import 'package:web_dex/analytics/events/misc_events.dart'; import 'package:web_dex/views/wallet/coin_details/coin_details_info/charts/portfolio_growth_chart.dart'; import 'package:web_dex/views/wallet/coin_details/coin_details_info/charts/portfolio_profit_loss_chart.dart'; import 'package:web_dex/views/wallet/wallet_page/charts/coin_prices_chart.dart'; -import 'package:web_dex/bloc/cex_market_data/price_chart/price_chart_bloc.dart'; -import 'package:web_dex/bloc/cex_market_data/price_chart/price_chart_event.dart'; -import 'package:komodo_defi_types/komodo_defi_types.dart'; import 'package:web_dex/views/wallet/wallet_page/common/assets_list.dart'; import 'package:web_dex/views/wallet/wallet_page/wallet_main/active_coins_list.dart'; import 'package:web_dex/views/wallet/wallet_page/wallet_main/wallet_manage_section.dart'; @@ -52,8 +52,7 @@ class WalletMain extends StatefulWidget { State createState() => _WalletMainState(); } -class _WalletMainState extends State - with SingleTickerProviderStateMixin { +class _WalletMainState extends State with TickerProviderStateMixin { bool _showCoinWithBalance = false; String _searchKey = ''; PopupDispatcher? _popupDispatcher; @@ -68,11 +67,21 @@ class _WalletMainState extends State _tabController = TabController(length: authenticated ? 3 : 2, vsync: this) ..addListener(() { if (_activeTabIndex != _tabController.index) { - setState(() => _activeTabIndex = _tabController.index); + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() => _activeTabIndex = _tabController.index); + }); } }); } + void _updateTabController(bool authenticated) { + final newLength = authenticated ? 3 : 2; + if (_tabController.length != newLength) { + _tabController.dispose(); + _initTabController(authenticated); + } + } + @override void initState() { super.initState(); @@ -107,14 +116,10 @@ class _WalletMainState extends State listener: (context, state) { if (state.currentUser?.wallet != null) { _loadWalletData(state.currentUser!.wallet.id).ignore(); - if (_tabController.length != 3) { - _initTabController(true); - } + _updateTabController(true); } else { _clearWalletData(); - if (_tabController.length != 2) { - _initTabController(false); - } + _updateTabController(false); } }, builder: (authContext, authState) {