diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/TradeStateController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/TradeStateController.java index a3184b5cf7..0f5ee715bb 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/TradeStateController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/TradeStateController.java @@ -48,6 +48,7 @@ import javax.annotation.Nullable; import java.util.Optional; +import java.util.Set; import static com.google.common.base.Preconditions.checkNotNull; @@ -120,7 +121,10 @@ public void onActivate() { tradePhaseBox.setBisqEasyTrade(bisqEasyTrade); bisqEasyTradeStatePin = bisqEasyTrade.tradeStateObservable().addObserver(state -> - UIThread.run(() -> applyStateInfoVBox(state))); + UIThread.run(() -> { + applyStateInfoVBox(state); + updateShouldShowSellerPriceApprovalOverlay(); + })); errorMessagePin = bisqEasyTrade.errorMessageObservable().addObserver(errorMessage -> { if (errorMessage != null) { @@ -207,26 +211,29 @@ void onInterruptTrade() { new Popup().warning(message) .actionButtonText(Res.get("confirmation.yes")) - .onAction(() -> { - switch (model.getTradeCloseType()) { - case REJECT: - channelService.sendSystemMessage(Res.get("bisqEasy.openTrades.systemMessage.rejected", - model.getChannel().get().getMyUserIdentity().getUserName()), model.getChannel().get()); - bisqEasyTradeService.rejectTrade(trade); - break; - case CANCEL: - channelService.sendSystemMessage(Res.get("bisqEasy.openTrades.systemMessage.cancelled", - model.getChannel().get().getMyUserIdentity().getUserName()), model.getChannel().get()); - bisqEasyTradeService.cancelTrade(trade); - break; - case COMPLETED: - default: - } - }) + .onAction(this::doInterruptTrade) .closeButtonText(Res.get("confirmation.no")) .show(); } + void doInterruptTrade() { + BisqEasyTrade trade = model.getBisqEasyTrade().get(); + switch (model.getTradeCloseType()) { + case REJECT: + channelService.sendSystemMessage(Res.get("bisqEasy.openTrades.systemMessage.rejected", + model.getChannel().get().getMyUserIdentity().getUserName()), model.getChannel().get()); + bisqEasyTradeService.rejectTrade(trade); + break; + case CANCEL: + channelService.sendSystemMessage(Res.get("bisqEasy.openTrades.systemMessage.cancelled", + model.getChannel().get().getMyUserIdentity().getUserName()), model.getChannel().get()); + bisqEasyTradeService.cancelTrade(trade); + break; + case COMPLETED: + default: + } + } + void onCloseTrade() { new Popup().warning(Res.get("bisqEasy.openTrades.closeTrade.warning.interrupted")) .actionButtonText(Res.get("confirmation.yes")) @@ -482,6 +489,15 @@ && requiresSellerPriceAcceptance() private boolean requiresSellerPriceAcceptance() { PriceSpec buyerPriceSpec = model.getBisqEasyTrade().get().getOffer().getPriceSpec(); PriceSpec sellerPriceSpec = model.getBisqEasyTrade().get().getContract().getAgreedPriceSpec(); - return !buyerPriceSpec.equals(sellerPriceSpec); + boolean priceSpecChanged = !buyerPriceSpec.equals(sellerPriceSpec); + + Set validStatesToRejectPrice = Set.of( + BisqEasyTradeState.MAKER_SENT_TAKE_OFFER_RESPONSE__BUYER_DID_NOT_SENT_BTC_ADDRESS__BUYER_DID_NOT_RECEIVED_ACCOUNT_DATA, + BisqEasyTradeState.MAKER_SENT_TAKE_OFFER_RESPONSE__BUYER_DID_NOT_SENT_BTC_ADDRESS__BUYER_RECEIVED_ACCOUNT_DATA, + BisqEasyTradeState.MAKER_DID_NOT_SENT_TAKE_OFFER_RESPONSE__BUYER_DID_NOT_SENT_BTC_ADDRESS__BUYER_RECEIVED_ACCOUNT_DATA, + BisqEasyTradeState.MAKER_SENT_TAKE_OFFER_RESPONSE__BUYER_DID_NOT_SENT_BTC_ADDRESS__BUYER_RECEIVED_ACCOUNT_DATA_ + ); + boolean isInValidStateToRejectPrice = validStatesToRejectPrice.contains(model.getBisqEasyTrade().get().tradeStateObservable().get()); + return priceSpecChanged && isInValidStateToRejectPrice; } } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/TradeStateView.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/TradeStateView.java index 5b7a80a51e..2a7c07aade 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/TradeStateView.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/trade_state/TradeStateView.java @@ -43,8 +43,8 @@ public class TradeStateView extends View { if (phaseAndInfoHBox.getChildren().size() == 2) { @@ -184,7 +183,7 @@ protected void onViewAttached() { cancelButton.setOnAction(e -> controller.onInterruptTrade()); closeTradeButton.setOnAction(e -> controller.onCloseTrade()); exportButton.setOnAction(e -> controller.onExportTrade()); - rejectPriceButton.setOnAction(e -> controller.onInterruptTrade()); + rejectPriceButton.setOnAction(e -> controller.doInterruptTrade()); reportToMediatorButton.setOnAction(e -> controller.onReportToMediator()); acceptSellersPriceButton.setOnAction(e -> controller.onAcceptSellersPriceButton()); } @@ -212,8 +211,6 @@ protected void onViewDetached() { cancelButton.managedProperty().unbind(); rejectPriceButton.textProperty().unbind(); - rejectPriceButton.visibleProperty().unbind(); - rejectPriceButton.managedProperty().unbind(); stateInfoVBoxPin.unsubscribe(); showSellersPriceApprovalOverlayPin.unsubscribe(); @@ -231,26 +228,33 @@ protected void onViewDetached() { } } - private void setUpSellerPriceApprovalOverlay() { - sellerPriceApprovalOverlay = new VBox(); - sellerPriceApprovalOverlay.setAlignment(Pos.TOP_LEFT); - sellerPriceApprovalOverlay.getStyleClass().addAll("trade-wizard-feedback-bg", "seller-price-approval-popup"); - sellerPriceApprovalOverlay.visibleProperty().set(false); - sellerPriceApprovalOverlay.managedProperty().set(false); + private VBox createAndGetSellerPriceApprovalOverlay() { + VBox overlay = new VBox(10); + overlay.setAlignment(Pos.TOP_LEFT); + overlay.getStyleClass().addAll("trade-wizard-feedback-bg", "seller-price-approval-popup"); + overlay.visibleProperty().set(false); + overlay.managedProperty().set(false); + + Label titleLabel = new Label(Res.get("bisqEasy.tradeState.acceptOrRejectSellersPrice.title")); + titleLabel.getStyleClass().addAll("seller-price-approval-title", "large-text", "font-default"); - Label sellerPriceApprovalTitleLabel = new Label(Res.get("bisqEasy.tradeState.acceptOrRejectSellersPrice.title")); - sellerPriceApprovalTitleLabel.getStyleClass().addAll("seller-price-approval-title", "large-text", "font-default"); - sellerPriceApprovalContent = new Pane(); sellerPriceApprovalContent.getStyleClass().addAll("seller-price-approval-description", "normal-text", "font-default"); - Label sellerPriceApprovalQuestionLabel = new Label(Res.get("bisqEasy.tradeState.acceptOrRejectSellersPrice.description.question")); - sellerPriceApprovalQuestionLabel.getStyleClass().addAll("seller-price-approval-description", "normal-text", + + Label questionLabel = new Label(Res.get("bisqEasy.tradeState.acceptOrRejectSellersPrice.description.question")); + questionLabel.getStyleClass().addAll("seller-price-approval-description", "normal-text", "font-default", "seller-price-approval-question"); - HBox sellerPriceApprovalButtons = new HBox(10, acceptSellersPriceButton, rejectPriceButton); - sellerPriceApprovalButtons.setAlignment(Pos.BOTTOM_RIGHT); - sellerPriceApprovalOverlay.getChildren().addAll(sellerPriceApprovalTitleLabel, sellerPriceApprovalContent, - sellerPriceApprovalQuestionLabel, Spacer.fillVBox(), sellerPriceApprovalButtons); - StackPane.setAlignment(sellerPriceApprovalOverlay, Pos.TOP_CENTER); - StackPane.setMargin(sellerPriceApprovalOverlay, new Insets(63, 0, 0, 0)); + Label disclaimerLabel = new Label(Res.get("bisqEasy.tradeState.acceptOrRejectSellersPrice.description.disclaimer")); + disclaimerLabel.getStyleClass().addAll("seller-price-approval-description", "small-text", "font-default", + "seller-price-approval-disclaimer"); + + HBox acceptOrRejectButtons = new HBox(10, acceptSellersPriceButton, rejectPriceButton); + acceptOrRejectButtons.setAlignment(Pos.BOTTOM_RIGHT); + + overlay.getChildren().addAll(titleLabel, sellerPriceApprovalContent, + questionLabel, disclaimerLabel, Spacer.fillVBox(), acceptOrRejectButtons); + StackPane.setAlignment(overlay, Pos.TOP_CENTER); + StackPane.setMargin(overlay, new Insets(63, 0, 0, 0)); + return overlay; } } diff --git a/apps/desktop/desktop/src/main/resources/css/bisq_easy.css b/apps/desktop/desktop/src/main/resources/css/bisq_easy.css index 9c992a0d57..30e36adf05 100644 --- a/apps/desktop/desktop/src/main/resources/css/bisq_easy.css +++ b/apps/desktop/desktop/src/main/resources/css/bisq_easy.css @@ -455,7 +455,7 @@ .seller-price-approval-popup .seller-price-approval-content { -fx-fill: -fx-light-text-color; -fx-text-fill: -fx-light-text-color; - -fx-padding: 30 0 0 0; + -fx-padding: 15 0 0 0; -fx-spacing: 10; } @@ -464,7 +464,14 @@ } .seller-price-approval-popup .seller-price-approval-question { - -fx-padding: 30 0 0 0; + -fx-line-spacing: 10; +} + +.seller-price-approval-popup .seller-price-approval-disclaimer { + -fx-padding: 10 0 0 0; + -fx-fill: -fx-mid-text-color; + -fx-text-fill: -fx-mid-text-color; + -fx-line-spacing: 10; } diff --git a/i18n/src/main/resources/bisq_easy.properties b/i18n/src/main/resources/bisq_easy.properties index c9f330c601..24f364f5d0 100644 --- a/i18n/src/main/resources/bisq_easy.properties +++ b/i18n/src/main/resources/bisq_easy.properties @@ -118,7 +118,7 @@ bisqEasy.price.feedback.sentence.veryGood=very good bisqEasy.price.feedback.learnWhySection.openButton=Why? bisqEasy.price.feedback.learnWhySection.closeButton=Back to Trade Price bisqEasy.price.feedback.learnWhySection.title=Why should I pay a higher price to the seller? -bisqEasy.price.feedback.learnWhySection.description.intro=The reason for that is that the seller has to cover extra expenses\ +bisqEasy.price.feedback.learnWhySection.description.intro=The reason for that is that the seller has to cover extra expenses \ and compensate for the seller's service specifically: bisqEasy.price.feedback.learnWhySection.description.exposition=\ - Build up reputation which can be costly.\ @@ -576,10 +576,11 @@ bisqEasy.openTrades.welcome.line3=Make yourself familiar with the trade rules bisqEasy.tradeState.requestMediation=Request mediation bisqEasy.tradeState.reportToMediator=Report to mediator -bisqEasy.tradeState.acceptOrRejectSellersPrice.title=Attention! -bisqEasy.tradeState.acceptOrRejectSellersPrice.description.buyersPrice=Your offer price to buy Bitcoin was: {0}. -bisqEasy.tradeState.acceptOrRejectSellersPrice.description.sellersPrice=However, the seller is offering you this price: {0}. -bisqEasy.tradeState.acceptOrRejectSellersPrice.description.question=Do you want to accept this new price or do you want to cancel/reject the trade? +bisqEasy.tradeState.acceptOrRejectSellersPrice.title=Attention to Price Change! +bisqEasy.tradeState.acceptOrRejectSellersPrice.description.buyersPrice=Your offer price to buy Bitcoin was {0}. +bisqEasy.tradeState.acceptOrRejectSellersPrice.description.sellersPrice=However, the seller is offering you this price {0}. +bisqEasy.tradeState.acceptOrRejectSellersPrice.description.question=Do you want to accept this new price or do you want to reject/cancel* the trade? +bisqEasy.tradeState.acceptOrRejectSellersPrice.description.disclaimer=(*Note that in this specific case, cancelling the trade will NOT be considered a violation of the trading rules.) bisqEasy.tradeState.acceptOrRejectSellersPrice.button.accept=Accept price # Trade State: Header