Merge branch 'feature/refactor-transaction-view' of https://github.com/tau3/exchange into tau3-feature/refactor-transaction-view

This commit is contained in:
Manfred Karrer 2018-02-01 13:38:29 -05:00
commit 6bce8f0cc6
No known key found for this signature in database
GPG key ID: 401250966A6B2C46
18 changed files with 763 additions and 106 deletions

View file

@ -224,6 +224,7 @@ public abstract class Trade implements Tradable, Model {
// Persistable
// Immutable
@Nullable
@Getter
private final Offer offer;
@Getter

View file

@ -27,6 +27,10 @@ import io.bisq.gui.common.view.CachingViewLoader;
import io.bisq.gui.common.view.ViewFactory;
import io.bisq.gui.common.view.ViewLoader;
import io.bisq.gui.common.view.guice.InjectorViewFactory;
import io.bisq.gui.main.funds.transactions.DisplayedTransactionsFactory;
import io.bisq.gui.main.funds.transactions.TradableRepository;
import io.bisq.gui.main.funds.transactions.TransactionAwareTradableFactory;
import io.bisq.gui.main.funds.transactions.TransactionListItemFactory;
import io.bisq.gui.main.offer.offerbook.OfferBook;
import io.bisq.gui.main.overlays.windows.TorNetworkSettingsWindow;
import io.bisq.gui.util.BSFormatter;
@ -66,6 +70,11 @@ public class GuiModule extends AppModule {
bind(Stage.class).toInstance(primaryStage);
bind(TradableRepository.class).in(Singleton.class);
bind(TransactionListItemFactory.class).in(Singleton.class);
bind(TransactionAwareTradableFactory.class).in(Singleton.class);
bind(DisplayedTransactionsFactory.class).in(Singleton.class);
bindConstant().annotatedWith(Names.named(AppOptionKeys.APP_NAME_KEY)).to(environment.getRequiredProperty(AppOptionKeys.APP_NAME_KEY));
}
}

View file

@ -0,0 +1,68 @@
/*
* 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 io.bisq.gui.main.funds.transactions;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.trade.Tradable;
import org.bitcoinj.core.Transaction;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
class DisplayedTransactions extends ObservableListDecorator<TransactionsListItem> {
private final BtcWalletService btcWalletService;
private final TradableRepository tradableRepository;
private final TransactionListItemFactory transactionListItemFactory;
private final TransactionAwareTradableFactory transactionAwareTradableFactory;
DisplayedTransactions(BtcWalletService btcWalletService, TradableRepository tradableRepository,
TransactionListItemFactory transactionListItemFactory,
TransactionAwareTradableFactory transactionAwareTradableFactory) {
this.btcWalletService = btcWalletService;
this.tradableRepository = tradableRepository;
this.transactionListItemFactory = transactionListItemFactory;
this.transactionAwareTradableFactory = transactionAwareTradableFactory;
}
void update() {
List<TransactionsListItem> transactionsListItems = getTransactionListItems();
// are sorted by getRecentTransactions
forEach(TransactionsListItem::cleanup);
setAll(transactionsListItems);
}
private List<TransactionsListItem> getTransactionListItems() {
Set<Transaction> transactions = btcWalletService.getTransactions(false);
return transactions.stream()
.map(this::convertTransactionToListItem)
.collect(Collectors.toList());
}
private TransactionsListItem convertTransactionToListItem(Transaction transaction) {
Set<Tradable> tradables = tradableRepository.getAll();
TransactionAwareTradable maybeTradable = tradables.stream()
.map(transactionAwareTradableFactory::create)
.filter(tradable -> tradable.isRelatedToTransaction(transaction))
.findAny()
.orElse(null);
return transactionListItemFactory.create(transaction, maybeTradable);
}
}

View file

@ -0,0 +1,44 @@
/*
* 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 io.bisq.gui.main.funds.transactions;
import io.bisq.core.btc.wallet.BtcWalletService;
import javax.inject.Inject;
public class DisplayedTransactionsFactory {
private final BtcWalletService btcWalletService;
private final TradableRepository tradableRepository;
private final TransactionListItemFactory transactionListItemFactory;
private final TransactionAwareTradableFactory transactionAwareTradableFactory;
@Inject
DisplayedTransactionsFactory(BtcWalletService btcWalletService, TradableRepository tradableRepository,
TransactionListItemFactory transactionListItemFactory,
TransactionAwareTradableFactory transactionAwareTradableFactory) {
this.btcWalletService = btcWalletService;
this.tradableRepository = tradableRepository;
this.transactionListItemFactory = transactionListItemFactory;
this.transactionAwareTradableFactory = transactionAwareTradableFactory;
}
DisplayedTransactions create() {
return new DisplayedTransactions(btcWalletService, tradableRepository, transactionListItemFactory,
transactionAwareTradableFactory);
}
}

View file

@ -0,0 +1,39 @@
/*
* 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 io.bisq.gui.main.funds.transactions;
import io.bisq.core.trade.Tradable;
import org.bitcoinj.core.Transaction;
class DummyTransactionAwareTradable implements TransactionAwareTradable {
private final Tradable delegate;
DummyTransactionAwareTradable(Tradable delegate) {
this.delegate = delegate;
}
@Override
public boolean isRelatedToTransaction(Transaction transaction) {
return false;
}
@Override
public Tradable asTradable() {
return delegate;
}
}

View file

@ -0,0 +1,50 @@
/*
* 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 io.bisq.gui.main.funds.transactions;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;
import org.jetbrains.annotations.NotNull;
import java.util.AbstractCollection;
import java.util.AbstractList;
import java.util.Collection;
import java.util.Iterator;
class ObservableListDecorator<T> extends AbstractList<T> {
private final ObservableList<T> delegate = FXCollections.observableArrayList();
SortedList<T> asSortedList() {
return new SortedList<>(delegate);
}
void setAll(Collection<? extends T> elements) {
delegate.setAll(elements);
}
@Override
public T get(int index) {
return delegate.get(index);
}
@Override
public int size() {
return delegate.size();
}
}

View file

@ -0,0 +1,53 @@
/*
* 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 io.bisq.gui.main.funds.transactions;
import com.google.common.collect.ImmutableSet;
import io.bisq.core.offer.OpenOfferManager;
import io.bisq.core.trade.Tradable;
import io.bisq.core.trade.TradeManager;
import io.bisq.core.trade.closed.ClosedTradableManager;
import io.bisq.core.trade.failed.FailedTradesManager;
import javax.inject.Inject;
import java.util.Set;
public class TradableRepository {
private final OpenOfferManager openOfferManager;
private final TradeManager tradeManager;
private final ClosedTradableManager closedTradableManager;
private final FailedTradesManager failedTradesManager;
@Inject
TradableRepository(OpenOfferManager openOfferManager, TradeManager tradeManager,
ClosedTradableManager closedTradableManager, FailedTradesManager failedTradesManager) {
this.openOfferManager = openOfferManager;
this.tradeManager = tradeManager;
this.closedTradableManager = closedTradableManager;
this.failedTradesManager = failedTradesManager;
}
Set<Tradable> getAll() {
return ImmutableSet.<Tradable>builder()
.addAll(openOfferManager.getObservableList())
.addAll(tradeManager.getTradableList())
.addAll(closedTradableManager.getClosedTradables())
.addAll(failedTradesManager.getFailedTrades())
.build();
}
}

View file

@ -0,0 +1,44 @@
/*
* 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 io.bisq.gui.main.funds.transactions;
import io.bisq.core.offer.Offer;
import io.bisq.core.offer.OpenOffer;
import io.bisq.core.trade.Tradable;
import org.bitcoinj.core.Transaction;
class TransactionAwareOpenOffer implements TransactionAwareTradable {
private final OpenOffer delegate;
TransactionAwareOpenOffer(OpenOffer delegate) {
this.delegate = delegate;
}
public boolean isRelatedToTransaction(Transaction transaction) {
Offer offer = delegate.getOffer();
String paymentTxId = offer.getOfferFeePaymentTxId();
String txId = transaction.getHashAsString();
return paymentTxId.equals(txId);
}
public Tradable asTradable() {
return delegate;
}
}

View file

@ -0,0 +1,27 @@
/*
* 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 io.bisq.gui.main.funds.transactions;
import io.bisq.core.trade.Tradable;
import org.bitcoinj.core.Transaction;
interface TransactionAwareTradable {
boolean isRelatedToTransaction(Transaction transaction);
Tradable asTradable();
}

View file

@ -0,0 +1,44 @@
/*
* 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 io.bisq.gui.main.funds.transactions;
import io.bisq.core.arbitration.DisputeManager;
import io.bisq.core.offer.OpenOffer;
import io.bisq.core.trade.Tradable;
import io.bisq.core.trade.Trade;
import javax.inject.Inject;
public class TransactionAwareTradableFactory {
private final DisputeManager disputeManager;
@Inject
TransactionAwareTradableFactory(DisputeManager disputeManager) {
this.disputeManager = disputeManager;
}
TransactionAwareTradable create(Tradable delegate) {
if (delegate instanceof OpenOffer) {
return new TransactionAwareOpenOffer((OpenOffer) delegate);
} else if (delegate instanceof Trade) {
return new TransactionAwareTrade((Trade) delegate, disputeManager);
} else {
return new DummyTransactionAwareTradable(delegate);
}
}
}

View file

@ -0,0 +1,93 @@
/*
* 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 io.bisq.gui.main.funds.transactions;
import io.bisq.core.arbitration.Dispute;
import io.bisq.core.arbitration.DisputeManager;
import io.bisq.core.offer.Offer;
import io.bisq.core.trade.Tradable;
import io.bisq.core.trade.Trade;
import javafx.collections.ObservableList;
import org.bitcoinj.core.Transaction;
import java.util.Optional;
class TransactionAwareTrade implements TransactionAwareTradable {
private final Trade delegate;
private final DisputeManager disputeManager;
TransactionAwareTrade(Trade delegate, DisputeManager disputeManager) {
this.delegate = delegate;
this.disputeManager = disputeManager;
}
@Override
public boolean isRelatedToTransaction(Transaction transaction) {
String txId = transaction.getHashAsString();
boolean isTakerOfferFeeTx = txId.equals(delegate.getTakerFeeTxId());
boolean isOfferFeeTx = isOfferFeeTx(txId);
boolean isDepositTx = isDepositTx(txId);
boolean isPayoutTx = isPayoutTx(txId);
boolean isDisputedPayoutTx = isDisputedPayoutTx(txId);
return isTakerOfferFeeTx || isOfferFeeTx || isDepositTx || isPayoutTx || isDisputedPayoutTx;
}
private boolean isPayoutTx(String txId) {
return Optional.ofNullable(delegate.getPayoutTx())
.map(Transaction::getHashAsString)
.map(hash -> hash.equals(txId))
.orElse(false);
}
private boolean isDepositTx(String txId) {
return Optional.ofNullable(delegate.getDepositTx())
.map(Transaction::getHashAsString)
.map(hash -> hash.equals(txId))
.orElse(false);
}
private boolean isOfferFeeTx(String txId) {
return Optional.ofNullable(delegate.getOffer())
.map(Offer::getOfferFeePaymentTxId)
.map(paymentTxId -> paymentTxId.equals(txId))
.orElse(false);
}
private boolean isDisputedPayoutTx(String txId) {
String delegateId = delegate.getId();
ObservableList<Dispute> disputes = disputeManager.getDisputesAsObservableList();
return disputes.stream()
.anyMatch(dispute -> {
String disputePayoutTxId = dispute.getDisputePayoutTxId();
boolean isDisputePayoutTx = txId.equals(disputePayoutTxId);
String disputeTradeId = dispute.getTradeId();
boolean isDisputeRelatedToThis = delegateId.equals(disputeTradeId);
return isDisputePayoutTx && isDisputeRelatedToThis;
});
}
@Override
public Tradable asTradable() {
return delegate;
}
}

View file

@ -0,0 +1,49 @@
/*
* 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 io.bisq.gui.main.funds.transactions;
import io.bisq.core.btc.wallet.BsqWalletService;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.trade.Tradable;
import io.bisq.gui.util.BSFormatter;
import org.bitcoinj.core.Transaction;
import javax.annotation.Nullable;
import javax.inject.Inject;
import java.util.Optional;
public class TransactionListItemFactory {
private final BtcWalletService btcWalletService;
private final BsqWalletService bsqWalletService;
private final BSFormatter formatter;
@Inject
TransactionListItemFactory(BtcWalletService btcWalletService, BsqWalletService bsqWalletService,
BSFormatter formatter) {
this.btcWalletService = btcWalletService;
this.bsqWalletService = bsqWalletService;
this.formatter = formatter;
}
TransactionsListItem create(Transaction transaction, @Nullable TransactionAwareTradable tradable) {
Optional<Tradable> maybeTradable = Optional.ofNullable(tradable)
.map(TransactionAwareTradable::asTradable);
return new TransactionsListItem(transaction, btcWalletService, bsqWalletService, maybeTradable, formatter);
}
}

View file

@ -23,17 +23,11 @@ import io.bisq.common.locale.Res;
import io.bisq.common.util.Tuple2;
import io.bisq.common.util.Tuple4;
import io.bisq.common.util.Utilities;
import io.bisq.core.arbitration.DisputeManager;
import io.bisq.core.btc.wallet.BsqWalletService;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.btc.wallet.WalletsSetup;
import io.bisq.core.offer.OpenOffer;
import io.bisq.core.offer.OpenOfferManager;
import io.bisq.core.trade.Tradable;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.TradeManager;
import io.bisq.core.trade.closed.ClosedTradableManager;
import io.bisq.core.trade.failed.FailedTradesManager;
import io.bisq.core.user.Preferences;
import io.bisq.gui.common.view.ActivatableView;
import io.bisq.gui.common.view.FxmlView;
@ -46,7 +40,6 @@ import io.bisq.gui.util.BSFormatter;
import io.bisq.gui.util.GUIUtil;
import io.bisq.network.p2p.P2PService;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;
import javafx.event.EventHandler;
@ -71,7 +64,6 @@ import javax.inject.Inject;
import java.text.DateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@FxmlView
public class TransactionsView extends ActivatableView<VBox, Void> {
@ -83,21 +75,15 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
@FXML
Button exportButton;
private final ObservableList<TransactionsListItem> observableList = FXCollections.observableArrayList();
private final SortedList<TransactionsListItem> sortedList = new SortedList<>(observableList);
private final DisplayedTransactions displayedTransactions;
private final SortedList<TransactionsListItem> sortedDisplayedTransactions;
private final BtcWalletService btcWalletService;
private final BsqWalletService bsqWalletService;
private final P2PService p2PService;
private final WalletsSetup walletsSetup;
private final TradeManager tradeManager;
private final OpenOfferManager openOfferManager;
private final ClosedTradableManager closedTradableManager;
private final FailedTradesManager failedTradesManager;
private final BSFormatter formatter;
private final Preferences preferences;
private final TradeDetailsWindow tradeDetailsWindow;
private final DisputeManager disputeManager;
private final Stage stage;
private final OfferDetailsWindow offerDetailsWindow;
@SuppressWarnings("deprecation")
@ -111,33 +97,24 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
@Inject
private TransactionsView(BtcWalletService btcWalletService,
BsqWalletService bsqWalletService,
P2PService p2PService,
WalletsSetup walletsSetup,
TradeManager tradeManager,
OpenOfferManager openOfferManager,
ClosedTradableManager closedTradableManager,
FailedTradesManager failedTradesManager,
BSFormatter formatter,
Preferences preferences,
TradeDetailsWindow tradeDetailsWindow,
DisputeManager disputeManager,
Stage stage,
OfferDetailsWindow offerDetailsWindow) {
OfferDetailsWindow offerDetailsWindow,
DisplayedTransactionsFactory displayedTransactionsFactory) {
this.btcWalletService = btcWalletService;
this.bsqWalletService = bsqWalletService;
this.p2PService = p2PService;
this.walletsSetup = walletsSetup;
this.tradeManager = tradeManager;
this.openOfferManager = openOfferManager;
this.closedTradableManager = closedTradableManager;
this.failedTradesManager = failedTradesManager;
this.formatter = formatter;
this.preferences = preferences;
this.tradeDetailsWindow = tradeDetailsWindow;
this.disputeManager = disputeManager;
this.stage = stage;
this.offerDetailsWindow = offerDetailsWindow;
this.displayedTransactions = displayedTransactionsFactory.create();
this.sortedDisplayedTransactions = displayedTransactions.asSortedList();
}
@Override
@ -161,17 +138,16 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
setConfidenceColumnCellFactory();
setRevertTxColumnCellFactory();
dateColumn.setComparator((o1, o2) -> o1.getDate().compareTo(o2.getDate()));
dateColumn.setComparator(Comparator.comparing(TransactionsListItem::getDate));
detailsColumn.setComparator((o1, o2) -> {
String id1 = o1.getTradable() != null ? o1.getTradable().getId() : o1.getDetails();
String id2 = o2.getTradable() != null ? o2.getTradable().getId() : o2.getDetails();
return id1.compareTo(id2);
});
addressColumn.setComparator((o1, o2) -> o1.getAddressString().compareTo(o2.getAddressString()));
transactionColumn.setComparator((o1, o2) -> o1.getTxId().compareTo(o2.getTxId()));
amountColumn.setComparator((o1, o2) -> o1.getAmountAsCoin().compareTo(o2.getAmountAsCoin()));
confidenceColumn.setComparator((o1, o2) -> Double.valueOf(o1.getTxConfidenceIndicator().getProgress())
.compareTo(o2.getTxConfidenceIndicator().getProgress()));
addressColumn.setComparator(Comparator.comparing(TransactionsListItem::getAddressString));
transactionColumn.setComparator(Comparator.comparing(TransactionsListItem::getTxId));
amountColumn.setComparator(Comparator.comparing(TransactionsListItem::getAmountAsCoin));
confidenceColumn.setComparator(Comparator.comparingDouble(item -> item.getTxConfidenceIndicator().getProgress()));
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
tableView.getSortOrder().add(dateColumn);
@ -180,17 +156,17 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
walletEventListener = new WalletEventListener() {
@Override
public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
updateList();
displayedTransactions.update();
}
@Override
public void onCoinsSent(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
updateList();
displayedTransactions.update();
}
@Override
public void onReorganize(Wallet wallet) {
updateList();
displayedTransactions.update();
}
@Override
@ -199,17 +175,17 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
@Override
public void onWalletChanged(Wallet wallet) {
updateList();
displayedTransactions.update();
}
@Override
public void onScriptsChanged(Wallet wallet, List<Script> scripts, boolean isAddingScripts) {
updateList();
displayedTransactions.update();
}
@Override
public void onKeysAdded(List<ECKey> keys) {
updateList();
displayedTransactions.update();
}
};
@ -225,9 +201,9 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
@Override
protected void activate() {
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
tableView.setItems(sortedList);
updateList();
sortedDisplayedTransactions.comparatorProperty().bind(tableView.comparatorProperty());
tableView.setItems(sortedDisplayedTransactions);
displayedTransactions.update();
btcWalletService.addEventListener(walletEventListener);
@ -256,14 +232,14 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
};
GUIUtil.exportCSV("transactions.csv", headerConverter, contentConverter,
new TransactionsListItem(), sortedList, stage);
new TransactionsListItem(), sortedDisplayedTransactions, stage);
});
}
@Override
protected void deactivate() {
sortedList.comparatorProperty().unbind();
observableList.forEach(TransactionsListItem::cleanup);
sortedDisplayedTransactions.comparatorProperty().unbind();
displayedTransactions.forEach(TransactionsListItem::cleanup);
btcWalletService.removeEventListener(walletEventListener);
if (scene != null)
@ -272,55 +248,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
exportButton.setOnAction(null);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void updateList() {
Stream<Tradable> concat1 = Stream.concat(openOfferManager.getObservableList().stream(), tradeManager.getTradableList().stream());
Stream<Tradable> concat2 = Stream.concat(concat1, closedTradableManager.getClosedTradables().stream());
Stream<Tradable> concat3 = Stream.concat(concat2, failedTradesManager.getFailedTrades().stream());
Set<Tradable> all = concat3.collect(Collectors.toSet());
Set<Transaction> transactions = btcWalletService.getTransactions(false);
List<TransactionsListItem> transactionsListItems = transactions.stream()
.map(transaction -> {
Optional<Tradable> tradableOptional = all.stream()
.filter(tradable -> {
String txId = transaction.getHashAsString();
if (tradable instanceof OpenOffer)
return tradable.getOffer().getOfferFeePaymentTxId().equals(txId);
else if (tradable instanceof Trade) {
Trade trade = (Trade) tradable;
boolean isTakeOfferFeeTx = txId.equals(trade.getTakerFeeTxId());
boolean isOfferFeeTx = trade.getOffer() != null &&
txId.equals(trade.getOffer().getOfferFeePaymentTxId());
boolean isDepositTx = trade.getDepositTx() != null &&
trade.getDepositTx().getHashAsString().equals(txId);
boolean isPayoutTx = trade.getPayoutTx() != null &&
trade.getPayoutTx().getHashAsString().equals(txId);
boolean isDisputedPayoutTx = disputeManager.getDisputesAsObservableList().stream()
.filter(dispute -> txId.equals(dispute.getDisputePayoutTxId()) &&
tradable.getId().equals(dispute.getTradeId()))
.findAny()
.isPresent();
return isTakeOfferFeeTx || isOfferFeeTx || isDepositTx || isPayoutTx || isDisputedPayoutTx;
} else
return false;
})
.findAny();
return new TransactionsListItem(transaction, btcWalletService, bsqWalletService, tradableOptional, formatter);
})
.collect(Collectors.toList());
// are sorted by getRecentTransactions
observableList.forEach(TransactionsListItem::cleanup);
observableList.setAll(transactionsListItems);
}
private void openTxInBlockExplorer(TransactionsListItem item) {
if (item.getTxId() != null)
GUIUtil.openWebPage(preferences.getBlockChainExplorer().txUrl + item.getTxId());
@ -599,7 +526,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
private void showStatisticsPopup() {
Map<Long, List<Coin>> map = new HashMap<>();
Map<String, Tuple4<Date, Integer, Integer, Integer>> dataByDayMap = new HashMap<>();
observableList.stream().forEach(item -> {
displayedTransactions.forEach(item -> {
Coin amountAsCoin = item.getAmountAsCoin();
List<Coin> amounts;
long key = amountAsCoin.getValue();
@ -643,12 +570,12 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
});
StringBuilder stringBuilder = new StringBuilder();
map.entrySet().stream().forEach(e -> {
map.forEach((key, value) -> {
// This is not intended for the public so we don't translate here
stringBuilder.append("No. of transactions for amount ").
append(formatter.formatCoinWithCode(Coin.valueOf(e.getKey()))).
append(formatter.formatCoinWithCode(Coin.valueOf(key))).
append(": ").
append(e.getValue().size()).
append(value.size()).
append("\n");
});
@ -656,16 +583,15 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
map(e -> {
Tuple4<Date, Integer, Integer, Integer> data = e.getValue();
return new Tuple4<>(e.getKey(), data.first, data.second, new Tuple2<>(data.third, data.forth));
}).
collect(Collectors.toList());
sortedDataByDayList.sort((o1, o2) -> o2.second.compareTo(o1.second));
}).sorted((o1, o2) -> o2.second.compareTo(o1.second))
.collect(Collectors.toList());
StringBuilder transactionsByDayStringBuilder = new StringBuilder();
StringBuilder offersStringBuilder = new StringBuilder();
StringBuilder tradesStringBuilder = new StringBuilder();
StringBuilder allStringBuilder = new StringBuilder();
// This is not intended for the public so we don't translate here
allStringBuilder.append(Res.get("shared.date")).append(";").append("Offers").append(";").append("Trades").append("\n");
sortedDataByDayList.stream().forEach(tuple4 -> {
sortedDataByDayList.forEach(tuple4 -> {
offersStringBuilder.append(tuple4.forth.first).append(",");
tradesStringBuilder.append(tuple4.forth.second).append(",");
allStringBuilder.append(tuple4.first).append(";").append(tuple4.forth.first).append(";").append(tuple4.forth.second).append("\n");

View file

@ -0,0 +1,60 @@
package io.bisq.gui.main.funds.transactions;
import com.google.common.collect.Sets;
import io.bisq.core.btc.wallet.BtcWalletService;
import org.bitcoinj.core.Transaction;
import org.junit.Test;
import java.util.Collections;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
public class DisplayedTransactionsTest {
@Test
public void testUpdate() {
Set<Transaction> transactions = Sets.newHashSet(mock(Transaction.class), mock(Transaction.class));
BtcWalletService walletService = mock(BtcWalletService.class);
when(walletService.getTransactions(false)).thenReturn(transactions);
TransactionListItemFactory transactionListItemFactory = mock(TransactionListItemFactory.class,
RETURNS_DEEP_STUBS);
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
DisplayedTransactions testedEntity = new DisplayedTransactions(
walletService,
mock(TradableRepository.class),
transactionListItemFactory,
mock(TransactionAwareTradableFactory.class));
testedEntity.update();
assertEquals(transactions.size(), testedEntity.size());
}
@Test
public void testUpdateWhenRepositoryIsEmpty(){
BtcWalletService walletService = mock(BtcWalletService.class);
when(walletService.getTransactions(false))
.thenReturn(Collections.singleton(mock(Transaction.class)));
TradableRepository tradableRepository = mock(TradableRepository.class);
when(tradableRepository.getAll()).thenReturn(Collections.emptySet());
TransactionListItemFactory transactionListItemFactory = mock(TransactionListItemFactory.class);
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
DisplayedTransactions testedEntity = new DisplayedTransactions(
walletService,
tradableRepository,
transactionListItemFactory,
mock(TransactionAwareTradableFactory.class));
testedEntity.update();
assertEquals(1, testedEntity.size());
verify(transactionListItemFactory).create(any(), nullable(TransactionAwareTradable.class));
}
}

View file

@ -0,0 +1,37 @@
package io.bisq.gui.main.funds.transactions;
import com.google.common.collect.Lists;
import org.junit.Test;
import java.util.Collection;
import java.util.function.Supplier;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class ObservableListDecoratorTest {
@Test
public void testSetAll() {
ObservableListDecorator<Integer> list = new ObservableListDecorator<>();
Collection<Integer> state = Lists.newArrayList(3, 2, 1);
list.setAll(state);
assertEquals(state, list);
state = Lists.newArrayList(0, 0, 0, 0);
list.setAll(state);
assertEquals(state, list);
}
@Test
public void testForEach() {
ObservableListDecorator<Supplier> list = new ObservableListDecorator<>();
Collection<Supplier> state = Lists.newArrayList(mock(Supplier.class), mock(Supplier.class));
list.setAll(state);
assertEquals(state, list);
list.forEach(Supplier::get);
state.forEach(supplier -> verify(supplier).get());
}
}

View file

@ -0,0 +1,28 @@
package io.bisq.gui.main.funds.transactions;
import io.bisq.core.arbitration.DisputeManager;
import io.bisq.core.offer.OpenOffer;
import io.bisq.core.trade.Tradable;
import io.bisq.core.trade.Trade;
import org.bitcoinj.core.Transaction;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.mock;
public class TransactionAwareTradableFactoryTest {
@Test
public void testCreateWhenNotOpenOfferOrTrade() {
DisputeManager manager = mock(DisputeManager.class);
TransactionAwareTradableFactory factory = new TransactionAwareTradableFactory(manager);
Tradable delegate = mock(Tradable.class);
assertFalse(delegate instanceof OpenOffer);
assertFalse(delegate instanceof Trade);
TransactionAwareTradable tradable = factory.create(delegate);
assertFalse(tradable.isRelatedToTransaction(mock(Transaction.class)));
}
}

View file

@ -0,0 +1,79 @@
package io.bisq.gui.main.funds.transactions;
import io.bisq.core.arbitration.Dispute;
import io.bisq.core.arbitration.DisputeManager;
import io.bisq.core.trade.Trade;
import javafx.collections.FXCollections;
import org.bitcoinj.core.Transaction;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.Collections;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
@RunWith(PowerMockRunner.class)
@PrepareForTest(Dispute.class)
@SuppressWarnings("ConstantConditions")
public class TransactionAwareTradeTest {
private static final String XID = "123";
private Transaction transaction;
private DisputeManager manager;
private Trade delegate;
private TransactionAwareTradable trade;
@Before
public void setUp() {
this.transaction = mock(Transaction.class);
when(transaction.getHashAsString()).thenReturn(XID);
this.delegate = mock(Trade.class, RETURNS_DEEP_STUBS);
this.manager = mock(DisputeManager.class, RETURNS_DEEP_STUBS);
this.trade = new TransactionAwareTrade(this.delegate, this.manager);
}
@Test
public void testIsRelatedToTransactionWhenTakerOfferFeeTx() {
when(delegate.getTakerFeeTxId()).thenReturn(XID);
assertTrue(trade.isRelatedToTransaction(transaction));
}
@Test
public void testIsRelatedToTransactionWhenPayoutTx() {
when(delegate.getPayoutTx().getHashAsString()).thenReturn(XID);
assertTrue(trade.isRelatedToTransaction(transaction));
}
@Test
public void testIsRelatedToTransactionWhenDepositTx() {
when(delegate.getDepositTx().getHashAsString()).thenReturn(XID);
assertTrue(trade.isRelatedToTransaction(transaction));
}
@Test
public void testIsRelatedToTransactionWhenOfferFeeTx() {
when(delegate.getOffer().getOfferFeePaymentTxId()).thenReturn(XID);
assertTrue(trade.isRelatedToTransaction(transaction));
}
@Test
public void testIsRelatedToTransactionWhenDisputedPayoutTx() {
final String tradeId = "7";
Dispute dispute = mock(Dispute.class);
when(dispute.getDisputePayoutTxId()).thenReturn(XID);
when(dispute.getTradeId()).thenReturn(tradeId);
when(manager.getDisputesAsObservableList())
.thenReturn(FXCollections.observableArrayList(Collections.singleton(dispute)));
when(delegate.getId()).thenReturn(tradeId);
assertTrue(trade.isRelatedToTransaction(transaction));
}
}

10
pom.xml
View file

@ -208,13 +208,19 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.7.5</version>
<version>2.8.9</version>
<exclusions>
<exclusion>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>
<dependency>