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
10 changes: 7 additions & 3 deletions lib/bloc/auth_bloc/auth_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -327,12 +327,16 @@ class AuthBloc extends Bloc<AuthBlocEvent, AuthBlocState> with TrezorAuthMixin {
Emitter<AuthBlocState> emit,
) async {
final currentUser = await _kdfSdk.auth.currentUser;
if (currentUser != null) {
final isTrezorWallet = currentUser?.walletId.authOptions.privKeyPolicy ==
const PrivateKeyPolicy.trezor();

if (currentUser != null && !isTrezorWallet) {
Copy link

Copilot AI Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition prevents emitting loggedIn for Trezor wallets on startup. If Trezor authentication should also trigger loggedIn, this logic may skip necessary state updates.

Suggested change
if (currentUser != null && !isTrezorWallet) {
if (currentUser != null) {

Copilot uses AI. Check for mistakes.
emit(AuthBlocState.loggedIn(currentUser));
_listenToAuthStateChanges();
}
}

@override
void _listenToAuthStateChanges() {
_authChangesSubscription?.cancel();
_authChangesSubscription = _kdfSdk.auth.watchCurrentUser().listen((user) {
Expand Down
86 changes: 60 additions & 26 deletions lib/bloc/auth_bloc/trezor_auth_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ mixin TrezorAuthMixin on Bloc<AuthBlocEvent, AuthBlocState> {
on<AuthTrezorCancelled>(_onTrezorCancel);
}

/// Abstract method overriden in [AuthBloc] to start listening
Copy link

Copilot AI Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spelling: "overriden" should be corrected to "overridden" in the doc comment.

Suggested change
/// Abstract method overriden in [AuthBloc] to start listening
/// Abstract method overridden in [AuthBloc] to start listening

Copilot uses AI. Check for mistakes.
/// to authentication state changes.
void _listenToAuthStateChanges();

Future<void> _onTrezorInitAndAuth(
AuthTrezorInitAndAuthStarted event,
Emitter<AuthBlocState> emit,
) async {
try {
final authOptions = AuthOptions(
const authOptions = AuthOptions(
derivationMethod: DerivationMethod.hdWallet,
privKeyPolicy: const PrivateKeyPolicy.trezor(),
privKeyPolicy: PrivateKeyPolicy.trezor(),
);

final Stream<AuthenticationState> authStream = _sdk.auth.signInStream(
Expand All @@ -35,6 +39,7 @@ mixin TrezorAuthMixin on Bloc<AuthBlocEvent, AuthBlocState> {
if (authState.status == AuthenticationStatus.completed ||
authState.status == AuthenticationStatus.error ||
authState.status == AuthenticationStatus.cancelled) {
_listenToAuthStateChanges();
break;
}
}
Expand All @@ -52,7 +57,8 @@ mixin TrezorAuthMixin on Bloc<AuthBlocEvent, AuthBlocState> {
}

Future<AuthBlocState> _handleAuthenticationState(
AuthenticationState authState) async {
AuthenticationState authState,
) async {
switch (authState.status) {
case AuthenticationStatus.initializing:
return AuthBlocState.trezorInitializing(
Expand Down Expand Up @@ -84,17 +90,21 @@ mixin TrezorAuthMixin on Bloc<AuthBlocEvent, AuthBlocState> {
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(
Copy link

Copilot AI Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The error emission code is duplicated across multiple branches; consider extracting a helper method to construct and emit error states to reduce repetition.

Copilot uses AI. Check for mistakes.
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,
),
);
}
}

Expand All @@ -107,10 +117,12 @@ mixin TrezorAuthMixin on Bloc<AuthBlocEvent, AuthBlocState> {
// 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);
Expand All @@ -134,16 +146,28 @@ mixin TrezorAuthMixin on Bloc<AuthBlocEvent, AuthBlocState> {
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,
),
),
);
}
}

Expand All @@ -154,8 +178,14 @@ mixin TrezorAuthMixin on Bloc<AuthBlocEvent, AuthBlocState> {
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;
}

Expand All @@ -165,10 +195,14 @@ mixin TrezorAuthMixin on Bloc<AuthBlocEvent, AuthBlocState> {
);
} 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,
),
),
);
}
}

Expand Down
37 changes: 21 additions & 16 deletions lib/views/wallet/wallet_page/wallet_main/wallet_main.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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';
Expand All @@ -52,8 +52,7 @@ class WalletMain extends StatefulWidget {
State<WalletMain> createState() => _WalletMainState();
}

class _WalletMainState extends State<WalletMain>
with SingleTickerProviderStateMixin {
class _WalletMainState extends State<WalletMain> with TickerProviderStateMixin {
bool _showCoinWithBalance = false;
String _searchKey = '';
PopupDispatcher? _popupDispatcher;
Expand All @@ -68,11 +67,21 @@ class _WalletMainState extends State<WalletMain>
_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();
Expand Down Expand Up @@ -107,14 +116,10 @@ class _WalletMainState extends State<WalletMain>
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) {
Expand Down
Loading