Skip to content
Closed
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
82 changes: 60 additions & 22 deletions lib/bloc/coin_addresses/bloc/coin_addresses_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,15 @@ 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'
show Asset, NewAddressStatus, AssetPubkeys;
import 'package:logging/logging.dart';
Comment thread
takenagain marked this conversation as resolved.
import 'package:web_dex/analytics/events.dart';
import 'package:web_dex/bloc/analytics/analytics_bloc.dart';
import 'package:web_dex/bloc/coin_addresses/bloc/coin_addresses_event.dart';
import 'package:web_dex/bloc/coin_addresses/bloc/coin_addresses_state.dart';
import 'package:web_dex/bloc/coins_bloc/asset_coin_extension.dart';

class CoinAddressesBloc extends Bloc<CoinAddressesEvent, CoinAddressesState> {
final KomodoDefiSdk sdk;
final String assetId;
final AnalyticsBloc analyticsBloc;

StreamSubscription<AssetPubkeys>? _pubkeysSub;
CoinAddressesBloc(this.sdk, this.assetId, this.analyticsBloc)
CoinAddressesBloc(this._sdk, this._assetId, this._analyticsBloc)
: super(const CoinAddressesState()) {
on<CoinAddressesAddressCreationSubmitted>(_onCreateAddressSubmitted);
on<CoinAddressesStarted>(_onStarted);
Expand All @@ -26,6 +22,14 @@ class CoinAddressesBloc extends Bloc<CoinAddressesEvent, CoinAddressesState> {
on<CoinAddressesPubkeysSubscriptionFailed>(_onPubkeysSubscriptionFailed);
}

final KomodoDefiSdk _sdk;
final String _assetId;
final AnalyticsBloc _analyticsBloc;

static final Logger _log = Logger('CoinAddressesBloc');

StreamSubscription<AssetPubkeys>? _pubkeysSub;

Future<void> _onStarted(
CoinAddressesStarted event,
Emitter<CoinAddressesState> emit,
Expand All @@ -44,8 +48,8 @@ class CoinAddressesBloc extends Bloc<CoinAddressesEvent, CoinAddressesState> {
),
);
try {
final asset = getSdkAsset(sdk, assetId);
final stream = sdk.pubkeys.watchCreateNewPubkey(asset);
final asset = getSdkAsset(_sdk, _assetId);
final stream = _sdk.pubkeys.watchCreateNewPubkey(asset);

await for (final newAddressState in stream) {
emit(state.copyWith(newAddressState: () => newAddressState));
Expand All @@ -57,11 +61,11 @@ class CoinAddressesBloc extends Bloc<CoinAddressesEvent, CoinAddressesState> {
if (derivation != null) {
try {
final parsed = parseDerivationPath(derivation);
analyticsBloc.logEvent(
_analyticsBloc.logEvent(
HdAddressGeneratedEventData(
accountIndex: parsed.accountIndex,
addressIndex: parsed.addressIndex,
asset: assetId,
asset: _assetId,
),
);
} catch (_) {
Expand Down Expand Up @@ -118,10 +122,10 @@ class CoinAddressesBloc extends Bloc<CoinAddressesEvent, CoinAddressesState> {
emit(state.copyWith(status: () => FormStatus.submitting));

try {
final asset = getSdkAsset(sdk, assetId);
final addresses = (await asset.getPubkeys(sdk)).keys;
final asset = getSdkAsset(_sdk, _assetId);
final addresses = (await asset.getPubkeys(_sdk)).keys;

final reasons = await asset.getCantCreateNewAddressReasons(sdk);
final reasons = await asset.getCantCreateNewAddressReasons(_sdk);

emit(
state.copyWith(
Expand Down Expand Up @@ -155,8 +159,8 @@ class CoinAddressesBloc extends Bloc<CoinAddressesEvent, CoinAddressesState> {
Emitter<CoinAddressesState> emit,
) async {
try {
final asset = getSdkAsset(sdk, assetId);
final reasons = await asset.getCantCreateNewAddressReasons(sdk);
final asset = getSdkAsset(_sdk, _assetId);
final reasons = await asset.getCantCreateNewAddressReasons(_sdk);
emit(
state.copyWith(
status: () => FormStatus.success,
Expand Down Expand Up @@ -184,28 +188,44 @@ class CoinAddressesBloc extends Bloc<CoinAddressesEvent, CoinAddressesState> {

Future<void> _startWatchingPubkeys(Asset asset) async {
try {
await _pubkeysSub?.cancel();
_pubkeysSub = null;
// Cancel any existing subscription first
await _cancelPubkeySubscription();

_log.fine('Starting pubkey watching for asset ${asset.id.id}');

// Pre-cache pubkeys to ensure that any newly created pubkeys are available
// when we start watching. UI flickering between old and new states is
// avoided this way. The watchPubkeys function yields the last known pubkeys
// when the pubkeys stream is first activated.
await sdk.pubkeys.precachePubkeys(asset);
_pubkeysSub = sdk.pubkeys
.watchPubkeys(asset, activateIfNeeded: true)
await _sdk.pubkeys.precachePubkeys(asset);
_pubkeysSub = _sdk.pubkeys
.watchPubkeys(asset)
.listen(
(AssetPubkeys assetPubkeys) {
if (!isClosed) {
_log.finest(
'Received pubkey update for asset ${asset.id.id}: ${assetPubkeys.keys.length} addresses',
);
add(CoinAddressesPubkeysUpdated(assetPubkeys.keys));
}
},
onError: (Object err) {
_log.warning(
'Pubkey subscription error for asset ${asset.id.id}: $err',
);
if (!isClosed) {
add(CoinAddressesPubkeysSubscriptionFailed(err.toString()));
}
},
);

_log.fine(
'Pubkey watching started successfully for asset ${asset.id.id}',
);
} catch (e) {
_log.severe(
'Failed to start pubkey watching for asset ${asset.id.id}: $e',
);
if (!isClosed) {
add(CoinAddressesPubkeysSubscriptionFailed(e.toString()));
}
Expand All @@ -214,8 +234,26 @@ class CoinAddressesBloc extends Bloc<CoinAddressesEvent, CoinAddressesState> {

@override
Future<void> close() async {
await _pubkeysSub?.cancel();
_pubkeysSub = null;
_log.fine('Closing CoinAddressesBloc for asset $_assetId');

// Cancel pubkey subscription
await _cancelPubkeySubscription();

return super.close();
}

/// Cancels the current pubkey subscription with proper error handling
Future<void> _cancelPubkeySubscription() async {
try {
await _pubkeysSub?.cancel();
_pubkeysSub = null;
_log.fine('Pubkey subscription cancelled for asset $_assetId');
} catch (e) {
_log.warning(
'Error cancelling pubkey subscription for asset $_assetId: $e',
);
// Still set to null to prevent further issues
_pubkeysSub = null;
}
}
}
13 changes: 12 additions & 1 deletion lib/bloc/coin_addresses/bloc/coin_addresses_event.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:komodo_defi_types/komodo_defi_types.dart' show PubkeyInfo;
import 'package:komodo_defi_types/komodo_defi_types.dart'
show PubkeyInfo, KdfUser;

abstract class CoinAddressesEvent extends Equatable {
const CoinAddressesEvent();
Expand Down Expand Up @@ -46,3 +47,13 @@ class CoinAddressesPubkeysSubscriptionFailed extends CoinAddressesEvent {
@override
List<Object?> get props => [error];
}

/// Event triggered when auth state changes (logout/wallet switch)
class CoinAddressesAuthStateChanged extends CoinAddressesEvent {
final KdfUser? user;

const CoinAddressesAuthStateChanged(this.user);

@override
List<Object?> get props => [user];
}
4 changes: 2 additions & 2 deletions lib/bloc/coins_bloc/coins_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ class CoinsBloc extends Bloc<CoinsEvent, CoinsState> {
// Ensure any cached addresses/pubkeys from a previous wallet are cleared
// so that UI fetches fresh pubkeys for the newly logged-in wallet.
emit(state.copyWith(pubkeys: {}));
_coinsRepo.flushCache();
await _coinsRepo.flushCache();
final Wallet currentWallet = event.signedInUser.wallet;

// Start off by emitting the newly activated coins so that they all appear
Expand Down Expand Up @@ -369,7 +369,7 @@ class CoinsBloc extends Bloc<CoinsEvent, CoinsState> {
pubkeys: {},
),
);
_coinsRepo.flushCache();
await _coinsRepo.flushCache();
}

void _scheduleInitialBalanceRefresh(Iterable<String> coinsToActivate) {
Expand Down
Loading
Loading