From 1606439144a4d98e81448ea61e0fdfbb0629e094 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Thu, 2 Dec 2021 13:39:11 +0100 Subject: [PATCH 1/5] Add support to duplicate bsq swap offers --- .../offer/bsq_swap/BsqSwapOfferModel.java | 13 ++++++- .../offer/bsq_swap/BsqSwapTakeOfferModel.java | 10 +----- .../bisq/desktop/main/offer/OfferView.java | 9 +++-- .../BsqSwapCreateOfferDataModel.java | 5 +-- .../create_offer/BsqSwapCreateOfferView.java | 8 +++-- .../BsqSwapCreateOfferViewModel.java | 34 +++++++++++++++++-- .../desktop/main/portfolio/PortfolioView.java | 18 ++++++++-- .../duplicateoffer/DuplicateOfferView.java | 4 +-- .../DuplicateOfferViewModel.java | 4 +-- 9 files changed, 79 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/bisq/core/offer/bsq_swap/BsqSwapOfferModel.java b/core/src/main/java/bisq/core/offer/bsq_swap/BsqSwapOfferModel.java index 33b3f963cd..9046363963 100644 --- a/core/src/main/java/bisq/core/offer/bsq_swap/BsqSwapOfferModel.java +++ b/core/src/main/java/bisq/core/offer/bsq_swap/BsqSwapOfferModel.java @@ -25,6 +25,7 @@ import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.monetary.Price; import bisq.core.monetary.Volume; +import bisq.core.offer.Offer; import bisq.core.offer.OfferDirection; import bisq.core.offer.OfferUtil; import bisq.core.payment.payload.PaymentMethod; @@ -124,10 +125,20 @@ public class BsqSwapOfferModel { // API /////////////////////////////////////////////////////////////////////////////////////////// - public void init(OfferDirection direction, boolean isMaker) { + public void init(OfferDirection direction, boolean isMaker, Offer offer) { this.direction = direction; this.isMaker = isMaker; + if (offer != null) { + setPrice(offer.getPrice()); + + setBtcAmount(Coin.valueOf(Math.min(offer.getAmount().value, getMaxTradeLimit()))); + calculateVolumeForAmount(getBtcAmount()); + + setMinAmount(offer.getMinAmount()); + calculateMinVolume(); + } + createListeners(); applyTxFeePerVbyte(); diff --git a/core/src/main/java/bisq/core/offer/bsq_swap/BsqSwapTakeOfferModel.java b/core/src/main/java/bisq/core/offer/bsq_swap/BsqSwapTakeOfferModel.java index 266cc2a856..7988186840 100644 --- a/core/src/main/java/bisq/core/offer/bsq_swap/BsqSwapTakeOfferModel.java +++ b/core/src/main/java/bisq/core/offer/bsq_swap/BsqSwapTakeOfferModel.java @@ -67,17 +67,9 @@ public class BsqSwapTakeOfferModel extends BsqSwapOfferModel { /////////////////////////////////////////////////////////////////////////////////////////// public void initWithData(Offer offer) { - super.init(offer.getDirection(), false); + super.init(offer.getDirection(), false, offer); this.offer = offer; - setPrice(offer.getPrice()); - - setBtcAmount(Coin.valueOf(Math.min(offer.getAmount().value, getMaxTradeLimit()))); - calculateVolumeForAmount(getBtcAmount()); - - setMinAmount(offer.getMinAmount()); - calculateMinVolume(); - offer.resetState(); } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java index cb61fe568b..1aad98f66a 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/OfferView.java @@ -37,6 +37,7 @@ import bisq.core.locale.Res; import bisq.core.locale.TradeCurrency; import bisq.core.offer.Offer; import bisq.core.offer.OfferDirection; +import bisq.core.offer.bsq_swap.BsqSwapOfferPayload; import bisq.core.payment.payload.PaymentMethod; import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager; import bisq.core.user.Preferences; @@ -56,6 +57,8 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import javax.annotation.Nullable; + public abstract class OfferView extends ActivatableView { private OfferBookView offerBookView; @@ -102,7 +105,7 @@ public abstract class OfferView extends ActivatableView { protected void initialize() { navigationListener = (viewPath, data) -> { if (viewPath.size() == 3 && viewPath.indexOf(this.getClass()) == 1) - loadView(viewPath.tip()); + loadView(viewPath.tip(), data); }; tabChangeListener = (observableValue, oldValue, newValue) -> { if (newValue != null) { @@ -198,7 +201,7 @@ public abstract class OfferView extends ActivatableView { return Res.get("offerbook.takeOffer").toUpperCase(); } - private void loadView(Class viewClass) { + private void loadView(Class viewClass, @Nullable Object data) { TabPane tabPane = root; tabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.ALL_TABS); View view; @@ -237,7 +240,7 @@ public abstract class OfferView extends ActivatableView { } else if (viewClass == BsqSwapCreateOfferView.class && bsqSwapCreateOfferView == null) { view = viewLoader.load(viewClass); bsqSwapCreateOfferView = (BsqSwapCreateOfferView) view; - bsqSwapCreateOfferView.initWithData(direction, offerActionHandler); + bsqSwapCreateOfferView.initWithData(direction, offerActionHandler, (BsqSwapOfferPayload) data); createOfferPane = bsqSwapCreateOfferView.getRoot(); createOfferTab = new Tab(getCreateOfferTabName(viewClass)); createOfferTab.setClosable(true); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferDataModel.java index b17cccf725..57ba523b0b 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferDataModel.java @@ -28,6 +28,7 @@ import bisq.core.offer.Offer; import bisq.core.offer.OfferDirection; import bisq.core.offer.OfferUtil; import bisq.core.offer.bsq_swap.BsqSwapOfferModel; +import bisq.core.offer.bsq_swap.BsqSwapOfferPayload; import bisq.core.offer.bsq_swap.OpenBsqSwapOfferService; import bisq.core.payment.PaymentAccount; import bisq.core.user.User; @@ -91,8 +92,8 @@ class BsqSwapCreateOfferDataModel extends BsqSwapOfferDataModel { // API /////////////////////////////////////////////////////////////////////////////////////////// - void initWithData(OfferDirection direction) { - bsqSwapOfferModel.init(direction, true); + void initWithData(OfferDirection direction, BsqSwapOfferPayload offerPayload) { + bsqSwapOfferModel.init(direction, true, offerPayload != null ? new Offer(offerPayload) : null); fillPaymentAccounts(); applyPaymentAccount(); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferView.java index 16e4210b94..db13cc9d3c 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferView.java @@ -37,6 +37,7 @@ import bisq.desktop.util.Layout; import bisq.core.locale.CurrencyUtil; import bisq.core.locale.Res; import bisq.core.offer.OfferDirection; +import bisq.core.offer.bsq_swap.BsqSwapOfferPayload; import bisq.core.payment.PaymentAccount; import bisq.core.user.DontShowAgainLookup; @@ -159,9 +160,12 @@ public class BsqSwapCreateOfferView extends BsqSwapOfferView btcMinAmount = dataModel.getMinAmount(); + if (btcMinAmount.get() != null) { + minAmountAsCoinListener.changed(btcMinAmount, null, btcMinAmount.get()); + } + + ObjectProperty btcAmount = dataModel.getBtcAmount(); + + if (btcAmount.get() != null && btcAmount.get() != null) { + syncMinAmountWithAmount = btcMinAmount.get().equals(dataModel.getBtcAmount().get()); + } + + if (btcAmount.get() != null) { + amountAsCoinListener.changed(btcAmount, null, btcAmount.get()); + } + + ObjectProperty price = dataModel.getPrice(); + if (price.get() != null) { + priceListener.changed(price, null, price.get()); + } + + ObjectProperty volume = dataModel.getVolume(); + if (volume.get() != null) { + volumeListener.changed(volume, null, volume.get()); + } + } + private void stopTimeoutTimer() { if (timeoutTimer != null) { timeoutTimer.stop(); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/PortfolioView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/PortfolioView.java index 08ed465c05..84bad05342 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/PortfolioView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/PortfolioView.java @@ -23,6 +23,9 @@ import bisq.desktop.common.view.CachingViewLoader; import bisq.desktop.common.view.FxmlView; import bisq.desktop.common.view.View; import bisq.desktop.main.MainView; +import bisq.desktop.main.offer.BuyOfferView; +import bisq.desktop.main.offer.SellOfferView; +import bisq.desktop.main.offer.bsq_swap.create_offer.BsqSwapCreateOfferView; import bisq.desktop.main.portfolio.bsqswaps.UnconfirmedBsqSwapsView; import bisq.desktop.main.portfolio.closedtrades.ClosedTradesView; import bisq.desktop.main.portfolio.duplicateoffer.DuplicateOfferView; @@ -32,8 +35,10 @@ import bisq.desktop.main.portfolio.openoffer.OpenOffersView; import bisq.desktop.main.portfolio.pendingtrades.PendingTradesView; import bisq.core.locale.Res; +import bisq.core.offer.OfferDirection; +import bisq.core.offer.OfferPayloadBase; import bisq.core.offer.OpenOffer; -import bisq.core.offer.bisq_v1.OfferPayload; +import bisq.core.offer.bsq_swap.BsqSwapOfferPayload; import bisq.core.trade.bisq_v1.FailedTradesManager; import bisq.core.trade.model.bisq_v1.Trade; @@ -228,11 +233,18 @@ public class PortfolioView extends ActivatableView { selectOpenOffersView((OpenOffersView) view); } } else if (view instanceof DuplicateOfferView) { - if (duplicateOfferView == null && data instanceof OfferPayload) { + if (duplicateOfferView == null && data instanceof OfferPayloadBase) { + // Switch to create BSQ swap offer + if (data instanceof BsqSwapOfferPayload) { + var offerViewClass = ((BsqSwapOfferPayload) data).getDirection() == OfferDirection.BUY ? BuyOfferView.class : SellOfferView.class; + navigation.navigateToWithData(data, MainView.class, offerViewClass, BsqSwapCreateOfferView.class); + return; + } + viewLoader.removeFromCache(viewClass); // remove cached dialog view = viewLoader.load(viewClass); // and load a fresh one duplicateOfferView = (DuplicateOfferView) view; - duplicateOfferView.initWithData((OfferPayload) data); + duplicateOfferView.initWithData((OfferPayloadBase) data); duplicateOfferTab = new Tab(Res.get("portfolio.tab.duplicateOffer").toUpperCase()); duplicateOfferView.setCloseHandler(() -> { root.getTabs().remove(duplicateOfferTab); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/duplicateoffer/DuplicateOfferView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/duplicateoffer/DuplicateOfferView.java index db6eb38ba1..5fdad85116 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/duplicateoffer/DuplicateOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/duplicateoffer/DuplicateOfferView.java @@ -23,7 +23,7 @@ import bisq.desktop.main.offer.bisq_v1.MutableOfferView; import bisq.desktop.main.overlays.windows.OfferDetailsWindow; import bisq.core.locale.CurrencyUtil; -import bisq.core.offer.bisq_v1.OfferPayload; +import bisq.core.offer.OfferPayloadBase; import bisq.core.user.Preferences; import bisq.core.util.FormattingUtils; import bisq.core.util.coin.BsqFormatter; @@ -61,7 +61,7 @@ public class DuplicateOfferView extends MutableOfferView Date: Fri, 3 Dec 2021 11:25:24 +0100 Subject: [PATCH 2/5] Add support to duplicate bsq swap offers also in History view --- .../main/portfolio/closedtrades/ClosedTradesView.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 8a3b5980b2..cdf86b8249 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 @@ -39,8 +39,8 @@ import bisq.desktop.util.GUIUtil; import bisq.core.alert.PrivateNotificationManager; import bisq.core.locale.Res; import bisq.core.offer.Offer; +import bisq.core.offer.OfferPayloadBase; import bisq.core.offer.OpenOffer; -import bisq.core.offer.bisq_v1.OfferPayload; import bisq.core.trade.model.Tradable; import bisq.core.trade.model.TradeModel; import bisq.core.trade.model.bisq_v1.Contract; @@ -265,9 +265,9 @@ public class ClosedTradesView extends ActivatableViewAndModel { try { - OfferPayload offerPayload = row.getItem().getOffer().getOfferPayload().orElseThrow(); - if (offerPayload.getPubKeyRing().equals(keyRing.getPubKeyRing())) { - navigation.navigateToWithData(offerPayload, MainView.class, PortfolioView.class, DuplicateOfferView.class); + OfferPayloadBase offerPayloadBase = row.getItem().getOffer().getOfferPayloadBase(); + if (offerPayloadBase.getPubKeyRing().equals(keyRing.getPubKeyRing())) { + navigation.navigateToWithData(offerPayloadBase, MainView.class, PortfolioView.class, DuplicateOfferView.class); } else { new Popup().warning(Res.get("portfolio.context.notYourOffer")).show(); } From daf762f8faab8bd9722cb1c6faee89bd8611d599 Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Fri, 3 Dec 2021 12:02:10 +0100 Subject: [PATCH 3/5] Add visual icon for duplicate offer functionality --- .../resources/i18n/displayStrings.properties | 1 + .../closedtrades/ClosedTradesView.fxml | 1 + .../closedtrades/ClosedTradesView.java | 80 +++++++++++++++---- .../portfolio/openoffer/OpenOffersView.fxml | 1 + .../portfolio/openoffer/OpenOffersView.java | 65 +++++++++++---- 5 files changed, 116 insertions(+), 32 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 1cc275d92b..fd502b9fdd 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -97,6 +97,7 @@ shared.BTCMinMax=BTC (min - max) shared.removeOffer=Remove offer shared.dontRemoveOffer=Don't remove offer shared.editOffer=Edit offer +shared.duplicateOffer=Duplicate offer shared.openLargeQRWindow=Open large QR code window shared.tradingAccount=Trading account shared.faq=Visit FAQ page 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 5e0f1cd0f1..0bdd24821a 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 @@ -55,6 +55,7 @@ + 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 cdf86b8249..48abfa3730 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 @@ -60,11 +60,14 @@ import com.googlecode.jcsv.writer.CSVEntryConverter; import javax.inject.Inject; import javax.inject.Named; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; + import javafx.fxml.FXML; import javafx.stage.Stage; import javafx.scene.Node; +import javafx.scene.control.Button; import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; @@ -85,6 +88,8 @@ import javafx.beans.binding.Bindings; import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.beans.value.ChangeListener; +import javafx.event.ActionEvent; + import javafx.collections.transformation.FilteredList; import javafx.collections.transformation.SortedList; @@ -94,6 +99,8 @@ import java.util.Comparator; import java.util.Date; import java.util.function.Function; +import static bisq.desktop.util.FormBuilder.getRegularIconButton; + @FxmlView public class ClosedTradesView extends ActivatableViewAndModel { private final boolean useDevPrivilegeKeys; @@ -132,7 +139,8 @@ public class ClosedTradesView extends ActivatableViewAndModel priceColumn, deviationColumn, amountColumn, volumeColumn, txFeeColumn, tradeFeeColumn, buyerSecurityDepositColumn, sellerSecurityDepositColumn, - marketColumn, directionColumn, dateColumn, tradeIdColumn, stateColumn, avatarColumn; + marketColumn, directionColumn, dateColumn, tradeIdColumn, stateColumn, + duplicateColumn, avatarColumn; @FXML HBox searchBox; @FXML @@ -198,6 +206,7 @@ public class ClosedTradesView extends ActivatableViewAndModel o.getId())); - dateColumn.setComparator(Comparator.comparing(o -> o.getDate())); + 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()))); @@ -229,7 +239,7 @@ public class ClosedTradesView extends ActivatableViewAndModel model.dataModel.getNumPastTrades(o), + model.dataModel::getNumPastTrades, Comparator.nullsFirst(Comparator.naturalOrder()) )); txFeeColumn.setComparator(nullsFirstComparing(o -> @@ -263,18 +273,7 @@ public class ClosedTradesView extends ActivatableViewAndModel row = new TableRow<>(); ContextMenu rowMenu = new ContextMenu(); MenuItem editItem = new MenuItem(Res.get("portfolio.context.offerLikeThis")); - editItem.setOnAction((event) -> { - try { - OfferPayloadBase offerPayloadBase = row.getItem().getOffer().getOfferPayloadBase(); - if (offerPayloadBase.getPubKeyRing().equals(keyRing.getPubKeyRing())) { - navigation.navigateToWithData(offerPayloadBase, MainView.class, PortfolioView.class, DuplicateOfferView.class); - } else { - new Popup().warning(Res.get("portfolio.context.notYourOffer")).show(); - } - } catch (NullPointerException e) { - log.warn("Unable to get offerPayload - {}", e.toString()); - } - }); + editItem.setOnAction((ActionEvent event) -> onDuplicateOffer(row.getItem().getOffer())); rowMenu.getItems().add(editItem); row.contextMenuProperty().bind( Bindings.when(Bindings.isNotNull(row.itemProperty())) @@ -579,6 +578,40 @@ public class ClosedTradesView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(offerListItem.getValue())); + duplicateColumn.setCellFactory( + new Callback<>() { + @Override + public TableCell call(TableColumn column) { + return new TableCell<>() { + Button button; + + @Override + public void updateItem(final Tradable item, boolean empty) { + super.updateItem(item, empty); + + if (item != null && !empty) { + if (button == null) { + button = getRegularIconButton(MaterialDesignIcon.CONTENT_COPY); + button.setTooltip(new Tooltip(Res.get("shared.duplicateOffer"))); + setGraphic(button); + } + button.setOnAction(event -> onDuplicateOffer(item.getOffer())); + } else { + setGraphic(null); + if (button != null) { + button.setOnAction(null); + button = null; + } + } + } + }; + } + }); + } + @SuppressWarnings("UnusedReturnValue") private TableColumn setAvatarColumnCellFactory() { avatarColumn.getStyleClass().addAll("last-column", "avatar-column"); @@ -593,7 +626,7 @@ public class ClosedTradesView extends ActivatableViewAndModel + 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 692d0339a1..9a7df97e51 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 @@ -98,7 +98,7 @@ public class OpenOffersView extends ActivatableViewAndModel priceColumn, deviationColumn, amountColumn, volumeColumn, marketColumn, directionColumn, dateColumn, offerIdColumn, deactivateItemColumn, - removeItemColumn, editItemColumn, triggerPriceColumn, triggerIconColumn, paymentMethodColumn; + removeItemColumn, editItemColumn, triggerPriceColumn, triggerIconColumn, paymentMethodColumn, duplicateItemColumn; @FXML HBox searchBox; @FXML @@ -152,6 +152,7 @@ public class OpenOffersView extends ActivatableViewAndModel row = new TableRow<>(); final ContextMenu rowMenu = new ContextMenu(); MenuItem editItem = new MenuItem(Res.get("portfolio.context.offerLikeThis")); - editItem.setOnAction((event) -> { - try { - OfferPayloadBase offerPayloadBase = row.getItem().getOffer().getOfferPayloadBase(); - navigation.navigateToWithData(offerPayloadBase, MainView.class, PortfolioView.class, - DuplicateOfferView.class); - } catch (NullPointerException e) { - log.warn("Unable to get offerPayload - {}", e.toString()); - } - }); + editItem.setOnAction((event) -> onDuplicateOffer(row.getItem().getOffer())); rowMenu.getItems().add(editItem); row.contextMenuProperty().bind( Bindings.when(Bindings.isNotNull(row.itemProperty())) @@ -356,11 +350,8 @@ public class OpenOffersView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(openOfferListItem.getValue())); offerIdColumn.getStyleClass().addAll("number-column", "first-column"); @@ -784,6 +785,40 @@ public class OpenOffersView extends ActivatableViewAndModel new ReadOnlyObjectWrapper<>(offerListItem.getValue())); + duplicateItemColumn.setCellFactory( + new Callback<>() { + @Override + public TableCell call(TableColumn column) { + return new TableCell<>() { + Button button; + + @Override + public void updateItem(final OpenOfferListItem item, boolean empty) { + super.updateItem(item, empty); + + if (item != null && !empty) { + if (button == null) { + button = getRegularIconButton(MaterialDesignIcon.CONTENT_COPY); + button.setTooltip(new Tooltip(Res.get("shared.duplicateOffer"))); + setGraphic(button); + } + button.setOnAction(event -> onDuplicateOffer(item.getOffer())); + } else { + setGraphic(null); + if (button != null) { + button.setOnAction(null); + button = null; + } + } + } + }; + } + }); + } + private void setTriggerIconColumnCellFactory() { triggerIconColumn.setCellValueFactory((offerListItem) -> new ReadOnlyObjectWrapper<>(offerListItem.getValue())); triggerIconColumn.setCellFactory( From a1ecf350076cf0f71d692bfa6f478cd7d031f44b Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Mon, 6 Dec 2021 11:04:01 +0100 Subject: [PATCH 4/5] Apply suggestions and fix edge case when BSQ legacy payment account offer is duplicated --- .../offer/bsq_swap/BsqSwapOfferModel.java | 2 +- .../BsqSwapCreateOfferDataModel.java | 4 +- .../create_offer/BsqSwapCreateOfferView.java | 4 +- .../BsqSwapCreateOfferViewModel.java | 6 +- .../desktop/main/portfolio/PortfolioView.java | 18 +----- .../closedtrades/ClosedTradesView.java | 20 ++++--- .../DuplicateOfferDataModel.java | 55 ++++++++++++++----- .../duplicateoffer/DuplicateOfferView.java | 4 +- .../DuplicateOfferViewModel.java | 4 +- .../portfolio/openoffer/OpenOffersView.java | 15 ++--- .../pendingtrades/PendingTradesView.java | 11 ++-- .../portfolio/presentation/PortfolioUtil.java | 42 ++++++++++++++ 12 files changed, 124 insertions(+), 61 deletions(-) create mode 100644 desktop/src/main/java/bisq/desktop/main/portfolio/presentation/PortfolioUtil.java diff --git a/core/src/main/java/bisq/core/offer/bsq_swap/BsqSwapOfferModel.java b/core/src/main/java/bisq/core/offer/bsq_swap/BsqSwapOfferModel.java index 9046363963..0d8776392f 100644 --- a/core/src/main/java/bisq/core/offer/bsq_swap/BsqSwapOfferModel.java +++ b/core/src/main/java/bisq/core/offer/bsq_swap/BsqSwapOfferModel.java @@ -125,7 +125,7 @@ public class BsqSwapOfferModel { // API /////////////////////////////////////////////////////////////////////////////////////////// - public void init(OfferDirection direction, boolean isMaker, Offer offer) { + public void init(OfferDirection direction, boolean isMaker, @Nullable Offer offer) { this.direction = direction; this.isMaker = isMaker; diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferDataModel.java index 57ba523b0b..13d9c52b6d 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferDataModel.java @@ -53,6 +53,8 @@ import java.util.function.Consumer; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import javax.annotation.Nullable; + import static com.google.common.base.Preconditions.checkArgument; import static java.util.Comparator.comparing; @@ -92,7 +94,7 @@ class BsqSwapCreateOfferDataModel extends BsqSwapOfferDataModel { // API /////////////////////////////////////////////////////////////////////////////////////////// - void initWithData(OfferDirection direction, BsqSwapOfferPayload offerPayload) { + void initWithData(OfferDirection direction, @Nullable BsqSwapOfferPayload offerPayload) { bsqSwapOfferModel.init(direction, true, offerPayload != null ? new Offer(offerPayload) : null); fillPaymentAccounts(); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferView.java b/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferView.java index db13cc9d3c..ec4037eeb9 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/bsq_swap/create_offer/BsqSwapCreateOfferView.java @@ -75,6 +75,8 @@ import java.util.concurrent.TimeUnit; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import javax.annotation.Nullable; + import static bisq.core.offer.bsq_swap.BsqSwapOfferModel.BSQ; import static bisq.desktop.util.FormBuilder.*; @@ -162,7 +164,7 @@ public class BsqSwapCreateOfferView extends BsqSwapOfferView btcAmount = dataModel.getBtcAmount(); - if (btcAmount.get() != null && btcAmount.get() != null) { + if (btcAmount.get() != null && btcMinAmount.get() != null) { syncMinAmountWithAmount = btcMinAmount.get().equals(dataModel.getBtcAmount().get()); } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/PortfolioView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/PortfolioView.java index 84bad05342..08ed465c05 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/PortfolioView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/PortfolioView.java @@ -23,9 +23,6 @@ import bisq.desktop.common.view.CachingViewLoader; import bisq.desktop.common.view.FxmlView; import bisq.desktop.common.view.View; import bisq.desktop.main.MainView; -import bisq.desktop.main.offer.BuyOfferView; -import bisq.desktop.main.offer.SellOfferView; -import bisq.desktop.main.offer.bsq_swap.create_offer.BsqSwapCreateOfferView; import bisq.desktop.main.portfolio.bsqswaps.UnconfirmedBsqSwapsView; import bisq.desktop.main.portfolio.closedtrades.ClosedTradesView; import bisq.desktop.main.portfolio.duplicateoffer.DuplicateOfferView; @@ -35,10 +32,8 @@ import bisq.desktop.main.portfolio.openoffer.OpenOffersView; import bisq.desktop.main.portfolio.pendingtrades.PendingTradesView; import bisq.core.locale.Res; -import bisq.core.offer.OfferDirection; -import bisq.core.offer.OfferPayloadBase; import bisq.core.offer.OpenOffer; -import bisq.core.offer.bsq_swap.BsqSwapOfferPayload; +import bisq.core.offer.bisq_v1.OfferPayload; import bisq.core.trade.bisq_v1.FailedTradesManager; import bisq.core.trade.model.bisq_v1.Trade; @@ -233,18 +228,11 @@ public class PortfolioView extends ActivatableView { selectOpenOffersView((OpenOffersView) view); } } else if (view instanceof DuplicateOfferView) { - if (duplicateOfferView == null && data instanceof OfferPayloadBase) { - // Switch to create BSQ swap offer - if (data instanceof BsqSwapOfferPayload) { - var offerViewClass = ((BsqSwapOfferPayload) data).getDirection() == OfferDirection.BUY ? BuyOfferView.class : SellOfferView.class; - navigation.navigateToWithData(data, MainView.class, offerViewClass, BsqSwapCreateOfferView.class); - return; - } - + if (duplicateOfferView == null && data instanceof OfferPayload) { viewLoader.removeFromCache(viewClass); // remove cached dialog view = viewLoader.load(viewClass); // and load a fresh one duplicateOfferView = (DuplicateOfferView) view; - duplicateOfferView.initWithData((OfferPayloadBase) data); + duplicateOfferView.initWithData((OfferPayload) data); duplicateOfferTab = new Tab(Res.get("portfolio.tab.duplicateOffer").toUpperCase()); duplicateOfferView.setCloseHandler(() -> { root.getTabs().remove(duplicateOfferTab); 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 48abfa3730..ee9de37c16 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 @@ -26,14 +26,12 @@ import bisq.desktop.components.AutoTooltipTableColumn; import bisq.desktop.components.HyperlinkWithIcon; import bisq.desktop.components.InputTextField; import bisq.desktop.components.PeerInfoIconTrading; -import bisq.desktop.main.MainView; import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.main.overlays.windows.BsqTradeDetailsWindow; import bisq.desktop.main.overlays.windows.ClosedTradesSummaryWindow; import bisq.desktop.main.overlays.windows.OfferDetailsWindow; import bisq.desktop.main.overlays.windows.TradeDetailsWindow; -import bisq.desktop.main.portfolio.PortfolioView; -import bisq.desktop.main.portfolio.duplicateoffer.DuplicateOfferView; +import bisq.desktop.main.portfolio.presentation.PortfolioUtil; import bisq.desktop.util.GUIUtil; import bisq.core.alert.PrivateNotificationManager; @@ -272,9 +270,9 @@ public class ClosedTradesView extends ActivatableViewAndModel { TableRow row = new TableRow<>(); ContextMenu rowMenu = new ContextMenu(); - MenuItem editItem = new MenuItem(Res.get("portfolio.context.offerLikeThis")); - editItem.setOnAction((ActionEvent event) -> onDuplicateOffer(row.getItem().getOffer())); - rowMenu.getItems().add(editItem); + MenuItem duplicateItem = new MenuItem(Res.get("portfolio.context.offerLikeThis")); + duplicateItem.setOnAction((ActionEvent event) -> onDuplicateOffer(row.getItem().getOffer())); + rowMenu.getItems().add(duplicateItem); row.contextMenuProperty().bind( Bindings.when(Bindings.isNotNull(row.itemProperty())) .then(rowMenu) @@ -592,7 +590,7 @@ public class ClosedTradesView extends ActivatableViewAndModel bsqOptional = CurrencyUtil.getTradeCurrency("BSQ"); + if (bsqOptional.isPresent() && tradeCurrency.equals(bsqOptional.get())) { + if (user.getPaymentAccounts() != null) { + Optional firstBsqPaymentAccount = user.getPaymentAccounts().stream().filter(paymentAccount1 -> { + Optional tradeCurrency = paymentAccount1.getTradeCurrency(); + return tradeCurrency.isPresent() && + tradeCurrency.get().equals(bsqOptional.get()) && + !paymentAccount1.getId().equals(BsqSwapAccount.ID); + }).findFirst(); + + if (firstBsqPaymentAccount.isPresent()) { + return firstBsqPaymentAccount.get(); + } + } + } + + return super.getPreselectedPaymentAccount(); + } } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/duplicateoffer/DuplicateOfferView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/duplicateoffer/DuplicateOfferView.java index 5fdad85116..db6eb38ba1 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/duplicateoffer/DuplicateOfferView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/duplicateoffer/DuplicateOfferView.java @@ -23,7 +23,7 @@ import bisq.desktop.main.offer.bisq_v1.MutableOfferView; import bisq.desktop.main.overlays.windows.OfferDetailsWindow; import bisq.core.locale.CurrencyUtil; -import bisq.core.offer.OfferPayloadBase; +import bisq.core.offer.bisq_v1.OfferPayload; import bisq.core.user.Preferences; import bisq.core.util.FormattingUtils; import bisq.core.util.coin.BsqFormatter; @@ -61,7 +61,7 @@ public class DuplicateOfferView extends MutableOfferView { final TableRow row = new TableRow<>(); final ContextMenu rowMenu = new ContextMenu(); - MenuItem editItem = new MenuItem(Res.get("portfolio.context.offerLikeThis")); - editItem.setOnAction((event) -> onDuplicateOffer(row.getItem().getOffer())); - rowMenu.getItems().add(editItem); + MenuItem duplicateItem = new MenuItem(Res.get("portfolio.context.offerLikeThis")); + duplicateItem.setOnAction((event) -> onDuplicateOffer(row.getItem().getOffer())); + rowMenu.getItems().add(duplicateItem); row.contextMenuProperty().bind( Bindings.when(Bindings.isNotNull(row.itemProperty())) .then(rowMenu) @@ -209,7 +208,7 @@ public class OpenOffersView extends ActivatableViewAndModel { final TableRow row = new TableRow<>(); final ContextMenu rowMenu = new ContextMenu(); - MenuItem editItem = new MenuItem(Res.get("portfolio.context.offerLikeThis")); - editItem.setOnAction((event) -> { + MenuItem duplicateItem = new MenuItem(Res.get("portfolio.context.offerLikeThis")); + duplicateItem.setOnAction((event) -> { try { OfferPayload offerPayload = row.getItem().getTrade().getOffer().getOfferPayload().orElseThrow(); if (offerPayload.getPubKeyRing().equals(keyRing.getPubKeyRing())) { - navigation.navigateToWithData(offerPayload, MainView.class, PortfolioView.class, DuplicateOfferView.class); + PortfolioUtil.duplicateOffer(navigation, offerPayload); } else { new Popup().warning(Res.get("portfolio.context.notYourOffer")).show(); } @@ -245,7 +244,7 @@ public class PendingTradesView extends ActivatableViewAndModel. + */ + +package bisq.desktop.main.portfolio.presentation; + +import bisq.desktop.Navigation; +import bisq.desktop.main.MainView; +import bisq.desktop.main.offer.BuyOfferView; +import bisq.desktop.main.offer.SellOfferView; +import bisq.desktop.main.offer.bsq_swap.create_offer.BsqSwapCreateOfferView; +import bisq.desktop.main.portfolio.PortfolioView; +import bisq.desktop.main.portfolio.duplicateoffer.DuplicateOfferView; + +import bisq.core.offer.OfferDirection; +import bisq.core.offer.OfferPayloadBase; +import bisq.core.offer.bsq_swap.BsqSwapOfferPayload; + +public class PortfolioUtil { + + public static void duplicateOffer(Navigation navigation, OfferPayloadBase offerPayload) { + if (offerPayload instanceof BsqSwapOfferPayload) { + var offerViewClass = offerPayload.getDirection() == OfferDirection.BUY ? BuyOfferView.class : SellOfferView.class; + navigation.navigateToWithData(offerPayload, MainView.class, offerViewClass, BsqSwapCreateOfferView.class); + } else { + navigation.navigateToWithData(offerPayload, MainView.class, PortfolioView.class, DuplicateOfferView.class); + } + } +} From d85373adbb192d22e4c7dc1b654f53fc9c54480c Mon Sep 17 00:00:00 2001 From: Christoph Atteneder Date: Mon, 6 Dec 2021 11:12:17 +0100 Subject: [PATCH 5/5] Make Codacy happy --- .../DuplicateOfferDataModel.java | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/duplicateoffer/DuplicateOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/duplicateoffer/DuplicateOfferDataModel.java index beb6c8d262..5c71a5faed 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/duplicateoffer/DuplicateOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/duplicateoffer/DuplicateOfferDataModel.java @@ -116,18 +116,16 @@ class DuplicateOfferDataModel extends MutableOfferDataModel { // If trade currency is BSQ don't use the BSQ swap payment account as it will automatically // close the duplicate offer view Optional bsqOptional = CurrencyUtil.getTradeCurrency("BSQ"); - if (bsqOptional.isPresent() && tradeCurrency.equals(bsqOptional.get())) { - if (user.getPaymentAccounts() != null) { - Optional firstBsqPaymentAccount = user.getPaymentAccounts().stream().filter(paymentAccount1 -> { - Optional tradeCurrency = paymentAccount1.getTradeCurrency(); - return tradeCurrency.isPresent() && - tradeCurrency.get().equals(bsqOptional.get()) && - !paymentAccount1.getId().equals(BsqSwapAccount.ID); - }).findFirst(); + if (bsqOptional.isPresent() && tradeCurrency.equals(bsqOptional.get()) && user.getPaymentAccounts() != null) { + Optional firstBsqPaymentAccount = user.getPaymentAccounts().stream().filter(paymentAccount1 -> { + Optional tradeCurrency = paymentAccount1.getTradeCurrency(); + return tradeCurrency.isPresent() && + tradeCurrency.get().equals(bsqOptional.get()) && + !paymentAccount1.getId().equals(BsqSwapAccount.ID); + }).findFirst(); - if (firstBsqPaymentAccount.isPresent()) { - return firstBsqPaymentAccount.get(); - } + if (firstBsqPaymentAccount.isPresent()) { + return firstBsqPaymentAccount.get(); } }