Skip to content

Commit

Permalink
Merge pull request #141 from KomodoPlatform/fix/chart-loading-fails
Browse files Browse the repository at this point in the history
fix(market-metrics): initial chart loading fails if wallet imported from Wallet page
  • Loading branch information
ca333 authored Sep 8, 2024
2 parents 78f3f14 + be47e00 commit 3750834
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,10 @@ class PortfolioGrowthBloc
PortfolioGrowthLoadRequested event,
Emitter<PortfolioGrowthState> emit,
) async {
final List<Coin> coins = List.from(event.coins);
await coins.removeWhereAsync(
(Coin coin) async {
final isCoinSupported = await portfolioGrowthRepository
.isCoinChartSupported(coin, event.fiatCoinId);
return !isCoinSupported;
},
);
if (coins.isEmpty) {
List<Coin> coins = await _removeUnsupportedCoins(event);
// Charts for individual coins (coin details) are parsed here as well,
// and should be hidden if not supported.
if (coins.isEmpty && event.coins.length <= 1) {
return emit(
PortfolioGrowthChartUnsupported(selectedPeriod: event.selectedPeriod),
);
Expand Down Expand Up @@ -128,6 +123,7 @@ class PortfolioGrowthBloc
await emit.forEach(
Stream.periodic(event.updateFrequency).asyncMap((_) async {
// Do not let transaction loading exceptions stop the periodic updates
coins = await _removeUnsupportedCoins(event);
try {
return await portfolioGrowthRepository.getPortfolioGrowthChart(
coins,
Expand Down Expand Up @@ -171,4 +167,17 @@ class PortfolioGrowthBloc
);
}
}

Future<List<Coin>> _removeUnsupportedCoins(
PortfolioGrowthLoadRequested event) async {
final List<Coin> coins = List.from(event.coins);
await coins.removeWhereAsync(
(Coin coin) async {
final isCoinSupported = await portfolioGrowthRepository
.isCoinChartSupported(coin.abbr, event.fiatCoinId);
return !isCoinSupported;
},
);
return coins;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:web_dex/bloc/cex_market_data/mockup/performance_mode.dart';
import 'package:web_dex/bloc/cex_market_data/models/graph_type.dart';
import 'package:web_dex/bloc/cex_market_data/models/models.dart';
import 'package:web_dex/bloc/transaction_history/transaction_history_repo.dart';
import 'package:web_dex/blocs/blocs.dart';
import 'package:web_dex/mm2/mm2_api/rpc/my_tx_history/transaction.dart';
import 'package:web_dex/model/coin.dart';

Expand Down Expand Up @@ -81,7 +82,7 @@ class PortfolioGrowthRepository {
///
/// Returns the growth [ChartData] for the coin ([List] of [Point]).
Future<ChartData> getCoinGrowthChart(
Coin coin, {
String coinId, {
// avoid the possibility of accidentally swapping the order of these
// required parameters by using named parameters
required String fiatCoinId,
Expand All @@ -92,7 +93,7 @@ class PortfolioGrowthRepository {
}) async {
if (useCache) {
final String compoundKey = GraphCache.getPrimaryKey(
coin.abbr,
coinId,
fiatCoinId,
GraphType.balanceGrowth,
walletId,
Expand All @@ -106,6 +107,9 @@ class PortfolioGrowthRepository {
}
}

// TODO: Refactor referenced coinsBloc method to a repository.
// NB: Even though the class is called [CoinsBloc], it is not a Bloc.
final Coin coin = coinsBlocRepository.getCoin(coinId)!;
final List<Transaction> transactions =
await _transactionHistoryRepository.fetchCompletedTransactions(coin);

Expand All @@ -115,7 +119,7 @@ class PortfolioGrowthRepository {
// called later with useCache set to false to fetch the transactions again
await _graphCache.insert(
GraphCache(
coinId: coin.abbr,
coinId: coinId,
fiatCoinId: fiatCoinId,
lastUpdated: DateTime.now(),
graph: List.empty(),
Expand Down Expand Up @@ -180,7 +184,7 @@ class PortfolioGrowthRepository {
/// [walletId] is the wallet id of the portfolio.
/// [useCache] is a flag to indicate whether to use the cache.
/// [startAt] and [endAt] will filter the final chart to the specified range,
/// and cache the filtered chart.
/// and cache the filtered chart.
/// [ignoreTransactionFetchErrors] is a flag to ignore transaction fetch errors
/// and return an empty chart instead.
///
Expand Down Expand Up @@ -208,7 +212,7 @@ class PortfolioGrowthRepository {
final chartDataFutures = coins.map((coin) async {
try {
return await getCoinGrowthChart(
coin,
coin.abbr,
fiatCoinId: fiatCoinId,
useCache: useCache,
walletId: walletId,
Expand Down Expand Up @@ -280,24 +284,25 @@ class PortfolioGrowthRepository {
/// Returns `true` if the coin is supported by the CEX API for charting.
/// Returns `false` if the coin is not supported by the CEX API for charting.
Future<bool> isCoinChartSupported(
Coin coin,
String coinId,
String fiatCoinId, {
bool allowFiatAsBase = true,
}) async {
final Coin coin = coinsBlocRepository.getCoin(coinId)!;
if (coin.isActivating || !coin.isActive) {
return false;
}

final supportedCoins = await _cexRepository.getCoinList();
final coinId = coin.abbr.split('-').firstOrNull?.toUpperCase() ?? '';
final coinTicker = coin.abbr.split('-').firstOrNull?.toUpperCase() ?? '';
// Allow fiat coins through, as they are represented by a constant value,
// 1, in the repository layer and are not supported by the CEX API
if (allowFiatAsBase && coinId == fiatCoinId.toUpperCase()) {
if (allowFiatAsBase && coinTicker == fiatCoinId.toUpperCase()) {
return true;
}

final coinPair = CexCoinPair(
baseCoinTicker: coinId,
baseCoinTicker: coinTicker,
relCoinTicker: fiatCoinId.toUpperCase(),
);
final isCoinSupported = coinPair.isCoinSupported(supportedCoins);
Expand Down
68 changes: 43 additions & 25 deletions lib/bloc/cex_market_data/profit_loss/profit_loss_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ class ProfitLossBloc extends Bloc<ProfitLossEvent, ProfitLossState> {
transformer: restartable(),
);

on<ProfitLossPeriodChanged>(_onPeriodChanged);
on<ProfitLossPortfolioPeriodChanged>(_onPortfolioPeriodChanged);
}

Expand All @@ -38,15 +37,10 @@ class ProfitLossBloc extends Bloc<ProfitLossEvent, ProfitLossState> {
Emitter<ProfitLossState> emit,
) async {
try {
final List<Coin> coins = List.from(event.coins);
await coins.removeWhereAsync(
(Coin coin) async {
final isCoinSupported = await _profitLossRepository
.isCoinChartSupported(coin.abbr, event.fiatCoinId);
return coin.isTestCoin || !isCoinSupported;
},
);
if (coins.isEmpty) {
final List<Coin> coins = await _removeUnsupportedCons(event);
// Charts for individual coins (coin details) are parsed here as well,
// and should be hidden if not supported.
if (coins.isEmpty && event.coins.length <= 1) {
return emit(
PortfolioProfitLossChartUnsupported(
selectedPeriod: event.selectedPeriod,
Expand Down Expand Up @@ -100,25 +94,49 @@ class ProfitLossBloc extends Bloc<ProfitLossEvent, ProfitLossState> {
),
);
}
}

Future<void> _onPeriodChanged(
ProfitLossPeriodChanged event,
Emitter<ProfitLossState> emit,
) async {
if (state is! PortfolioProfitLossChartLoadSuccess) {
return;
}
await emit.forEach(
Stream.periodic(event.updateFrequency).asyncMap((_) async {
return await _getSortedProfitLossChartForCoins(
event,
useCache: false,
);
}), onData: (profitLossChart) {
if (profitLossChart.isEmpty) {
return state;
}

final currentState = state as PortfolioProfitLossChartLoadSuccess;
add(
ProfitLossPortfolioChartLoadRequested(
coins: currentState.coins,
fiatCoinId: currentState.fiatCurrency,
final unCachedProfitIncrease = profitLossChart.increase;
final unCachedPercentageIncrease = profitLossChart.percentageIncrease;
return PortfolioProfitLossChartLoadSuccess(
profitLossChart: profitLossChart,
totalValue: unCachedProfitIncrease,
percentageIncrease: unCachedPercentageIncrease,
coins: event.coins,
fiatCurrency: event.fiatCoinId,
selectedPeriod: event.selectedPeriod,
walletId: currentState.walletId,
),
walletId: event.walletId,
);
}, onError: (e, s) {
logger.log('Failed to load portfolio profit/loss: $e', isError: true);
return ProfitLossLoadFailure(
error: TextError(error: 'Failed to load portfolio profit/loss: $e'),
selectedPeriod: event.selectedPeriod,
);
});
}

Future<List<Coin>> _removeUnsupportedCons(
ProfitLossPortfolioChartLoadRequested event) async {
final List<Coin> coins = List.from(event.coins);
await coins.removeWhereAsync(
(Coin coin) async {
final isCoinSupported = await _profitLossRepository
.isCoinChartSupported(coin.abbr, event.fiatCoinId);
return coin.isTestCoin || !isCoinSupported;
},
);
return coins;
}

Future<void> _onPortfolioPeriodChanged(
Expand Down
25 changes: 12 additions & 13 deletions lib/bloc/cex_market_data/profit_loss/profit_loss_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,34 @@ class ProfitLossPortfolioChartLoadRequested extends ProfitLossEvent {
required this.fiatCoinId,
required this.selectedPeriod,
required this.walletId,
this.updateFrequency = const Duration(minutes: 1),
});

final List<Coin> coins;
final String fiatCoinId;
final Duration selectedPeriod;
final Duration updateFrequency;
final String walletId;

@override
List<Object> get props => [coins, fiatCoinId, selectedPeriod, walletId];
}

class ProfitLossPeriodChanged extends ProfitLossEvent {
const ProfitLossPeriodChanged({
required this.selectedPeriod,
});

final Duration selectedPeriod;

@override
List<Object> get props => [selectedPeriod];
List<Object> get props => [
coins,
fiatCoinId,
selectedPeriod,
walletId,
updateFrequency,
];
}

class ProfitLossPortfolioPeriodChanged extends ProfitLossEvent {
const ProfitLossPortfolioPeriodChanged({
required this.selectedPeriod,
this.updateFrequency = const Duration(minutes: 1),
});

final Duration selectedPeriod;
final Duration updateFrequency;

@override
List<Object> get props => [selectedPeriod];
List<Object> get props => [selectedPeriod, updateFrequency];
}

0 comments on commit 3750834

Please sign in to comment.