diff --git a/core/src/main/java/bisq/core/trade/bisq_v1/TradeUtil.java b/core/src/main/java/bisq/core/trade/bisq_v1/TradeUtil.java index 9d8212a060b..492b36c9093 100644 --- a/core/src/main/java/bisq/core/trade/bisq_v1/TradeUtil.java +++ b/core/src/main/java/bisq/core/trade/bisq_v1/TradeUtil.java @@ -175,16 +175,6 @@ public String getMarketDescription(Trade trade) { 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 diff --git a/desktop/src/main/java/bisq/desktop/components/list/FilterBox.java b/desktop/src/main/java/bisq/desktop/components/list/FilterBox.java new file mode 100644 index 00000000000..91aca5721a7 --- /dev/null +++ b/desktop/src/main/java/bisq/desktop/components/list/FilterBox.java @@ -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 . + */ + +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 filteredList; + + private ChangeListener 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 filteredList, + TableView 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)); + } +} diff --git a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositListItem.java b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositListItem.java index 94b486addd2..722b5a8c8bc 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositListItem.java @@ -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 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 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 @@ public Coin getBalanceAsCoin() { 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); + } } diff --git a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.fxml b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.fxml index bde328b7160..a4d9235a568 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.fxml @@ -23,12 +23,13 @@ + - + diff --git a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java index cc2c4fed018..e291ad99618 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/deposit/DepositView.java @@ -25,6 +25,7 @@ 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.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 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 { @@ -99,6 +104,8 @@ public class DepositView extends ActivatableView { @FXML GridPane gridPane; @FXML + FilterBox filterBox; + @FXML TableView tableView; @FXML TableColumn addressColumn, balanceColumn, confirmationsColumn, usageColumn; @@ -114,7 +121,8 @@ public class DepositView extends ActivatableView { private final CoinFormatter formatter; private String paymentLabelString; private final ObservableList observableList = FXCollections.observableArrayList(); - private final SortedList sortedList = new SortedList<>(observableList); + private final FilteredList filteredList = new FilteredList<>(observableList); + private final SortedList sortedList = new SortedList<>(filteredList); private BalanceListener balanceListener; private Subscription amountTextFieldSubscription; private ChangeListener tableViewSelectionListener; @@ -135,7 +143,7 @@ private DepositView(BtcWalletService walletService, @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 void onBalanceChanged(Coin balance, Transaction tx) { @Override protected void activate() { + filterBox.activate(); + tableView.getSelectionModel().selectedItemProperty().addListener(tableViewSelectionListener); sortedList.comparatorProperty().bind(tableView.comparatorProperty()); @@ -255,6 +265,7 @@ protected void activate() { @Override protected void deactivate() { + filterBox.deactivate(); tableView.getSelectionModel().selectedItemProperty().removeListener(tableViewSelectionListener); sortedList.comparatorProperty().unbind(); observableList.forEach(DepositListItem::cleanup); @@ -267,7 +278,6 @@ protected void deactivate() { // UI handlers /////////////////////////////////////////////////////////////////////////////////////////// - private void fillForm(String address) { titledGroupBg.setVisible(true); titledGroupBg.setManaged(true); diff --git a/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedListItem.java b/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedListItem.java index e7f483078dd..bf234e96e01 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedListItem.java @@ -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.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 @@ public String getDateString() { 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); + } } diff --git a/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedView.fxml b/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedView.fxml index 623c3e99f67..622a8aeafb6 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedView.fxml @@ -25,12 +25,13 @@ + - + diff --git a/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedView.java b/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedView.java index 45544d09e48..fc52fad5736 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedView.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/locked/LockedView.java @@ -23,6 +23,7 @@ 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.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 @@ @FxmlView public class LockedView extends ActivatableView { + @FXML + FilterBox filterBox; @FXML TableView tableView; @FXML @@ -102,7 +106,8 @@ public class LockedView extends ActivatableView { private final OfferDetailsWindow offerDetailsWindow; private final TradeDetailsWindow tradeDetailsWindow; private final ObservableList observableList = FXCollections.observableArrayList(); - private final SortedList sortedList = new SortedList<>(observableList); + private final FilteredList filteredList = new FilteredList<>(observableList); + private final SortedList sortedList = new SortedList<>(filteredList); private BalanceListener balanceListener; private ListChangeListener openOfferListChangeListener; private ListChangeListener tradeListChangeListener; @@ -131,6 +136,7 @@ private LockedView(BtcWalletService btcWalletService, @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 void onBalanceChanged(Coin balance, Transaction tx) { @Override protected void activate() { + filterBox.activate(); openOfferManager.getObservableList().addListener(openOfferListChangeListener); tradeManager.getObservableList().addListener(tradeListChangeListener); sortedList.comparatorProperty().bind(tableView.comparatorProperty()); @@ -206,6 +213,7 @@ protected void activate() { @Override protected void deactivate() { + filterBox.deactivate(); openOfferManager.getObservableList().removeListener(openOfferListChangeListener); tradeManager.getObservableList().removeListener(tradeListChangeListener); sortedList.comparatorProperty().unbind(); diff --git a/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedListItem.java b/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedListItem.java index 04d7ad75a2e..b0b127b5f61 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedListItem.java @@ -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.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 @@ public String getDetails() { 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); + } } diff --git a/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedView.fxml b/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedView.fxml index 8a43556b99e..0a2e01cd988 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedView.fxml @@ -25,12 +25,13 @@ + - + diff --git a/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedView.java b/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedView.java index ab42a4fd745..31404bc1e1b 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedView.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/reserved/ReservedView.java @@ -23,6 +23,7 @@ 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.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 @@ @FxmlView public class ReservedView extends ActivatableView { + @FXML + FilterBox filterBox; @FXML TableView tableView; @FXML @@ -102,7 +106,8 @@ public class ReservedView extends ActivatableView { private final OfferDetailsWindow offerDetailsWindow; private final TradeDetailsWindow tradeDetailsWindow; private final ObservableList observableList = FXCollections.observableArrayList(); - private final SortedList sortedList = new SortedList<>(observableList); + private final FilteredList filteredList = new FilteredList<>(observableList); + private final SortedList sortedList = new SortedList<>(filteredList); private BalanceListener balanceListener; private ListChangeListener openOfferListChangeListener; private ListChangeListener tradeListChangeListener; @@ -131,6 +136,7 @@ private ReservedView(BtcWalletService btcWalletService, @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 void onBalanceChanged(Coin balance, Transaction tx) { @Override protected void activate() { + filterBox.activate(); openOfferManager.getObservableList().addListener(openOfferListChangeListener); tradeManager.getObservableList().addListener(tradeListChangeListener); sortedList.comparatorProperty().bind(tableView.comparatorProperty()); @@ -206,6 +213,7 @@ protected void activate() { @Override protected void deactivate() { + filterBox.deactivate(); openOfferManager.getObservableList().removeListener(openOfferListChangeListener); tradeManager.getObservableList().removeListener(tradeListChangeListener); sortedList.comparatorProperty().unbind(); diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/DisplayedTransactions.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/DisplayedTransactions.java deleted file mode 100644 index a94466f9cc4..00000000000 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/DisplayedTransactions.java +++ /dev/null @@ -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 . - */ - -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 { - 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 transactionsListItems = getTransactionListItems(); - // are sorted by getRecentTransactions - forEach(TransactionsListItem::cleanup); - setAll(transactionsListItems); - } - - private List getTransactionListItems() { - Set transactions = btcWalletService.getTransactions(false); - return transactions.stream() - .map(this::convertTransactionToListItem) - .collect(Collectors.toList()); - } - - private TransactionsListItem convertTransactionToListItem(Transaction transaction) { - Set tradables = tradableRepository.getAll(); - - TransactionAwareTradable maybeTradable = tradables.stream() - .map(transactionAwareTradableFactory::create) - .filter(tradable -> tradable.isRelatedToTransaction(transaction)) - .findAny() - .orElse(null); - - return transactionListItemFactory.create(transaction, maybeTradable); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/DisplayedTransactionsFactory.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/DisplayedTransactionsFactory.java deleted file mode 100644 index 049b8efe6b4..00000000000 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/DisplayedTransactionsFactory.java +++ /dev/null @@ -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 . - */ - -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); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/ObservableListDecorator.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/ObservableListDecorator.java deleted file mode 100644 index f5f6ea3d649..00000000000 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/ObservableListDecorator.java +++ /dev/null @@ -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 . - */ - -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 extends AbstractList { - private final ObservableList delegate = FXCollections.observableArrayList(); - - SortedList asSortedList() { - return new SortedList<>(delegate); - } - - void setAll(Collection elements) { - delegate.setAll(elements); - } - - @Override - public T get(int index) { - return delegate.get(index); - } - - @Override - public int size() { - return delegate.size(); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareTradableFactory.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareTradableFactory.java deleted file mode 100644 index 3c699d9a0f0..00000000000 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionAwareTradableFactory.java +++ /dev/null @@ -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 . - */ - -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); - } - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionListItemFactory.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionListItemFactory.java deleted file mode 100644 index 55234b486cd..00000000000 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionListItemFactory.java +++ /dev/null @@ -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 . - */ - -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()); - } -} diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java index d6a41f76f3b..2f3ada6b383 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsListItem.java @@ -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 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 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 @@ public String getNumConfirmations() { 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); + } } diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.fxml b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.fxml index b2554dfca6d..ea8d8e5d678 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.fxml @@ -25,11 +25,13 @@ + + diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.java b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.java index 49e307f1b07..20075212c57 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/transactions/TransactionsView.java @@ -24,6 +24,7 @@ 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.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 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.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 { + @FXML + FilterBox filterBox; @FXML TableView tableView; @FXML @@ -98,13 +115,21 @@ public class TransactionsView extends ActivatableView { @FXML AutoTooltipButton exportButton; - private final DisplayedTransactions displayedTransactions; - private final SortedList sortedDisplayedTransactions; + private final ObservableList observableList = FXCollections.observableArrayList(); + private final FilteredList filteredList = new FilteredList<>(observableList); + private final SortedList 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 { @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 void initialize() { tableView.getSortOrder().add(dateColumn); walletChangeEventListener = wallet -> { - displayedTransactions.update(); + updateList(); }; keyEventEventHandler = event -> { @@ -202,9 +239,10 @@ public void initialize() { @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 @@ protected void activate() { 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> tableColumns = tableView.getColumns(); final int reportColumns = tableColumns.size() - 1; // CSV report excludes the last column (an icon) @@ -235,14 +273,15 @@ protected void activate() { }; 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 @@ protected void deactivate() { exportButton.setOnAction(null); } + private void updateList() { + List transactionsListItems = btcWalletService.getTransactions(false) + .stream() + .map(transaction -> { + Set 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); diff --git a/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalListItem.java b/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalListItem.java index 0aebe8e23ae..4c50c070153 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalListItem.java @@ -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.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 @@ public Coin getBalance() { 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); + + } } diff --git a/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.fxml b/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.fxml index e3755d40145..1d78c5844df 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.fxml @@ -23,12 +23,14 @@ + + diff --git a/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.java b/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.java index 35dbd66154d..2b1a8011144 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.java +++ b/desktop/src/main/java/bisq/desktop/main/funds/withdrawal/WithdrawalView.java @@ -25,6 +25,7 @@ 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.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; import javafx.collections.transformation.SortedList; import javafx.util.Callback; @@ -117,10 +119,11 @@ @FxmlView public class WithdrawalView extends ActivatableView { - @FXML GridPane gridPane; @FXML + FilterBox filterBox; + @FXML TableView tableView; @FXML TableColumn addressColumn, balanceColumn, selectColumn; @@ -138,7 +141,8 @@ public class WithdrawalView extends ActivatableView { private final BtcAddressValidator btcAddressValidator; private final WalletPasswordWindow walletPasswordWindow; private final ObservableList observableList = FXCollections.observableArrayList(); - private final SortedList sortedList = new SortedList<>(observableList); + private final FilteredList filteredList = new FilteredList<>(observableList); + private final SortedList sortedList = new SortedList<>(filteredList); private final Set selectedItems = new HashSet<>(); private BalanceListener balanceListener; private Set fromAddresses = new HashSet<>(); @@ -183,7 +187,7 @@ private WithdrawalView(BtcWalletService btcWalletService, @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 @@ private void updateInputSelection() { @Override protected void activate() { + filterBox.activate(); sortedList.comparatorProperty().bind(tableView.comparatorProperty()); tableView.setItems(sortedList); updateList(); @@ -374,6 +379,7 @@ protected void activate() { @Override protected void deactivate() { + filterBox.deactivate(); sortedList.comparatorProperty().unbind(); observableList.forEach(WithdrawalListItem::cleanup); btcWalletService.removeBalanceListener(balanceListener); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsDataModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsDataModel.java index 3f01e7b9db2..32e24600772 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsDataModel.java @@ -21,13 +21,17 @@ 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 @@ class UnconfirmedBsqSwapsDataModel extends ActivatableDataModel { - final BsqSwapTradeManager bsqSwapTradeManager; + private final BsqSwapTradeManager bsqSwapTradeManager; private final BsqWalletService bsqWalletService; private final ObservableList list = FXCollections.observableArrayList(); private final ListChangeListener 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 @@ public ObservableList getList() { 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 diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsListItem.java b/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsListItem.java index 5ccc33f71d2..a6c02257ebb 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsListItem.java @@ -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; - - UnconfirmedBsqSwapsListItem(BsqWalletService bsqWalletService, BsqSwapTrade bsqSwapTrade) { + private final TxConfidenceIndicator txConfidenceIndicator; + private final TxConfidenceListener txConfidenceListener; + + 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 @@ public void onTransactionConfidenceChanged(TransactionConfidence confidence) { 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 @@ public void cleanup() { 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); + } } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsView.fxml b/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsView.fxml index 0231c587eec..56c2c030ea6 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsView.fxml @@ -18,28 +18,21 @@ --> - - - + - - - - - - + diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsView.java index 9dfd8c7941d..4c3e056f4b2 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsView.java @@ -22,14 +22,14 @@ 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.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 String toString() { 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 String toString() { private final BsqTradeDetailsWindow window; private final Preferences preferences; private final PrivateNotificationManager privateNotificationManager; + private final AccountAgeWitnessService accountAgeWitnessService; private SortedList sortedList; private FilteredList filteredList; - private ChangeListener filterTextFieldListener; private ChangeListener widthListener; @Inject @@ -142,11 +135,13 @@ public UnconfirmedBsqSwapsView(UnconfirmedBsqSwapsViewModel model, 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 void initialize() { 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 void initialize() { 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 void initialize() { @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 headerConverter = item -> { @@ -242,25 +231,23 @@ protected void activate() { }; CSVEntryConverter 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 @@ protected void deactivate() { sortedList.comparatorProperty().unbind(); exportButton.setOnAction(null); - filterTextField.textProperty().removeListener(filterTextFieldListener); + filterBox.deactivate(); root.widthProperty().removeListener(widthListener); } @@ -295,57 +282,6 @@ private void onWidthChange(double width) { 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 TableCell call( 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 TableCell call( @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 TableCell call( @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 void updateItem(final UnconfirmedBsqSwapsListItem newItem, boolean empty) 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 void updateItem(final UnconfirmedBsqSwapsListItem newItem, boolean empty) privateNotificationManager, bsqSwapTrade.getOffer(), preferences, - model.accountAgeWitnessService, + accountAgeWitnessService, useDevPrivilegeKeys); setPadding(new Insets(1, 15, 0, 0)); setGraphic(peerInfoIcon); @@ -491,7 +431,11 @@ public TableCell call( @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 TableCell call( @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 TableCell call( @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 TableCell call( @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 TableCell call( @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 TableCell call( @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); } }; } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsViewModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsViewModel.java index 78ff626177d..0cdff0249d7 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/bsqswaps/UnconfirmedBsqSwapsViewModel.java @@ -19,121 +19,12 @@ 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 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 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()); } } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesDataModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesDataModel.java index 406a816edde..eb06164bb0d 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesDataModel.java @@ -24,10 +24,9 @@ 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.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 list = FXCollections.observableArrayList(); + private final ObservableList list = FXCollections.observableArrayList(); private final ListChangeListener 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 @@ protected void deactivate() { bsqWalletService.removeBsqBalanceListener(bsqBalanceListener); } - ObservableList getList() { + ObservableList getList() { return list; } - OfferDirection getDirection(Offer offer) { - return closedTradableManager.wasMyOffer(offer) ? offer.getDirection() : offer.getMirroredDirection(); + List getListAsTradables() { + return list.stream().map(ClosedTradesListItem::getTradable).collect(Collectors.toList()); } Coin getTotalAmount() { - return ClosedTradableUtil.getTotalAmount(list); + return ClosedTradableUtil.getTotalAmount(getListAsTradables()); } Optional getVolumeInUserFiatCurrency(Coin amount) { @@ -126,15 +128,11 @@ Volume getBsqVolumeInUsdWithAveragePrice(Coin amount) { } 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 @@ boolean isCurrencyForTradeFeeBtc(Tradable item) { 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 getTradableStream() { - return Stream.concat(bsqSwapTradeManager.getConfirmedBsqSwapTrades(), - closedTradableManager.getObservableList().stream()); + list.sort((o1, o2) -> o2.getTradable().getDate().compareTo(o1.getTradable().getDate())); } } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesListItem.java b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesListItem.java new file mode 100644 index 00000000000..9a683c80e68 --- /dev/null +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesListItem.java @@ -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 . + */ + +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); + } +} diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.fxml b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.fxml index 0bdd24821a8..9e86f8a2413 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.fxml @@ -18,28 +18,21 @@ --> - - - + - - - - - - + diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.java index 0d5b2445e6f..5078d06c939 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.java @@ -24,8 +24,8 @@ 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.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.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.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.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 String toString() { } @FXML - TableView tableView; + TableView tableView; @FXML - TableColumn priceColumn, deviationColumn, amountColumn, volumeColumn, + TableColumn 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 String toString() { private final Preferences preferences; private final TradeDetailsWindow tradeDetailsWindow; private final PrivateNotificationManager privateNotificationManager; - private SortedList sortedList; - private FilteredList filteredList; - private ChangeListener filterTextFieldListener; + private SortedList sortedList; + private FilteredList filteredList; private ChangeListener widthListener; @Inject @@ -226,52 +214,49 @@ public void initialize() { 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 row = new TableRow<>(); + TableRow 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 void initialize() { 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 @@ protected void activate() { 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 headerConverter = item -> { + CSVEntryConverter headerConverter = item -> { String[] columns = new String[ColumnNames.values().length]; for (ColumnNames m : ColumnNames.values()) { columns[m.ordinal()] = m.toString(); } return columns; }; - CSVEntryConverter contentConverter = item -> { + CSVEntryConverter 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 @@ protected void deactivate() { exportButton.setOnAction(null); summaryButton.setOnAction(null); - filterTextField.textProperty().removeListener(filterTextFieldListener); + filterBox.deactivate(); root.widthProperty().removeListener(widthListener); } - private static > Comparator nullsFirstComparing(Function keyExtractor) { + private static > Comparator nullsFirstComparing(Function keyExtractor) { return Comparator.comparing( o -> o != null ? keyExtractor.apply(o) : null, Comparator.nullsFirst(Comparator.naturalOrder()) ); } - private static > Comparator nullsFirstComparingAsTrade(Function keyExtractor) { + private static > Comparator nullsFirstComparingAsTrade(Function 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 @@ private void onWidthChange(double width) { 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 @@ private void setTradeIdColumnCellFactory() { new Callback<>() { @Override - public TableCell call(TableColumn column) { + public TableCell call(TableColumn 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 void updateItem(final Tradable tradable, boolean empty) { } private void setDateColumnCellFactory() { - dateColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + dateColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); dateColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn 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 void updateItem(final Tradable item, boolean empty) { } private void setMarketColumnCellFactory() { - marketColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + marketColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); marketColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn 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 void updateItem(final Tradable item, boolean empty) { } private void setStateColumnCellFactory() { - stateColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + stateColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); stateColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn 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 @@ private void setDuplicateColumnCellFactory() { duplicateColumn.setCellFactory( new Callback<>() { @Override - public TableCell call(TableColumn column) { + public TableCell call(TableColumn 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 void updateItem(final Tradable item, boolean empty) { } @SuppressWarnings("UnusedReturnValue") - private TableColumn setAvatarColumnCellFactory() { + private TableColumn 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 call(TableColumn column) { + public TableCell call(TableColumn 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 void updateItem(final Tradable item, boolean empty) { } private void setAmountColumnCellFactory() { - amountColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + amountColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); amountColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn 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 void updateItem(final Tradable item, boolean empty) { } private void setPriceColumnCellFactory() { - priceColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + priceColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); priceColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn 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 void updateItem(final Tradable item, boolean empty) { } private void setDeviationColumnCellFactory() { - deviationColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + deviationColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); deviationColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn 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 void updateItem(final Tradable item, boolean empty) { } private void setVolumeColumnCellFactory() { - volumeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + volumeColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); volumeColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn 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 void updateItem(final Tradable item, boolean empty) { } private void setDirectionColumnCellFactory() { - directionColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + directionColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); directionColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn 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 void updateItem(final Tradable item, boolean empty) { } private void setTxFeeColumnCellFactory() { - txFeeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + txFeeColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); txFeeColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn 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 void updateItem(final Tradable item, boolean empty) { } private void setTradeFeeColumnCellFactory() { - tradeFeeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + tradeFeeColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); tradeFeeColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn 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 void updateItem(final Tradable item, boolean empty) { } private void setBuyerSecurityDepositColumnCellFactory() { - buyerSecurityDepositColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + buyerSecurityDepositColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); buyerSecurityDepositColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn 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 void updateItem(final Tradable item, boolean empty) { } private void setSellerSecurityDepositColumnCellFactory() { - sellerSecurityDepositColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + sellerSecurityDepositColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); sellerSecurityDepositColumn.setCellFactory( new Callback<>() { @Override - public TableCell call( - TableColumn column) { + public TableCell call( + TableColumn 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 @@ private void onDuplicateOffer(Offer offer) { 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; - } - }; - } } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesViewModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesViewModel.java index 2ba1f9b7f82..1512a23e23f 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesViewModel.java @@ -19,13 +19,9 @@ 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 ClosedTradesViewModel(ClosedTradesDataModel dataModel, ClosedTradableForm 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 String getTotalAmountWithVolume(Coin totalTradeAmount) { } public Map getTotalVolumeByCurrency() { - return closedTradableFormatter.getTotalVolumeByCurrencyAsString(dataModel.getList()); + return closedTradableFormatter.getTotalVolumeByCurrencyAsString(dataModel.getListAsTradables()); } public String getTotalTxFee(Coin totalTradeAmount) { diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesDataModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesDataModel.java index ea658e1f935..69c6af27b39 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesDataModel.java @@ -19,11 +19,11 @@ 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 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 list = FXCollections.observableArrayList(); private final ListChangeListener 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 @@ public ObservableList getList() { 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())); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesListItem.java b/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesListItem.java index 52973a2316d..36e7b864601 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesListItem.java @@ -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; + } + + 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"); } - FailedTradesListItem() { - this.trade = null; + @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); } } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesView.fxml b/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesView.fxml index d89bb0a5bba..aeab9a7fb71 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesView.fxml @@ -18,26 +18,20 @@ --> - - - + - - - - - + diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesView.java index bfc10599e40..7123744d268 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesView.java @@ -22,15 +22,13 @@ 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.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.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 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 sortedList; private FilteredList filteredList; private EventHandler keyEventEventHandler; - private ChangeListener filterTextFieldListener; private Scene scene; private final boolean allowFaultyDelayedTxs; @@ -147,8 +136,8 @@ public void initialize() { 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 void initialize() { } }; - 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 @@ protected void activate() { 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> tableColumns = tableView.getColumns(); @@ -210,27 +196,24 @@ protected void activate() { }; CSVEntryConverter 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 @@ protected void deactivate() { 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 TableCell call(TableColumn tradeDetailsWindow.show(item.getTrade())); field.setTooltip(new Tooltip(Res.get("tooltip.openPopupForDetails"))); setGraphic(field); @@ -394,7 +312,7 @@ public TableCell call( 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 TableCell call( @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 TableCell call( 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 TableCell call( @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 TableCell call( @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 TableCell call( 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 TableCell call( @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); + } } }; } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesViewModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesViewModel.java index 0e747d54bf4..5775d7d57f5 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/failedtrades/FailedTradesViewModel.java @@ -19,74 +19,12 @@ 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 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 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") : ""; } } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOfferListItem.java b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOfferListItem.java index 6640f2b0d10..11cf4266284 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOfferListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOfferListItem.java @@ -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) { - this.openOffer = openOffer; - } - OpenOfferListItem() { - openOffer = null; + OpenOfferListItem(OpenOffer openOffer, PriceUtil priceUtil, CoinFormatter btcFormatter, BsqFormatter bsqFormatter, OpenOfferManager openOfferManager) { + this.openOffer = openOffer; + 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); + } } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersDataModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersDataModel.java index 752d52d8467..30e3c814a69 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersDataModel.java @@ -26,12 +26,18 @@ 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 list = FXCollections.observableArrayList(); private final ListChangeListener 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 @@ public OfferDirection getDirection(Offer offer) { 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())); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.fxml b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.fxml index e3e729317c0..4a0e6201e85 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.fxml @@ -18,9 +18,8 @@ --> - - + @@ -34,10 +33,9 @@ - - - - + + + diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.java index f4f6af87ed1..349aa054d88 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.java @@ -25,7 +25,7 @@ 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.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 String toString() { 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 String toString() { private final OfferDetailsWindow offerDetailsWindow; private final BsqSwapOfferDetailsWindow bsqSwapOfferDetailsWindow; private SortedList sortedList; - private FilteredList filteredList; - private ChangeListener filterTextFieldListener; private PortfolioView.OpenOfferActionHandler openOfferActionHandler; private ChangeListener widthListener; @@ -197,10 +188,10 @@ public void initialize() { 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 void initialize() { 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 void initialize() { @Override protected void activate() { - filteredList = new FilteredList<>(model.getList()); + FilteredList 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 @@ protected void activate() { }; CSVEntryConverter 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 @@ protected void activate() { 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 @@ protected void deactivate() { sortedList.comparatorProperty().unbind(); exportButton.setOnAction(null); - filterTextField.textProperty().removeListener(filterTextFieldListener); + filterBox.deactivate(); root.widthProperty().removeListener(widthListener); } @@ -327,44 +312,6 @@ private void updateSelectToggleButtonState() { } } - 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 @@ private void onActivateOpenOffer(OpenOffer openOffer) { } } - 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 TableCell call(TableColumn { if (item.getOffer().isBsqSwapOffer()) { bsqSwapOfferDetailsWindow.show(item.getOffer()); @@ -503,8 +451,8 @@ public void updateItem(final OpenOfferListItem item, boolean empty) { 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 void updateItem(final OpenOfferListItem item, boolean empty) { 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 void updateItem(final OpenOfferListItem item, boolean empty) { 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 void updateItem(final OpenOfferListItem item, boolean empty) { 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 void updateItem(final OpenOfferListItem item, boolean empty) { 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 void updateItem(final OpenOfferListItem item, boolean empty) { 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 void updateItem(final OpenOfferListItem item, boolean empty) { 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 void updateItem(final OpenOfferListItem item, boolean empty) { 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 void updateItem(final OpenOfferListItem item, boolean empty) { 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 void updateItem(final OpenOfferListItem item, boolean empty) { 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 void updateItem(final OpenOfferListItem item, boolean empty) { 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); } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersViewModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersViewModel.java index e07fe0e2f2f..ce4893a4055 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersViewModel.java @@ -19,54 +19,31 @@ 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 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 @@ void onRemoveOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMe dataModel.onRemoveOpenOffer(openOffer, resultHandler, errorMessageHandler); } - public ObservableList 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()); - } - } } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java index 4a3a8d392e2..ca913b7d579 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java @@ -57,6 +57,7 @@ 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 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 list = FXCollections.observableArrayList(); private final ListChangeListener tradesListChangeListener; @@ -145,7 +149,8 @@ public PendingTradesDataModel(TradeManager tradeManager, 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 PendingTradesDataModel(TradeManager tradeManager, this.walletPasswordWindow = walletPasswordWindow; this.notificationCenter = notificationCenter; this.offerUtil = offerUtil; + this.btcFormatter = formatter; tradesListChangeListener = change -> onListChanged(); notificationCenter.setSelectItemByTradeIdConsumer(this::selectItemByTradeId); @@ -381,7 +387,7 @@ public String getReference() { 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 diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesListItem.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesListItem.java index e63383786ae..ff4bc15d7db 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesListItem.java @@ -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.apache.commons.lang3.StringUtils; -import org.bitcoinj.core.Coin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import javafx.beans.property.ReadOnlyObjectProperty; +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 tradeAmountProperty() { - return trade.amountProperty(); + public String getPriceAsString() { + return FormattingUtils.formatPrice(trade.getPrice()); } - public ReadOnlyObjectProperty 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); + } } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesView.fxml b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesView.fxml index b8f27271572..15e97dc48f3 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesView.fxml @@ -21,12 +21,13 @@ + - + diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesView.java index 7fad26cda3f..3c136ed9f37 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesView.java @@ -23,6 +23,7 @@ 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.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.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 interface ChatCallback { 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 tableView; @FXML TableColumn priceColumn, volumeColumn, amountColumn, avatarColumn, marketColumn, roleColumn, paymentMethodColumn, tradeIdColumn, dateColumn, chatColumn, moveTradeToFailedColumn; + private FilteredList filteredList; private SortedList sortedList; private TradeSubView selectedSubView; private EventHandler keyEventEventHandler; @@ -161,7 +163,6 @@ public PendingTradesView(PendingTradesViewModel model, 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 PendingTradesView(PendingTradesViewModel model, 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 void initialize() { 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 void initialize() { @Override protected void activate() { ObservableList 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 @@ protected void activate() { 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 @@ else if (root.getChildren().size() == 2) @Override protected void deactivate() { + filterBox.deactivate(); sortedList.comparatorProperty().unbind(); selectedItemSubscription.unsubscribe(); selectedTableItemSubscription.unsubscribe(); @@ -683,7 +688,7 @@ public TableCell call( 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 TableCell call( 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 TableCell call( 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 TableCell call( @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); + } } }; } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java index 06de2be4eba..6237fa14509 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java @@ -245,16 +245,6 @@ ReadOnlyObjectProperty getSellerState() { 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 @@ String getMyRole(PendingTradesListItem item) { } } - String getPaymentMethod(PendingTradesListItem item) { - return item == null ? "" : tradeUtil.getPaymentMethodNameWithCountryCode(item.getTrade()); - } - // summary public String getTradeVolume() { return dataModel.getTrade() != null diff --git a/desktop/src/main/java/bisq/desktop/main/funds/transactions/DummyTransactionAwareTradable.java b/desktop/src/main/java/bisq/desktop/util/filtering/FilterableListItem.java similarity index 56% rename from desktop/src/main/java/bisq/desktop/main/funds/transactions/DummyTransactionAwareTradable.java rename to desktop/src/main/java/bisq/desktop/util/filtering/FilterableListItem.java index a5ee768e53f..40a220c6f13 100644 --- a/desktop/src/main/java/bisq/desktop/main/funds/transactions/DummyTransactionAwareTradable.java +++ b/desktop/src/main/java/bisq/desktop/util/filtering/FilterableListItem.java @@ -15,26 +15,8 @@ * along with Bisq. If not, see . */ -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); } diff --git a/desktop/src/main/java/bisq/desktop/util/filtering/FilteringUtils.java b/desktop/src/main/java/bisq/desktop/util/filtering/FilteringUtils.java new file mode 100644 index 00000000000..ba97114802a --- /dev/null +++ b/desktop/src/main/java/bisq/desktop/util/filtering/FilteringUtils.java @@ -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 . + */ + +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; + } +} diff --git a/desktop/src/test/java/bisq/desktop/GuiceSetupTest.java b/desktop/src/test/java/bisq/desktop/GuiceSetupTest.java index 72111708124..1e0f1088eb5 100644 --- a/desktop/src/test/java/bisq/desktop/GuiceSetupTest.java +++ b/desktop/src/test/java/bisq/desktop/GuiceSetupTest.java @@ -5,10 +5,7 @@ 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 void testGuiceSetup() { assertSingleton(DaoPresentation.class); assertSingleton(Transitions.class); assertSingleton(TradableRepository.class); - assertSingleton(TransactionListItemFactory.class); - assertSingleton(TransactionAwareTradableFactory.class); - assertSingleton(DisplayedTransactionsFactory.class); assertSingleton(BondingViewUtils.class); // core module diff --git a/desktop/src/test/java/bisq/desktop/main/funds/transactions/DisplayedTransactionsTest.java b/desktop/src/test/java/bisq/desktop/main/funds/transactions/DisplayedTransactionsTest.java deleted file mode 100644 index 09c84516b73..00000000000 --- a/desktop/src/test/java/bisq/desktop/main/funds/transactions/DisplayedTransactionsTest.java +++ /dev/null @@ -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 . - */ - -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 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)); - } -} diff --git a/desktop/src/test/java/bisq/desktop/main/funds/transactions/ObservableListDecoratorTest.java b/desktop/src/test/java/bisq/desktop/main/funds/transactions/ObservableListDecoratorTest.java deleted file mode 100644 index 20746218637..00000000000 --- a/desktop/src/test/java/bisq/desktop/main/funds/transactions/ObservableListDecoratorTest.java +++ /dev/null @@ -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 . - */ - -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 list = new ObservableListDecorator<>(); - Collection 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 list = new ObservableListDecorator<>(); - Collection 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()); - } -} diff --git a/desktop/src/test/java/bisq/desktop/main/funds/transactions/TransactionAwareTradableFactoryTest.java b/desktop/src/test/java/bisq/desktop/main/funds/transactions/TransactionAwareTradableFactoryTest.java deleted file mode 100644 index 38a86b5290c..00000000000 --- a/desktop/src/test/java/bisq/desktop/main/funds/transactions/TransactionAwareTradableFactoryTest.java +++ /dev/null @@ -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 . - */ - -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))); - } -} diff --git a/desktop/src/test/java/bisq/desktop/main/funds/transactions/TransactionAwareTradeTest.java b/desktop/src/test/java/bisq/desktop/main/funds/transactions/TransactionAwareTradeTest.java index c682ed3b344..711dac141c0 100644 --- a/desktop/src/test/java/bisq/desktop/main/funds/transactions/TransactionAwareTradeTest.java +++ b/desktop/src/test/java/bisq/desktop/main/funds/transactions/TransactionAwareTradeTest.java @@ -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 void setUp() { 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); }