mirror of
https://github.com/bisq-network/bisq.git
synced 2025-03-03 10:46:54 +01:00
Merge pull request #6579 from stejbac/further-speed-up-transactions-view-load
Further speed up transactions view load
This commit is contained in:
commit
581fbd1d2d
8 changed files with 222 additions and 100 deletions
|
@ -7,6 +7,7 @@ import bisq.core.monetary.Altcoin;
|
|||
import bisq.core.monetary.Price;
|
||||
|
||||
import bisq.common.util.MathUtils;
|
||||
import bisq.common.util.Tuple3;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Monetary;
|
||||
|
@ -33,12 +34,15 @@ import org.jetbrains.annotations.NotNull;
|
|||
public class FormattingUtils {
|
||||
public static final String BTC_FORMATTER_KEY = "BTC";
|
||||
|
||||
public final static String RANGE_SEPARATOR = " - ";
|
||||
public static final String RANGE_SEPARATOR = " - ";
|
||||
|
||||
private static final MonetaryFormat fiatPriceFormat = new MonetaryFormat().shift(0).minDecimals(4).repeatOptionalDecimals(0, 0);
|
||||
private static final MonetaryFormat altcoinFormat = new MonetaryFormat().shift(0).minDecimals(8).repeatOptionalDecimals(0, 0);
|
||||
private static final DecimalFormat decimalFormat = new DecimalFormat("#.#");
|
||||
|
||||
private static final ThreadLocal<Tuple3<Locale, DateFormat, DateFormat>> cachedUtcDateTimeFormatters = new ThreadLocal<>();
|
||||
private static final ThreadLocal<Tuple3<Locale, DateFormat, DateFormat>> cachedLocalDateTimeFormatters = new ThreadLocal<>();
|
||||
|
||||
public static String formatCoinWithCode(long value, MonetaryFormat coinFormat) {
|
||||
return formatCoinWithCode(Coin.valueOf(value), coinFormat);
|
||||
}
|
||||
|
@ -183,12 +187,25 @@ public class FormattingUtils {
|
|||
|
||||
public static String formatDateTime(Date date, boolean useLocaleAndLocalTimezone) {
|
||||
Locale locale = useLocaleAndLocalTimezone ? GlobalSettings.getLocale() : Locale.US;
|
||||
DateFormat dateInstance = DateFormat.getDateInstance(DateFormat.DEFAULT, locale);
|
||||
DateFormat timeInstance = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale);
|
||||
if (!useLocaleAndLocalTimezone) {
|
||||
dateInstance.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
timeInstance.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
|
||||
var formatterTuple = (useLocaleAndLocalTimezone ?
|
||||
cachedLocalDateTimeFormatters : cachedUtcDateTimeFormatters).get();
|
||||
if (formatterTuple == null || !formatterTuple.first.equals(locale)) {
|
||||
formatterTuple = new Tuple3<>(locale,
|
||||
DateFormat.getDateInstance(DateFormat.DEFAULT, locale),
|
||||
DateFormat.getTimeInstance(DateFormat.DEFAULT, locale));
|
||||
|
||||
if (useLocaleAndLocalTimezone) {
|
||||
cachedLocalDateTimeFormatters.set(formatterTuple);
|
||||
} else {
|
||||
formatterTuple.second.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
formatterTuple.third.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
cachedUtcDateTimeFormatters.set(formatterTuple);
|
||||
}
|
||||
}
|
||||
DateFormat dateInstance = formatterTuple.second;
|
||||
DateFormat timeInstance = formatterTuple.third;
|
||||
|
||||
return formatDateTime(date, dateInstance, timeInstance);
|
||||
}
|
||||
|
||||
|
@ -288,7 +305,8 @@ public class FormattingUtils {
|
|||
}
|
||||
|
||||
@NotNull
|
||||
public static String fillUpPlacesWithEmptyStrings(String formattedNumber, @SuppressWarnings("unused") int maxNumberOfDigits) {
|
||||
public static String fillUpPlacesWithEmptyStrings(String formattedNumber,
|
||||
@SuppressWarnings("unused") int maxNumberOfDigits) {
|
||||
//FIXME: temporary deactivate adding spaces in front of numbers as we don't use a monospace font right now.
|
||||
/*int numberOfPlacesToFill = maxNumberOfDigits - formattedNumber.length();
|
||||
for (int i = 0; i < numberOfPlacesToFill; i++) {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.desktop.main.funds.transactions;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static bisq.desktop.main.funds.transactions.TransactionAwareTradable.TX_FILTER_SIZE;
|
||||
|
||||
public class RelatedTransactionFilterSlices {
|
||||
private final List<TransactionAwareTradable> tradables;
|
||||
private final BitSet[] filterSlices;
|
||||
|
||||
public RelatedTransactionFilterSlices(Collection<? extends TransactionAwareTradable> tradables) {
|
||||
this.tradables = List.copyOf(tradables);
|
||||
|
||||
filterSlices = new BitSet[TX_FILTER_SIZE];
|
||||
Arrays.setAll(filterSlices, i -> new BitSet(this.tradables.size()));
|
||||
|
||||
IntStream.range(0, this.tradables.size())
|
||||
.forEach(j -> this.tradables.get(j).getRelatedTransactionFilter()
|
||||
.forEach(i -> filterSlices[i].set(j)));
|
||||
}
|
||||
|
||||
public Stream<TransactionAwareTradable> getAllRelatedTradables(Transaction tx) {
|
||||
int i = TransactionAwareTradable.bucketIndex(tx);
|
||||
return filterSlices[i].stream()
|
||||
.mapToObj(tradables::get)
|
||||
.filter(t -> t.isRelatedToTransaction(tx));
|
||||
}
|
||||
}
|
|
@ -23,6 +23,8 @@ import bisq.core.trade.model.Tradable;
|
|||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class TransactionAwareOpenOffer implements TransactionAwareTradable {
|
||||
private final OpenOffer delegate;
|
||||
|
||||
|
@ -34,12 +36,18 @@ class TransactionAwareOpenOffer implements TransactionAwareTradable {
|
|||
Offer offer = delegate.getOffer();
|
||||
String paymentTxId = offer.getOfferFeePaymentTxId();
|
||||
|
||||
String txId = transaction.getTxId().toString();
|
||||
|
||||
return paymentTxId != null && paymentTxId.equals(txId);
|
||||
return paymentTxId != null && paymentTxId.equals(transaction.getTxId().toString());
|
||||
}
|
||||
|
||||
public Tradable asTradable() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntStream getRelatedTransactionFilter() {
|
||||
Offer offer = delegate.getOffer();
|
||||
String paymentTxId = offer.getOfferFeePaymentTxId();
|
||||
return IntStream.of(TransactionAwareTradable.bucketIndex(paymentTxId))
|
||||
.filter(i -> i >= 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,35 @@ package bisq.desktop.main.funds.transactions;
|
|||
|
||||
import bisq.core.trade.model.Tradable;
|
||||
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
interface TransactionAwareTradable {
|
||||
int TX_FILTER_SIZE = 64;
|
||||
int DELAYED_PAYOUT_TX_BUCKET_INDEX = TX_FILTER_SIZE - 1;
|
||||
|
||||
boolean isRelatedToTransaction(Transaction transaction);
|
||||
|
||||
Tradable asTradable();
|
||||
|
||||
/** Returns a list of bucket indices of all transactions which might be related to this Tradable. */
|
||||
IntStream getRelatedTransactionFilter();
|
||||
|
||||
static int bucketIndex(Transaction tx) {
|
||||
return tx.getLockTime() == 0 ? bucketIndex(tx.getTxId()) : DELAYED_PAYOUT_TX_BUCKET_INDEX;
|
||||
}
|
||||
|
||||
static int bucketIndex(Sha256Hash hash) {
|
||||
int i = hash.getBytes()[31] & 255;
|
||||
return i % TX_FILTER_SIZE != DELAYED_PAYOUT_TX_BUCKET_INDEX ?
|
||||
i % TX_FILTER_SIZE : i / TX_FILTER_SIZE;
|
||||
}
|
||||
|
||||
static int bucketIndex(@Nullable String txId) {
|
||||
return txId != null ? bucketIndex(Sha256Hash.wrap(txId)) : -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,18 +29,23 @@ import bisq.core.trade.model.bisq_v1.Trade;
|
|||
import bisq.core.trade.model.bsq_swap.BsqSwapTrade;
|
||||
|
||||
import bisq.common.crypto.PubKeyRing;
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionOutput;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.desktop.main.funds.transactions.TransactionAwareTradable.bucketIndex;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@Slf4j
|
||||
|
@ -51,6 +56,11 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
|||
private final BtcWalletService btcWalletService;
|
||||
private final PubKeyRing pubKeyRing;
|
||||
|
||||
// As Sha256Hash.toString() is expensive, cache the last result, which will usually be next one needed.
|
||||
private static Tuple2<Sha256Hash, String> lastTxIdTuple;
|
||||
// Similarly, cache the last computed set of tx receiver addresses, to speed up 'isRefundPayoutTx'.
|
||||
private static Tuple2<String, Set<String>> lastReceiverAddressStringsTuple;
|
||||
|
||||
TransactionAwareTrade(TradeModel tradeModel,
|
||||
ArbitrationManager arbitrationManager,
|
||||
RefundManager refundManager,
|
||||
|
@ -66,15 +76,19 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
|||
@Override
|
||||
public boolean isRelatedToTransaction(Transaction transaction) {
|
||||
Sha256Hash hash = transaction.getTxId();
|
||||
String txId = hash.toString();
|
||||
var txIdTuple = lastTxIdTuple;
|
||||
if (txIdTuple == null || !txIdTuple.first.equals(hash)) {
|
||||
lastTxIdTuple = txIdTuple = new Tuple2<>(hash, hash.toString());
|
||||
}
|
||||
String txId = txIdTuple.second;
|
||||
|
||||
boolean tradeRelated = false;
|
||||
if (tradeModel instanceof Trade) {
|
||||
Trade trade = (Trade) tradeModel;
|
||||
boolean isTakerOfferFeeTx = txId.equals(trade.getTakerFeeTxId());
|
||||
boolean isOfferFeeTx = isOfferFeeTx(txId);
|
||||
boolean isDepositTx = isDepositTx(hash);
|
||||
boolean isPayoutTx = isPayoutTx(hash);
|
||||
boolean isDepositTx = isDepositTx(txId);
|
||||
boolean isPayoutTx = isPayoutTx(txId);
|
||||
boolean isDisputedPayoutTx = isDisputedPayoutTx(txId);
|
||||
boolean isDelayedPayoutTx = transaction.getLockTime() != 0 && isDelayedPayoutTx(txId);
|
||||
boolean isRefundPayoutTx = isRefundPayoutTx(trade, txId);
|
||||
|
@ -91,36 +105,28 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
|||
return tradeRelated || isBsqSwapTrade;
|
||||
}
|
||||
|
||||
private boolean isPayoutTx(Sha256Hash txId) {
|
||||
private boolean isPayoutTx(String txId) {
|
||||
if (isBsqSwapTrade())
|
||||
return false;
|
||||
|
||||
Trade trade = (Trade) tradeModel;
|
||||
return Optional.ofNullable(trade.getPayoutTx())
|
||||
.map(Transaction::getTxId)
|
||||
.map(hash -> hash.equals(txId))
|
||||
.orElse(false);
|
||||
return txId.equals(trade.getPayoutTxId());
|
||||
}
|
||||
|
||||
private boolean isDepositTx(Sha256Hash txId) {
|
||||
private boolean isDepositTx(String txId) {
|
||||
if (isBsqSwapTrade())
|
||||
return false;
|
||||
|
||||
Trade trade = (Trade) tradeModel;
|
||||
return Optional.ofNullable(trade.getDepositTx())
|
||||
.map(Transaction::getTxId)
|
||||
.map(hash -> hash.equals(txId))
|
||||
.orElse(false);
|
||||
return txId.equals(trade.getDepositTxId());
|
||||
}
|
||||
|
||||
private boolean isOfferFeeTx(String txId) {
|
||||
if (isBsqSwapTrade())
|
||||
return false;
|
||||
|
||||
return Optional.ofNullable(tradeModel.getOffer())
|
||||
.map(Offer::getOfferFeePaymentTxId)
|
||||
.map(paymentTxId -> paymentTxId.equals(txId))
|
||||
.orElse(false);
|
||||
Offer offer = tradeModel.getOffer();
|
||||
return offer != null && txId.equals(offer.getOfferFeePaymentTxId());
|
||||
}
|
||||
|
||||
private boolean isDisputedPayoutTx(String txId) {
|
||||
|
@ -130,7 +136,7 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
|||
String delegateId = tradeModel.getId();
|
||||
ObservableList<Dispute> disputes = arbitrationManager.getDisputesAsObservableList();
|
||||
|
||||
boolean isAnyDisputeRelatedToThis = arbitrationManager.getDisputedTradeIds().contains(tradeModel.getId());
|
||||
boolean isAnyDisputeRelatedToThis = arbitrationManager.getDisputedTradeIds().contains(delegateId);
|
||||
|
||||
return isAnyDisputeRelatedToThis && disputes.stream()
|
||||
.anyMatch(dispute -> {
|
||||
|
@ -155,7 +161,7 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
|||
if (transaction.getLockTime() == 0)
|
||||
return false;
|
||||
|
||||
if (transaction.getInputs() == null)
|
||||
if (transaction.getInputs() == null || transaction.getInputs().size() != 1)
|
||||
return false;
|
||||
|
||||
return transaction.getInputs().stream()
|
||||
|
@ -168,7 +174,7 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
|||
if (parentTransaction == null) {
|
||||
return false;
|
||||
}
|
||||
return isDepositTx(parentTransaction.getTxId());
|
||||
return isDepositTx(parentTransaction.getTxId().toString());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -177,33 +183,45 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
|||
return false;
|
||||
|
||||
String tradeId = tradeModel.getId();
|
||||
ObservableList<Dispute> disputes = refundManager.getDisputesAsObservableList();
|
||||
|
||||
boolean isAnyDisputeRelatedToThis = refundManager.getDisputedTradeIds().contains(tradeId);
|
||||
|
||||
if (isAnyDisputeRelatedToThis) {
|
||||
Transaction tx = btcWalletService.getTransaction(txId);
|
||||
if (tx != null) {
|
||||
for (TransactionOutput txo : tx.getOutputs()) {
|
||||
if (btcWalletService.isTransactionOutputMine(txo)) {
|
||||
try {
|
||||
Address receiverAddress = txo.getScriptPubKey().getToAddress(btcWalletService.getParams());
|
||||
Contract contract = checkNotNull(trade.getContract());
|
||||
String myPayoutAddressString = contract.isMyRoleBuyer(pubKeyRing) ?
|
||||
contract.getBuyerPayoutAddressString() :
|
||||
contract.getSellerPayoutAddressString();
|
||||
if (receiverAddress != null && myPayoutAddressString.equals(receiverAddress.toString())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return getReceiverAddressStrings(txId).contains(myPayoutAddressString);
|
||||
} catch (RuntimeException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Set<String> getReceiverAddressStrings(String txId) {
|
||||
var tuple = lastReceiverAddressStringsTuple;
|
||||
if (tuple == null || !tuple.first.equals(txId)) {
|
||||
lastReceiverAddressStringsTuple = tuple = computeReceiverAddressStringsTuple(txId);
|
||||
}
|
||||
return tuple != null ? tuple.second : ImmutableSet.of();
|
||||
}
|
||||
|
||||
private Tuple2<String, Set<String>> computeReceiverAddressStringsTuple(String txId) {
|
||||
Transaction tx = btcWalletService.getTransaction(txId);
|
||||
if (tx == null) {
|
||||
// Clear cache if the tx isn't found, as theoretically it could be added to the wallet later.
|
||||
return null;
|
||||
}
|
||||
Set<String> addressStrings = tx.getOutputs().stream()
|
||||
.filter(btcWalletService::isTransactionOutputMine)
|
||||
.map(txo -> txo.getScriptPubKey().getToAddress(btcWalletService.getParams()))
|
||||
.map(Address::toString)
|
||||
.collect(ImmutableSet.toImmutableSet());
|
||||
|
||||
return new Tuple2<>(txId, addressStrings);
|
||||
}
|
||||
|
||||
private boolean isBsqSwapTrade() {
|
||||
return tradeModel instanceof BsqSwapTrade;
|
||||
}
|
||||
|
@ -219,4 +237,27 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
|||
public Tradable asTradable() {
|
||||
return tradeModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntStream getRelatedTransactionFilter() {
|
||||
if (tradeModel instanceof Trade && !arbitrationManager.getDisputedTradeIds().contains(tradeModel.getId()) &&
|
||||
!refundManager.getDisputedTradeIds().contains(tradeModel.getId())) {
|
||||
Trade trade = (Trade) tradeModel;
|
||||
String takerFeeTxId = trade.getTakerFeeTxId();
|
||||
String offerFeeTxId = trade.getOffer() != null ? trade.getOffer().getOfferFeePaymentTxId() : null;
|
||||
String depositTxId = trade.getDepositTxId();
|
||||
String payoutTxId = trade.getPayoutTxId();
|
||||
return IntStream.of(DELAYED_PAYOUT_TX_BUCKET_INDEX, bucketIndex(takerFeeTxId), bucketIndex(offerFeeTxId),
|
||||
bucketIndex(depositTxId), bucketIndex(payoutTxId))
|
||||
.filter(i -> i >= 0);
|
||||
} else if (tradeModel instanceof BsqSwapTrade) {
|
||||
BsqSwapTrade trade = (BsqSwapTrade) tradeModel;
|
||||
String swapTxId = trade.getTxId();
|
||||
return IntStream.of(bucketIndex(swapTxId))
|
||||
.filter(i -> i >= 0);
|
||||
} else {
|
||||
// We are involved in a dispute (rare) - don't do any initial tx filtering.
|
||||
return IntStream.range(0, TX_FILTER_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,12 +17,11 @@
|
|||
|
||||
package bisq.desktop.main.funds.transactions;
|
||||
|
||||
import bisq.desktop.util.filtering.FilterableListItem;
|
||||
import bisq.desktop.components.indicator.TxConfidenceIndicator;
|
||||
import bisq.desktop.util.DisplayUtils;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
import bisq.desktop.util.filtering.FilterableListItem;
|
||||
|
||||
import bisq.core.btc.listeners.TxConfidenceListener;
|
||||
import bisq.core.btc.wallet.BsqWalletService;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.WalletService;
|
||||
|
@ -59,7 +58,6 @@ import javax.annotation.Nullable;
|
|||
|
||||
@Slf4j
|
||||
class TransactionsListItem implements FilterableListItem {
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final CoinFormatter formatter;
|
||||
private String dateString;
|
||||
private final Date date;
|
||||
|
@ -69,7 +67,6 @@ class TransactionsListItem implements FilterableListItem {
|
|||
private String details = "";
|
||||
private String addressString = "";
|
||||
private String direction = "";
|
||||
private TxConfidenceListener txConfidenceListener;
|
||||
private boolean received;
|
||||
private Coin amountAsCoin = Coin.ZERO;
|
||||
private String memo = "";
|
||||
|
@ -91,7 +88,6 @@ class TransactionsListItem implements FilterableListItem {
|
|||
// used at exportCSV
|
||||
TransactionsListItem() {
|
||||
date = null;
|
||||
btcWalletService = null;
|
||||
txId = null;
|
||||
formatter = null;
|
||||
isDustAttackTx = false;
|
||||
|
@ -105,7 +101,6 @@ class TransactionsListItem implements FilterableListItem {
|
|||
DaoFacade daoFacade,
|
||||
CoinFormatter formatter,
|
||||
long ignoreDustThreshold) {
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.formatter = formatter;
|
||||
this.memo = transaction.getMemo();
|
||||
|
||||
|
@ -197,7 +192,6 @@ class TransactionsListItem implements FilterableListItem {
|
|||
|
||||
if (optionalTradable.isPresent()) {
|
||||
tradable = optionalTradable.get();
|
||||
String tradeId = tradable.getShortId();
|
||||
if (tradable instanceof OpenOffer) {
|
||||
details = Res.get("funds.tx.createOfferFee");
|
||||
} else if (tradable instanceof Trade) {
|
||||
|
@ -304,19 +298,6 @@ class TransactionsListItem implements FilterableListItem {
|
|||
GUIUtil.updateConfidence(confidence, tooltip, txConfidenceIndicator);
|
||||
confirmations = confidence.getDepthInBlocks();
|
||||
}});
|
||||
|
||||
txConfidenceListener = new TxConfidenceListener(txId) {
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
|
||||
GUIUtil.updateConfidence(confidence, lazy().tooltip, lazy().txConfidenceIndicator);
|
||||
confirmations = confidence.getDepthInBlocks();
|
||||
}
|
||||
};
|
||||
btcWalletService.addTxConfidenceListener(txConfidenceListener);
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
btcWalletService.removeTxConfidenceListener(txConfidenceListener);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ import javafx.util.Callback;
|
|||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -201,7 +202,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
|
||||
dateColumn.setComparator(Comparator.comparing(TransactionsListItem::getDate));
|
||||
tradeIdColumn.setComparator(Comparator.comparing(o -> o.getTradable() != null ? o.getTradable().getId() : ""));
|
||||
detailsColumn.setComparator(Comparator.comparing(o -> o.getDetails()));
|
||||
detailsColumn.setComparator(Comparator.comparing(TransactionsListItem::getDetails));
|
||||
addressColumn.setComparator(Comparator.comparing(item -> item.getDirection() + item.getAddressString()));
|
||||
transactionColumn.setComparator(Comparator.comparing(TransactionsListItem::getTxId));
|
||||
amountColumn.setComparator(Comparator.comparing(TransactionsListItem::getAmountAsCoin));
|
||||
|
@ -211,9 +212,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||
tableView.getSortOrder().add(dateColumn);
|
||||
|
||||
walletChangeEventListener = wallet -> {
|
||||
updateList();
|
||||
};
|
||||
walletChangeEventListener = wallet -> updateList();
|
||||
|
||||
keyEventEventHandler = event -> {
|
||||
// Not intended to be public to users as the feature is not well tested
|
||||
|
@ -280,7 +279,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
protected void deactivate() {
|
||||
filterBox.deactivate();
|
||||
sortedList.comparatorProperty().unbind();
|
||||
observableList.forEach(TransactionsListItem::cleanup);
|
||||
btcWalletService.removeChangeEventListener(walletChangeEventListener);
|
||||
|
||||
if (scene != null)
|
||||
|
@ -290,12 +288,8 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
}
|
||||
|
||||
private void updateList() {
|
||||
List<TransactionsListItem> transactionsListItems = btcWalletService.getTransactions(false)
|
||||
.stream()
|
||||
.map(transaction -> {
|
||||
Set<Tradable> tradables = tradableRepository.getAll();
|
||||
|
||||
TransactionAwareTradable maybeTradable = tradables.stream()
|
||||
var filterSlices = new RelatedTransactionFilterSlices(tradables.stream()
|
||||
.map(tradable -> {
|
||||
if (tradable instanceof OpenOffer) {
|
||||
return new TransactionAwareOpenOffer((OpenOffer) tradable);
|
||||
|
@ -311,7 +305,13 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
return null;
|
||||
}
|
||||
})
|
||||
.filter(tradable -> tradable != null && tradable.isRelatedToTransaction(transaction))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toUnmodifiableList()));
|
||||
|
||||
List<TransactionsListItem> transactionsListItems = btcWalletService.getTransactions(false)
|
||||
.stream()
|
||||
.map(transaction -> {
|
||||
TransactionAwareTradable maybeTradable = filterSlices.getAllRelatedTradables(transaction)
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
|
||||
|
@ -327,7 +327,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
})
|
||||
.collect(Collectors.toList());
|
||||
// are sorted by getRecentTransactions
|
||||
transactionsListItems.forEach(TransactionsListItem::cleanup);
|
||||
observableList.setAll(transactionsListItems);
|
||||
}
|
||||
|
||||
|
@ -419,15 +418,13 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
TransactionsListItem> column) {
|
||||
return new TableCell<>() {
|
||||
|
||||
private HyperlinkWithIcon hyperlinkWithIcon;
|
||||
|
||||
@Override
|
||||
public void updateItem(final TransactionsListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
if (item.isDustAttackTx()) {
|
||||
hyperlinkWithIcon = new HyperlinkWithIcon(item.getDetails(), AwesomeIcon.WARNING_SIGN);
|
||||
var hyperlinkWithIcon = new HyperlinkWithIcon(item.getDetails(), AwesomeIcon.WARNING_SIGN);
|
||||
hyperlinkWithIcon.setOnAction(event -> new Popup().warning(Res.get("funds.tx.dustAttackTx.popup")).show());
|
||||
setGraphic(hyperlinkWithIcon);
|
||||
} else {
|
||||
|
|
|
@ -66,13 +66,13 @@ public class TransactionAwareTradeTest {
|
|||
|
||||
@Test
|
||||
public void testIsRelatedToTransactionWhenPayoutTx() {
|
||||
when(delegate.getPayoutTx().getTxId()).thenReturn(XID);
|
||||
when(delegate.getPayoutTxId()).thenReturn(XID.toString());
|
||||
assertTrue(trade.isRelatedToTransaction(transaction));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsRelatedToTransactionWhenDepositTx() {
|
||||
when(delegate.getDepositTx().getTxId()).thenReturn(XID);
|
||||
when(delegate.getDepositTxId()).thenReturn(XID.toString());
|
||||
assertTrue(trade.isRelatedToTransaction(transaction));
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue