Skip to content

Commit

Permalink
Merge pull request #2676 from axpoems/add-payments-filter
Browse files Browse the repository at this point in the history
Add payments filter in offer list
  • Loading branch information
HenrikJannsen authored Aug 25, 2024
2 parents 7274656 + 891e78a commit 9f6fcb7
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,7 @@ private void attachListeners() {
// Once the contextMenu has calculated the width on the first render time we update the items
// so that they all have the same size.
for (MenuItem item : contextMenu.getItems()) {
if (item instanceof DropdownMenuItem) {
DropdownMenuItem dropdownMenuItem = (DropdownMenuItem) item;
if (item instanceof DropdownMenuItem dropdownMenuItem) {
dropdownMenuItem.updateWidth(contextMenu.getWidth() - 18); // Remove margins
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
import javafx.scene.control.CustomMenuItem;
import lombok.Getter;

@Getter
public class DropdownMenuItem extends CustomMenuItem {
@Getter
private final BisqMenuItem bisqMenuItem;

public DropdownMenuItem(String defaultIconId, String activeIconId, String text) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

package bisq.desktop.main.content.bisq_easy.offerbook.offerbook_list;

import bisq.account.payment_method.FiatPaymentMethod;
import bisq.account.payment_method.FiatPaymentMethodUtil;
import bisq.bonded_roles.market_price.MarketPriceService;
import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookChannel;
import bisq.chat.bisqeasy.offerbook.BisqEasyOfferbookMessage;
Expand Down Expand Up @@ -48,7 +50,7 @@ public class OfferbookListController implements bisq.desktop.common.view.Control
private final MarketPriceService marketPriceService;
private final ReputationService reputationService;
private Pin showBuyOffersPin, showOfferListExpandedSettingsPin, offerMessagesPin;
private Subscription showBuyOffersFromModelPin;
private Subscription showBuyOffersFromModelPin, activeMarketPaymentsCountPin;

public OfferbookListController(ServiceProvider serviceProvider,
ChatMessageContainerController chatMessageContainerController) {
Expand All @@ -69,10 +71,12 @@ public ReadOnlyBooleanProperty getShowOfferListExpanded() {
public void onActivate() {
showBuyOffersPin = FxBindings.bindBiDir(model.getShowBuyOffers()).to(settingsService.getShowBuyOffers());
showOfferListExpandedSettingsPin = FxBindings.bindBiDir(model.getShowOfferListExpanded()).to(settingsService.getShowOfferListExpanded());
showBuyOffersFromModelPin = EasyBind.subscribe(model.getShowBuyOffers(), showBuyOffers ->
model.getFilteredOfferbookListItems().setPredicate(item ->
showBuyOffers == item.isBuyOffer()
));
showBuyOffersFromModelPin = EasyBind.subscribe(model.getShowBuyOffers(), showBuyOffers -> applyPredicate());
activeMarketPaymentsCountPin = EasyBind.subscribe(model.getActiveMarketPaymentsCount(), count -> {
String hint = count.intValue() == 0 ? Res.get("bisqEasy.offerbook.offerList.table.filters.paymentMethods.title.all") : count.toString();
model.getPaymentFilterTitle().set(Res.get("bisqEasy.offerbook.offerList.table.filters.paymentMethods.title", hint));
applyPredicate();
});
}

@Override
Expand All @@ -82,6 +86,7 @@ public void onDeactivate() {
showBuyOffersPin.unbind();
showOfferListExpandedSettingsPin.unbind();
showBuyOffersFromModelPin.unsubscribe();
activeMarketPaymentsCountPin.unsubscribe();
if (offerMessagesPin != null) {
offerMessagesPin.unbind();
}
Expand All @@ -96,6 +101,9 @@ public void setSelectedChannel(BisqEasyOfferbookChannel channel) {
model.getFiatAmountTitle().set(Res.get("bisqEasy.offerbook.offerList.table.columns.fiatAmount",
channel.getMarket().getQuoteCurrencyCode()).toUpperCase());

model.getAvailableMarketPayments().setAll(FiatPaymentMethodUtil.getPaymentMethods(channel.getMarket().getQuoteCurrencyCode()));
resetPaymentFilters();

offerMessagesPin = channel.getChatMessages().addObserver(new CollectionObserver<>() {
@Override
public void add(BisqEasyOfferbookMessage bisqEasyOfferbookMessage) {
Expand Down Expand Up @@ -158,4 +166,45 @@ void onSelectBuyFromFilter() {
void onSelectSellToFilter() {
model.getShowBuyOffers().set(true);
}

void toggleMethodFilter(FiatPaymentMethod paymentMethod, boolean isSelected) {
if (isSelected) {
model.getSelectedMarketPayments().add(paymentMethod);
} else {
model.getSelectedMarketPayments().remove(paymentMethod);
}
updateActiveMarketPaymentsCount();
}

void toggleCustomMethodFilter(boolean isSelected) {
model.getIsCustomPaymentsSelected().set(isSelected);
updateActiveMarketPaymentsCount();
}

private void resetPaymentFilters() {
model.getSelectedMarketPayments().clear();
model.getIsCustomPaymentsSelected().set(false);
updateActiveMarketPaymentsCount();
}

private void updateActiveMarketPaymentsCount() {
int count = model.getSelectedMarketPayments().size();
if (model.getIsCustomPaymentsSelected().get()) {
++count;
}
model.getActiveMarketPaymentsCount().set(count);
}

private void applyPredicate() {
model.getFilteredOfferbookListItems().setPredicate(this::shouldShowListItem);
}

private boolean shouldShowListItem(OfferbookListItem item) {
boolean matchesDirection = model.getShowBuyOffers().get() == item.isBuyOffer();
boolean paymentFiltersApplied = model.getActiveMarketPaymentsCount().get() != 0;
boolean matchesPaymentFilters = paymentFiltersApplied && item.getFiatPaymentMethods().stream()
.anyMatch(payment -> (payment.isCustomPaymentMethod() && model.getIsCustomPaymentsSelected().get())
|| model.getSelectedMarketPayments().contains(payment));
return matchesDirection && (!paymentFiltersApplied || matchesPaymentFilters);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ private void updatePriceSpecAsPercent() {
}
}


private List<FiatPaymentMethod> retrieveAndSortFiatPaymentMethods() {
List<FiatPaymentMethod> paymentMethods =
PaymentMethodSpecUtil.getPaymentMethods(bisqEasyOffer.getQuoteSidePaymentMethodSpecs());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@

package bisq.desktop.main.content.bisq_easy.offerbook.offerbook_list;

import bisq.account.payment_method.FiatPaymentMethod;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import lombok.Getter;
Expand All @@ -35,6 +39,11 @@ class OfferbookListModel implements bisq.desktop.common.view.Model {
private final StringProperty fiatAmountTitle = new SimpleStringProperty();
private final BooleanProperty showBuyOffers = new SimpleBooleanProperty();
private final BooleanProperty showOfferListExpanded = new SimpleBooleanProperty();
private final StringProperty paymentFilterTitle = new SimpleStringProperty();
private final ObservableList<FiatPaymentMethod> availableMarketPayments = FXCollections.observableArrayList();
private final ObservableSet<FiatPaymentMethod> selectedMarketPayments = FXCollections.observableSet();
private final BooleanProperty isCustomPaymentsSelected = new SimpleBooleanProperty();
private final IntegerProperty activeMarketPaymentsCount = new SimpleIntegerProperty();

OfferbookListModel() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
import bisq.desktop.main.content.components.UserProfileIcon;
import bisq.i18n.Res;
import com.google.common.base.Joiner;
import javafx.collections.ListChangeListener;
import javafx.css.PseudoClass;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
Expand Down Expand Up @@ -62,13 +64,15 @@ public class OfferbookListView extends bisq.desktop.common.view.View<VBox, Offer
private static final double HEADER_HEIGHT = BaseChatView.HEADER_HEIGHT;
private static final double LIST_CELL_HEIGHT = BisqEasyOfferbookView.LIST_CELL_HEIGHT;

private final Label title, offerListByDirectionFilter;
private final Label title;
private final BisqTableView<OfferbookListItem> tableView;
private final BisqTooltip titleTooltip;
private final HBox header;
private final ImageView offerListWhiteIcon, offerListGreyIcon, offerListGreenIcon;
private final DropdownMenu filterDropdownMenu;
private final DropdownMenuItem buyFromOffers, sellToOffers;
private final DropdownMenu offerDirectionFilterMenu, paymentsFilterMenu;
private final ListChangeListener<FiatPaymentMethod> listChangeListener;
private DropdownMenuItem buyFromOffers, sellToOffers;
private Label offerDirectionFilterLabel, paymentsFilterLabel;
private Subscription showOfferListExpandedPin, showBuyFromOffersPin, offerListTableViewSelectionPin;

OfferbookListView(OfferbookListModel model, OfferbookListController controller) {
Expand All @@ -90,18 +94,14 @@ public class OfferbookListView extends bisq.desktop.common.view.View<VBox, Offer
header.setMaxHeight(HEADER_HEIGHT);
header.getStyleClass().add("chat-header-title");

filterDropdownMenu = new DropdownMenu("chevron-drop-menu-grey", "chevron-drop-menu-white", false);
filterDropdownMenu.getStyleClass().add("dropdown-offer-list-direction-filter-menu");
offerListByDirectionFilter = new Label();
filterDropdownMenu.setLabel(offerListByDirectionFilter);
buyFromOffers = new DropdownMenuItem(Res.get("bisqEasy.offerbook.offerList.table.filters.offerDirection.buyFrom"));
sellToOffers = new DropdownMenuItem(Res.get("bisqEasy.offerbook.offerList.table.filters.offerDirection.sellTo"));
filterDropdownMenu.addMenuItems(buyFromOffers, sellToOffers);
listChangeListener = change -> updateMarketPaymentFilters();
offerDirectionFilterMenu = createAndGetOffersDirectionFilterMenu();
paymentsFilterMenu = createAndGetPaymentsFilterDropdownMenu();

HBox subheader = new HBox();
HBox subheader = new HBox(10);
subheader.setAlignment(Pos.CENTER_LEFT);
subheader.getStyleClass().add("offer-list-subheader");
subheader.getChildren().add(filterDropdownMenu);
subheader.getChildren().addAll(offerDirectionFilterMenu, paymentsFilterMenu);

tableView = new BisqTableView<>(model.getSortedOfferbookListItems());
tableView.getStyleClass().add("offers-list");
Expand All @@ -117,12 +117,14 @@ public class OfferbookListView extends bisq.desktop.common.view.View<VBox, Offer

@Override
protected void onViewAttached() {
paymentsFilterLabel.textProperty().bind(model.getPaymentFilterTitle());

showOfferListExpandedPin = EasyBind.subscribe(model.getShowOfferListExpanded(), showOfferListExpanded -> {
if (showOfferListExpanded != null) {
tableView.setVisible(showOfferListExpanded);
tableView.setManaged(showOfferListExpanded);
filterDropdownMenu.setVisible(showOfferListExpanded);
filterDropdownMenu.setManaged(showOfferListExpanded);
offerDirectionFilterMenu.setVisible(showOfferListExpanded);
offerDirectionFilterMenu.setManaged(showOfferListExpanded);
title.setGraphic(offerListGreyIcon);
if (showOfferListExpanded) {
header.setAlignment(Pos.CENTER_LEFT);
Expand Down Expand Up @@ -161,17 +163,20 @@ protected void onViewAttached() {

showBuyFromOffersPin = EasyBind.subscribe(model.getShowBuyOffers(), showBuyFromOffers -> {
if (showBuyFromOffers != null) {
offerListByDirectionFilter.getStyleClass().clear();
offerDirectionFilterLabel.getStyleClass().clear();
if (showBuyFromOffers) {
offerListByDirectionFilter.setText(sellToOffers.getLabelText());
offerListByDirectionFilter.getStyleClass().add("sell-to-offers");
offerDirectionFilterLabel.setText(sellToOffers.getLabelText());
offerDirectionFilterLabel.getStyleClass().add("sell-to-offers");
} else {
offerListByDirectionFilter.setText(buyFromOffers.getLabelText());
offerListByDirectionFilter.getStyleClass().add("buy-from-offers");
offerDirectionFilterLabel.setText(buyFromOffers.getLabelText());
offerDirectionFilterLabel.getStyleClass().add("buy-from-offers");
}
}
});

model.getAvailableMarketPayments().addListener(listChangeListener);
updateMarketPaymentFilters();

title.setOnMouseEntered(e -> title.setGraphic(offerListWhiteIcon));
title.setOnMouseClicked(e -> controller.toggleOfferList());
buyFromOffers.setOnAction(e -> controller.onSelectBuyFromFilter());
Expand All @@ -182,17 +187,72 @@ protected void onViewAttached() {

@Override
protected void onViewDetached() {
paymentsFilterLabel.textProperty().unbind();

showOfferListExpandedPin.unsubscribe();
offerListTableViewSelectionPin.unsubscribe();
showBuyFromOffersPin.unsubscribe();

model.getAvailableMarketPayments().removeListener(listChangeListener);

title.setOnMouseEntered(null);
title.setOnMouseExited(null);
title.setOnMouseClicked(null);
buyFromOffers.setOnAction(null);
sellToOffers.setOnAction(null);

title.setTooltip(null);

cleanUpPaymentsFilterMenu();
}

private DropdownMenu createAndGetOffersDirectionFilterMenu() {
DropdownMenu menu = new DropdownMenu("chevron-drop-menu-grey", "chevron-drop-menu-white", false);
menu.getStyleClass().add("dropdown-offer-list-direction-filter-menu");
menu.setOpenToTheRight(true);
offerDirectionFilterLabel = new Label();
menu.setLabel(offerDirectionFilterLabel);
buyFromOffers = new DropdownMenuItem(Res.get("bisqEasy.offerbook.offerList.table.filters.offerDirection.buyFrom"));
sellToOffers = new DropdownMenuItem(Res.get("bisqEasy.offerbook.offerList.table.filters.offerDirection.sellTo"));
menu.addMenuItems(buyFromOffers, sellToOffers);
return menu;
}

private DropdownMenu createAndGetPaymentsFilterDropdownMenu() {
DropdownMenu menu = new DropdownMenu("chevron-drop-menu-grey", "chevron-drop-menu-white", false);
menu.getStyleClass().add("dropdown-offer-list-payment-filter-menu");
menu.setOpenToTheRight(true);
paymentsFilterLabel = new Label();
menu.setLabel(paymentsFilterLabel);
return menu;
}

private void updateMarketPaymentFilters() {
cleanUpPaymentsFilterMenu();

model.getAvailableMarketPayments().forEach(payment -> {
PaymentMenuItem item = new PaymentMenuItem(payment.getDisplayString());
item.setOnAction(e -> {
item.updateSelection(!item.isSelected());
controller.toggleMethodFilter(payment, item.isSelected());
});
paymentsFilterMenu.addMenuItems(item);
});

PaymentMenuItem customItem = new PaymentMenuItem(Res.get("bisqEasy.offerbook.offerList.table.filters.paymentMethods.customMethod"));
customItem.setOnAction(e -> {
customItem.updateSelection(!customItem.isSelected());
controller.toggleCustomMethodFilter(customItem.isSelected());
});
paymentsFilterMenu.addMenuItems(customItem);
}

private void cleanUpPaymentsFilterMenu() {
paymentsFilterMenu.getMenuItems().stream()
.filter(item -> item instanceof PaymentMenuItem)
.map(item -> (PaymentMenuItem) item)
.forEach(PaymentMenuItem::dispose);
paymentsFilterMenu.clearMenuItems();
}

private void configOffersTableView() {
Expand Down Expand Up @@ -249,7 +309,6 @@ private void configOffersTableView() {
.build());
}


private Callback<TableColumn<OfferbookListItem, OfferbookListItem>,
TableCell<OfferbookListItem, OfferbookListItem>> getUserProfileCellFactory() {
return column -> new TableCell<>() {
Expand Down Expand Up @@ -406,4 +465,32 @@ protected void updateItem(OfferbookListItem item, boolean empty) {
}
};
}

private static final class PaymentMenuItem extends DropdownMenuItem {
private static final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected");

PaymentMenuItem(String displayName) {
// TODO: Update code so that we can pass label instead of text
super("check-white", "check-white", displayName);

getStyleClass().add("dropdown-menu-item");
updateSelection(false);
initialize();
}

public void initialize() {
}

public void dispose() {
setOnAction(null);
}

void updateSelection(boolean isSelected) {
getContent().pseudoClassStateChanged(SELECTED_PSEUDO_CLASS, isSelected);
}

boolean isSelected() {
return getContent().getPseudoClassStates().contains(SELECTED_PSEUDO_CLASS);
}
}
}
5 changes: 5 additions & 0 deletions apps/desktop/desktop/src/main/resources/css/bisq_easy.css
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,11 @@
-fx-text-fill: -bisq2-red-lit-40;
}

/* PAYMENT METHODS FILTER */
.dropdown-offer-list-payment-filter-menu {
-fx-padding: 0 5 0 5;
}

/* COLLAPSE AND EXPAND COLUMNS */
.collapsed-offer-list-container {
-fx-background-color: -bisq-dark-grey-20;
Expand Down
3 changes: 3 additions & 0 deletions i18n/src/main/resources/bisq_easy.properties
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,9 @@ bisqEasy.offerbook.offerList.table.columns.paymentMethod=Payment
bisqEasy.offerbook.offerList.table.columns.settlementMethod=Settlement
bisqEasy.offerbook.offerList.table.filters.offerDirection.buyFrom=Buy from
bisqEasy.offerbook.offerList.table.filters.offerDirection.sellTo=Sell to
bisqEasy.offerbook.offerList.table.filters.paymentMethods.title=Payments ({0})
bisqEasy.offerbook.offerList.table.filters.paymentMethods.title.all=All
bisqEasy.offerbook.offerList.table.filters.paymentMethods.customMethod=Custom methods

bisqEasy.offerbook.offerList.table.columns.price.tooltip.fixPrice=Fixed price: {0}\nPercentage from current market price: {1}
bisqEasy.offerbook.offerList.table.columns.price.tooltip.marketPrice=Market price: {0}
Expand Down

0 comments on commit 9f6fcb7

Please sign in to comment.