Merge pull request #5963 from xyzmaker123/transactions-filtering

Add missing filtering on lists
This commit is contained in:
Christoph Atteneder 2022-01-26 09:59:53 +01:00 committed by GitHub
commit ffd2a92ae9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 1405 additions and 1650 deletions

View file

@ -175,16 +175,6 @@ public class TradeUtil {
return getCurrencyPair(trade.getOffer().getCurrencyCode());
}
public String getPaymentMethodNameWithCountryCode(Trade trade) {
if (trade == null)
return "";
Offer offer = trade.getOffer();
checkNotNull(offer);
checkNotNull(offer.getPaymentMethod());
return offer.getPaymentMethodNameWithCountryCode();
}
/**
* Returns a string describing a trader's role for a given trade.
* @param trade Trade

View file

@ -0,0 +1,73 @@
/*
* 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.components.list;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.InputTextField;
import bisq.desktop.util.filtering.FilterableListItem;
import bisq.core.locale.Res;
import javafx.scene.control.TableView;
import javafx.scene.layout.HBox;
import javafx.geometry.Insets;
import javafx.beans.value.ChangeListener;
import javafx.collections.transformation.FilteredList;
public class FilterBox extends HBox {
private final InputTextField textField;
private FilteredList<? extends FilterableListItem> filteredList;
private ChangeListener<String> listener;
public FilterBox() {
super();
setSpacing(5.0);
AutoTooltipLabel label = new AutoTooltipLabel(Res.get("shared.filter"));
HBox.setMargin(label, new Insets(5.0, 0, 0, 10.0));
textField = new InputTextField();
textField.setMinWidth(500);
getChildren().addAll(label, textField);
}
public void initialize(FilteredList<? extends FilterableListItem> filteredList,
TableView<? extends FilterableListItem> tableView) {
this.filteredList = filteredList;
listener = (observable, oldValue, newValue) -> {
tableView.getSelectionModel().clearSelection();
applyFilteredListPredicate(textField.getText());
};
}
public void activate() {
textField.textProperty().addListener(listener);
applyFilteredListPredicate(textField.getText());
}
public void deactivate() {
textField.textProperty().removeListener(listener);
}
private void applyFilteredListPredicate(String filterString) {
filteredList.setPredicate(item -> item.match(filterString));
}
}

View file

@ -17,6 +17,7 @@
package bisq.desktop.main.funds.deposit;
import bisq.desktop.util.filtering.FilterableListItem;
import bisq.desktop.components.indicator.TxConfidenceIndicator;
import bisq.desktop.util.GUIUtil;
@ -34,6 +35,8 @@ import org.bitcoinj.core.TransactionConfidence;
import com.google.common.base.Suppliers;
import org.apache.commons.lang3.StringUtils;
import javafx.scene.control.Tooltip;
import javafx.beans.property.SimpleStringProperty;
@ -44,7 +47,7 @@ import java.util.function.Supplier;
import lombok.extern.slf4j.Slf4j;
@Slf4j
class DepositListItem {
class DepositListItem implements FilterableListItem {
private final StringProperty balance = new SimpleStringProperty();
private final BtcWalletService walletService;
private Coin balanceAsCoin;
@ -149,4 +152,18 @@ class DepositListItem {
public int getNumTxOutputs() {
return numTxOutputs;
}
@Override
public boolean match(String filterString) {
if (filterString.isEmpty()) {
return true;
}
if (StringUtils.containsIgnoreCase(getAddressString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getUsage(), filterString)) {
return true;
}
return getBalance().contains(filterString);
}
}

View file

@ -23,12 +23,13 @@
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.geometry.Insets?>
<?import bisq.desktop.components.list.FilterBox?>
<VBox fx:id="root" fx:controller="bisq.desktop.main.funds.deposit.DepositView"
spacing="10" xmlns:fx="http://javafx.com/fxml">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0"/>
</padding>
<FilterBox fx:id="filterBox" />
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="addressColumn" minWidth="320"/>

View file

@ -25,6 +25,7 @@ import bisq.desktop.components.ExternalHyperlink;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.InputTextField;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.components.list.FilterBox;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.main.overlays.windows.QRCodeWindow;
import bisq.desktop.util.GUIUtil;
@ -80,6 +81,7 @@ import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.util.Callback;
@ -91,7 +93,10 @@ import java.util.concurrent.TimeUnit;
import org.jetbrains.annotations.NotNull;
import static bisq.desktop.util.FormBuilder.*;
import static bisq.desktop.util.FormBuilder.addAddressTextField;
import static bisq.desktop.util.FormBuilder.addButtonCheckBoxWithBox;
import static bisq.desktop.util.FormBuilder.addInputTextField;
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
@FxmlView
public class DepositView extends ActivatableView<VBox, Void> {
@ -99,6 +104,8 @@ public class DepositView extends ActivatableView<VBox, Void> {
@FXML
GridPane gridPane;
@FXML
FilterBox filterBox;
@FXML
TableView<DepositListItem> tableView;
@FXML
TableColumn<DepositListItem, DepositListItem> addressColumn, balanceColumn, confirmationsColumn, usageColumn;
@ -114,7 +121,8 @@ public class DepositView extends ActivatableView<VBox, Void> {
private final CoinFormatter formatter;
private String paymentLabelString;
private final ObservableList<DepositListItem> observableList = FXCollections.observableArrayList();
private final SortedList<DepositListItem> sortedList = new SortedList<>(observableList);
private final FilteredList<DepositListItem> filteredList = new FilteredList<>(observableList);
private final SortedList<DepositListItem> sortedList = new SortedList<>(filteredList);
private BalanceListener balanceListener;
private Subscription amountTextFieldSubscription;
private ChangeListener<DepositListItem> tableViewSelectionListener;
@ -135,7 +143,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
@Override
public void initialize() {
filterBox.initialize(filteredList, tableView);
paymentLabelString = Res.get("funds.deposit.fundBisqWallet");
addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address")));
balanceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.balanceWithCur", Res.getBaseCurrencyCode())));
@ -238,6 +246,8 @@ public class DepositView extends ActivatableView<VBox, Void> {
@Override
protected void activate() {
filterBox.activate();
tableView.getSelectionModel().selectedItemProperty().addListener(tableViewSelectionListener);
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
@ -255,6 +265,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
@Override
protected void deactivate() {
filterBox.deactivate();
tableView.getSelectionModel().selectedItemProperty().removeListener(tableViewSelectionListener);
sortedList.comparatorProperty().unbind();
observableList.forEach(DepositListItem::cleanup);
@ -267,7 +278,6 @@ public class DepositView extends ActivatableView<VBox, Void> {
// UI handlers
///////////////////////////////////////////////////////////////////////////////////////////
private void fillForm(String address) {
titledGroupBg.setVisible(true);
titledGroupBg.setManaged(true);

View file

@ -17,8 +17,10 @@
package bisq.desktop.main.funds.locked;
import bisq.desktop.util.filtering.FilterableListItem;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.filtering.FilteringUtils;
import bisq.core.btc.listeners.BalanceListener;
import bisq.core.btc.model.AddressEntry;
@ -32,13 +34,15 @@ import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import org.apache.commons.lang3.StringUtils;
import javafx.scene.control.Label;
import lombok.Getter;
import javax.annotation.Nullable;
class LockedListItem {
class LockedListItem implements FilterableListItem {
private final BalanceListener balanceListener;
private final BtcWalletService btcWalletService;
private final CoinFormatter formatter;
@ -117,4 +121,28 @@ class LockedListItem {
DisplayUtils.formatDateTime(trade.getDate()) :
Res.get("shared.noDateAvailable");
}
@Override
public boolean match(String filterString) {
if (filterString.isEmpty())
return true;
if (StringUtils.containsIgnoreCase(getDetails(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getDateString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getAddressString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getBalanceString(), filterString)) {
return true;
}
return FilteringUtils.match(getTrade(), filterString);
}
}

View file

@ -25,12 +25,13 @@
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?>
<?import javafx.geometry.Insets?>
<?import bisq.desktop.components.list.FilterBox?>
<VBox fx:id="root" fx:controller="bisq.desktop.main.funds.locked.LockedView"
spacing="10" xmlns:fx="http://javafx.com/fxml">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0"/>
</padding>
<FilterBox fx:id="filterBox" />
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="dateColumn" minWidth="180" maxWidth="180"/>

View file

@ -23,6 +23,7 @@ import bisq.desktop.components.AutoTooltipButton;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.ExternalHyperlink;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.list.FilterBox;
import bisq.desktop.main.overlays.windows.OfferDetailsWindow;
import bisq.desktop.main.overlays.windows.TradeDetailsWindow;
import bisq.desktop.util.GUIUtil;
@ -71,6 +72,7 @@ import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.util.Callback;
@ -83,6 +85,8 @@ import java.util.stream.Collectors;
@FxmlView
public class LockedView extends ActivatableView<VBox, Void> {
@FXML
FilterBox filterBox;
@FXML
TableView<LockedListItem> tableView;
@FXML
@ -102,7 +106,8 @@ public class LockedView extends ActivatableView<VBox, Void> {
private final OfferDetailsWindow offerDetailsWindow;
private final TradeDetailsWindow tradeDetailsWindow;
private final ObservableList<LockedListItem> observableList = FXCollections.observableArrayList();
private final SortedList<LockedListItem> sortedList = new SortedList<>(observableList);
private final FilteredList<LockedListItem> filteredList = new FilteredList<>(observableList);
private final SortedList<LockedListItem> sortedList = new SortedList<>(filteredList);
private BalanceListener balanceListener;
private ListChangeListener<OpenOffer> openOfferListChangeListener;
private ListChangeListener<Trade> tradeListChangeListener;
@ -131,6 +136,7 @@ public class LockedView extends ActivatableView<VBox, Void> {
@Override
public void initialize() {
filterBox.initialize(filteredList, tableView);
dateColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.dateTime")));
detailsColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.details")));
addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address")));
@ -168,6 +174,7 @@ public class LockedView extends ActivatableView<VBox, Void> {
@Override
protected void activate() {
filterBox.activate();
openOfferManager.getObservableList().addListener(openOfferListChangeListener);
tradeManager.getObservableList().addListener(tradeListChangeListener);
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
@ -206,6 +213,7 @@ public class LockedView extends ActivatableView<VBox, Void> {
@Override
protected void deactivate() {
filterBox.deactivate();
openOfferManager.getObservableList().removeListener(openOfferListChangeListener);
tradeManager.getObservableList().removeListener(tradeListChangeListener);
sortedList.comparatorProperty().unbind();

View file

@ -17,8 +17,10 @@
package bisq.desktop.main.funds.reserved;
import bisq.desktop.util.filtering.FilterableListItem;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.filtering.FilteringUtils;
import bisq.core.btc.listeners.BalanceListener;
import bisq.core.btc.model.AddressEntry;
@ -31,13 +33,15 @@ import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import org.apache.commons.lang3.StringUtils;
import javafx.scene.control.Label;
import java.util.Optional;
import lombok.Getter;
class ReservedListItem {
class ReservedListItem implements FilterableListItem {
private final BalanceListener balanceListener;
private final BtcWalletService btcWalletService;
private final CoinFormatter formatter;
@ -114,4 +118,24 @@ class ReservedListItem {
Res.get("funds.reserved.reserved", openOffer.getShortId()) :
Res.get("shared.noDetailsAvailable");
}
@Override
public boolean match(String filterString) {
if (filterString.isEmpty()) {
return true;
}
if (StringUtils.containsIgnoreCase(getDetails(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getAddressString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getDateAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getBalanceString(), filterString)) {
return true;
}
return FilteringUtils.match(getOpenOffer().getOffer(), filterString);
}
}

View file

@ -25,12 +25,13 @@
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?>
<?import javafx.geometry.Insets?>
<?import bisq.desktop.components.list.FilterBox?>
<VBox fx:id="root" fx:controller="bisq.desktop.main.funds.reserved.ReservedView"
spacing="10" xmlns:fx="http://javafx.com/fxml">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0"/>
</padding>
<FilterBox fx:id="filterBox" />
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="dateColumn" minWidth="180" maxWidth="180"/>

View file

@ -23,6 +23,7 @@ import bisq.desktop.components.AutoTooltipButton;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.ExternalHyperlink;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.list.FilterBox;
import bisq.desktop.main.overlays.windows.OfferDetailsWindow;
import bisq.desktop.main.overlays.windows.TradeDetailsWindow;
import bisq.desktop.util.GUIUtil;
@ -71,6 +72,7 @@ import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.util.Callback;
@ -83,6 +85,8 @@ import java.util.stream.Collectors;
@FxmlView
public class ReservedView extends ActivatableView<VBox, Void> {
@FXML
FilterBox filterBox;
@FXML
TableView<ReservedListItem> tableView;
@FXML
@ -102,7 +106,8 @@ public class ReservedView extends ActivatableView<VBox, Void> {
private final OfferDetailsWindow offerDetailsWindow;
private final TradeDetailsWindow tradeDetailsWindow;
private final ObservableList<ReservedListItem> observableList = FXCollections.observableArrayList();
private final SortedList<ReservedListItem> sortedList = new SortedList<>(observableList);
private final FilteredList<ReservedListItem> filteredList = new FilteredList<>(observableList);
private final SortedList<ReservedListItem> sortedList = new SortedList<>(filteredList);
private BalanceListener balanceListener;
private ListChangeListener<OpenOffer> openOfferListChangeListener;
private ListChangeListener<Trade> tradeListChangeListener;
@ -131,6 +136,7 @@ public class ReservedView extends ActivatableView<VBox, Void> {
@Override
public void initialize() {
filterBox.initialize(filteredList, tableView);
dateColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.dateTime")));
detailsColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.details")));
addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address")));
@ -168,6 +174,7 @@ public class ReservedView extends ActivatableView<VBox, Void> {
@Override
protected void activate() {
filterBox.activate();
openOfferManager.getObservableList().addListener(openOfferListChangeListener);
tradeManager.getObservableList().addListener(tradeListChangeListener);
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
@ -206,6 +213,7 @@ public class ReservedView extends ActivatableView<VBox, Void> {
@Override
protected void deactivate() {
filterBox.deactivate();
openOfferManager.getObservableList().removeListener(openOfferListChangeListener);
tradeManager.getObservableList().removeListener(tradeListChangeListener);
sortedList.comparatorProperty().unbind();

View file

@ -1,69 +0,0 @@
/*
* 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 bisq.core.btc.wallet.BtcWalletService;
import bisq.core.trade.model.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

@ -1,47 +0,0 @@
/*
* 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 bisq.core.btc.wallet.BtcWalletService;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
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

@ -1,47 +0,0 @@
/*
* 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 javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;
import java.util.AbstractList;
import java.util.Collection;
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

@ -1,64 +0,0 @@
/*
* 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 bisq.core.btc.wallet.BtcWalletService;
import bisq.core.offer.OpenOffer;
import bisq.core.support.dispute.arbitration.ArbitrationManager;
import bisq.core.support.dispute.refund.RefundManager;
import bisq.core.trade.model.Tradable;
import bisq.core.trade.model.TradeModel;
import bisq.common.crypto.PubKeyRing;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class TransactionAwareTradableFactory {
private final ArbitrationManager arbitrationManager;
private final RefundManager refundManager;
private final BtcWalletService btcWalletService;
private final PubKeyRing pubKeyRing;
@Inject
TransactionAwareTradableFactory(ArbitrationManager arbitrationManager,
RefundManager refundManager,
BtcWalletService btcWalletService,
PubKeyRing pubKeyRing) {
this.arbitrationManager = arbitrationManager;
this.refundManager = refundManager;
this.btcWalletService = btcWalletService;
this.pubKeyRing = pubKeyRing;
}
TransactionAwareTradable create(Tradable tradable) {
if (tradable instanceof OpenOffer) {
return new TransactionAwareOpenOffer((OpenOffer) tradable);
} else if (tradable instanceof TradeModel) {
return new TransactionAwareTrade((TradeModel) tradable,
arbitrationManager,
refundManager,
btcWalletService,
pubKeyRing);
} else {
return new DummyTransactionAwareTradable(tradable);
}
}
}

View file

@ -1,66 +0,0 @@
/*
* 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 bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.dao.DaoFacade;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;
import org.bitcoinj.core.Transaction;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.annotation.Nullable;
@Singleton
public class TransactionListItemFactory {
private final BtcWalletService btcWalletService;
private final BsqWalletService bsqWalletService;
private final DaoFacade daoFacade;
private final CoinFormatter formatter;
private final Preferences preferences;
@Inject
TransactionListItemFactory(BtcWalletService btcWalletService,
BsqWalletService bsqWalletService,
DaoFacade daoFacade,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
Preferences preferences) {
this.btcWalletService = btcWalletService;
this.bsqWalletService = bsqWalletService;
this.daoFacade = daoFacade;
this.formatter = formatter;
this.preferences = preferences;
}
TransactionsListItem create(Transaction transaction, @Nullable TransactionAwareTradable tradable) {
return new TransactionsListItem(transaction,
btcWalletService,
bsqWalletService,
tradable,
daoFacade,
formatter,
preferences.getIgnoreDustThreshold());
}
}

View file

@ -17,6 +17,7 @@
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;
@ -43,6 +44,8 @@ import org.bitcoinj.core.TransactionOutput;
import com.google.common.base.Suppliers;
import org.apache.commons.lang3.StringUtils;
import javafx.scene.control.Tooltip;
import java.util.Date;
@ -55,7 +58,7 @@ import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
@Slf4j
class TransactionsListItem {
class TransactionsListItem implements FilterableListItem {
private final BtcWalletService btcWalletService;
private final CoinFormatter formatter;
private String dateString;
@ -377,4 +380,30 @@ class TransactionsListItem {
public String getMemo() {
return memo;
}
@Override
public boolean match(String filterString) {
if (filterString.isEmpty()) {
return true;
}
if (StringUtils.containsIgnoreCase(getTxId(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getDetails(), filterString)) {
return true;
}
if (getMemo() != null && StringUtils.containsIgnoreCase(getMemo(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getDirection(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getDateString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getAmount(), filterString)) {
return true;
}
return StringUtils.containsIgnoreCase(getAddressString(), filterString);
}
}

View file

@ -25,11 +25,13 @@
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?>
<?import javafx.geometry.Insets?>
<?import bisq.desktop.components.list.FilterBox?>
<VBox fx:id="root" fx:controller="bisq.desktop.main.funds.transactions.TransactionsView"
spacing="10" alignment="CENTER_RIGHT" xmlns:fx="http://javafx.com/fxml">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0"/>
</padding>
<FilterBox fx:id="filterBox" />
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="dateColumn" minWidth="180" maxWidth="180"/>

View file

@ -24,6 +24,7 @@ import bisq.desktop.components.AutoTooltipButton;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.ExternalHyperlink;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.list.FilterBox;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.main.overlays.windows.BsqTradeDetailsWindow;
import bisq.desktop.main.overlays.windows.OfferDetailsWindow;
@ -31,16 +32,24 @@ import bisq.desktop.main.overlays.windows.TradeDetailsWindow;
import bisq.desktop.util.GUIUtil;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.dao.DaoFacade;
import bisq.core.locale.Res;
import bisq.core.offer.OpenOffer;
import bisq.core.support.dispute.arbitration.ArbitrationManager;
import bisq.core.support.dispute.refund.RefundManager;
import bisq.core.trade.model.Tradable;
import bisq.core.trade.model.TradeModel;
import bisq.core.trade.model.bisq_v1.Trade;
import bisq.core.trade.model.bsq_swap.BsqSwapTrade;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;
import bisq.network.p2p.P2PService;
import bisq.common.crypto.PubKeyRing;
import bisq.common.util.Utilities;
import org.bitcoinj.core.TransactionConfidence;
@ -49,6 +58,7 @@ import org.bitcoinj.wallet.listeners.WalletChangeEventListener;
import com.googlecode.jcsv.writer.CSVEntryConverter;
import javax.inject.Inject;
import javax.inject.Named;
import de.jensd.fx.fontawesome.AwesomeIcon;
@ -76,17 +86,24 @@ import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.event.EventHandler;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.util.Callback;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
@FxmlView
public class TransactionsView extends ActivatableView<VBox, Void> {
@FXML
FilterBox filterBox;
@FXML
TableView<TransactionsListItem> tableView;
@FXML
@ -98,13 +115,21 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
@FXML
AutoTooltipButton exportButton;
private final DisplayedTransactions displayedTransactions;
private final SortedList<TransactionsListItem> sortedDisplayedTransactions;
private final ObservableList<TransactionsListItem> observableList = FXCollections.observableArrayList();
private final FilteredList<TransactionsListItem> filteredList = new FilteredList<>(observableList);
private final SortedList<TransactionsListItem> sortedList = new SortedList<>(filteredList);
private final BtcWalletService btcWalletService;
private final BsqWalletService bsqWalletService;
private final CoinFormatter formatter;
private final DaoFacade daoFacade;
private final P2PService p2PService;
private final WalletsSetup walletsSetup;
private final Preferences preferences;
private final TradableRepository tradableRepository;
private final ArbitrationManager arbitrationManager;
private final RefundManager refundManager;
private final PubKeyRing pubKeyRing;
private final TradeDetailsWindow tradeDetailsWindow;
private final BsqTradeDetailsWindow bsqTradeDetailsWindow;
private final OfferDetailsWindow offerDetailsWindow;
@ -120,26 +145,38 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
@Inject
private TransactionsView(BtcWalletService btcWalletService,
BsqWalletService bsqWalletService,
DaoFacade daoFacade,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
P2PService p2PService,
WalletsSetup walletsSetup,
Preferences preferences,
TradableRepository tradableRepository,
ArbitrationManager arbitrationManager,
RefundManager refundManager,
PubKeyRing pubKeyRing,
TradeDetailsWindow tradeDetailsWindow,
BsqTradeDetailsWindow bsqTradeDetailsWindow,
OfferDetailsWindow offerDetailsWindow,
DisplayedTransactionsFactory displayedTransactionsFactory) {
OfferDetailsWindow offerDetailsWindow) {
this.btcWalletService = btcWalletService;
this.bsqWalletService = bsqWalletService;
this.daoFacade = daoFacade;
this.formatter = formatter;
this.p2PService = p2PService;
this.walletsSetup = walletsSetup;
this.preferences = preferences;
this.tradableRepository = tradableRepository;
this.arbitrationManager = arbitrationManager;
this.refundManager = refundManager;
this.pubKeyRing = pubKeyRing;
this.tradeDetailsWindow = tradeDetailsWindow;
this.bsqTradeDetailsWindow = bsqTradeDetailsWindow;
this.offerDetailsWindow = offerDetailsWindow;
this.displayedTransactions = displayedTransactionsFactory.create();
this.sortedDisplayedTransactions = displayedTransactions.asSortedList();
}
@Override
public void initialize() {
filterBox.initialize(filteredList, tableView);
dateColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.dateTime")));
detailsColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.details")));
addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address")));
@ -179,7 +216,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
tableView.getSortOrder().add(dateColumn);
walletChangeEventListener = wallet -> {
displayedTransactions.update();
updateList();
};
keyEventEventHandler = event -> {
@ -202,9 +239,10 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
@Override
protected void activate() {
sortedDisplayedTransactions.comparatorProperty().bind(tableView.comparatorProperty());
tableView.setItems(sortedDisplayedTransactions);
displayedTransactions.update();
filterBox.activate();
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
tableView.setItems(sortedList);
updateList();
btcWalletService.addChangeEventListener(walletChangeEventListener);
@ -212,7 +250,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
if (scene != null)
scene.addEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler);
numItems.setText(Res.get("shared.numItemsLabel", sortedDisplayedTransactions.size()));
numItems.setText(Res.get("shared.numItemsLabel", sortedList.size()));
exportButton.setOnAction(event -> {
final ObservableList<TableColumn<TransactionsListItem, ?>> tableColumns = tableView.getColumns();
final int reportColumns = tableColumns.size() - 1; // CSV report excludes the last column (an icon)
@ -235,14 +273,15 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
};
GUIUtil.exportCSV("transactions.csv", headerConverter, contentConverter,
new TransactionsListItem(), sortedDisplayedTransactions, (Stage) root.getScene().getWindow());
new TransactionsListItem(), sortedList, (Stage) root.getScene().getWindow());
});
}
@Override
protected void deactivate() {
sortedDisplayedTransactions.comparatorProperty().unbind();
displayedTransactions.forEach(TransactionsListItem::cleanup);
filterBox.deactivate();
sortedList.comparatorProperty().unbind();
observableList.forEach(TransactionsListItem::cleanup);
btcWalletService.removeChangeEventListener(walletChangeEventListener);
if (scene != null)
@ -251,6 +290,48 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
exportButton.setOnAction(null);
}
private void updateList() {
List<TransactionsListItem> transactionsListItems = btcWalletService.getTransactions(false)
.stream()
.map(transaction -> {
Set<Tradable> tradables = tradableRepository.getAll();
TransactionAwareTradable maybeTradable = tradables.stream()
.map(tradable -> {
if (tradable instanceof OpenOffer) {
return new TransactionAwareOpenOffer((OpenOffer) tradable);
} else if (tradable instanceof TradeModel) {
return new TransactionAwareTrade(
(TradeModel) tradable,
arbitrationManager,
refundManager,
btcWalletService,
pubKeyRing
);
} else {
return null;
}
})
.filter(tradable -> tradable != null && tradable.isRelatedToTransaction(transaction))
.findAny()
.orElse(null);
return new TransactionsListItem(
transaction,
btcWalletService,
bsqWalletService,
maybeTradable,
daoFacade,
formatter,
preferences.getIgnoreDustThreshold()
);
})
.collect(Collectors.toList());
// are sorted by getRecentTransactions
transactionsListItems.forEach(TransactionsListItem::cleanup);
observableList.setAll(transactionsListItems);
}
private void openTxInBlockExplorer(TransactionsListItem item) {
if (item.getTxId() != null)
GUIUtil.openWebPage(preferences.getBlockChainExplorer().txUrl + item.getTxId(), false);

View file

@ -17,6 +17,7 @@
package bisq.desktop.main.funds.withdrawal;
import bisq.desktop.util.filtering.FilterableListItem;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.core.btc.listeners.BalanceListener;
@ -29,12 +30,14 @@ import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import org.apache.commons.lang3.StringUtils;
import javafx.scene.control.Label;
import lombok.Getter;
import lombok.Setter;
class WithdrawalListItem {
class WithdrawalListItem implements FilterableListItem {
private final BalanceListener balanceListener;
private final Label balanceLabel;
private final AddressEntry addressEntry;
@ -118,7 +121,23 @@ class WithdrawalListItem {
return balance;
}
public String getBalanceAsString() {
return formatter.formatCoin(balance);
}
public String getAddressString() {
return addressString;
}
@Override
public boolean match(String filterString) {
if (filterString.isEmpty()) {
return true;
}
if (StringUtils.containsIgnoreCase(getBalanceAsString(), filterString)) {
return true;
}
return StringUtils.containsIgnoreCase(getAddressString(), filterString);
}
}

View file

@ -23,12 +23,14 @@
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.geometry.Insets?>
<?import bisq.desktop.components.list.FilterBox?>
<VBox fx:id="root" fx:controller="bisq.desktop.main.funds.withdrawal.WithdrawalView"
spacing="10" xmlns:fx="http://javafx.com/fxml">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0"/>
</padding>
<FilterBox fx:id="filterBox" />
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="selectColumn" minWidth="60" maxWidth="60" sortable="false"/>

View file

@ -25,6 +25,7 @@ import bisq.desktop.components.ExternalHyperlink;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.InputTextField;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.components.list.FilterBox;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.main.overlays.windows.TxDetails;
import bisq.desktop.main.overlays.windows.WalletPasswordWindow;
@ -96,6 +97,7 @@ import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.util.Callback;
@ -117,10 +119,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
@FxmlView
public class WithdrawalView extends ActivatableView<VBox, Void> {
@FXML
GridPane gridPane;
@FXML
FilterBox filterBox;
@FXML
TableView<WithdrawalListItem> tableView;
@FXML
TableColumn<WithdrawalListItem, WithdrawalListItem> addressColumn, balanceColumn, selectColumn;
@ -138,7 +141,8 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
private final BtcAddressValidator btcAddressValidator;
private final WalletPasswordWindow walletPasswordWindow;
private final ObservableList<WithdrawalListItem> observableList = FXCollections.observableArrayList();
private final SortedList<WithdrawalListItem> sortedList = new SortedList<>(observableList);
private final FilteredList<WithdrawalListItem> filteredList = new FilteredList<>(observableList);
private final SortedList<WithdrawalListItem> sortedList = new SortedList<>(filteredList);
private final Set<WithdrawalListItem> selectedItems = new HashSet<>();
private BalanceListener balanceListener;
private Set<String> fromAddresses = new HashSet<>();
@ -183,7 +187,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
@Override
public void initialize() {
filterBox.initialize(filteredList, tableView);
final TitledGroupBg titledGroupBg = addTitledGroupBg(gridPane, rowIndex, 4, Res.get("funds.deposit.withdrawFromWallet"));
titledGroupBg.getStyleClass().add("last");
@ -343,6 +347,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
@Override
protected void activate() {
filterBox.activate();
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
tableView.setItems(sortedList);
updateList();
@ -374,6 +379,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
@Override
protected void deactivate() {
filterBox.deactivate();
sortedList.comparatorProperty().unbind();
observableList.forEach(WithdrawalListItem::cleanup);
btcWalletService.removeBalanceListener(balanceListener);

View file

@ -21,13 +21,17 @@ import bisq.desktop.common.model.ActivatableDataModel;
import bisq.core.btc.listeners.BsqBalanceListener;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.trade.ClosedTradableManager;
import bisq.core.trade.bsq_swap.BsqSwapTradeManager;
import bisq.core.trade.model.bsq_swap.BsqSwapTrade;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.BsqFormatter;
import bisq.core.util.coin.CoinFormatter;
import com.google.inject.Inject;
import javax.inject.Named;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
@ -36,17 +40,26 @@ import java.util.stream.Collectors;
class UnconfirmedBsqSwapsDataModel extends ActivatableDataModel {
final BsqSwapTradeManager bsqSwapTradeManager;
private final BsqSwapTradeManager bsqSwapTradeManager;
private final BsqWalletService bsqWalletService;
private final ObservableList<UnconfirmedBsqSwapsListItem> list = FXCollections.observableArrayList();
private final ListChangeListener<BsqSwapTrade> tradesListChangeListener;
private final BsqBalanceListener bsqBalanceListener;
private final BsqFormatter bsqFormatter;
private final CoinFormatter btcFormatter;
private final ClosedTradableManager closedTradableManager;
@Inject
public UnconfirmedBsqSwapsDataModel(BsqSwapTradeManager bsqSwapTradeManager,
BsqWalletService bsqWalletService) {
BsqWalletService bsqWalletService,
BsqFormatter bsqFormatter,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
ClosedTradableManager closedTradableManager) {
this.bsqSwapTradeManager = bsqSwapTradeManager;
this.bsqWalletService = bsqWalletService;
this.bsqFormatter = bsqFormatter;
this.btcFormatter = btcFormatter;
this.closedTradableManager = closedTradableManager;
tradesListChangeListener = change -> applyList();
bsqBalanceListener = (availableBalance, availableNonBsqBalance, unverifiedBalance,
@ -71,15 +84,18 @@ class UnconfirmedBsqSwapsDataModel extends ActivatableDataModel {
return list;
}
public OfferDirection getDirection(Offer offer) {
return bsqSwapTradeManager.wasMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection();
}
private void applyList() {
list.clear();
list.addAll(bsqSwapTradeManager.getUnconfirmedBsqSwapTrades()
.map(bsqSwapTrade -> new UnconfirmedBsqSwapsListItem(bsqWalletService, bsqSwapTrade))
.map(bsqSwapTrade -> new UnconfirmedBsqSwapsListItem(
bsqSwapTrade,
bsqWalletService,
btcFormatter,
bsqFormatter,
bsqSwapTradeManager,
closedTradableManager
))
.collect(Collectors.toList()));
// we sort by date, the earliest first

View file

@ -18,34 +18,63 @@
package bisq.desktop.main.portfolio.bsqswaps;
import bisq.desktop.components.indicator.TxConfidenceIndicator;
import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.GUIUtil;
import bisq.desktop.util.filtering.FilterableListItem;
import bisq.desktop.util.filtering.FilteringUtils;
import bisq.core.btc.listeners.TxConfidenceListener;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.locale.CurrencyUtil;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.trade.ClosedTradableManager;
import bisq.core.trade.bsq_swap.BsqSwapTradeManager;
import bisq.core.trade.model.bsq_swap.BsqSwapTrade;
import bisq.core.util.FormattingUtils;
import bisq.core.util.VolumeUtil;
import bisq.core.util.coin.BsqFormatter;
import bisq.core.util.coin.CoinFormatter;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.TransactionConfidence;
import org.apache.commons.lang3.StringUtils;
import javafx.scene.control.Tooltip;
import lombok.Getter;
import javax.annotation.Nullable;
class UnconfirmedBsqSwapsListItem {
class UnconfirmedBsqSwapsListItem implements FilterableListItem {
@Getter
private final BsqSwapTrade bsqSwapTrade;
private final BsqWalletService bsqWalletService;
private final CoinFormatter btcFormatter;
private final BsqFormatter bsqFormatter;
private final BsqSwapTradeManager bsqSwapTradeManager;
private final ClosedTradableManager closedTradableManager;
private final String txId;
@Getter
private int confirmations = 0;
@Getter
private TxConfidenceIndicator txConfidenceIndicator;
private TxConfidenceListener txConfidenceListener;
private final TxConfidenceIndicator txConfidenceIndicator;
private final TxConfidenceListener txConfidenceListener;
UnconfirmedBsqSwapsListItem(BsqWalletService bsqWalletService, BsqSwapTrade bsqSwapTrade) {
UnconfirmedBsqSwapsListItem(
BsqSwapTrade bsqSwapTrade,
BsqWalletService bsqWalletService,
CoinFormatter btcFormatter,
BsqFormatter bsqFormatter,
BsqSwapTradeManager bsqSwapTradeManager,
ClosedTradableManager closedTradableManager) {
this.bsqSwapTrade = bsqSwapTrade;
this.bsqWalletService = bsqWalletService;
this.btcFormatter = btcFormatter;
this.bsqFormatter = bsqFormatter;
this.bsqSwapTradeManager = bsqSwapTradeManager;
this.closedTradableManager = closedTradableManager;
txId = bsqSwapTrade.getTxId();
txConfidenceIndicator = new TxConfidenceIndicator();
@ -65,13 +94,6 @@ class UnconfirmedBsqSwapsListItem {
updateConfidence(bsqWalletService.getConfidenceForTxId(txId), tooltip);
}
UnconfirmedBsqSwapsListItem() {
bsqSwapTrade = null;
bsqWalletService = null;
txId = null;
}
private void updateConfidence(@Nullable TransactionConfidence confidence, Tooltip tooltip) {
if (confidence != null) {
GUIUtil.updateConfidence(confidence, tooltip, txConfidenceIndicator);
@ -83,4 +105,91 @@ class UnconfirmedBsqSwapsListItem {
bsqWalletService.removeTxConfidenceListener(txConfidenceListener);
}
public String getTradeId() {
return bsqSwapTrade.getShortId();
}
public String getAmountAsString() {
return btcFormatter.formatCoin(bsqSwapTrade.getAmount());
}
public String getPriceAsString() {
return FormattingUtils.formatPrice(bsqSwapTrade.getPrice());
}
public String getVolumeAsString() {
return VolumeUtil.formatVolumeWithCode(bsqSwapTrade.getVolume());
}
public String getTxFeeAsString() {
return btcFormatter.formatCoinWithCode(Coin.valueOf(bsqSwapTrade.getBsqSwapProtocolModel().getTxFee()));
}
public String getTradeFeeAsString() {
if (bsqSwapTradeManager.wasMyOffer(bsqSwapTrade.getOffer())) {
return bsqFormatter.formatCoinWithCode(bsqSwapTrade.getMakerFeeAsLong());
} else {
return bsqFormatter.formatCoinWithCode(bsqSwapTrade.getTakerFeeAsLong());
}
}
public String getDirectionLabel() {
Offer offer = bsqSwapTrade.getOffer();
OfferDirection direction = bsqSwapTradeManager.wasMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection();
return DisplayUtils.getDirectionWithCode(direction, bsqSwapTrade.getOffer().getCurrencyCode());
}
public String getDateAsString() {
return DisplayUtils.formatDateTime(bsqSwapTrade.getDate());
}
public String getMarketLabel() {
return CurrencyUtil.getCurrencyPair(bsqSwapTrade.getOffer().getCurrencyCode());
}
public int getConfidence() {
return getConfirmations();
}
public int getNumPastTrades() {
return closedTradableManager.getNumPastTrades(bsqSwapTrade);
}
@Override
public boolean match(String filterString) {
if (filterString.isEmpty()) {
return true;
}
if (StringUtils.containsIgnoreCase(getDateAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getMarketLabel(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getPriceAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getVolumeAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getAmountAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getTradeFeeAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getTxFeeAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(String.valueOf(getConfidence()), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getDirectionLabel(), filterString)) {
return true;
}
if (FilteringUtils.match(getBsqSwapTrade(), filterString)) {
return true;
}
return FilteringUtils.match(getBsqSwapTrade().getOffer(), filterString);
}
}

View file

@ -18,28 +18,21 @@
-->
<?import bisq.desktop.components.AutoTooltipButton?>
<?import bisq.desktop.components.AutoTooltipLabel?>
<?import bisq.desktop.components.InputTextField?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?>
<?import javafx.geometry.Insets?>
<?import bisq.desktop.components.list.FilterBox?>
<VBox fx:id="root" fx:controller="bisq.desktop.main.portfolio.bsqswaps.UnconfirmedBsqSwapsView"
spacing="10" alignment="CENTER_RIGHT" xmlns:fx="http://javafx.com/fxml">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0"/>
</padding>
<HBox fx:id="searchBox">
<AutoTooltipLabel fx:id="filterLabel"/>
<InputTextField fx:id="filterTextField" minWidth="500"/>
<Pane fx:id="searchBoxSpacer"/>
</HBox>
<FilterBox fx:id="filterBox" />
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="tradeIdColumn" minWidth="110" maxWidth="120"/>

View file

@ -22,14 +22,14 @@ import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipButton;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.InputTextField;
import bisq.desktop.components.PeerInfoIconTrading;
import bisq.desktop.components.list.FilterBox;
import bisq.desktop.main.overlays.windows.BsqTradeDetailsWindow;
import bisq.desktop.util.GUIUtil;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.alert.PrivateNotificationManager;
import bisq.core.locale.Res;
import bisq.core.offer.Offer;
import bisq.core.trade.model.bsq_swap.BsqSwapTrade;
import bisq.core.user.Preferences;
@ -53,7 +53,6 @@ import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
@ -115,13 +114,7 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
confidenceColumn,
avatarColumn;
@FXML
HBox searchBox;
@FXML
AutoTooltipLabel filterLabel;
@FXML
InputTextField filterTextField;
@FXML
Pane searchBoxSpacer;
FilterBox filterBox;
@FXML
AutoTooltipButton exportButton;
@FXML
@ -132,9 +125,9 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
private final BsqTradeDetailsWindow window;
private final Preferences preferences;
private final PrivateNotificationManager privateNotificationManager;
private final AccountAgeWitnessService accountAgeWitnessService;
private SortedList<UnconfirmedBsqSwapsListItem> sortedList;
private FilteredList<UnconfirmedBsqSwapsListItem> filteredList;
private ChangeListener<String> filterTextFieldListener;
private ChangeListener<Number> widthListener;
@Inject
@ -142,11 +135,13 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
BsqTradeDetailsWindow bsqTradeDetailsWindow,
Preferences preferences,
PrivateNotificationManager privateNotificationManager,
AccountAgeWitnessService accountAgeWitnessService,
@Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys) {
super(model);
this.window = bsqTradeDetailsWindow;
this.preferences = preferences;
this.privateNotificationManager = privateNotificationManager;
this.accountAgeWitnessService = accountAgeWitnessService;
this.useDevPrivilegeKeys = useDevPrivilegeKeys;
}
@ -183,20 +178,17 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
tradeIdColumn.setComparator(Comparator.comparing(o -> o.getBsqSwapTrade().getId()));
dateColumn.setComparator(Comparator.comparing(o -> o.getBsqSwapTrade().getDate()));
directionColumn.setComparator(Comparator.comparing(o -> o.getBsqSwapTrade().getOffer().getDirection()));
marketColumn.setComparator(Comparator.comparing(model::getMarketLabel));
priceColumn.setComparator(Comparator.comparing(model::getPrice, Comparator.nullsFirst(Comparator.naturalOrder())));
marketColumn.setComparator(Comparator.comparing(UnconfirmedBsqSwapsListItem::getMarketLabel));
priceColumn.setComparator(Comparator.comparing(UnconfirmedBsqSwapsListItem::getPriceAsString, Comparator.nullsFirst(Comparator.naturalOrder())));
volumeColumn.setComparator(nullsFirstComparingAsTrade(BsqSwapTrade::getVolume));
amountColumn.setComparator(Comparator.comparing(model::getAmount, Comparator.nullsFirst(Comparator.naturalOrder())));
avatarColumn.setComparator(Comparator.comparing(
o -> model.getNumPastTrades(o.getBsqSwapTrade()),
Comparator.nullsFirst(Comparator.naturalOrder())
));
amountColumn.setComparator(Comparator.comparing(UnconfirmedBsqSwapsListItem::getAmountAsString, Comparator.nullsFirst(Comparator.naturalOrder())));
avatarColumn.setComparator(Comparator.comparing(UnconfirmedBsqSwapsListItem::getNumPastTrades, Comparator.nullsFirst(Comparator.naturalOrder())));
txFeeColumn.setComparator(nullsFirstComparing(BsqSwapTrade::getTxFeePerVbyte));
txFeeColumn.setComparator(Comparator.comparing(model::getTxFee, Comparator.nullsFirst(Comparator.naturalOrder())));
txFeeColumn.setComparator(Comparator.comparing(UnconfirmedBsqSwapsListItem::getTxFeeAsString, Comparator.nullsFirst(Comparator.naturalOrder())));
//
tradeFeeColumn.setComparator(Comparator.comparing(item -> {
String tradeFee = model.getTradeFee(item);
String tradeFee = item.getTradeFeeAsString();
// We want to separate BSQ and BTC fees so we use a prefix
if (item.getBsqSwapTrade().getOffer().isCurrencyForMakerFeeBtc()) {
return "BTC" + tradeFee;
@ -204,17 +196,11 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
return "BSQ" + tradeFee;
}
}, Comparator.nullsFirst(Comparator.naturalOrder())));
confidenceColumn.setComparator(Comparator.comparing(model::getConfidence));
confidenceColumn.setComparator(Comparator.comparing(UnconfirmedBsqSwapsListItem::getConfidence));
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
tableView.getSortOrder().add(dateColumn);
filterLabel.setText(Res.get("shared.filter"));
HBox.setMargin(filterLabel, new Insets(5, 0, 0, 10));
filterTextFieldListener = (observable, oldValue, newValue) -> applyFilteredListPredicate(filterTextField.getText());
searchBox.setSpacing(5);
HBox.setHgrow(searchBoxSpacer, Priority.ALWAYS);
numItems.setId("num-offers");
numItems.setPadding(new Insets(-5, 0, 0, 10));
HBox.setHgrow(footerSpacer, Priority.ALWAYS);
@ -224,13 +210,16 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
@Override
protected void activate() {
filteredList = new FilteredList<>(model.getList());
filteredList = new FilteredList<>(model.dataModel.getList());
sortedList = new SortedList<>(filteredList);
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
tableView.setItems(sortedList);
filterBox.initialize(filteredList, tableView); // here because filteredList is instantiated here
filterBox.activate();
numItems.setText(Res.get("shared.numItemsLabel", sortedList.size()));
exportButton.setOnAction(event -> {
CSVEntryConverter<UnconfirmedBsqSwapsListItem> headerConverter = item -> {
@ -242,25 +231,23 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
};
CSVEntryConverter<UnconfirmedBsqSwapsListItem> contentConverter = item -> {
String[] columns = new String[ColumnNames.values().length];
columns[ColumnNames.TRADE_ID.ordinal()] = model.getTradeId(item);
columns[ColumnNames.DATE.ordinal()] = model.getDate(item);
columns[ColumnNames.MARKET.ordinal()] = model.getMarketLabel(item);
columns[ColumnNames.PRICE.ordinal()] = model.getPrice(item);
columns[ColumnNames.AMOUNT.ordinal()] = model.getAmount(item);
columns[ColumnNames.VOLUME.ordinal()] = model.getVolume(item);
columns[ColumnNames.TX_FEE.ordinal()] = model.getTxFee(item);
columns[ColumnNames.TRADE_FEE.ordinal()] = model.getTradeFee(item);
columns[ColumnNames.OFFER_TYPE.ordinal()] = model.getDirectionLabel(item);
columns[ColumnNames.CONF.ordinal()] = String.valueOf(model.getConfidence(item));
columns[ColumnNames.TRADE_ID.ordinal()] = item.getTradeId();
columns[ColumnNames.DATE.ordinal()] = item.getDateAsString();
columns[ColumnNames.MARKET.ordinal()] = item.getMarketLabel();
columns[ColumnNames.PRICE.ordinal()] = item.getPriceAsString();
columns[ColumnNames.AMOUNT.ordinal()] = item.getAmountAsString();
columns[ColumnNames.VOLUME.ordinal()] = item.getVolumeAsString();
columns[ColumnNames.TX_FEE.ordinal()] = item.getTxFeeAsString();
columns[ColumnNames.TRADE_FEE.ordinal()] = item.getTradeFeeAsString();
columns[ColumnNames.OFFER_TYPE.ordinal()] = item.getDirectionLabel();
columns[ColumnNames.CONF.ordinal()] = String.valueOf(item.getConfidence());
return columns;
};
GUIUtil.exportCSV("bsqSwapHistory.csv", headerConverter, contentConverter,
new UnconfirmedBsqSwapsListItem(), sortedList, (Stage) root.getScene().getWindow());
null, sortedList, (Stage) root.getScene().getWindow());
});
filterTextField.textProperty().addListener(filterTextFieldListener);
applyFilteredListPredicate(filterTextField.getText());
root.widthProperty().addListener(widthListener);
onWidthChange(root.getWidth());
}
@ -270,7 +257,7 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
sortedList.comparatorProperty().unbind();
exportButton.setOnAction(null);
filterTextField.textProperty().removeListener(filterTextFieldListener);
filterBox.deactivate();
root.widthProperty().removeListener(widthListener);
}
@ -295,57 +282,6 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
tradeFeeColumn.setVisible(width > 1300);
}
private void applyFilteredListPredicate(String filterString) {
filteredList.setPredicate(item -> {
if (filterString.isEmpty())
return true;
BsqSwapTrade bsqSwapTrade = item.getBsqSwapTrade();
Offer offer = bsqSwapTrade.getOffer();
if (offer.getId().contains(filterString)) {
return true;
}
if (model.getDate(item).contains(filterString)) {
return true;
}
if (model.getMarketLabel(item).contains(filterString)) {
return true;
}
if (model.getPrice(item).contains(filterString)) {
return true;
}
if (model.getVolume(item).contains(filterString)) {
return true;
}
if (model.getAmount(item).contains(filterString)) {
return true;
}
if (model.getTradeFee(item).contains(filterString)) {
return true;
}
if (model.getTxFee(item).contains(filterString)) {
return true;
}
if (String.valueOf(model.getConfidence(item)).contains(filterString)) {
return true;
}
if (model.getDirectionLabel(item).contains(filterString)) {
return true;
}
if (offer.getPaymentMethod().getDisplayString().contains(filterString)) {
return true;
}
if (bsqSwapTrade.getTxId() != null && bsqSwapTrade.getTxId().contains(filterString)) {
return true;
}
if (bsqSwapTrade.getTradingPeerNodeAddress().getFullAddress().contains(filterString)) {
return true;
}
return false;
});
}
private void setTradeIdColumnCellFactory() {
tradeIdColumn.getStyleClass().add("first-column");
tradeIdColumn.setCellValueFactory((offerListItem) -> new ReadOnlyObjectWrapper<>(offerListItem.getValue()));
@ -362,7 +298,7 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
public void updateItem(final UnconfirmedBsqSwapsListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
field = new HyperlinkWithIcon(model.getTradeId(item));
field = new HyperlinkWithIcon(item.getTradeId());
field.setOnAction(event -> {
window.show(item.getBsqSwapTrade());
});
@ -390,8 +326,8 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
@Override
public void updateItem(final UnconfirmedBsqSwapsListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setGraphic(new AutoTooltipLabel(model.getDate(item)));
if (item != null && !empty)
setGraphic(new AutoTooltipLabel(item.getDateAsString()));
else
setGraphic(null);
}
@ -411,7 +347,11 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
@Override
public void updateItem(final UnconfirmedBsqSwapsListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getMarketLabel(item)));
if (item != null && !empty) {
setGraphic(new AutoTooltipLabel(item.getMarketLabel()));
} else {
setGraphic(null);
}
}
};
}
@ -457,7 +397,7 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
if (newItem != null && !empty/* && newItem.getAtomicTrade() instanceof Trade*/) {
var bsqSwapTrade = newItem.getBsqSwapTrade();
int numPastTrades = model.getNumPastTrades(bsqSwapTrade);
int numPastTrades = newItem.getNumPastTrades();
final NodeAddress tradingPeerNodeAddress = bsqSwapTrade.getTradingPeerNodeAddress();
String role = Res.get("peerInfoIcon.tooltip.tradePeer");
Node peerInfoIcon = new PeerInfoIconTrading(tradingPeerNodeAddress,
@ -466,7 +406,7 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
privateNotificationManager,
bsqSwapTrade.getOffer(),
preferences,
model.accountAgeWitnessService,
accountAgeWitnessService,
useDevPrivilegeKeys);
setPadding(new Insets(1, 15, 0, 0));
setGraphic(peerInfoIcon);
@ -491,7 +431,11 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
@Override
public void updateItem(final UnconfirmedBsqSwapsListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getAmount(item)));
if (item != null && !empty) {
setGraphic(new AutoTooltipLabel(item.getAmountAsString()));
} else {
setGraphic(null);
}
}
};
}
@ -509,7 +453,11 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
@Override
public void updateItem(final UnconfirmedBsqSwapsListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getPrice(item)));
if (item != null && !empty) {
setGraphic(new AutoTooltipLabel(item.getPriceAsString()));
} else {
setGraphic(null);
}
}
};
}
@ -527,8 +475,8 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
@Override
public void updateItem(final UnconfirmedBsqSwapsListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setGraphic(new AutoTooltipLabel(model.getVolume(item)));
if (item != null && !empty)
setGraphic(new AutoTooltipLabel(item.getVolumeAsString()));
else
setGraphic(null);
}
@ -548,7 +496,10 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
@Override
public void updateItem(final UnconfirmedBsqSwapsListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getDirectionLabel(item)));
if (item != null && !empty)
setGraphic(new AutoTooltipLabel(item.getDirectionLabel()));
else
setGraphic(null);
}
};
}
@ -566,7 +517,10 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
@Override
public void updateItem(final UnconfirmedBsqSwapsListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getTxFee(item)));
if (item != null && !empty)
setGraphic(new AutoTooltipLabel(item.getTxFeeAsString()));
else
setGraphic(null);
}
};
}
@ -584,7 +538,10 @@ public class UnconfirmedBsqSwapsView extends ActivatableViewAndModel<VBox, Uncon
@Override
public void updateItem(final UnconfirmedBsqSwapsListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getTradeFee(item)));
if (item != null && !empty)
setGraphic(new AutoTooltipLabel(item.getTradeFeeAsString()));
else
setGraphic(null);
}
};
}

View file

@ -19,121 +19,12 @@ package bisq.desktop.main.portfolio.bsqswaps;
import bisq.desktop.common.model.ActivatableWithDataModel;
import bisq.desktop.common.model.ViewModel;
import bisq.desktop.util.DisplayUtils;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.locale.CurrencyUtil;
import bisq.core.trade.ClosedTradableManager;
import bisq.core.trade.model.bsq_swap.BsqSwapTrade;
import bisq.core.util.FormattingUtils;
import bisq.core.util.VolumeUtil;
import bisq.core.util.coin.BsqFormatter;
import bisq.core.util.coin.CoinFormatter;
import org.bitcoinj.core.Coin;
import com.google.inject.Inject;
import javax.inject.Named;
import javafx.collections.ObservableList;
class UnconfirmedBsqSwapsViewModel extends ActivatableWithDataModel<UnconfirmedBsqSwapsDataModel> implements ViewModel {
private final BsqFormatter bsqFormatter;
private final CoinFormatter btcFormatter;
final AccountAgeWitnessService accountAgeWitnessService;
private final ClosedTradableManager closedTradableManager;
@Inject
public UnconfirmedBsqSwapsViewModel(UnconfirmedBsqSwapsDataModel dataModel,
AccountAgeWitnessService accountAgeWitnessService,
ClosedTradableManager closedTradableManager,
BsqFormatter bsqFormatter,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter) {
public UnconfirmedBsqSwapsViewModel(UnconfirmedBsqSwapsDataModel dataModel) {
super(dataModel);
this.accountAgeWitnessService = accountAgeWitnessService;
this.closedTradableManager = closedTradableManager;
this.bsqFormatter = bsqFormatter;
this.btcFormatter = btcFormatter;
}
public ObservableList<UnconfirmedBsqSwapsListItem> getList() {
return dataModel.getList();
}
String getTradeId(UnconfirmedBsqSwapsListItem item) {
return item.getBsqSwapTrade().getShortId();
}
String getAmount(UnconfirmedBsqSwapsListItem item) {
if (item == null)
return "";
return btcFormatter.formatCoin(item.getBsqSwapTrade().getAmount());
}
String getPrice(UnconfirmedBsqSwapsListItem item) {
if (item == null)
return "";
return FormattingUtils.formatPrice(item.getBsqSwapTrade().getPrice());
}
String getVolume(UnconfirmedBsqSwapsListItem item) {
if (item == null)
return "";
return VolumeUtil.formatVolumeWithCode(item.getBsqSwapTrade().getVolume());
}
String getTxFee(UnconfirmedBsqSwapsListItem item) {
if (item == null)
return "";
return btcFormatter.formatCoinWithCode(Coin.valueOf(item.getBsqSwapTrade().getBsqSwapProtocolModel().getTxFee()));
}
String getTradeFee(UnconfirmedBsqSwapsListItem item) {
if (item == null)
return "";
if (wasMyOffer(item.getBsqSwapTrade())) {
return bsqFormatter.formatCoinWithCode(item.getBsqSwapTrade().getMakerFeeAsLong());
} else {
return bsqFormatter.formatCoinWithCode(item.getBsqSwapTrade().getTakerFeeAsLong());
}
}
String getDirectionLabel(UnconfirmedBsqSwapsListItem item) {
if (item == null)
return "";
return DisplayUtils.getDirectionWithCode(dataModel.getDirection(item.getBsqSwapTrade().getOffer()),
item.getBsqSwapTrade().getOffer().getCurrencyCode());
}
String getDate(UnconfirmedBsqSwapsListItem item) {
return DisplayUtils.formatDateTime(item.getBsqSwapTrade().getDate());
}
String getMarketLabel(UnconfirmedBsqSwapsListItem item) {
if (item == null)
return "";
return CurrencyUtil.getCurrencyPair(item.getBsqSwapTrade().getOffer().getCurrencyCode());
}
int getConfidence(UnconfirmedBsqSwapsListItem item) {
if ((item == null))
return 0;
return item.getConfirmations();
}
int getNumPastTrades(BsqSwapTrade bsqSwapTrade) {
return closedTradableManager.getNumPastTrades(bsqSwapTrade);
}
boolean wasMyOffer(BsqSwapTrade bsqSwapTrade) {
return dataModel.bsqSwapTradeManager.wasMyOffer(bsqSwapTrade.getOffer());
}
}

View file

@ -24,10 +24,9 @@ import bisq.core.btc.listeners.BsqBalanceListener;
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.monetary.Price;
import bisq.core.monetary.Volume;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.provider.price.MarketPrice;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.ClosedTradableFormatter;
import bisq.core.trade.ClosedTradableManager;
import bisq.core.trade.ClosedTradableUtil;
import bisq.core.trade.bsq_swap.BsqSwapTradeManager;
@ -44,30 +43,33 @@ import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class ClosedTradesDataModel extends ActivatableDataModel {
final ClosedTradableManager closedTradableManager;
final ClosedTradableFormatter closedTradableFormatter;
private final BsqWalletService bsqWalletService;
private final BsqSwapTradeManager bsqSwapTradeManager;
private final Preferences preferences;
private final PriceFeedService priceFeedService;
final AccountAgeWitnessService accountAgeWitnessService;
private final ObservableList<Tradable> list = FXCollections.observableArrayList();
private final ObservableList<ClosedTradesListItem> list = FXCollections.observableArrayList();
private final ListChangeListener<Tradable> tradesListChangeListener;
private final BsqBalanceListener bsqBalanceListener;
@Inject
public ClosedTradesDataModel(ClosedTradableManager closedTradableManager,
ClosedTradableFormatter closedTradableFormatter,
BsqSwapTradeManager bsqSwapTradeManager,
BsqWalletService bsqWalletService,
Preferences preferences,
PriceFeedService priceFeedService,
AccountAgeWitnessService accountAgeWitnessService) {
this.closedTradableManager = closedTradableManager;
this.closedTradableFormatter = closedTradableFormatter;
this.bsqSwapTradeManager = bsqSwapTradeManager;
this.bsqWalletService = bsqWalletService;
this.preferences = preferences;
@ -95,16 +97,16 @@ class ClosedTradesDataModel extends ActivatableDataModel {
bsqWalletService.removeBsqBalanceListener(bsqBalanceListener);
}
ObservableList<Tradable> getList() {
ObservableList<ClosedTradesListItem> getList() {
return list;
}
OfferDirection getDirection(Offer offer) {
return closedTradableManager.wasMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection();
List<Tradable> getListAsTradables() {
return list.stream().map(ClosedTradesListItem::getTradable).collect(Collectors.toList());
}
Coin getTotalAmount() {
return ClosedTradableUtil.getTotalAmount(list);
return ClosedTradableUtil.getTotalAmount(getListAsTradables());
}
Optional<Volume> getVolumeInUserFiatCurrency(Coin amount) {
@ -126,15 +128,11 @@ class ClosedTradesDataModel extends ActivatableDataModel {
}
Coin getTotalTxFee() {
return ClosedTradableUtil.getTotalTxFee(list);
return ClosedTradableUtil.getTotalTxFee(getListAsTradables());
}
Coin getTotalTradeFee(boolean expectBtcFee) {
return closedTradableManager.getTotalTradeFee(list, expectBtcFee);
}
int getNumPastTrades(Tradable tradable) {
return closedTradableManager.getNumPastTrades(tradable);
return closedTradableManager.getTotalTradeFee(getListAsTradables(), expectBtcFee);
}
boolean isCurrencyForTradeFeeBtc(Tradable item) {
@ -143,13 +141,17 @@ class ClosedTradesDataModel extends ActivatableDataModel {
private void applyList() {
list.clear();
list.addAll(getTradableStream().collect(Collectors.toList()));
list.addAll(
bsqSwapTradeManager.getConfirmedBsqSwapTrades()
.map(tradable -> new ClosedTradesListItem(tradable, closedTradableFormatter, closedTradableManager))
.collect(Collectors.toList())
);
list.addAll(
closedTradableManager.getObservableList().stream()
.map(tradable -> new ClosedTradesListItem(tradable, closedTradableFormatter, closedTradableManager))
.collect(Collectors.toList())
);
// We sort by date, the earliest first
list.sort((o1, o2) -> o2.getDate().compareTo(o1.getDate()));
}
private Stream<Tradable> getTradableStream() {
return Stream.concat(bsqSwapTradeManager.getConfirmedBsqSwapTrades(),
closedTradableManager.getObservableList().stream());
list.sort((o1, o2) -> o2.getTradable().getDate().compareTo(o1.getTradable().getDate()));
}
}

View file

@ -0,0 +1,167 @@
/*
* 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.portfolio.closedtrades;
import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.filtering.FilterableListItem;
import bisq.desktop.util.filtering.FilteringUtils;
import bisq.core.locale.CurrencyUtil;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.trade.ClosedTradableFormatter;
import bisq.core.trade.ClosedTradableManager;
import bisq.core.trade.model.Tradable;
import bisq.core.trade.model.bisq_v1.Trade;
import bisq.core.trade.model.bsq_swap.BsqSwapTrade;
import org.apache.commons.lang3.StringUtils;
import lombok.Getter;
public class ClosedTradesListItem implements FilterableListItem {
@Getter
private final Tradable tradable;
private final ClosedTradableFormatter closedTradableFormatter;
private final ClosedTradableManager closedTradableManager;
public ClosedTradesListItem(
Tradable tradable,
ClosedTradableFormatter closedTradableFormatter,
ClosedTradableManager closedTradableManager) {
this.tradable = tradable;
this.closedTradableFormatter = closedTradableFormatter;
this.closedTradableManager = closedTradableManager;
}
public String getTradeId() {
return tradable.getShortId();
}
public String getAmountAsString() {
return closedTradableFormatter.getAmountAsString(tradable);
}
public String getPriceAsString() {
return closedTradableFormatter.getPriceAsString(tradable);
}
public String getPriceDeviationAsString() {
return closedTradableFormatter.getPriceDeviationAsString(tradable);
}
public String getVolumeAsString(boolean appendCode) {
return closedTradableFormatter.getVolumeAsString(tradable, appendCode);
}
public String getVolumeCurrencyAsString() {
return closedTradableFormatter.getVolumeCurrencyAsString(tradable);
}
public String getTxFeeAsString() {
return closedTradableFormatter.getTxFeeAsString(tradable);
}
public String getTradeFeeAsString(boolean appendCode) {
return closedTradableFormatter.getTradeFeeAsString(tradable, appendCode);
}
public String getBuyerSecurityDepositAsString() {
return closedTradableFormatter.getBuyerSecurityDepositAsString(tradable);
}
public String getSellerSecurityDepositAsString() {
return closedTradableFormatter.getSellerSecurityDepositAsString(tradable);
}
public String getDirectionLabel() {
Offer offer = tradable.getOffer();
OfferDirection direction = closedTradableManager.wasMyOffer(offer)
? offer.getDirection()
: offer.getMirroredDirection();
String currencyCode = tradable.getOffer().getCurrencyCode();
return DisplayUtils.getDirectionWithCode(direction, currencyCode);
}
public String getDateAsString() {
return DisplayUtils.formatDateTime(tradable.getDate());
}
public String getMarketLabel() {
return CurrencyUtil.getCurrencyPair(tradable.getOffer().getCurrencyCode());
}
public String getState() {
return closedTradableFormatter.getStateAsString(tradable);
}
public int getNumPastTrades() {
return closedTradableManager.getNumPastTrades(tradable);
}
@Override
public boolean match(String filterString) {
if (filterString.isEmpty()) {
return true;
}
if (StringUtils.containsIgnoreCase(getDateAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getMarketLabel(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getPriceAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getPriceDeviationAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getVolumeAsString(true), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getAmountAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getTradeFeeAsString(true), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getTxFeeAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getBuyerSecurityDepositAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getSellerSecurityDepositAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getState(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getDirectionLabel(), filterString)) {
return true;
}
if (FilteringUtils.match(getTradable().getOffer(), filterString)) {
return true;
}
if (getTradable() instanceof BsqSwapTrade && FilteringUtils.match((BsqSwapTrade) getTradable(), filterString)) {
return true;
}
return getTradable() instanceof Trade && FilteringUtils.match((Trade) getTradable(), filterString);
}
}

View file

@ -18,28 +18,21 @@
-->
<?import bisq.desktop.components.AutoTooltipButton?>
<?import bisq.desktop.components.AutoTooltipLabel?>
<?import bisq.desktop.components.InputTextField?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?>
<?import javafx.geometry.Insets?>
<?import bisq.desktop.components.list.FilterBox?>
<VBox fx:id="root" fx:controller="bisq.desktop.main.portfolio.closedtrades.ClosedTradesView"
spacing="10" alignment="CENTER_RIGHT" xmlns:fx="http://javafx.com/fxml">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0"/>
</padding>
<HBox fx:id="searchBox">
<AutoTooltipLabel fx:id="filterLabel"/>
<InputTextField fx:id="filterTextField" minWidth="500"/>
<Pane fx:id="searchBoxSpacer"/>
</HBox>
<FilterBox fx:id="filterBox" />
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="tradeIdColumn" minWidth="110" maxWidth="120"/>

View file

@ -24,8 +24,8 @@ import bisq.desktop.components.AutoTooltipButton;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.AutoTooltipTableColumn;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.InputTextField;
import bisq.desktop.components.PeerInfoIconTrading;
import bisq.desktop.components.list.FilterBox;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.main.overlays.windows.BsqTradeDetailsWindow;
import bisq.desktop.main.overlays.windows.ClosedTradesSummaryWindow;
@ -41,7 +41,6 @@ import bisq.core.offer.OfferPayloadBase;
import bisq.core.offer.OpenOffer;
import bisq.core.trade.model.Tradable;
import bisq.core.trade.model.TradeModel;
import bisq.core.trade.model.bisq_v1.Contract;
import bisq.core.trade.model.bisq_v1.Trade;
import bisq.core.trade.model.bsq_swap.BsqSwapTrade;
import bisq.core.user.Preferences;
@ -51,8 +50,6 @@ import bisq.network.p2p.NodeAddress;
import bisq.common.config.Config;
import bisq.common.crypto.KeyRing;
import com.google.protobuf.Message;
import com.googlecode.jcsv.writer.CSVEntryConverter;
import javax.inject.Inject;
@ -75,7 +72,6 @@ import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
@ -94,7 +90,6 @@ import javafx.collections.transformation.SortedList;
import javafx.util.Callback;
import java.util.Comparator;
import java.util.Date;
import java.util.function.Function;
import static bisq.desktop.util.FormBuilder.getRegularIconButton;
@ -133,20 +128,14 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}
@FXML
TableView<Tradable> tableView;
TableView<ClosedTradesListItem> tableView;
@FXML
TableColumn<Tradable, Tradable> priceColumn, deviationColumn, amountColumn, volumeColumn,
TableColumn<ClosedTradesListItem, ClosedTradesListItem> priceColumn, deviationColumn, amountColumn, volumeColumn,
txFeeColumn, tradeFeeColumn, buyerSecurityDepositColumn, sellerSecurityDepositColumn,
marketColumn, directionColumn, dateColumn, tradeIdColumn, stateColumn,
duplicateColumn, avatarColumn;
@FXML
HBox searchBox;
@FXML
AutoTooltipLabel filterLabel;
@FXML
InputTextField filterTextField;
@FXML
Pane searchBoxSpacer;
FilterBox filterBox;
@FXML
AutoTooltipButton exportButton, summaryButton;
@FXML
@ -161,9 +150,8 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
private final Preferences preferences;
private final TradeDetailsWindow tradeDetailsWindow;
private final PrivateNotificationManager privateNotificationManager;
private SortedList<Tradable> sortedList;
private FilteredList<Tradable> filteredList;
private ChangeListener<String> filterTextFieldListener;
private SortedList<ClosedTradesListItem> sortedList;
private FilteredList<ClosedTradesListItem> filteredList;
private ChangeListener<Number> widthListener;
@Inject
@ -226,52 +214,49 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
setDuplicateColumnCellFactory();
setAvatarColumnCellFactory();
tradeIdColumn.setComparator(Comparator.comparing(Tradable::getId));
dateColumn.setComparator(Comparator.comparing(Tradable::getDate));
directionColumn.setComparator(Comparator.comparing(o -> o.getOffer().getDirection()));
marketColumn.setComparator(Comparator.comparing(model::getMarketLabel));
priceColumn.setComparator(Comparator.comparing(model::getPrice, Comparator.nullsFirst(Comparator.naturalOrder())));
tradeIdColumn.setComparator(Comparator.comparing(ClosedTradesListItem::getTradeId));
dateColumn.setComparator(Comparator.comparing(ClosedTradesListItem::getDateAsString));
directionColumn.setComparator(Comparator.comparing(o -> o.getTradable().getOffer().getDirection()));
marketColumn.setComparator(Comparator.comparing(ClosedTradesListItem::getMarketLabel));
priceColumn.setComparator(Comparator.comparing(ClosedTradesListItem::getPriceAsString, Comparator.nullsFirst(Comparator.naturalOrder())));
deviationColumn.setComparator(Comparator.comparing(o ->
o.getOffer().isUseMarketBasedPrice() ? o.getOffer().getMarketPriceMargin() : 1,
o.getTradable().getOffer().isUseMarketBasedPrice() ? o.getTradable().getOffer().getMarketPriceMargin() : 1,
Comparator.nullsFirst(Comparator.naturalOrder())));
volumeColumn.setComparator(nullsFirstComparingAsTrade(TradeModel::getVolume));
amountColumn.setComparator(Comparator.comparing(model::getAmount, Comparator.nullsFirst(Comparator.naturalOrder())));
avatarColumn.setComparator(Comparator.comparing(
model.dataModel::getNumPastTrades,
Comparator.nullsFirst(Comparator.naturalOrder())
));
amountColumn.setComparator(Comparator.comparing(ClosedTradesListItem::getAmountAsString, Comparator.nullsFirst(Comparator.naturalOrder())));
avatarColumn.setComparator(Comparator.comparing(ClosedTradesListItem::getNumPastTrades, Comparator.nullsFirst(Comparator.naturalOrder())));
txFeeColumn.setComparator(nullsFirstComparing(o ->
o instanceof TradeModel ? ((TradeModel) o).getTxFee() : o.getOffer().getTxFee()
o.getTradable() instanceof TradeModel ? ((TradeModel) o.getTradable()).getTxFee() : o.getTradable().getOffer().getTxFee()
));
txFeeColumn.setComparator(Comparator.comparing(model::getTxFee, Comparator.nullsFirst(Comparator.naturalOrder())));
txFeeColumn.setComparator(Comparator.comparing(ClosedTradesListItem::getTxFeeAsString, Comparator.nullsFirst(Comparator.naturalOrder())));
//
tradeFeeColumn.setComparator(Comparator.comparing(item -> {
String tradeFee = model.getTradeFee(item, true);
String tradeFee = item.getTradeFeeAsString(true);
// We want to separate BSQ and BTC fees so we use a prefix
if (item.getOffer().isCurrencyForMakerFeeBtc()) {
if (item.getTradable().getOffer().isCurrencyForMakerFeeBtc()) {
return "BTC" + tradeFee;
} else {
return "BSQ" + tradeFee;
}
}, Comparator.nullsFirst(Comparator.naturalOrder())));
buyerSecurityDepositColumn.setComparator(nullsFirstComparing(o ->
o.getOffer() != null ? o.getOffer().getBuyerSecurityDeposit() : null
o.getTradable().getOffer() != null ? o.getTradable().getOffer().getBuyerSecurityDeposit() : null
));
sellerSecurityDepositColumn.setComparator(nullsFirstComparing(o ->
o.getOffer() != null ? o.getOffer().getSellerSecurityDeposit() : null
o.getTradable().getOffer() != null ? o.getTradable().getOffer().getSellerSecurityDeposit() : null
));
stateColumn.setComparator(Comparator.comparing(model::getState));
stateColumn.setComparator(Comparator.comparing(ClosedTradesListItem::getState));
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
tableView.getSortOrder().add(dateColumn);
tableView.setRowFactory(
tableView -> {
TableRow<Tradable> row = new TableRow<>();
TableRow<ClosedTradesListItem> row = new TableRow<>();
ContextMenu rowMenu = new ContextMenu();
MenuItem duplicateItem = new MenuItem(Res.get("portfolio.context.offerLikeThis"));
duplicateItem.setOnAction((ActionEvent event) -> onDuplicateOffer(row.getItem().getOffer()));
duplicateItem.setOnAction((ActionEvent event) -> onDuplicateOffer(row.getItem().getTradable().getOffer()));
rowMenu.getItems().add(duplicateItem);
row.contextMenuProperty().bind(
Bindings.when(Bindings.isNotNull(row.itemProperty()))
@ -280,12 +265,6 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
return row;
});
filterLabel.setText(Res.get("shared.filter"));
HBox.setMargin(filterLabel, new Insets(5, 0, 0, 10));
filterTextFieldListener = (observable, oldValue, newValue) -> applyFilteredListPredicate(filterTextField.getText());
searchBox.setSpacing(5);
HBox.setHgrow(searchBoxSpacer, Priority.ALWAYS);
numItems.setId("num-offers");
numItems.setPadding(new Insets(-5, 0, 0, 10));
HBox.setHgrow(footerSpacer, Priority.ALWAYS);
@ -303,48 +282,49 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
tableView.setItems(sortedList);
filterBox.initialize(filteredList, tableView); // here because filteredList is instantiated here
filterBox.activate();
numItems.setText(Res.get("shared.numItemsLabel", sortedList.size()));
exportButton.setOnAction(event -> {
CSVEntryConverter<Tradable> headerConverter = item -> {
CSVEntryConverter<ClosedTradesListItem> headerConverter = item -> {
String[] columns = new String[ColumnNames.values().length];
for (ColumnNames m : ColumnNames.values()) {
columns[m.ordinal()] = m.toString();
}
return columns;
};
CSVEntryConverter<Tradable> contentConverter = item -> {
CSVEntryConverter<ClosedTradesListItem> contentConverter = item -> {
String[] columns = new String[ColumnNames.values().length];
columns[ColumnNames.TRADE_ID.ordinal()] = model.getTradeId(item);
columns[ColumnNames.DATE.ordinal()] = model.getDate(item);
columns[ColumnNames.MARKET.ordinal()] = model.getMarketLabel(item);
columns[ColumnNames.PRICE.ordinal()] = model.getPrice(item);
columns[ColumnNames.DEVIATION.ordinal()] = model.getPriceDeviation(item);
columns[ColumnNames.AMOUNT.ordinal()] = model.getAmount(item);
columns[ColumnNames.VOLUME.ordinal()] = model.getVolume(item, false);
columns[ColumnNames.VOLUME_CURRENCY.ordinal()] = model.getVolumeCurrency(item);
columns[ColumnNames.TX_FEE.ordinal()] = model.getTxFee(item);
if (model.dataModel.isCurrencyForTradeFeeBtc(item)) {
columns[ColumnNames.TRADE_FEE_BTC.ordinal()] = model.getTradeFee(item, false);
columns[ColumnNames.TRADE_ID.ordinal()] = item.getTradeId();
columns[ColumnNames.DATE.ordinal()] = item.getDateAsString();
columns[ColumnNames.MARKET.ordinal()] = item.getMarketLabel();
columns[ColumnNames.PRICE.ordinal()] = item.getPriceAsString();
columns[ColumnNames.DEVIATION.ordinal()] = item.getPriceDeviationAsString();
columns[ColumnNames.AMOUNT.ordinal()] = item.getAmountAsString();
columns[ColumnNames.VOLUME.ordinal()] = item.getVolumeAsString(false);
columns[ColumnNames.VOLUME_CURRENCY.ordinal()] = item.getVolumeCurrencyAsString();
columns[ColumnNames.TX_FEE.ordinal()] = item.getTxFeeAsString();
if (model.dataModel.isCurrencyForTradeFeeBtc(item.getTradable())) {
columns[ColumnNames.TRADE_FEE_BTC.ordinal()] = item.getTradeFeeAsString(false);
columns[ColumnNames.TRADE_FEE_BSQ.ordinal()] = "";
} else {
columns[ColumnNames.TRADE_FEE_BTC.ordinal()] = "";
columns[ColumnNames.TRADE_FEE_BSQ.ordinal()] = model.getTradeFee(item, false);
columns[ColumnNames.TRADE_FEE_BSQ.ordinal()] = item.getTradeFeeAsString(false);
}
columns[ColumnNames.BUYER_SEC.ordinal()] = model.getBuyerSecurityDeposit(item);
columns[ColumnNames.SELLER_SEC.ordinal()] = model.getSellerSecurityDeposit(item);
columns[ColumnNames.OFFER_TYPE.ordinal()] = model.getDirectionLabel(item);
columns[ColumnNames.STATUS.ordinal()] = model.getState(item);
columns[ColumnNames.BUYER_SEC.ordinal()] = item.getBuyerSecurityDepositAsString();
columns[ColumnNames.SELLER_SEC.ordinal()] = item.getSellerSecurityDepositAsString();
columns[ColumnNames.OFFER_TYPE.ordinal()] = item.getDirectionLabel();
columns[ColumnNames.STATUS.ordinal()] = item.getState();
return columns;
};
GUIUtil.exportCSV("tradeHistory.csv", headerConverter, contentConverter,
getDummyTradable(), sortedList, (Stage) root.getScene().getWindow());
null, sortedList, (Stage) root.getScene().getWindow());
});
summaryButton.setOnAction(event -> new ClosedTradesSummaryWindow(model).show());
filterTextField.textProperty().addListener(filterTextFieldListener);
applyFilteredListPredicate(filterTextField.getText());
root.widthProperty().addListener(widthListener);
onWidthChange(root.getWidth());
}
@ -355,20 +335,20 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
exportButton.setOnAction(null);
summaryButton.setOnAction(null);
filterTextField.textProperty().removeListener(filterTextFieldListener);
filterBox.deactivate();
root.widthProperty().removeListener(widthListener);
}
private static <T extends Comparable<T>> Comparator<Tradable> nullsFirstComparing(Function<Tradable, T> keyExtractor) {
private static <T extends Comparable<T>> Comparator<ClosedTradesListItem> nullsFirstComparing(Function<ClosedTradesListItem, T> keyExtractor) {
return Comparator.comparing(
o -> o != null ? keyExtractor.apply(o) : null,
Comparator.nullsFirst(Comparator.naturalOrder())
);
}
private static <T extends Comparable<T>> Comparator<Tradable> nullsFirstComparingAsTrade(Function<TradeModel, T> keyExtractor) {
private static <T extends Comparable<T>> Comparator<ClosedTradesListItem> nullsFirstComparingAsTrade(Function<TradeModel, T> keyExtractor) {
return Comparator.comparing(
o -> o instanceof TradeModel ? keyExtractor.apply((TradeModel) o) : null,
o -> o.getTradable() instanceof TradeModel ? keyExtractor.apply((TradeModel) o.getTradable()) : null,
Comparator.nullsFirst(Comparator.naturalOrder())
);
}
@ -380,103 +360,6 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
sellerSecurityDepositColumn.setVisible(width > 1500);
}
private void applyFilteredListPredicate(String filterString) {
filteredList.setPredicate(tradable -> {
if (filterString.isEmpty())
return true;
Offer offer = tradable.getOffer();
if (offer.getId().contains(filterString)) {
return true;
}
if (model.getDate(tradable).contains(filterString)) {
return true;
}
if (model.getMarketLabel(tradable).contains(filterString)) {
return true;
}
if (model.getPrice(tradable).contains(filterString)) {
return true;
}
if (model.getPriceDeviation(tradable).contains(filterString)) {
return true;
}
if (model.getVolume(tradable, true).contains(filterString)) {
return true;
}
if (model.getAmount(tradable).contains(filterString)) {
return true;
}
if (model.getTradeFee(tradable, true).contains(filterString)) {
return true;
}
if (model.getTxFee(tradable).contains(filterString)) {
return true;
}
if (model.getBuyerSecurityDeposit(tradable).contains(filterString)) {
return true;
}
if (model.getSellerSecurityDeposit(tradable).contains(filterString)) {
return true;
}
if (model.getState(tradable).contains(filterString)) {
return true;
}
if (model.getDirectionLabel(tradable).contains(filterString)) {
return true;
}
if (offer.getPaymentMethod().getDisplayString().contains(filterString)) {
return true;
}
if (offer.getOfferFeePaymentTxId() != null &&
offer.getOfferFeePaymentTxId().contains(filterString)) {
return true;
}
if (tradable instanceof BsqSwapTrade) {
BsqSwapTrade bsqSwapTrade = (BsqSwapTrade) tradable;
if (bsqSwapTrade.getTxId() != null && bsqSwapTrade.getTxId().contains(filterString)) {
return true;
}
if (bsqSwapTrade.getTradingPeerNodeAddress().getFullAddress().contains(filterString)) {
return true;
}
}
if (tradable instanceof Trade) {
Trade trade = (Trade) tradable;
if (trade.getTakerFeeTxId() != null && trade.getTakerFeeTxId().contains(filterString)) {
return true;
}
if (trade.getDepositTxId() != null && trade.getDepositTxId().contains(filterString)) {
return true;
}
if (trade.getPayoutTxId() != null && trade.getPayoutTxId().contains(filterString)) {
return true;
}
Contract contract = trade.getContract();
boolean isBuyerOnion = false;
boolean isSellerOnion = false;
boolean matchesBuyersPaymentAccountData = false;
boolean matchesSellersPaymentAccountData = false;
if (contract != null) {
isBuyerOnion = contract.getBuyerNodeAddress().getFullAddress().contains(filterString);
isSellerOnion = contract.getSellerNodeAddress().getFullAddress().contains(filterString);
matchesBuyersPaymentAccountData = contract.getBuyerPaymentAccountPayload() != null &&
contract.getBuyerPaymentAccountPayload().getPaymentDetails().contains(filterString);
matchesSellersPaymentAccountData = contract.getSellerPaymentAccountPayload() != null &&
contract.getSellerPaymentAccountPayload().getPaymentDetails().contains(filterString);
}
return isBuyerOnion || isSellerOnion ||
matchesBuyersPaymentAccountData || matchesSellersPaymentAccountData;
} else {
return false;
}
});
}
private void setTradeIdColumnCellFactory() {
tradeIdColumn.getStyleClass().add("first-column");
tradeIdColumn.setCellValueFactory((offerListItem) -> new ReadOnlyObjectWrapper<>(offerListItem.getValue()));
@ -484,17 +367,18 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(TableColumn<Tradable,
Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(TableColumn<ClosedTradesListItem,
ClosedTradesListItem> column) {
return new TableCell<>() {
private HyperlinkWithIcon field;
@Override
public void updateItem(final Tradable tradable, boolean empty) {
super.updateItem(tradable, empty);
if (tradable != null && !empty) {
field = new HyperlinkWithIcon(model.getTradeId(tradable));
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
field = new HyperlinkWithIcon(item.getTradeId());
field.setOnAction(event -> {
Tradable tradable = item.getTradable();
if (tradable instanceof Trade) {
tradeDetailsWindow.show((Trade) tradable);
} else if (tradable instanceof BsqSwapTrade) {
@ -517,18 +401,18 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}
private void setDateColumnCellFactory() {
dateColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
dateColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
dateColumn.setCellFactory(
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(
TableColumn<Tradable, Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(
TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final Tradable item, boolean empty) {
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setGraphic(new AutoTooltipLabel(model.getDate(item)));
setGraphic(new AutoTooltipLabel(item.getDateAsString()));
else
setGraphic(null);
}
@ -538,17 +422,21 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}
private void setMarketColumnCellFactory() {
marketColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
marketColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
marketColumn.setCellFactory(
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(
TableColumn<Tradable, Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(
TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final Tradable item, boolean empty) {
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getMarketLabel(item)));
if (item != null && !empty) {
setGraphic(new AutoTooltipLabel(item.getMarketLabel()));
} else {
setGraphic(null);
}
}
};
}
@ -556,18 +444,18 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}
private void setStateColumnCellFactory() {
stateColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
stateColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
stateColumn.setCellFactory(
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(
TableColumn<Tradable, Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(
TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final Tradable item, boolean empty) {
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setGraphic(new AutoTooltipLabel(model.getState(item)));
setGraphic(new AutoTooltipLabel(item.getState()));
else
setGraphic(null);
}
@ -582,21 +470,21 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
duplicateColumn.setCellFactory(
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(TableColumn<Tradable, Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<>() {
Button button;
@Override
public void updateItem(final Tradable item, boolean empty) {
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty && isMyOfferAsMaker(item.getOffer().getOfferPayloadBase())) {
if (item != null && !empty && isMyOfferAsMaker(item.getTradable().getOffer().getOfferPayloadBase())) {
if (button == null) {
button = getRegularIconButton(MaterialDesignIcon.CONTENT_COPY);
button.setTooltip(new Tooltip(Res.get("shared.duplicateOffer")));
setGraphic(button);
}
button.setOnAction(event -> onDuplicateOffer(item.getOffer()));
button.setOnAction(event -> onDuplicateOffer(item.getTradable().getOffer()));
} else {
setGraphic(null);
if (button != null) {
@ -611,22 +499,22 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}
@SuppressWarnings("UnusedReturnValue")
private TableColumn<Tradable, Tradable> setAvatarColumnCellFactory() {
private TableColumn<ClosedTradesListItem, ClosedTradesListItem> setAvatarColumnCellFactory() {
avatarColumn.getStyleClass().addAll("last-column", "avatar-column");
avatarColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
avatarColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
avatarColumn.setCellFactory(
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(TableColumn<Tradable, Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final Tradable item, boolean empty) {
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item instanceof TradeModel) {
TradeModel tradeModel = (TradeModel) item;
int numPastTrades = model.dataModel.getNumPastTrades(tradeModel);
if (!empty && item != null && item.getTradable() instanceof TradeModel) {
TradeModel tradeModel = (TradeModel) item.getTradable();
int numPastTrades = item.getNumPastTrades();
NodeAddress tradingPeerNodeAddress = tradeModel.getTradingPeerNodeAddress();
String role = Res.get("peerInfoIcon.tooltip.tradePeer");
Node peerInfoIcon = new PeerInfoIconTrading(tradingPeerNodeAddress,
@ -650,17 +538,21 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}
private void setAmountColumnCellFactory() {
amountColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
amountColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
amountColumn.setCellFactory(
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(
TableColumn<Tradable, Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(
TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final Tradable item, boolean empty) {
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getAmount(item)));
if (item != null && !empty) {
setGraphic(new AutoTooltipLabel(item.getAmountAsString()));
} else {
setGraphic(null);
}
}
};
}
@ -668,17 +560,21 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}
private void setPriceColumnCellFactory() {
priceColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
priceColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
priceColumn.setCellFactory(
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(
TableColumn<Tradable, Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(
TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final Tradable item, boolean empty) {
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getPrice(item)));
if (item != null && !empty) {
setGraphic(new AutoTooltipLabel(item.getPriceAsString()));
} else {
setGraphic(null);
}
}
};
}
@ -686,17 +582,21 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}
private void setDeviationColumnCellFactory() {
deviationColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
deviationColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
deviationColumn.setCellFactory(
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(
TableColumn<Tradable, Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(
TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final Tradable item, boolean empty) {
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getPriceDeviation(item)));
if (item != null && !empty) {
setGraphic(new AutoTooltipLabel(item.getPriceDeviationAsString()));
} else {
setGraphic(null);
}
}
};
}
@ -704,18 +604,18 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}
private void setVolumeColumnCellFactory() {
volumeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
volumeColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
volumeColumn.setCellFactory(
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(
TableColumn<Tradable, Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(
TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final Tradable item, boolean empty) {
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setGraphic(new AutoTooltipLabel(model.getVolume(item, true)));
setGraphic(new AutoTooltipLabel(item.getVolumeAsString(true)));
else
setGraphic(null);
}
@ -725,17 +625,21 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}
private void setDirectionColumnCellFactory() {
directionColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
directionColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
directionColumn.setCellFactory(
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(
TableColumn<Tradable, Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(
TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final Tradable item, boolean empty) {
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getDirectionLabel(item)));
if (item != null && !empty) {
setGraphic(new AutoTooltipLabel(item.getDirectionLabel()));
} else {
setGraphic(null);
}
}
};
}
@ -743,17 +647,21 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}
private void setTxFeeColumnCellFactory() {
txFeeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
txFeeColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
txFeeColumn.setCellFactory(
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(
TableColumn<Tradable, Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(
TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final Tradable item, boolean empty) {
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getTxFee(item)));
if (item != null && !empty) {
setGraphic(new AutoTooltipLabel(item.getTxFeeAsString()));
} else {
setGraphic(null);
}
}
};
}
@ -761,17 +669,21 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}
private void setTradeFeeColumnCellFactory() {
tradeFeeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
tradeFeeColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
tradeFeeColumn.setCellFactory(
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(
TableColumn<Tradable, Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(
TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final Tradable item, boolean empty) {
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getTradeFee(item, true)));
if (item != null && !empty) {
setGraphic(new AutoTooltipLabel(item.getTradeFeeAsString(true)));
} else {
setGraphic(null);
}
}
};
}
@ -779,17 +691,21 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}
private void setBuyerSecurityDepositColumnCellFactory() {
buyerSecurityDepositColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
buyerSecurityDepositColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
buyerSecurityDepositColumn.setCellFactory(
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(
TableColumn<Tradable, Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(
TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final Tradable item, boolean empty) {
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getBuyerSecurityDeposit(item)));
if (item != null && !empty) {
setGraphic(new AutoTooltipLabel(item.getBuyerSecurityDepositAsString()));
} else {
setGraphic(null);
}
}
};
}
@ -797,17 +713,21 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
}
private void setSellerSecurityDepositColumnCellFactory() {
sellerSecurityDepositColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
sellerSecurityDepositColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
sellerSecurityDepositColumn.setCellFactory(
new Callback<>() {
@Override
public TableCell<Tradable, Tradable> call(
TableColumn<Tradable, Tradable> column) {
public TableCell<ClosedTradesListItem, ClosedTradesListItem> call(
TableColumn<ClosedTradesListItem, ClosedTradesListItem> column) {
return new TableCell<>() {
@Override
public void updateItem(final Tradable item, boolean empty) {
public void updateItem(final ClosedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getSellerSecurityDeposit(item)));
if (item != null && !empty) {
setGraphic(new AutoTooltipLabel(item.getSellerSecurityDepositAsString()));
} else {
setGraphic(null);
}
}
};
}
@ -830,33 +750,4 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
private boolean isMyOfferAsMaker(OfferPayloadBase offerPayloadBase) {
return offerPayloadBase.getPubKeyRing().equals(keyRing.getPubKeyRing());
}
private Tradable getDummyTradable() {
return new Tradable() {
@Override
public Offer getOffer() {
return null;
}
@Override
public Date getDate() {
return null;
}
@Override
public String getId() {
return null;
}
@Override
public String getShortId() {
return null;
}
@Override
public Message toProtoMessage() {
return null;
}
};
}
}

View file

@ -19,13 +19,9 @@ package bisq.desktop.main.portfolio.closedtrades;
import bisq.desktop.common.model.ActivatableWithDataModel;
import bisq.desktop.common.model.ViewModel;
import bisq.desktop.util.DisplayUtils;
import bisq.core.locale.CurrencyUtil;
import bisq.core.monetary.Volume;
import bisq.core.offer.OfferDirection;
import bisq.core.trade.ClosedTradableFormatter;
import bisq.core.trade.model.Tradable;
import org.bitcoinj.core.Coin;
@ -43,69 +39,6 @@ public class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTrades
this.closedTradableFormatter = closedTradableFormatter;
}
String getTradeId(Tradable item) {
return item.getShortId();
}
String getAmount(Tradable item) {
return item != null ? closedTradableFormatter.getAmountAsString(item) : "";
}
String getPrice(Tradable item) {
return item != null ? closedTradableFormatter.getPriceAsString(item) : "";
}
String getPriceDeviation(Tradable item) {
return item != null ? closedTradableFormatter.getPriceDeviationAsString(item) : "";
}
String getVolume(Tradable item, boolean appendCode) {
return item != null ? closedTradableFormatter.getVolumeAsString(item, appendCode) : "";
}
String getVolumeCurrency(Tradable item) {
return item != null ? closedTradableFormatter.getVolumeCurrencyAsString(item) : "";
}
String getTxFee(Tradable item) {
return item != null ? closedTradableFormatter.getTxFeeAsString(item) : "";
}
String getTradeFee(Tradable item, boolean appendCode) {
return item != null ? closedTradableFormatter.getTradeFeeAsString(item, appendCode) : "";
}
String getBuyerSecurityDeposit(Tradable item) {
return item != null ? closedTradableFormatter.getBuyerSecurityDepositAsString(item) : "";
}
String getSellerSecurityDeposit(Tradable item) {
return item != null ? closedTradableFormatter.getSellerSecurityDepositAsString(item) : "";
}
String getDirectionLabel(Tradable item) {
if ((item != null)) {
OfferDirection direction = dataModel.getDirection(item.getOffer());
String currencyCode = item.getOffer().getCurrencyCode();
return DisplayUtils.getDirectionWithCode(direction, currencyCode);
} else {
return "";
}
}
String getDate(Tradable item) {
return DisplayUtils.formatDateTime(item.getDate());
}
String getMarketLabel(Tradable item) {
return item != null ? CurrencyUtil.getCurrencyPair(item.getOffer().getCurrencyCode()) : "";
}
String getState(Tradable item) {
return item != null ? closedTradableFormatter.getStateAsString(item) : "";
}
///////////////////////////////////////////////////////////////////////////////////////////
// Used in ClosedTradesSummaryWindow
///////////////////////////////////////////////////////////////////////////////////////////
@ -121,7 +54,7 @@ public class ClosedTradesViewModel extends ActivatableWithDataModel<ClosedTrades
}
public Map<String, String> getTotalVolumeByCurrency() {
return closedTradableFormatter.getTotalVolumeByCurrencyAsString(dataModel.getList());
return closedTradableFormatter.getTotalVolumeByCurrencyAsString(dataModel.getListAsTradables());
}
public String getTotalTxFee(Coin totalTradeAmount) {

View file

@ -19,11 +19,11 @@ package bisq.desktop.main.portfolio.failedtrades;
import bisq.desktop.common.model.ActivatableDataModel;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.trade.TradeManager;
import bisq.core.trade.bisq_v1.FailedTradesManager;
import bisq.core.trade.model.bisq_v1.Trade;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.P2PService;
@ -32,6 +32,8 @@ import bisq.common.crypto.KeyRing;
import com.google.inject.Inject;
import javax.inject.Named;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
@ -45,6 +47,7 @@ class FailedTradesDataModel extends ActivatableDataModel {
private final TradeManager tradeManager;
private final P2PService p2PService;
private final KeyRing keyRing;
private final CoinFormatter btcFormatter;
private final ObservableList<FailedTradesListItem> list = FXCollections.observableArrayList();
private final ListChangeListener<Trade> tradesListChangeListener;
@ -53,11 +56,13 @@ class FailedTradesDataModel extends ActivatableDataModel {
public FailedTradesDataModel(FailedTradesManager failedTradesManager,
TradeManager tradeManager,
P2PService p2PService,
KeyRing keyRing) {
KeyRing keyRing,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter) {
this.failedTradesManager = failedTradesManager;
this.tradeManager = tradeManager;
this.p2PService = p2PService;
this.keyRing = keyRing;
this.btcFormatter = btcFormatter;
tradesListChangeListener = change -> applyList();
}
@ -77,14 +82,15 @@ class FailedTradesDataModel extends ActivatableDataModel {
return list;
}
public OfferDirection getDirection(Offer offer) {
return failedTradesManager.wasMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection();
}
private void applyList() {
list.clear();
list.addAll(failedTradesManager.getObservableList().stream().map(FailedTradesListItem::new).collect(Collectors.toList()));
list.addAll(
failedTradesManager.getObservableList().stream()
.map(trade -> new FailedTradesListItem(trade, btcFormatter, failedTradesManager))
.collect(Collectors.toList())
);
// we sort by date, earliest first
list.sort((o1, o2) -> o2.getTrade().getDate().compareTo(o1.getTrade().getDate()));

View file

@ -17,19 +17,92 @@
package bisq.desktop.main.portfolio.failedtrades;
import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.filtering.FilterableListItem;
import bisq.desktop.util.filtering.FilteringUtils;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.trade.bisq_v1.FailedTradesManager;
import bisq.core.trade.model.bisq_v1.Trade;
import bisq.core.util.FormattingUtils;
import bisq.core.util.VolumeUtil;
import bisq.core.util.coin.CoinFormatter;
import org.apache.commons.lang3.StringUtils;
import lombok.Getter;
class FailedTradesListItem {
class FailedTradesListItem implements FilterableListItem {
@Getter
private final Trade trade;
private final CoinFormatter btcFormatter;
private final FailedTradesManager failedTradesManager;
FailedTradesListItem(Trade trade) {
FailedTradesListItem(Trade trade, CoinFormatter btcFormatter, FailedTradesManager failedTradesManager) {
this.trade = trade;
this.btcFormatter = btcFormatter;
this.failedTradesManager = failedTradesManager;
}
FailedTradesListItem() {
this.trade = null;
public String getDateAsString() {
return DisplayUtils.formatDateTime(trade.getDate());
}
public String getMarketLabel() {
return CurrencyUtil.getCurrencyPair(trade.getOffer().getCurrencyCode());
}
public String getAmountAsString() {
return btcFormatter.formatCoin(trade.getAmount());
}
public String getPriceAsString() {
return FormattingUtils.formatPrice(trade.getPrice());
}
public String getVolumeAsString() {
return VolumeUtil.formatVolumeWithCode(trade.getVolume());
}
public String getDirectionLabel() {
Offer offer = trade.getOffer();
OfferDirection direction = failedTradesManager.wasMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection();
return DisplayUtils.getDirectionWithCode(direction, trade.getOffer().getCurrencyCode());
}
public String getState() {
return Res.get("portfolio.failed.Failed");
}
@Override
public boolean match(String filterString) {
if (filterString.isEmpty()) {
return true;
}
if (StringUtils.containsIgnoreCase(getDateAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getMarketLabel(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getPriceAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getVolumeAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getAmountAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getDirectionLabel(), filterString)) {
return true;
}
if (FilteringUtils.match(getTrade().getOffer(), filterString)) {
return true;
}
return FilteringUtils.match(getTrade(), filterString);
}
}

View file

@ -18,26 +18,20 @@
-->
<?import bisq.desktop.components.AutoTooltipButton?>
<?import bisq.desktop.components.AutoTooltipLabel?>
<?import bisq.desktop.components.InputTextField?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.VBox?>
<?import javafx.geometry.Insets?>
<?import bisq.desktop.components.list.FilterBox?>
<VBox fx:id="root" fx:controller="bisq.desktop.main.portfolio.failedtrades.FailedTradesView"
spacing="10" xmlns:fx="http://javafx.com/fxml">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0"/>
</padding>
<HBox fx:id="searchBox">
<AutoTooltipLabel fx:id="filterLabel"/>
<InputTextField fx:id="filterTextField" minWidth="500"/>
<Pane fx:id="searchBoxSpacer"/>
</HBox>
<FilterBox fx:id="filterBox" />
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="tradeIdColumn" minWidth="120" maxWidth="120"/>

View file

@ -22,15 +22,13 @@ import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipButton;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.InputTextField;
import bisq.desktop.components.list.FilterBox;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.main.overlays.windows.TradeDetailsWindow;
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.GUIUtil;
import bisq.core.locale.Res;
import bisq.core.offer.Offer;
import bisq.core.trade.model.bisq_v1.Contract;
import bisq.core.trade.model.bisq_v1.Trade;
import bisq.common.config.Config;
@ -58,7 +56,6 @@ import javafx.scene.control.Tooltip;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
@ -66,7 +63,6 @@ import javafx.scene.layout.VBox;
import javafx.geometry.Insets;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.value.ChangeListener;
import javafx.event.EventHandler;
@ -87,13 +83,7 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
TableColumn<FailedTradesListItem, FailedTradesListItem> priceColumn, amountColumn, volumeColumn,
marketColumn, directionColumn, dateColumn, tradeIdColumn, stateColumn, removeTradeColumn;
@FXML
HBox searchBox;
@FXML
AutoTooltipLabel filterLabel;
@FXML
InputTextField filterTextField;
@FXML
Pane searchBoxSpacer;
FilterBox filterBox;
@FXML
Label numItems;
@FXML
@ -105,7 +95,6 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
private SortedList<FailedTradesListItem> sortedList;
private FilteredList<FailedTradesListItem> filteredList;
private EventHandler<KeyEvent> keyEventEventHandler;
private ChangeListener<String> filterTextFieldListener;
private Scene scene;
private final boolean allowFaultyDelayedTxs;
@ -147,8 +136,8 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
priceColumn.setComparator(Comparator.comparing(o -> o.getTrade().getPrice()));
volumeColumn.setComparator(Comparator.comparing(o -> o.getTrade().getVolume(), Comparator.nullsFirst(Comparator.naturalOrder())));
amountColumn.setComparator(Comparator.comparing(o -> o.getTrade().getAmount(), Comparator.nullsFirst(Comparator.naturalOrder())));
stateColumn.setComparator(Comparator.comparing(model::getState));
marketColumn.setComparator(Comparator.comparing(model::getMarketLabel));
stateColumn.setComparator(Comparator.comparing(FailedTradesListItem::getState));
marketColumn.setComparator(Comparator.comparing(FailedTradesListItem::getMarketLabel));
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
tableView.getSortOrder().add(dateColumn);
@ -173,12 +162,6 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
}
};
filterLabel.setText(Res.get("shared.filter"));
HBox.setMargin(filterLabel, new Insets(5, 0, 0, 10));
filterTextFieldListener = (observable, oldValue, newValue) -> applyFilteredListPredicate(filterTextField.getText());
searchBox.setSpacing(5);
HBox.setHgrow(searchBoxSpacer, Priority.ALWAYS);
numItems.setId("num-offers");
numItems.setPadding(new Insets(-5, 0, 0, 10));
HBox.setHgrow(footerSpacer, Priority.ALWAYS);
@ -193,11 +176,14 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
scene.addEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler);
}
filteredList = new FilteredList<>(model.getList());
filteredList = new FilteredList<>(model.dataModel.getList());
sortedList = new SortedList<>(filteredList);
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
tableView.setItems(sortedList);
filterBox.initialize(filteredList, tableView); // here because filteredList is instantiated here
filterBox.activate();
numItems.setText(Res.get("shared.numItemsLabel", sortedList.size()));
exportButton.setOnAction(event -> {
ObservableList<TableColumn<FailedTradesListItem, ?>> tableColumns = tableView.getColumns();
@ -210,27 +196,24 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
};
CSVEntryConverter<FailedTradesListItem> contentConverter = item -> {
String[] columns = new String[reportColumns];
columns[0] = model.getTradeId(item);
columns[1] = model.getDate(item);
columns[2] = model.getMarketLabel(item);
columns[3] = model.getPrice(item);
columns[4] = model.getAmount(item);
columns[5] = model.getVolume(item);
columns[6] = model.getDirectionLabel(item);
columns[7] = model.getState(item);
columns[0] = item.getTrade().getShortId();
columns[1] = item.getDateAsString();
columns[2] = item.getMarketLabel();
columns[3] = item.getPriceAsString();
columns[4] = item.getAmountAsString();
columns[5] = item.getVolumeAsString();
columns[6] = item.getDirectionLabel();
columns[7] = item.getState();
return columns;
};
GUIUtil.exportCSV("failedTrades.csv",
headerConverter,
contentConverter,
new FailedTradesListItem(),
null,
sortedList,
(Stage) root.getScene().getWindow());
});
filterTextField.textProperty().addListener(filterTextFieldListener);
applyFilteredListPredicate(filterTextField.getText());
}
@Override
@ -241,72 +224,7 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
sortedList.comparatorProperty().unbind();
exportButton.setOnAction(null);
filterTextField.textProperty().removeListener(filterTextFieldListener);
}
private void applyFilteredListPredicate(String filterString) {
filteredList.setPredicate(item -> {
if (filterString.isEmpty())
return true;
Offer offer = item.getTrade().getOffer();
if (offer.getId().contains(filterString)) {
return true;
}
if (model.getDate(item).contains(filterString)) {
return true;
}
if (model.getMarketLabel(item).contains(filterString)) {
return true;
}
if (model.getPrice(item).contains(filterString)) {
return true;
}
if (model.getVolume(item).contains(filterString)) {
return true;
}
if (model.getAmount(item).contains(filterString)) {
return true;
}
if (model.getDirectionLabel(item).contains(filterString)) {
return true;
}
if (offer.getOfferFeePaymentTxId() != null &&
offer.getOfferFeePaymentTxId().contains(filterString)) {
return true;
}
Trade trade = item.getTrade();
if (trade.getTakerFeeTxId() != null && trade.getTakerFeeTxId().contains(filterString)) {
return true;
}
if (trade.getDepositTxId() != null && trade.getDepositTxId().contains(filterString)) {
return true;
}
if (trade.getPayoutTxId() != null && trade.getPayoutTxId().contains(filterString)) {
return true;
}
Contract contract = trade.getContract();
boolean isBuyerOnion = false;
boolean isSellerOnion = false;
boolean matchesBuyersPaymentAccountData = false;
boolean matchesSellersPaymentAccountData = false;
if (contract != null) {
isBuyerOnion = contract.getBuyerNodeAddress().getFullAddress().contains(filterString);
isSellerOnion = contract.getSellerNodeAddress().getFullAddress().contains(filterString);
matchesBuyersPaymentAccountData = contract.getBuyerPaymentAccountPayload() != null &&
contract.getBuyerPaymentAccountPayload().getPaymentDetails().contains(filterString);
matchesSellersPaymentAccountData = contract.getSellerPaymentAccountPayload() != null &&
contract.getSellerPaymentAccountPayload().getPaymentDetails().contains(filterString);
}
return isBuyerOnion || isSellerOnion ||
matchesBuyersPaymentAccountData || matchesSellersPaymentAccountData;
});
filterBox.deactivate();
}
private void onUnfail() {
@ -367,7 +285,7 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
public void updateItem(final FailedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
field = new HyperlinkWithIcon(model.getTradeId(item));
field = new HyperlinkWithIcon(item.getTrade().getId());
field.setOnAction(event -> tradeDetailsWindow.show(item.getTrade()));
field.setTooltip(new Tooltip(Res.get("tooltip.openPopupForDetails")));
setGraphic(field);
@ -394,7 +312,7 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
public void updateItem(final FailedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setGraphic(new AutoTooltipLabel(model.getDate(item)));
setGraphic(new AutoTooltipLabel(item.getDateAsString()));
else
setGraphic(null);
}
@ -414,7 +332,11 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
@Override
public void updateItem(final FailedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getMarketLabel(item)));
if (!empty && item != null) {
setGraphic(new AutoTooltipLabel(item.getMarketLabel()));
} else {
setGraphic(null);
}
}
};
}
@ -434,7 +356,7 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
public void updateItem(final FailedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setGraphic(new AutoTooltipLabel(model.getState(item)));
setGraphic(new AutoTooltipLabel(item.getState()));
else
setGraphic(null);
}
@ -455,7 +377,11 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
@Override
public void updateItem(final FailedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getAmount(item)));
if (!empty && item != null) {
setGraphic(new AutoTooltipLabel(item.getAmountAsString()));
} else {
setGraphic(null);
}
}
};
}
@ -473,7 +399,11 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
@Override
public void updateItem(final FailedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getPrice(item)));
if (!empty && item != null) {
setGraphic(new AutoTooltipLabel(item.getPriceAsString()));
} else {
setGraphic(null);
}
}
};
}
@ -492,7 +422,7 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
public void updateItem(final FailedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setGraphic(new AutoTooltipLabel(model.getVolume(item)));
setGraphic(new AutoTooltipLabel(item.getVolumeAsString()));
else
setGraphic(null);
}
@ -512,7 +442,11 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
@Override
public void updateItem(final FailedTradesListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getDirectionLabel(item)));
if (!empty && item != null) {
setGraphic(new AutoTooltipLabel(item.getDirectionLabel()));
} else {
setGraphic(null);
}
}
};
}

View file

@ -19,74 +19,12 @@ package bisq.desktop.main.portfolio.failedtrades;
import bisq.desktop.common.model.ActivatableWithDataModel;
import bisq.desktop.common.model.ViewModel;
import bisq.desktop.util.DisplayUtils;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.util.FormattingUtils;
import bisq.core.util.VolumeUtil;
import bisq.core.util.coin.CoinFormatter;
import com.google.inject.Inject;
import javax.inject.Named;
import javafx.collections.ObservableList;
class FailedTradesViewModel extends ActivatableWithDataModel<FailedTradesDataModel> implements ViewModel {
private final CoinFormatter formatter;
@Inject
public FailedTradesViewModel(FailedTradesDataModel dataModel,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter) {
public FailedTradesViewModel(FailedTradesDataModel dataModel) {
super(dataModel);
this.formatter = formatter;
}
public ObservableList<FailedTradesListItem> getList() {
return dataModel.getList();
}
String getTradeId(FailedTradesListItem item) {
return item.getTrade().getShortId();
}
String getAmount(FailedTradesListItem item) {
if (item != null && item.getTrade() != null)
return formatter.formatCoin(item.getTrade().getAmount());
else
return "";
}
String getPrice(FailedTradesListItem item) {
return (item != null) ? FormattingUtils.formatPrice(item.getTrade().getPrice()) : "";
}
String getVolume(FailedTradesListItem item) {
if (item != null && item.getTrade() != null)
return VolumeUtil.formatVolumeWithCode(item.getTrade().getVolume());
else
return "";
}
String getDirectionLabel(FailedTradesListItem item) {
return (item != null) ? DisplayUtils.getDirectionWithCode(dataModel.getDirection(item.getTrade().getOffer()), item.getTrade().getOffer().getCurrencyCode()) : "";
}
String getMarketLabel(FailedTradesListItem item) {
if ((item == null))
return "";
return CurrencyUtil.getCurrencyPair(item.getTrade().getOffer().getCurrencyCode());
}
String getDate(FailedTradesListItem item) {
return DisplayUtils.formatDateTime(item.getTrade().getDate());
}
String getState(FailedTradesListItem item) {
return item != null ? Res.get("portfolio.failed.Failed") : "";
}
}

View file

@ -17,27 +17,152 @@
package bisq.desktop.main.portfolio.openoffer;
import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.filtering.FilterableListItem;
import bisq.desktop.util.filtering.FilteringUtils;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.monetary.Price;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.offer.OpenOffer;
import bisq.core.offer.OpenOfferManager;
import bisq.core.util.FormattingUtils;
import bisq.core.util.PriceUtil;
import bisq.core.util.VolumeUtil;
import bisq.core.util.coin.BsqFormatter;
import bisq.core.util.coin.CoinFormatter;
import org.apache.commons.lang3.StringUtils;
import lombok.Getter;
/**
* We could remove that wrapper if it is not needed for additional UI only fields.
*/
class OpenOfferListItem {
class OpenOfferListItem implements FilterableListItem {
@Getter
private final OpenOffer openOffer;
private final PriceUtil priceUtil;
private final CoinFormatter btcFormatter;
private final BsqFormatter bsqFormatter;
private final OpenOfferManager openOfferManager;
OpenOfferListItem(OpenOffer openOffer) {
OpenOfferListItem(OpenOffer openOffer, PriceUtil priceUtil, CoinFormatter btcFormatter, BsqFormatter bsqFormatter, OpenOfferManager openOfferManager) {
this.openOffer = openOffer;
}
OpenOfferListItem() {
openOffer = null;
this.priceUtil = priceUtil;
this.btcFormatter = btcFormatter;
this.bsqFormatter = bsqFormatter;
this.openOfferManager = openOfferManager;
}
public Offer getOffer() {
return openOffer.getOffer();
}
public String getDateAsString() {
return DisplayUtils.formatDateTime(getOffer().getDate());
}
public String getMarketDescription() {
return CurrencyUtil.getCurrencyPair(getOffer().getCurrencyCode());
}
public String getPriceAsString() {
Price price = getOffer().getPrice();
if (price != null) {
return FormattingUtils.formatPrice(price);
} else {
return Res.get("shared.na");
}
}
public Double getPriceDeviationAsDouble() {
Offer offer = getOffer();
return priceUtil.getMarketBasedPrice(offer, offer.getMirroredDirection()).orElse(0d);
}
public String getPriceDeviationAsString() {
Offer offer = getOffer();
return priceUtil.getMarketBasedPrice(offer, offer.getMirroredDirection())
.map(FormattingUtils::formatPercentagePrice)
.orElse("");
}
public String getPaymentMethodAsString() {
return getOffer().getPaymentMethodNameWithCountryCode();
}
public String getVolumeAsString() {
return VolumeUtil.formatVolume(getOffer(), false, 0) + " " + getOffer().getCurrencyCode();
}
public String getAmountAsString() {
return DisplayUtils.formatAmount(getOffer(), btcFormatter);
}
public String getDirectionLabel() {
Offer offer = getOffer();
OfferDirection direction = openOfferManager.isMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection();
return DisplayUtils.getDirectionWithCode(direction, getOffer().getCurrencyCode());
}
public boolean hasMakerFee() {
return getOffer().getMakerFee().isPositive();
}
public String getMakerFeeAsString() {
Offer offer = getOffer();
return offer.isCurrencyForMakerFeeBtc() ?
btcFormatter.formatCoinWithCode(offer.getMakerFee()) :
bsqFormatter.formatCoinWithCode(offer.getMakerFee());
}
public boolean isNotPublished() {
return openOffer.isDeactivated() || (getOffer().isBsqSwapOffer() && openOffer.isBsqSwapOfferHasMissingFunds());
}
public String getTriggerPriceAsString() {
Offer offer = getOffer();
long triggerPrice = openOffer.getTriggerPrice();
if (!offer.isUseMarketBasedPrice() || triggerPrice <= 0) {
return Res.get("shared.na");
} else {
return PriceUtil.formatMarketPrice(triggerPrice, offer.getCurrencyCode());
}
}
@Override
public boolean match(String filterString) {
if (filterString.isEmpty()) {
return true;
}
if (StringUtils.containsIgnoreCase(getDateAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getMarketDescription(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getPriceAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getPriceDeviationAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getPaymentMethodAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getVolumeAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getAmountAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getDirectionLabel(), filterString)) {
return true;
}
return FilteringUtils.match(getOffer(), filterString);
}
}

View file

@ -26,12 +26,18 @@ import bisq.core.offer.OpenOfferManager;
import bisq.core.offer.bisq_v1.TriggerPriceService;
import bisq.core.offer.bsq_swap.OpenBsqSwapOfferService;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.util.FormattingUtils;
import bisq.core.util.PriceUtil;
import bisq.core.util.coin.BsqFormatter;
import bisq.core.util.coin.CoinFormatter;
import bisq.common.handlers.ErrorMessageHandler;
import bisq.common.handlers.ResultHandler;
import com.google.inject.Inject;
import javax.inject.Named;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
@ -44,6 +50,9 @@ class OpenOffersDataModel extends ActivatableDataModel {
private final OpenOfferManager openOfferManager;
private final OpenBsqSwapOfferService openBsqSwapOfferService;
private final PriceFeedService priceFeedService;
private final PriceUtil priceUtil;
private final CoinFormatter btcFormatter;
private final BsqFormatter bsqFormatter;
private final ObservableList<OpenOfferListItem> list = FXCollections.observableArrayList();
private final ListChangeListener<OpenOffer> tradesListChangeListener;
@ -52,10 +61,16 @@ class OpenOffersDataModel extends ActivatableDataModel {
@Inject
public OpenOffersDataModel(OpenOfferManager openOfferManager,
OpenBsqSwapOfferService openBsqSwapOfferService,
PriceFeedService priceFeedService) {
PriceFeedService priceFeedService,
PriceUtil priceUtil,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
BsqFormatter bsqFormatter) {
this.openOfferManager = openOfferManager;
this.openBsqSwapOfferService = openBsqSwapOfferService;
this.priceFeedService = priceFeedService;
this.priceUtil = priceUtil;
this.btcFormatter = btcFormatter;
this.bsqFormatter = bsqFormatter;
tradesListChangeListener = change -> applyList();
currenciesUpdateFlagPropertyListener = (observable, oldValue, newValue) -> applyList();
@ -106,7 +121,11 @@ class OpenOffersDataModel extends ActivatableDataModel {
private void applyList() {
list.clear();
list.addAll(openOfferManager.getObservableList().stream().map(OpenOfferListItem::new).collect(Collectors.toList()));
list.addAll(
openOfferManager.getObservableList().stream()
.map(item -> new OpenOfferListItem(item, priceUtil, btcFormatter, bsqFormatter, openOfferManager))
.collect(Collectors.toList())
);
// we sort by date, earliest first
list.sort((o1, o2) -> o2.getOffer().getDate().compareTo(o1.getOffer().getDate()));

View file

@ -18,9 +18,8 @@
-->
<?import bisq.desktop.components.AutoTooltipButton?>
<?import bisq.desktop.components.AutoTooltipLabel?>
<?import bisq.desktop.components.AutoTooltipSlideToggleButton?>
<?import bisq.desktop.components.InputTextField?>
<?import bisq.desktop.components.list.FilterBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
@ -34,10 +33,9 @@
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0"/>
</padding>
<HBox fx:id="searchBox">
<AutoTooltipLabel fx:id="filterLabel"/>
<InputTextField fx:id="filterTextField" minWidth="500"/>
<Pane fx:id="searchBoxSpacer"/>
<HBox>
<FilterBox fx:id="filterBox" />
<Pane HBox.hgrow="ALWAYS"/>
<AutoTooltipSlideToggleButton fx:id="selectToggleButton"/>
</HBox>
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">

View file

@ -25,7 +25,7 @@ import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.AutoTooltipSlideToggleButton;
import bisq.desktop.components.AutoTooltipTableColumn;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.InputTextField;
import bisq.desktop.components.list.FilterBox;
import bisq.desktop.main.MainView;
import bisq.desktop.main.funds.FundsView;
import bisq.desktop.main.funds.withdrawal.WithdrawalView;
@ -62,7 +62,6 @@ import javafx.scene.control.TableView;
import javafx.scene.control.Tooltip;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
@ -121,13 +120,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
marketColumn, directionColumn, dateColumn, offerIdColumn, deactivateItemColumn,
removeItemColumn, editItemColumn, triggerPriceColumn, triggerIconColumn, paymentMethodColumn, duplicateItemColumn;
@FXML
HBox searchBox;
@FXML
AutoTooltipLabel filterLabel;
@FXML
InputTextField filterTextField;
@FXML
Pane searchBoxSpacer;
FilterBox filterBox;
@FXML
Label numItems;
@FXML
@ -141,8 +134,6 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
private final OfferDetailsWindow offerDetailsWindow;
private final BsqSwapOfferDetailsWindow bsqSwapOfferDetailsWindow;
private SortedList<OpenOfferListItem> sortedList;
private FilteredList<OpenOfferListItem> filteredList;
private ChangeListener<String> filterTextFieldListener;
private PortfolioView.OpenOfferActionHandler openOfferActionHandler;
private ChangeListener<Number> widthListener;
@ -197,10 +188,10 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
offerIdColumn.setComparator(Comparator.comparing(o -> o.getOffer().getId()));
directionColumn.setComparator(Comparator.comparing(o -> o.getOffer().getDirection()));
marketColumn.setComparator(Comparator.comparing(model::getMarketLabel));
marketColumn.setComparator(Comparator.comparing(OpenOfferListItem::getMarketDescription));
amountColumn.setComparator(Comparator.comparing(o -> o.getOffer().getAmount()));
priceColumn.setComparator(Comparator.comparing(o -> o.getOffer().getPrice(), Comparator.nullsFirst(Comparator.naturalOrder())));
deviationColumn.setComparator(Comparator.comparing(model::getPriceDeviationAsDouble, Comparator.nullsFirst(Comparator.naturalOrder())));
deviationColumn.setComparator(Comparator.comparing(OpenOfferListItem::getPriceDeviationAsDouble, Comparator.nullsFirst(Comparator.naturalOrder())));
triggerPriceColumn.setComparator(Comparator.comparing(o -> o.getOpenOffer().getTriggerPrice(),
Comparator.nullsFirst(Comparator.naturalOrder())));
volumeColumn.setComparator(Comparator.comparing(o -> o.getOffer().getVolume(), Comparator.nullsFirst(Comparator.naturalOrder())));
@ -224,12 +215,6 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
return row;
});
filterLabel.setText(Res.get("shared.filter"));
HBox.setMargin(filterLabel, new Insets(5, 0, 0, 10));
filterTextFieldListener = (observable, oldValue, newValue) -> applyFilteredListPredicate(filterTextField.getText());
searchBox.setSpacing(5);
HBox.setHgrow(searchBoxSpacer, Priority.ALWAYS);
selectToggleButton.setPadding(new Insets(0, 90, -20, 0));
selectToggleButton.setText(Res.get("shared.enabled"));
selectToggleButton.setDisable(true);
@ -243,11 +228,14 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
@Override
protected void activate() {
filteredList = new FilteredList<>(model.getList());
FilteredList<OpenOfferListItem> filteredList = new FilteredList<>(model.dataModel.getList());
sortedList = new SortedList<>(filteredList);
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
tableView.setItems(sortedList);
filterBox.initialize(filteredList, tableView); // here because filteredList is instantiated here
filterBox.activate();
updateSelectToggleButtonState();
selectToggleButton.setOnAction(event -> {
@ -272,16 +260,16 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
};
CSVEntryConverter<OpenOfferListItem> contentConverter = item -> {
String[] columns = new String[ColumnNames.values().length];
columns[ColumnNames.OFFER_ID.ordinal()] = model.getOfferId(item);
columns[ColumnNames.DATE.ordinal()] = model.getDate(item);
columns[ColumnNames.MARKET.ordinal()] = model.getMarketLabel(item);
columns[ColumnNames.PRICE.ordinal()] = model.getPrice(item);
columns[ColumnNames.DEVIATION.ordinal()] = model.getPriceDeviation(item);
columns[ColumnNames.TRIGGER_PRICE.ordinal()] = model.getTriggerPrice(item);
columns[ColumnNames.AMOUNT.ordinal()] = model.getAmount(item);
columns[ColumnNames.VOLUME.ordinal()] = model.getVolume(item);
columns[ColumnNames.PAYMENT_METHOD.ordinal()] = model.getPaymentMethod(item);
columns[ColumnNames.DIRECTION.ordinal()] = model.getDirectionLabel(item);
columns[ColumnNames.OFFER_ID.ordinal()] = item.getOffer().getShortId();
columns[ColumnNames.DATE.ordinal()] = item.getDateAsString();
columns[ColumnNames.MARKET.ordinal()] = item.getMarketDescription();
columns[ColumnNames.PRICE.ordinal()] = item.getPriceAsString();
columns[ColumnNames.DEVIATION.ordinal()] = item.getPriceDeviationAsString();
columns[ColumnNames.TRIGGER_PRICE.ordinal()] = item.getTriggerPriceAsString();
columns[ColumnNames.AMOUNT.ordinal()] = item.getAmountAsString();
columns[ColumnNames.VOLUME.ordinal()] = item.getVolumeAsString();
columns[ColumnNames.PAYMENT_METHOD.ordinal()] = item.getPaymentMethodAsString();
columns[ColumnNames.DIRECTION.ordinal()] = item.getDirectionLabel();
columns[ColumnNames.STATUS.ordinal()] = String.valueOf(!item.getOpenOffer().isDeactivated());
return columns;
};
@ -289,14 +277,11 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
GUIUtil.exportCSV("openOffers.csv",
headerConverter,
contentConverter,
new OpenOfferListItem(),
new OpenOfferListItem(null, null, null, null, null),
sortedList,
(Stage) root.getScene().getWindow());
});
filterTextField.textProperty().addListener(filterTextFieldListener);
applyFilteredListPredicate(filterTextField.getText());
root.widthProperty().addListener(widthListener);
onWidthChange(root.getWidth());
}
@ -306,7 +291,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
sortedList.comparatorProperty().unbind();
exportButton.setOnAction(null);
filterTextField.textProperty().removeListener(filterTextFieldListener);
filterBox.deactivate();
root.widthProperty().removeListener(widthListener);
}
@ -327,44 +312,6 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
}
}
private void applyFilteredListPredicate(String filterString) {
filteredList.setPredicate(item -> {
if (filterString.isEmpty())
return true;
Offer offer = item.getOpenOffer().getOffer();
if (offer.getId().contains(filterString)) {
return true;
}
if (model.getDate(item).contains(filterString)) {
return true;
}
if (model.getMarketLabel(item).contains(filterString)) {
return true;
}
if (model.getPrice(item).contains(filterString)) {
return true;
}
if (model.getPriceDeviation(item).contains(filterString)) {
return true;
}
if (model.getPaymentMethod(item).contains(filterString)) {
return true;
}
if (model.getVolume(item).contains(filterString)) {
return true;
}
if (model.getAmount(item).contains(filterString)) {
return true;
}
if (model.getDirectionLabel(item).contains(filterString)) {
return true;
}
return offer.getOfferFeePaymentTxId() != null &&
offer.getOfferFeePaymentTxId().contains(filterString);
});
}
private void onWidthChange(double width) {
triggerPriceColumn.setVisible(width > 1200);
}
@ -393,12 +340,13 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
}
}
private void onRemoveOpenOffer(OpenOffer openOffer) {
private void onRemoveOpenOffer(OpenOfferListItem item) {
OpenOffer openOffer = item.getOpenOffer();
if (model.isBootstrappedOrShowPopup()) {
String key = "RemoveOfferWarning";
if (DontShowAgainLookup.showAgain(key)) {
String message = model.hasMakerFee(openOffer) ?
Res.get("popup.warning.removeOffer", model.getMakerFeeAsString(openOffer)) :
String message = item.hasMakerFee() ?
Res.get("popup.warning.removeOffer", item.getMakerFeeAsString()) :
Res.get("popup.warning.removeNoFeeOffer");
new Popup().warning(message)
.actionButtonText(Res.get("shared.removeOffer"))
@ -468,7 +416,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
public void updateItem(final OpenOfferListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
field = new HyperlinkWithIcon(model.getOfferId(item));
field = new HyperlinkWithIcon(item.getOffer().getShortId());
field.setOnAction(event -> {
if (item.getOffer().isBsqSwapOffer()) {
bsqSwapOfferDetailsWindow.show(item.getOffer());
@ -503,8 +451,8 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
super.updateItem(item, empty);
getStyleClass().removeAll("offer-disabled");
if (item != null) {
if (model.isNotPublished(item)) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(model.getDate(item)));
if (item.isNotPublished()) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(item.getDateAsString()));
} else {
setGraphic(null);
}
@ -528,8 +476,8 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
getStyleClass().removeAll("offer-disabled");
if (item != null) {
if (model.isNotPublished(item)) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(model.getAmount(item)));
if (item.isNotPublished()) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(item.getAmountAsString()));
} else {
setGraphic(null);
}
@ -553,8 +501,8 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
getStyleClass().removeAll("offer-disabled");
if (item != null) {
if (model.isNotPublished(item)) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(model.getPrice(item)));
if (item.isNotPublished()) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(item.getPriceAsString()));
} else {
setGraphic(null);
}
@ -578,8 +526,8 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
getStyleClass().removeAll("offer-disabled");
if (item != null) {
if (model.isNotPublished(item)) getStyleClass().add("offer-disabled");
AutoTooltipLabel autoTooltipLabel = new AutoTooltipLabel(model.getPriceDeviation(item));
if (item.isNotPublished()) getStyleClass().add("offer-disabled");
AutoTooltipLabel autoTooltipLabel = new AutoTooltipLabel(item.getPriceDeviationAsString());
autoTooltipLabel.setOpacity(item.getOffer().isUseMarketBasedPrice() ? 1 : 0.4);
setGraphic(autoTooltipLabel);
} else {
@ -604,8 +552,8 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
super.updateItem(item, empty);
getStyleClass().removeAll("offer-disabled");
if (item != null) {
if (model.isNotPublished(item)) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(model.getTriggerPrice(item)));
if (item.isNotPublished()) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(item.getTriggerPriceAsString()));
} else {
setGraphic(null);
}
@ -629,8 +577,8 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
getStyleClass().removeAll("offer-disabled");
if (item != null) {
if (model.isNotPublished(item)) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(model.getVolume(item)));
if (item.isNotPublished()) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(item.getVolumeAsString()));
} else {
setGraphic(null);
}
@ -654,8 +602,8 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
getStyleClass().removeAll("offer-disabled");
if (item != null) {
if (model.isNotPublished(item)) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(model.getPaymentMethod(item)));
if (item.isNotPublished()) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(item.getPaymentMethodAsString()));
} else {
setGraphic(null);
}
@ -679,8 +627,8 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
getStyleClass().removeAll("offer-disabled");
if (item != null) {
if (model.isNotPublished(item)) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(model.getDirectionLabel(item)));
if (item.isNotPublished()) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(item.getDirectionLabel()));
} else {
setGraphic(null);
}
@ -704,8 +652,8 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
getStyleClass().removeAll("offer-disabled");
if (item != null) {
if (model.isNotPublished(item)) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(model.getMarketLabel(item)));
if (item.isNotPublished()) getStyleClass().add("offer-disabled");
setGraphic(new AutoTooltipLabel(item.getMarketDescription()));
} else {
setGraphic(null);
}
@ -785,7 +733,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
button.setTooltip(new Tooltip(Res.get("shared.removeOffer")));
setGraphic(button);
}
button.setOnAction(event -> onRemoveOpenOffer(item.getOpenOffer()));
button.setOnAction(event -> onRemoveOpenOffer(item));
} else {
setGraphic(null);
if (button != null) {
@ -857,7 +805,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
button.setTooltip(new Tooltip(Res.get("openOffer.triggered")));
} else {
button.getGraphic().getStyleClass().remove("warning");
button.setTooltip(new Tooltip(Res.get("openOffer.triggerPrice", model.getTriggerPrice(item))));
button.setTooltip(new Tooltip(Res.get("openOffer.triggerPrice", item.getTriggerPriceAsString())));
}
setGraphic(button);
}

View file

@ -19,54 +19,31 @@ package bisq.desktop.main.portfolio.openoffer;
import bisq.desktop.common.model.ActivatableWithDataModel;
import bisq.desktop.common.model.ViewModel;
import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.GUIUtil;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.monetary.Price;
import bisq.core.offer.Offer;
import bisq.core.offer.OpenOffer;
import bisq.core.util.FormattingUtils;
import bisq.core.util.PriceUtil;
import bisq.core.util.VolumeUtil;
import bisq.core.util.coin.BsqFormatter;
import bisq.core.util.coin.CoinFormatter;
import bisq.network.p2p.P2PService;
import bisq.common.handlers.ErrorMessageHandler;
import bisq.common.handlers.ResultHandler;
import org.bitcoinj.core.Coin;
import com.google.inject.Inject;
import javax.inject.Named;
import javafx.collections.ObservableList;
import static com.google.common.base.Preconditions.checkNotNull;
class OpenOffersViewModel extends ActivatableWithDataModel<OpenOffersDataModel> implements ViewModel {
private final P2PService p2PService;
private final PriceUtil priceUtil;
private final CoinFormatter btcFormatter;
private final BsqFormatter bsqFormatter;
@Inject
public OpenOffersViewModel(OpenOffersDataModel dataModel,
P2PService p2PService,
PriceUtil priceUtil,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
BsqFormatter bsqFormatter) {
PriceUtil priceUtil) {
super(dataModel);
this.p2PService = p2PService;
this.priceUtil = priceUtil;
this.btcFormatter = btcFormatter;
this.bsqFormatter = bsqFormatter;
}
@Override
@ -90,122 +67,7 @@ class OpenOffersViewModel extends ActivatableWithDataModel<OpenOffersDataModel>
dataModel.onRemoveOpenOffer(openOffer, resultHandler, errorMessageHandler);
}
public ObservableList<OpenOfferListItem> getList() {
return dataModel.getList();
}
String getOfferId(OpenOfferListItem item) {
return item.getOffer().getShortId();
}
String getAmount(OpenOfferListItem item) {
return (item != null) ? DisplayUtils.formatAmount(item.getOffer(), btcFormatter) : "";
}
String getPrice(OpenOfferListItem item) {
if ((item == null))
return "";
Offer offer = item.getOffer();
Price price = offer.getPrice();
if (price != null) {
return FormattingUtils.formatPrice(price);
} else {
return Res.get("shared.na");
}
}
String getPriceDeviation(OpenOfferListItem item) {
Offer offer = item.getOffer();
return priceUtil.getMarketBasedPrice(offer, offer.getMirroredDirection())
.map(FormattingUtils::formatPercentagePrice)
.orElse("");
}
Double getPriceDeviationAsDouble(OpenOfferListItem item) {
Offer offer = item.getOffer();
return priceUtil.getMarketBasedPrice(offer, offer.getMirroredDirection()).orElse(0d);
}
String getVolume(OpenOfferListItem item) {
return (item != null)
? VolumeUtil.formatVolume(item.getOffer(), false, 0) + " " + item.getOffer().getCurrencyCode()
: "";
}
String getDirectionLabel(OpenOfferListItem item) {
if ((item == null))
return "";
return DisplayUtils.getDirectionWithCode(dataModel.getDirection(item.getOffer()), item.getOffer().getCurrencyCode());
}
String getMarketLabel(OpenOfferListItem item) {
if ((item == null))
return "";
return CurrencyUtil.getCurrencyPair(item.getOffer().getCurrencyCode());
}
String getPaymentMethod(OpenOfferListItem item) {
String result = "";
if (item != null) {
Offer offer = item.getOffer();
checkNotNull(offer);
checkNotNull(offer.getPaymentMethod());
result = offer.getPaymentMethodNameWithCountryCode();
}
return result;
}
String getDate(OpenOfferListItem item) {
return DisplayUtils.formatDateTime(item.getOffer().getDate());
}
boolean isNotPublished(OpenOfferListItem item) {
return isDeactivated(item) || isBsqSwapOfferHasMissingFunds(item);
}
boolean isDeactivated(OpenOfferListItem item) {
return item != null &&
item.getOpenOffer() != null &&
item.getOpenOffer().isDeactivated();
}
boolean isBsqSwapOfferHasMissingFunds(OpenOfferListItem item) {
return item != null &&
item.getOpenOffer() != null &&
item.getOpenOffer().getOffer().isBsqSwapOffer() &&
item.getOpenOffer().isBsqSwapOfferHasMissingFunds();
}
boolean isBootstrappedOrShowPopup() {
return GUIUtil.isBootstrappedOrShowPopup(p2PService);
}
public boolean hasMakerFee(OpenOffer openOffer) {
Coin makerFee = openOffer.getOffer().getMakerFee();
return makerFee.isPositive();
}
public String getMakerFeeAsString(OpenOffer openOffer) {
Offer offer = openOffer.getOffer();
return offer.isCurrencyForMakerFeeBtc() ?
btcFormatter.formatCoinWithCode(offer.getMakerFee()) :
bsqFormatter.formatCoinWithCode(offer.getMakerFee());
}
String getTriggerPrice(OpenOfferListItem item) {
if ((item == null)) {
return "";
}
Offer offer = item.getOffer();
long triggerPrice = item.getOpenOffer().getTriggerPrice();
if (!offer.isUseMarketBasedPrice() || triggerPrice <= 0) {
return Res.get("shared.na");
} else {
return PriceUtil.formatMarketPrice(triggerPrice, offer.getCurrencyCode());
}
}
}

View file

@ -57,6 +57,7 @@ import bisq.core.trade.protocol.bisq_v1.DisputeProtocol;
import bisq.core.trade.protocol.bisq_v1.SellerProtocol;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;
import bisq.network.p2p.P2PService;
@ -71,6 +72,8 @@ import org.bitcoinj.core.TransactionConfidence;
import com.google.inject.Inject;
import javax.inject.Named;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
@ -109,6 +112,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
public final WalletPasswordWindow walletPasswordWindow;
private final NotificationCenter notificationCenter;
private final OfferUtil offerUtil;
private final CoinFormatter btcFormatter;
final ObservableList<PendingTradesListItem> list = FXCollections.observableArrayList();
private final ListChangeListener<Trade> tradesListChangeListener;
@ -145,7 +149,8 @@ public class PendingTradesDataModel extends ActivatableDataModel {
Navigation navigation,
WalletPasswordWindow walletPasswordWindow,
NotificationCenter notificationCenter,
OfferUtil offerUtil) {
OfferUtil offerUtil,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter) {
this.tradeManager = tradeManager;
this.btcWalletService = btcWalletService;
this.pubKeyRing = pubKeyRing;
@ -161,6 +166,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
this.walletPasswordWindow = walletPasswordWindow;
this.notificationCenter = notificationCenter;
this.offerUtil = offerUtil;
this.btcFormatter = formatter;
tradesListChangeListener = change -> onListChanged();
notificationCenter.setSelectItemByTradeIdConsumer(this::selectItemByTradeId);
@ -381,7 +387,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
private void onListChanged() {
list.clear();
list.addAll(tradeManager.getObservableList().stream()
.map(PendingTradesListItem::new)
.map(trade -> new PendingTradesListItem(trade, btcFormatter))
.collect(Collectors.toList()));
// we sort by date, earliest first

View file

@ -17,39 +17,69 @@
package bisq.desktop.main.portfolio.pendingtrades;
import bisq.core.monetary.Price;
import bisq.core.monetary.Volume;
import bisq.desktop.util.filtering.FilterableListItem;
import bisq.core.trade.model.bisq_v1.Trade;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;
import org.bitcoinj.core.Coin;
import org.apache.commons.lang3.StringUtils;
import javafx.beans.property.ReadOnlyObjectProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static bisq.core.locale.CurrencyUtil.getCurrencyPair;
/**
* We could remove that wrapper if it is not needed for additional UI only fields.
*/
public class PendingTradesListItem {
public class PendingTradesListItem implements FilterableListItem {
public static final Logger log = LoggerFactory.getLogger(PendingTradesListItem.class);
private final CoinFormatter btcFormatter;
private final Trade trade;
public PendingTradesListItem(Trade trade) {
public PendingTradesListItem(Trade trade, CoinFormatter btcFormatter) {
this.trade = trade;
this.btcFormatter = btcFormatter;
}
public Trade getTrade() {
return trade;
}
public ReadOnlyObjectProperty<Coin> tradeAmountProperty() {
return trade.amountProperty();
public String getPriceAsString() {
return FormattingUtils.formatPrice(trade.getPrice());
}
public ReadOnlyObjectProperty<Volume> tradeVolumeProperty() {
return trade.volumeProperty();
public String getAmountAsString() {
return btcFormatter.formatCoin(trade.getAmount());
}
public Price getPrice() {
return trade.getPrice();
public String getPaymentMethod() {
return trade.getOffer().getPaymentMethodNameWithCountryCode();
}
public String getMarketDescription() {
return getCurrencyPair(trade.getOffer().getCurrencyCode());
}
@Override
public boolean match(String filterString) {
if (filterString.isEmpty()) {
return true;
}
if (StringUtils.containsIgnoreCase(getTrade().getId(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getAmountAsString(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getPaymentMethod(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(getMarketDescription(), filterString)) {
return true;
}
return StringUtils.containsIgnoreCase(getPriceAsString(), filterString);
}
}

View file

@ -21,12 +21,13 @@
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.VBox?>
<?import javafx.geometry.Insets?>
<?import bisq.desktop.components.list.FilterBox?>
<VBox fx:id="root" fx:controller="bisq.desktop.main.portfolio.pendingtrades.PendingTradesView"
spacing="20" xmlns:fx="http://javafx.com/fxml">
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0"/>
</padding>
<FilterBox fx:id="filterBox" />
<TableView fx:id="tableView" VBox.vgrow="SOMETIMES">
<columns>
<TableColumn fx:id="tradeIdColumn" minWidth="100"/>

View file

@ -23,6 +23,7 @@ import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.PeerInfoIconTrading;
import bisq.desktop.components.list.FilterBox;
import bisq.desktop.main.MainView;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.main.overlays.windows.TradeDetailsWindow;
@ -42,9 +43,7 @@ import bisq.core.support.traderchat.TraderChatManager;
import bisq.core.trade.model.bisq_v1.Contract;
import bisq.core.trade.model.bisq_v1.Trade;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
import bisq.core.util.VolumeUtil;
import bisq.core.util.coin.CoinFormatter;
import bisq.network.p2p.NodeAddress;
@ -102,6 +101,7 @@ import javafx.event.EventHandler;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.util.Callback;
@ -119,16 +119,18 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
private final TradeDetailsWindow tradeDetailsWindow;
private final Navigation navigation;
private final KeyRing keyRing;
private final CoinFormatter formatter;
private final PrivateNotificationManager privateNotificationManager;
private final boolean useDevPrivilegeKeys;
private final boolean useDevModeHeader;
private final Preferences preferences;
@FXML
FilterBox filterBox;
@FXML
TableView<PendingTradesListItem> tableView;
@FXML
TableColumn<PendingTradesListItem, PendingTradesListItem> priceColumn, volumeColumn, amountColumn, avatarColumn,
marketColumn, roleColumn, paymentMethodColumn, tradeIdColumn, dateColumn, chatColumn, moveTradeToFailedColumn;
private FilteredList<PendingTradesListItem> filteredList;
private SortedList<PendingTradesListItem> sortedList;
private TradeSubView selectedSubView;
private EventHandler<KeyEvent> keyEventEventHandler;
@ -161,7 +163,6 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
TradeDetailsWindow tradeDetailsWindow,
Navigation navigation,
KeyRing keyRing,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
PrivateNotificationManager privateNotificationManager,
Preferences preferences,
@Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys,
@ -170,7 +171,6 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
this.tradeDetailsWindow = tradeDetailsWindow;
this.navigation = navigation;
this.keyRing = keyRing;
this.formatter = formatter;
this.privateNotificationManager = privateNotificationManager;
this.preferences = preferences;
this.useDevPrivilegeKeys = useDevPrivilegeKeys;
@ -211,14 +211,14 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
dateColumn.setComparator(Comparator.comparing(o -> o.getTrade().getDate()));
volumeColumn.setComparator(Comparator.comparing(o -> o.getTrade().getVolume(), Comparator.nullsFirst(Comparator.naturalOrder())));
amountColumn.setComparator(Comparator.comparing(o -> o.getTrade().getAmount(), Comparator.nullsFirst(Comparator.naturalOrder())));
priceColumn.setComparator(Comparator.comparing(item -> FormattingUtils.formatPrice(item.getPrice())));
priceColumn.setComparator(Comparator.comparing(PendingTradesListItem::getPriceAsString));
paymentMethodColumn.setComparator(Comparator.comparing(
item -> item.getTrade().getOffer() != null ?
Res.get(item.getTrade().getOffer().getPaymentMethod().getId()) :
null,
Comparator.nullsFirst(Comparator.naturalOrder())));
marketColumn.setComparator(Comparator.comparing(model::getMarketLabel));
marketColumn.setComparator(Comparator.comparing(PendingTradesListItem::getMarketDescription));
roleColumn.setComparator(Comparator.comparing(model::getMyRole));
avatarColumn.setComparator(Comparator.comparing(
o -> model.getNumPastTrades(o.getTrade()),
@ -281,10 +281,14 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
@Override
protected void activate() {
ObservableList<PendingTradesListItem> list = model.dataModel.list;
sortedList = new SortedList<>(list);
filteredList = new FilteredList<>(list);
sortedList = new SortedList<>(filteredList);
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
tableView.setItems(sortedList);
filterBox.initialize(filteredList, tableView); // here because filteredList is instantiated here
filterBox.activate();
updateMoveTradeToFailedColumnState();
scene = root.getScene();
@ -303,10 +307,10 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
selectedSubView.setMinHeight(440);
VBox.setVgrow(selectedSubView, Priority.ALWAYS);
if (root.getChildren().size() == 1)
if (root.getChildren().size() == 2)
root.getChildren().add(selectedSubView);
else if (root.getChildren().size() == 2)
root.getChildren().set(1, selectedSubView);
else if (root.getChildren().size() == 3)
root.getChildren().set(2, selectedSubView);
// create and register a callback so we can be notified when the subview
// wants to open the chat window
@ -340,6 +344,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
@Override
protected void deactivate() {
filterBox.deactivate();
sortedList.comparatorProperty().unbind();
selectedItemSubscription.unsubscribe();
selectedTableItemSubscription.unsubscribe();
@ -683,7 +688,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
public void updateItem(final PendingTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty)
setGraphic(new AutoTooltipLabel(formatter.formatCoin(item.getTrade().getAmount())));
setGraphic(new AutoTooltipLabel(item.getAmountAsString()));
else
setGraphic(null);
}
@ -704,7 +709,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
public void updateItem(final PendingTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty)
setGraphic(new AutoTooltipLabel(FormattingUtils.formatPrice(item.getPrice())));
setGraphic(new AutoTooltipLabel(item.getPriceAsString()));
else
setGraphic(null);
}
@ -751,7 +756,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
public void updateItem(final PendingTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty)
setGraphic(new AutoTooltipLabel(model.getPaymentMethod(item)));
setGraphic(new AutoTooltipLabel(item.getPaymentMethod()));
else
setGraphic(null);
}
@ -771,7 +776,12 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
@Override
public void updateItem(final PendingTradesListItem item, boolean empty) {
super.updateItem(item, empty);
setGraphic(new AutoTooltipLabel(model.getMarketLabel(item)));
if (item != null && !empty) {
setGraphic(new AutoTooltipLabel(item.getMarketDescription()));
} else {
setGraphic(null);
}
}
};
}

View file

@ -245,16 +245,6 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
return sellerState;
}
public String getPayoutAmount() {
return dataModel.getTrade() != null
? btcFormatter.formatCoinWithCode(dataModel.getTrade().getPayoutAmount())
: "";
}
String getMarketLabel(PendingTradesListItem item) {
return item == null ? "" : tradeUtil.getMarketDescription(item.getTrade());
}
public String getRemainingTradeDurationAsWords() {
checkNotNull(dataModel.getTrade(), "model's trade must not be null");
return tradeUtil.getRemainingTradeDurationAsWords(dataModel.getTrade());
@ -297,10 +287,6 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
}
}
String getPaymentMethod(PendingTradesListItem item) {
return item == null ? "" : tradeUtil.getPaymentMethodNameWithCountryCode(item.getTrade());
}
// summary
public String getTradeVolume() {
return dataModel.getTrade() != null

View file

@ -15,26 +15,8 @@
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.main.funds.transactions;
package bisq.desktop.util.filtering;
import bisq.core.trade.model.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;
}
public interface FilterableListItem {
boolean match(String filterString);
}

View file

@ -0,0 +1,77 @@
/*
* 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.util.filtering;
import bisq.core.offer.Offer;
import bisq.core.trade.model.bisq_v1.Contract;
import bisq.core.trade.model.bisq_v1.Trade;
import bisq.core.trade.model.bsq_swap.BsqSwapTrade;
import org.apache.commons.lang3.StringUtils;
public class FilteringUtils {
public static boolean match(Offer offer, String filterString) {
if (StringUtils.containsIgnoreCase(offer.getId(), filterString)) {
return true;
}
if (StringUtils.containsIgnoreCase(offer.getPaymentMethod().getDisplayString(), filterString)) {
return true;
}
return offer.getOfferFeePaymentTxId() != null && StringUtils.containsIgnoreCase(offer.getOfferFeePaymentTxId(), filterString);
}
public static boolean match(BsqSwapTrade bsqSwapTrade, String filterString) {
if (bsqSwapTrade.getTxId() != null && StringUtils.containsIgnoreCase(bsqSwapTrade.getTxId(), filterString)) {
return true;
}
return StringUtils.containsIgnoreCase(bsqSwapTrade.getTradingPeerNodeAddress().getFullAddress(), filterString);
}
public static boolean match(Trade trade, String filterString) {
if (trade == null) {
return false;
}
if (trade.getTakerFeeTxId() != null && StringUtils.containsIgnoreCase(trade.getTakerFeeTxId(), filterString)) {
return true;
}
if (trade.getDepositTxId() != null && StringUtils.containsIgnoreCase(trade.getDepositTxId(), filterString)) {
return true;
}
if (trade.getPayoutTxId() != null && StringUtils.containsIgnoreCase(trade.getPayoutTxId(), filterString)) {
return true;
}
return match(trade.getContract(), filterString);
}
private static boolean match(Contract contract, String filterString) {
boolean isBuyerOnion = false;
boolean isSellerOnion = false;
boolean matchesBuyersPaymentAccountData = false;
boolean matchesSellersPaymentAccountData = false;
if (contract != null) {
isBuyerOnion = StringUtils.containsIgnoreCase(contract.getBuyerNodeAddress().getFullAddress(), filterString);
isSellerOnion = StringUtils.containsIgnoreCase(contract.getSellerNodeAddress().getFullAddress(), filterString);
matchesBuyersPaymentAccountData = contract.getBuyerPaymentAccountPayload() != null &&
StringUtils.containsIgnoreCase(contract.getBuyerPaymentAccountPayload().getPaymentDetails(), filterString);
matchesSellersPaymentAccountData = contract.getSellerPaymentAccountPayload() != null &&
StringUtils.containsIgnoreCase(contract.getSellerPaymentAccountPayload().getPaymentDetails(), filterString);
}
return isBuyerOnion || isSellerOnion ||
matchesBuyersPaymentAccountData || matchesSellersPaymentAccountData;
}
}

View file

@ -5,10 +5,7 @@ import bisq.desktop.common.view.CachingViewLoader;
import bisq.desktop.common.view.ViewLoader;
import bisq.desktop.common.view.guice.InjectorViewFactory;
import bisq.desktop.main.dao.bonding.BondingViewUtils;
import bisq.desktop.main.funds.transactions.DisplayedTransactionsFactory;
import bisq.desktop.main.funds.transactions.TradableRepository;
import bisq.desktop.main.funds.transactions.TransactionAwareTradableFactory;
import bisq.desktop.main.funds.transactions.TransactionListItemFactory;
import bisq.desktop.main.offer.offerbook.OfferBook;
import bisq.desktop.main.overlays.notifications.NotificationCenter;
import bisq.desktop.main.overlays.windows.TorNetworkSettingsWindow;
@ -99,9 +96,6 @@ public class GuiceSetupTest {
assertSingleton(DaoPresentation.class);
assertSingleton(Transitions.class);
assertSingleton(TradableRepository.class);
assertSingleton(TransactionListItemFactory.class);
assertSingleton(TransactionAwareTradableFactory.class);
assertSingleton(DisplayedTransactionsFactory.class);
assertSingleton(BondingViewUtils.class);
// core module

View file

@ -1,82 +0,0 @@
/*
* 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 bisq.core.btc.wallet.BtcWalletService;
import org.bitcoinj.core.Transaction;
import com.google.common.collect.Sets;
import javafx.collections.FXCollections;
import java.util.Collections;
import java.util.Set;
import org.junit.Test;
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(FXCollections.emptyObservableSet());
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

@ -1,55 +0,0 @@
/*
* 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 com.google.common.collect.Lists;
import java.util.Collection;
import java.util.function.Supplier;
import org.junit.Test;
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

@ -1,48 +0,0 @@
/*
* 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 bisq.core.offer.OpenOffer;
import bisq.core.support.dispute.arbitration.ArbitrationManager;
import bisq.core.trade.model.Tradable;
import bisq.core.trade.model.bisq_v1.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() {
ArbitrationManager arbitrationManager = mock(ArbitrationManager.class);
TransactionAwareTradableFactory factory = new TransactionAwareTradableFactory(arbitrationManager,
null, null, null);
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

@ -45,8 +45,6 @@ public class TransactionAwareTradeTest {
private ArbitrationManager arbitrationManager;
private Trade delegate;
private TransactionAwareTradable trade;
private RefundManager refundManager;
private BtcWalletService btcWalletService;
@Before
public void setUp() {
@ -55,8 +53,8 @@ public class TransactionAwareTradeTest {
delegate = mock(Trade.class, RETURNS_DEEP_STUBS);
arbitrationManager = mock(ArbitrationManager.class, RETURNS_DEEP_STUBS);
refundManager = mock(RefundManager.class, RETURNS_DEEP_STUBS);
btcWalletService = mock(BtcWalletService.class, RETURNS_DEEP_STUBS);
RefundManager refundManager = mock(RefundManager.class, RETURNS_DEEP_STUBS);
BtcWalletService btcWalletService = mock(BtcWalletService.class, RETURNS_DEEP_STUBS);
trade = new TransactionAwareTrade(delegate, arbitrationManager, refundManager, btcWalletService, null);
}