Merge branch 'add-toggle-for-hiding-not-takable-offers' into add-new-filter-entries

This commit is contained in:
chimp1984 2021-01-04 11:48:02 -05:00
commit cfabf79ca4
No known key found for this signature in database
GPG key ID: 9801B4EC591F90E3
6 changed files with 160 additions and 33 deletions

View file

@ -772,6 +772,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
requestPersistence();
}
public void setShowOffersMatchingMyAccounts(boolean value) {
prefPayload.setShowOffersMatchingMyAccounts(value);
requestPersistence();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getter
@ -1081,5 +1086,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
void setAutoConfirmSettings(AutoConfirmSettings autoConfirmSettings);
void setHideNonAccountPaymentMethods(boolean hideNonAccountPaymentMethods);
void setShowOffersMatchingMyAccounts(boolean value);
}
}

View file

@ -131,6 +131,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
// Added in 1.5.5
private boolean hideNonAccountPaymentMethods;
private boolean showOffersMatchingMyAccounts;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -195,7 +197,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
.addAllAutoConfirmSettings(autoConfirmSettingsList.stream()
.map(autoConfirmSettings -> ((protobuf.AutoConfirmSettings) autoConfirmSettings.toProtoMessage()))
.collect(Collectors.toList()))
.setHideNonAccountPaymentMethods(hideNonAccountPaymentMethods);
.setHideNonAccountPaymentMethods(hideNonAccountPaymentMethods)
.setShowOffersMatchingMyAccounts(showOffersMatchingMyAccounts);
Optional.ofNullable(backupDirectory).ifPresent(builder::setBackupDirectory);
Optional.ofNullable(preferredTradeCurrency).ifPresent(e -> builder.setPreferredTradeCurrency((protobuf.TradeCurrency) e.toProtoMessage()));
@ -290,7 +293,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
new ArrayList<>(proto.getAutoConfirmSettingsList().stream()
.map(AutoConfirmSettings::fromProto)
.collect(Collectors.toList())),
proto.getHideNonAccountPaymentMethods()
proto.getHideNonAccountPaymentMethods(),
proto.getShowOffersMatchingMyAccounts()
);
}
}

View file

@ -342,6 +342,7 @@ offerbook.offerersAcceptedBankSeats=Accepted seat of bank countries (taker):\n {
offerbook.availableOffers=Available offers
offerbook.filterByCurrency=Filter by currency
offerbook.filterByPaymentMethod=Filter by payment method
offerbook.matchingOffers=Offers matching my accounts
offerbook.timeSinceSigning=Account info
offerbook.timeSinceSigning.info=This account was verified and {0}
offerbook.timeSinceSigning.info.arbitrator=signed by an arbitrator and can sign peer accounts

View file

@ -22,6 +22,7 @@ import bisq.desktop.common.view.ActivatableViewAndModel;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.AutoTooltipButton;
import bisq.desktop.components.AutoTooltipLabel;
import bisq.desktop.components.AutoTooltipSlideToggleButton;
import bisq.desktop.components.AutoTooltipTableColumn;
import bisq.desktop.components.AutocompleteComboBox;
import bisq.desktop.components.ColoredDecimalPlacesWithZerosText;
@ -126,6 +127,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
private AutocompleteComboBox<TradeCurrency> currencyComboBox;
private AutocompleteComboBox<PaymentMethod> paymentMethodComboBox;
private AutoTooltipButton createOfferButton;
private AutoTooltipSlideToggleButton matchingOffersToggle;
private AutoTooltipTableColumn<OfferBookListItem, OfferBookListItem> amountColumn, volumeColumn, marketColumn,
priceColumn, paymentMethodColumn, depositColumn, signingStateColumn, avatarColumn;
private TableView<OfferBookListItem> tableView;
@ -174,10 +176,25 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
hBox.setSpacing(35);
hBox.setPadding(new Insets(10, 0, 0, 0));
final Tuple3<VBox, Label, AutocompleteComboBox<TradeCurrency>> currencyBoxTuple = FormBuilder.addTopLabelAutocompleteComboBox(
Tuple3<VBox, Label, AutocompleteComboBox<TradeCurrency>> currencyBoxTuple = FormBuilder.addTopLabelAutocompleteComboBox(
Res.get("offerbook.filterByCurrency"));
final Tuple3<VBox, Label, AutocompleteComboBox<PaymentMethod>> paymentBoxTuple = FormBuilder.addTopLabelAutocompleteComboBox(
currencyComboBox = currencyBoxTuple.third;
currencyComboBox.setPrefWidth(270);
Tuple3<VBox, Label, AutocompleteComboBox<PaymentMethod>> paymentBoxTuple = FormBuilder.addTopLabelAutocompleteComboBox(
Res.get("offerbook.filterByPaymentMethod"));
paymentMethodComboBox = paymentBoxTuple.third;
paymentMethodComboBox.setCellFactory(GUIUtil.getPaymentMethodCellFactory());
paymentMethodComboBox.setPrefWidth(270);
matchingOffersToggle = new AutoTooltipSlideToggleButton();
matchingOffersToggle.setText(Res.get("offerbook.matchingOffers"));
HBox.setMargin(matchingOffersToggle, new Insets(7, 0, -9, -15));
hBox.getChildren().addAll(currencyBoxTuple.first, paymentBoxTuple.first, matchingOffersToggle);
AnchorPane.setLeftAnchor(hBox, 0d);
AnchorPane.setTopAnchor(hBox, 0d);
AnchorPane.setBottomAnchor(hBox, 0d);
createOfferButton = new AutoTooltipButton();
createOfferButton.setMinHeight(40);
@ -185,11 +202,6 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
AnchorPane.setRightAnchor(createOfferButton, 0d);
AnchorPane.setBottomAnchor(createOfferButton, 0d);
hBox.getChildren().addAll(currencyBoxTuple.first, paymentBoxTuple.first, createOfferButton);
AnchorPane.setLeftAnchor(hBox, 0d);
AnchorPane.setTopAnchor(hBox, 0d);
AnchorPane.setBottomAnchor(hBox, 0d);
AnchorPane anchorPane = new AnchorPane();
anchorPane.getChildren().addAll(hBox, createOfferButton);
@ -199,11 +211,6 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
GridPane.setMargin(anchorPane, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 0));
root.getChildren().add(anchorPane);
currencyComboBox = currencyBoxTuple.third;
paymentMethodComboBox = paymentBoxTuple.third;
paymentMethodComboBox.setCellFactory(GUIUtil.getPaymentMethodCellFactory());
tableView = new TableView<>();
GridPane.setRowIndex(tableView, ++gridRow);
@ -328,6 +335,10 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
currencyComboBox.getEditor().setText(new CurrencyStringConverter(currencyComboBox).toString(currencyComboBox.getSelectionModel().getSelectedItem()));
matchingOffersToggle.setSelected(model.useOffersMatchingMyAccountsFilter);
matchingOffersToggle.disableProperty().bind(model.disableMatchToggle);
matchingOffersToggle.setOnAction(e -> model.onShowOffersMatchingMyAccounts(matchingOffersToggle.isSelected()));
volumeColumn.sortableProperty().bind(model.showAllTradeCurrenciesProperty.not());
model.getOfferList().comparatorProperty().bind(tableView.comparatorProperty());
@ -424,6 +435,8 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
@Override
protected void deactivate() {
createOfferButton.setOnAction(null);
matchingOffersToggle.setOnAction(null);
matchingOffersToggle.disableProperty().unbind();
model.getOfferList().comparatorProperty().unbind();
volumeColumn.sortableProperty().unbind();
@ -1024,6 +1037,10 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
final Offer offer = item.getOffer();
boolean myOffer = model.isMyOffer(offer);
if (tableRow != null) {
// this code is duplicated in model.getOffersMatchingMyAccountsPredicate but as
// we want to pass the results for displaying relevant info in popups we
// cannot simply replace it with the predicate. If there are any changes we
// need to maintain both.
isPaymentAccountValidForOffer = model.isAnyPaymentAccountValidForOffer(offer);
isInsufficientCounterpartyTradeLimit = model.isInsufficientCounterpartyTradeLimit(offer);
hasSameProtocolVersion = model.hasSameProtocolVersion(offer);

View file

@ -78,16 +78,19 @@ import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.SetChangeListener;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import java.text.DecimalFormat;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
@ -122,15 +125,19 @@ class OfferBookViewModel extends ActivatableViewModel {
// If id is empty string we ignore filter (display all methods)
PaymentMethod selectedPaymentMethod = PaymentMethod.getDummyPaymentMethod(GUIUtil.SHOW_ALL_FLAG);
PaymentMethod selectedPaymentMethod = getShowAllEntryForPaymentMethod();
private boolean isTabSelected;
final BooleanProperty showAllTradeCurrenciesProperty = new SimpleBooleanProperty(true);
final BooleanProperty disableMatchToggle = new SimpleBooleanProperty();
final IntegerProperty maxPlacesForAmount = new SimpleIntegerProperty();
final IntegerProperty maxPlacesForVolume = new SimpleIntegerProperty();
final IntegerProperty maxPlacesForPrice = new SimpleIntegerProperty();
final IntegerProperty maxPlacesForMarketPriceMargin = new SimpleIntegerProperty();
boolean showAllPaymentMethods = true;
boolean useOffersMatchingMyAccountsFilter;
private final Map<String, Boolean> myInsufficientTradeLimitCache = new HashMap<>();
private final Map<String, Boolean> insufficientCounterpartyTradeLimitCache = new HashMap<>();
///////////////////////////////////////////////////////////////////////////////////////////
@ -206,6 +213,12 @@ class OfferBookViewModel extends ActivatableViewModel {
highestMarketPriceMarginOffer.ifPresent(offerBookListItem -> maxPlacesForMarketPriceMargin.set(formatMarketPriceMargin(offerBookListItem.getOffer(), false).length()));
};
// If our accounts have changed we reset our myInsufficientTradeLimitCache as it depends on account data
if (user != null) {
user.getPaymentAccountsAsObservable().addListener((SetChangeListener<PaymentAccount>) c ->
myInsufficientTradeLimitCache.clear());
}
}
@Override
@ -213,7 +226,7 @@ class OfferBookViewModel extends ActivatableViewModel {
filteredItems.addListener(filterItemsListener);
String code = direction == OfferPayload.Direction.BUY ? preferences.getBuyScreenCurrencyCode() : preferences.getSellScreenCurrencyCode();
if (code != null && !code.equals(GUIUtil.SHOW_ALL_FLAG) && !code.isEmpty() &&
if (code != null && !code.isEmpty() && !isShowAllEntry(code) &&
CurrencyUtil.getTradeCurrency(code).isPresent()) {
showAllTradeCurrenciesProperty.set(false);
selectedTradeCurrency = CurrencyUtil.getTradeCurrency(code).get();
@ -223,10 +236,15 @@ class OfferBookViewModel extends ActivatableViewModel {
}
tradeCurrencyCode.set(selectedTradeCurrency.getCode());
if (user != null) {
disableMatchToggle.set(user.getPaymentAccounts() == null || user.getPaymentAccounts().isEmpty());
}
useOffersMatchingMyAccountsFilter = !disableMatchToggle.get() && isShowOffersMatchingMyAccounts();
fillAllTradeCurrencies();
preferences.getTradeCurrenciesAsObservable().addListener(tradeCurrencyListChangeListener);
offerBook.fillOfferBookListItems();
applyFilterPredicate();
filterOffers();
setMarketPriceFeedCurrency();
priceUtil.recalculateBsq30DayAveragePrice();
@ -238,6 +256,7 @@ class OfferBookViewModel extends ActivatableViewModel {
preferences.getTradeCurrenciesAsObservable().removeListener(tradeCurrencyListChangeListener);
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
@ -268,7 +287,7 @@ class OfferBookViewModel extends ActivatableViewModel {
}
setMarketPriceFeedCurrency();
applyFilterPredicate();
filterOffers();
if (direction == OfferPayload.Direction.BUY)
preferences.setBuyScreenCurrencyCode(code);
@ -288,23 +307,34 @@ class OfferBookViewModel extends ActivatableViewModel {
// If we select TransferWise we switch to show all currencies as TransferWise supports
// sending to most currencies.
if (paymentMethod.getId().equals(PaymentMethod.TRANSFERWISE_ID)) {
onSetTradeCurrency(new CryptoCurrency(GUIUtil.SHOW_ALL_FLAG, ""));
onSetTradeCurrency(getShowAllEntryForCurrency());
}
} else {
this.selectedPaymentMethod = PaymentMethod.getDummyPaymentMethod(GUIUtil.SHOW_ALL_FLAG);
this.selectedPaymentMethod = getShowAllEntryForPaymentMethod();
}
applyFilterPredicate();
filterOffers();
}
void onRemoveOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
openOfferManager.removeOffer(offer, resultHandler, errorMessageHandler);
}
void onShowOffersMatchingMyAccounts(boolean isSelected) {
useOffersMatchingMyAccountsFilter = isSelected;
preferences.setShowOffersMatchingMyAccounts(useOffersMatchingMyAccountsFilter);
filterOffers();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
boolean isShowOffersMatchingMyAccounts() {
return preferences.isShowOffersMatchingMyAccounts();
}
SortedList<OfferBookListItem> getOfferList() {
return sortedItems;
}
@ -348,7 +378,7 @@ class OfferBookViewModel extends ActivatableViewModel {
}
list.sort(Comparator.naturalOrder());
list.add(0, PaymentMethod.getDummyPaymentMethod(GUIUtil.SHOW_ALL_FLAG));
list.add(0, getShowAllEntryForPaymentMethod());
return list;
}
@ -528,11 +558,12 @@ class OfferBookViewModel extends ActivatableViewModel {
private void fillAllTradeCurrencies() {
allTradeCurrencies.clear();
// Used for ignoring filter (show all)
allTradeCurrencies.add(new CryptoCurrency(GUIUtil.SHOW_ALL_FLAG, ""));
allTradeCurrencies.add(getShowAllEntryForCurrency());
allTradeCurrencies.addAll(preferences.getTradeCurrenciesAsObservable());
allTradeCurrencies.add(new CryptoCurrency(GUIUtil.EDIT_FLAG, ""));
allTradeCurrencies.add(getEditEntryForCurrency());
}
///////////////////////////////////////////////////////////////////////////////////////////
// Checks
///////////////////////////////////////////////////////////////////////////////////////////
@ -560,8 +591,15 @@ class OfferBookViewModel extends ActivatableViewModel {
// Filters
///////////////////////////////////////////////////////////////////////////////////////////
private void applyFilterPredicate() {
filteredItems.setPredicate(offerBookListItem -> {
private void filterOffers() {
Predicate<OfferBookListItem> predicate = useOffersMatchingMyAccountsFilter ?
getCurrencyAndMethodPredicate().and(getOffersMatchingMyAccountsPredicate()) :
getCurrencyAndMethodPredicate();
filteredItems.setPredicate(predicate);
}
private Predicate<OfferBookListItem> getCurrencyAndMethodPredicate() {
return offerBookListItem -> {
Offer offer = offerBookListItem.getOffer();
boolean directionResult = offer.getDirection() != direction;
boolean currencyResult = (showAllTradeCurrenciesProperty.get()) ||
@ -570,7 +608,37 @@ class OfferBookViewModel extends ActivatableViewModel {
offer.getPaymentMethod().equals(selectedPaymentMethod);
boolean notMyOfferOrShowMyOffersActivated = !isMyOffer(offerBookListItem.getOffer()) || preferences.isShowOwnOffersInOfferBook();
return directionResult && currencyResult && paymentMethodResult && notMyOfferOrShowMyOffersActivated;
});
};
}
private Predicate<OfferBookListItem> getOffersMatchingMyAccountsPredicate() {
// This code duplicates code in the view at the button column. We need there the different results for
// display in popups so we cannot replace that with the predicate. Any change need to be applied in both
// places.
return offerBookListItem -> {
Offer offer = offerBookListItem.getOffer();
boolean isPaymentAccountValidForOffer = isAnyPaymentAccountValidForOffer(offer);
boolean isInsufficientCounterpartyTradeLimit = isInsufficientCounterpartyTradeLimit(offer);
boolean hasSameProtocolVersion = hasSameProtocolVersion(offer);
boolean isIgnored = isIgnored(offer);
boolean isOfferBanned = isOfferBanned(offer);
boolean isCurrencyBanned = isCurrencyBanned(offer);
boolean isPaymentMethodBanned = isPaymentMethodBanned(offer);
boolean isNodeAddressBanned = isNodeAddressBanned(offer);
boolean requireUpdateToNewVersion = requireUpdateToNewVersion();
boolean isMyInsufficientTradeLimit = isMyInsufficientTradeLimit(offer);
boolean isTradable = isPaymentAccountValidForOffer &&
!isInsufficientCounterpartyTradeLimit &&
hasSameProtocolVersion &&
!isIgnored &&
!isOfferBanned &&
!isCurrencyBanned &&
!isPaymentMethodBanned &&
!isNodeAddressBanned &&
!requireUpdateToNewVersion &&
!isMyInsufficientTradeLimit;
return isTradable;
};
}
boolean isIgnored(Offer offer) {
@ -598,13 +666,28 @@ class OfferBookViewModel extends ActivatableViewModel {
return filterManager.requireUpdateToNewVersionForTrading();
}
// This call is a bit expensive so we cache results
boolean isInsufficientCounterpartyTradeLimit(Offer offer) {
return CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) &&
!accountAgeWitnessService.verifyPeersTradeAmount(offer, offer.getAmount(), errorMessage -> {
});
String offerId = offer.getId();
if (insufficientCounterpartyTradeLimitCache.containsKey(offerId)) {
return insufficientCounterpartyTradeLimitCache.get(offerId);
}
boolean result = CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) &&
!accountAgeWitnessService.verifyPeersTradeAmount(offer, offer.getAmount(),
errorMessage -> {
});
insufficientCounterpartyTradeLimitCache.put(offerId, result);
return result;
}
// This call is a bit expensive so we cache results
boolean isMyInsufficientTradeLimit(Offer offer) {
String offerId = offer.getId();
if (myInsufficientTradeLimitCache.containsKey(offerId)) {
return myInsufficientTradeLimitCache.get(offerId);
}
Optional<PaymentAccount> accountOptional = getMostMaturePaymentAccountForOffer(offer);
long myTradeLimit = accountOptional
.map(paymentAccount -> accountAgeWitnessService.getMyTradeLimit(paymentAccount,
@ -615,9 +698,11 @@ class OfferBookViewModel extends ActivatableViewModel {
accountOptional.isPresent() ? accountOptional.get().getAccountName() : "null",
Coin.valueOf(myTradeLimit).toFriendlyString(),
Coin.valueOf(offerMinAmount).toFriendlyString());
return CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) &&
boolean result = CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) &&
accountOptional.isPresent() &&
myTradeLimit < offerMinAmount;
myInsufficientTradeLimitCache.put(offerId, result);
return result;
}
boolean hasSameProtocolVersion(Offer offer) {
@ -645,11 +730,11 @@ class OfferBookViewModel extends ActivatableViewModel {
public boolean hasSelectionAccountSigning() {
if (showAllTradeCurrenciesProperty.get()) {
if (!selectedPaymentMethod.getId().equals(GUIUtil.SHOW_ALL_FLAG)) {
if (!isShowAllEntry(selectedPaymentMethod.getId())) {
return PaymentMethod.hasChargebackRisk(selectedPaymentMethod);
}
} else {
if (selectedPaymentMethod.getId().equals(GUIUtil.SHOW_ALL_FLAG))
if (isShowAllEntry(selectedPaymentMethod.getId()))
return CurrencyUtil.getMatureMarketCurrencies().stream()
.anyMatch(c -> c.getCode().equals(selectedTradeCurrency.getCode()));
else
@ -675,4 +760,16 @@ class OfferBookViewModel extends ActivatableViewModel {
var percentage = FormattingUtils.formatToRoundedPercentWithSymbol(deposit.getValue() / (double) amount);
return btcFormatter.formatCoin(deposit) + " (" + percentage + ")";
}
private TradeCurrency getShowAllEntryForCurrency() {
return new CryptoCurrency(GUIUtil.SHOW_ALL_FLAG, "");
}
private TradeCurrency getEditEntryForCurrency() {
return new CryptoCurrency(GUIUtil.EDIT_FLAG, "");
}
private PaymentMethod getShowAllEntryForPaymentMethod() {
return PaymentMethod.getDummyPaymentMethod(GUIUtil.SHOW_ALL_FLAG);
}
}

View file

@ -1616,6 +1616,7 @@ message PreferencesPayload {
repeated AutoConfirmSettings auto_confirm_settings = 56;
double bsq_average_trim_threshold = 57;
bool hide_non_account_payment_methods = 58;
bool show_offers_matching_my_accounts = 59;
}
message AutoConfirmSettings {