mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 23:18:17 +01:00
Merge pull request #6099 from jmacxx/mediation_enhancements_202203
Dispute subsystem enhancements
This commit is contained in:
commit
a188266d65
5 changed files with 120 additions and 28 deletions
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package bisq.core.support.dispute;
|
package bisq.core.support.dispute;
|
||||||
|
|
||||||
|
import bisq.core.locale.Res;
|
||||||
import bisq.core.support.messages.ChatMessage;
|
import bisq.core.support.messages.ChatMessage;
|
||||||
|
|
||||||
import bisq.common.proto.ProtoUtil;
|
import bisq.common.proto.ProtoUtil;
|
||||||
|
@ -68,14 +69,30 @@ public final class DisputeResult implements NetworkPayload {
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PayoutSuggestion {
|
public enum PayoutSuggestion {
|
||||||
UNKNOWN,
|
UNKNOWN("shared.na", null),
|
||||||
BUYER_GETS_TRADE_AMOUNT,
|
BUYER_GETS_TRADE_AMOUNT("disputeSummaryWindow.payout.getsTradeAmount", "shared.buyer"),
|
||||||
BUYER_GETS_TRADE_AMOUNT_PLUS_COMPENSATION,
|
BUYER_GETS_TRADE_AMOUNT_PLUS_COMPENSATION("disputeSummaryWindow.payout.getsCompensation", "shared.buyer"),
|
||||||
BUYER_GETS_TRADE_AMOUNT_MINUS_PENALTY,
|
BUYER_GETS_TRADE_AMOUNT_MINUS_PENALTY("disputeSummaryWindow.payout.getsPenalty", "shared.buyer"),
|
||||||
SELLER_GETS_TRADE_AMOUNT,
|
SELLER_GETS_TRADE_AMOUNT("disputeSummaryWindow.payout.getsTradeAmount", "shared.seller"),
|
||||||
SELLER_GETS_TRADE_AMOUNT_PLUS_COMPENSATION,
|
SELLER_GETS_TRADE_AMOUNT_PLUS_COMPENSATION("disputeSummaryWindow.payout.getsCompensation", "shared.seller"),
|
||||||
SELLER_GETS_TRADE_AMOUNT_MINUS_PENALTY,
|
SELLER_GETS_TRADE_AMOUNT_MINUS_PENALTY("disputeSummaryWindow.payout.getsPenalty", "shared.seller"),
|
||||||
CUSTOM_PAYOUT
|
CUSTOM_PAYOUT("disputeSummaryWindow.payout.custom", null);
|
||||||
|
|
||||||
|
private String suggestionKey;
|
||||||
|
@Nullable private String buyerSellerKey;
|
||||||
|
|
||||||
|
PayoutSuggestion(String suggestionKey, @Nullable String buyerSellerKey) {
|
||||||
|
this.suggestionKey = suggestionKey;
|
||||||
|
this.buyerSellerKey = buyerSellerKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
if (buyerSellerKey == null) {
|
||||||
|
return Res.get(suggestionKey);
|
||||||
|
} else {
|
||||||
|
return Res.get(suggestionKey, Res.get(buyerSellerKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final String tradeId;
|
private final String tradeId;
|
||||||
|
@ -259,6 +276,17 @@ public final class DisputeResult implements NetworkPayload {
|
||||||
return new Date(closeDate);
|
return new Date(closeDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getPayoutSuggestionText() {
|
||||||
|
if (payoutSuggestion.equals(PayoutSuggestion.BUYER_GETS_TRADE_AMOUNT_MINUS_PENALTY)
|
||||||
|
|| payoutSuggestion.equals(PayoutSuggestion.BUYER_GETS_TRADE_AMOUNT_PLUS_COMPENSATION)
|
||||||
|
|| payoutSuggestion.equals(PayoutSuggestion.SELLER_GETS_TRADE_AMOUNT_MINUS_PENALTY)
|
||||||
|
|| payoutSuggestion.equals(PayoutSuggestion.SELLER_GETS_TRADE_AMOUNT_PLUS_COMPENSATION)) {
|
||||||
|
return payoutSuggestion + " " + payoutAdjustmentPercent + "%";
|
||||||
|
}
|
||||||
|
return payoutSuggestion.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "DisputeResult{" +
|
return "DisputeResult{" +
|
||||||
|
|
|
@ -1223,14 +1223,16 @@ support.state=State
|
||||||
support.chat=Chat
|
support.chat=Chat
|
||||||
support.closed=Closed
|
support.closed=Closed
|
||||||
support.open=Open
|
support.open=Open
|
||||||
|
support.moreButton=MORE...
|
||||||
support.sendLogFiles=Send Log Files
|
support.sendLogFiles=Send Log Files
|
||||||
|
support.uploadTraderChat=Upload Trader Chat
|
||||||
support.process=Process
|
support.process=Process
|
||||||
support.buyerOfferer=BTC buyer/Maker
|
support.buyerOfferer=BTC buyer/Maker
|
||||||
support.sellerOfferer=BTC seller/Maker
|
support.sellerOfferer=BTC seller/Maker
|
||||||
support.buyerTaker=BTC buyer/Taker
|
support.buyerTaker=BTC buyer/Taker
|
||||||
support.sellerTaker=BTC seller/Taker
|
support.sellerTaker=BTC seller/Taker
|
||||||
support.sendLogs.title=Send Log Files
|
support.sendLogs.title=Send Log Files
|
||||||
support.sendLogs.backgroundInfo=When you experience a bug, mediators and support staff will often request copies of the your log files to diagnose the issue.\n\n\ \
|
support.sendLogs.backgroundInfo=When you experience a bug, mediators and support staff will often request copies of the your log files to diagnose the issue.\n\n\
|
||||||
Upon pressing 'Send', your log files will be compressed and transmitted directly to the mediator.
|
Upon pressing 'Send', your log files will be compressed and transmitted directly to the mediator.
|
||||||
support.sendLogs.step1=Create Zip Archive of Log Files
|
support.sendLogs.step1=Create Zip Archive of Log Files
|
||||||
support.sendLogs.step2=Connection Request to Mediator
|
support.sendLogs.step2=Connection Request to Mediator
|
||||||
|
@ -2726,11 +2728,12 @@ disputeSummaryWindow.close.msg=Ticket closed on {0}\n\
|
||||||
Summary:\n\
|
Summary:\n\
|
||||||
Trade ID: {3}\n\
|
Trade ID: {3}\n\
|
||||||
Currency: {4}\n\
|
Currency: {4}\n\
|
||||||
Trade amount: {5}\n\
|
Reason for dispute: {5}\n\
|
||||||
Payout amount for BTC buyer: {6}\n\
|
Payout suggestion: {6}\n\
|
||||||
Payout amount for BTC seller: {7}\n\n\
|
Trade amount: {7}\n\
|
||||||
Reason for dispute: {8}\n\n\
|
Payout amount for BTC buyer: {8}\n\
|
||||||
Summary notes:\n{9}\n
|
Payout amount for BTC seller: {9}\n\n\
|
||||||
|
Summary notes:\n{10}\n
|
||||||
|
|
||||||
# Do no change any line break or order of tokens as the structure is used for signature verification
|
# Do no change any line break or order of tokens as the structure is used for signature verification
|
||||||
disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3}
|
disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3}
|
||||||
|
|
|
@ -332,13 +332,20 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTradeAmountPayoutControls() {
|
private void addTradeAmountPayoutControls() {
|
||||||
buyerGetsTradeAmountRadioButton = new AutoTooltipRadioButton(Res.get("disputeSummaryWindow.payout.getsTradeAmount", Res.get("shared.buyer")));
|
buyerGetsTradeAmountRadioButton = new AutoTooltipRadioButton(
|
||||||
buyerGetsCompensationRadioButton = new AutoTooltipRadioButton(Res.get("disputeSummaryWindow.payout.getsCompensation", Res.get("shared.buyer")));
|
DisputeResult.PayoutSuggestion.BUYER_GETS_TRADE_AMOUNT.toString());
|
||||||
buyerGetsTradeAmountMinusPenaltyRadioButton = new AutoTooltipRadioButton(Res.get("disputeSummaryWindow.payout.getsPenalty", Res.get("shared.buyer")));
|
buyerGetsCompensationRadioButton = new AutoTooltipRadioButton(
|
||||||
sellerGetsTradeAmountRadioButton = new AutoTooltipRadioButton(Res.get("disputeSummaryWindow.payout.getsTradeAmount", Res.get("shared.seller")));
|
DisputeResult.PayoutSuggestion.BUYER_GETS_TRADE_AMOUNT_PLUS_COMPENSATION.toString());
|
||||||
sellerGetsCompensationRadioButton = new AutoTooltipRadioButton(Res.get("disputeSummaryWindow.payout.getsCompensation", Res.get("shared.seller")));
|
buyerGetsTradeAmountMinusPenaltyRadioButton = new AutoTooltipRadioButton(
|
||||||
sellerGetsTradeAmountMinusPenaltyRadioButton = new AutoTooltipRadioButton(Res.get("disputeSummaryWindow.payout.getsPenalty", Res.get("shared.seller")));
|
DisputeResult.PayoutSuggestion.BUYER_GETS_TRADE_AMOUNT_MINUS_PENALTY.toString());
|
||||||
customRadioButton = new AutoTooltipRadioButton(Res.get("disputeSummaryWindow.payout.custom"));
|
sellerGetsTradeAmountRadioButton = new AutoTooltipRadioButton(
|
||||||
|
DisputeResult.PayoutSuggestion.SELLER_GETS_TRADE_AMOUNT.toString());
|
||||||
|
sellerGetsCompensationRadioButton = new AutoTooltipRadioButton(
|
||||||
|
DisputeResult.PayoutSuggestion.SELLER_GETS_TRADE_AMOUNT_PLUS_COMPENSATION.toString());
|
||||||
|
sellerGetsTradeAmountMinusPenaltyRadioButton = new AutoTooltipRadioButton(
|
||||||
|
DisputeResult.PayoutSuggestion.SELLER_GETS_TRADE_AMOUNT_MINUS_PENALTY.toString());
|
||||||
|
customRadioButton = new AutoTooltipRadioButton(
|
||||||
|
DisputeResult.PayoutSuggestion.CUSTOM_PAYOUT.toString());
|
||||||
|
|
||||||
VBox radioButtonPane = new VBox();
|
VBox radioButtonPane = new VBox();
|
||||||
radioButtonPane.setSpacing(10);
|
radioButtonPane.setSpacing(10);
|
||||||
|
@ -875,10 +882,11 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||||
agentNodeAddress,
|
agentNodeAddress,
|
||||||
dispute.getShortTradeId(),
|
dispute.getShortTradeId(),
|
||||||
currencyCode,
|
currencyCode,
|
||||||
|
Res.get("disputeSummaryWindow.reason." + reason.name()),
|
||||||
|
disputeResult.getPayoutSuggestionText(),
|
||||||
amount,
|
amount,
|
||||||
formatter.formatCoinWithCode(disputeResult.getBuyerPayoutAmount()),
|
formatter.formatCoinWithCode(disputeResult.getBuyerPayoutAmount()),
|
||||||
formatter.formatCoinWithCode(disputeResult.getSellerPayoutAmount()),
|
formatter.formatCoinWithCode(disputeResult.getSellerPayoutAmount()),
|
||||||
Res.get("disputeSummaryWindow.reason." + reason.name()),
|
|
||||||
disputeResult.summaryNotesProperty().get()
|
disputeResult.summaryNotesProperty().get()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||||
|
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
|
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ListCell;
|
import javafx.scene.control.ListCell;
|
||||||
|
@ -112,7 +113,7 @@ public class ChatView extends AnchorPane {
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
@Getter
|
@Getter
|
||||||
Button extraButton;
|
Node extraButton;
|
||||||
@Getter
|
@Getter
|
||||||
private ReadOnlyDoubleProperty widthProperty;
|
private ReadOnlyDoubleProperty widthProperty;
|
||||||
@Setter
|
@Setter
|
||||||
|
@ -169,7 +170,7 @@ public class ChatView extends AnchorPane {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void display(SupportSession supportSession,
|
public void display(SupportSession supportSession,
|
||||||
@Nullable Button extraButton,
|
@Nullable Node extraButton,
|
||||||
ReadOnlyDoubleProperty widthProperty) {
|
ReadOnlyDoubleProperty widthProperty) {
|
||||||
optionalSupportSession = Optional.of(supportSession);
|
optionalSupportSession = Optional.of(supportSession);
|
||||||
removeListenersOnSessionChange();
|
removeListenersOnSessionChange();
|
||||||
|
@ -233,7 +234,6 @@ public class ChatView extends AnchorPane {
|
||||||
buttonBox.getChildren().addAll(sendButton, sendMsgBusyAnimation, sendMsgInfoLabel);
|
buttonBox.getChildren().addAll(sendButton, sendMsgBusyAnimation, sendMsgInfoLabel);
|
||||||
|
|
||||||
if (extraButton != null) {
|
if (extraButton != null) {
|
||||||
extraButton.setDefaultButton(true);
|
|
||||||
Pane spacer = new Pane();
|
Pane spacer = new Pane();
|
||||||
HBox.setHgrow(spacer, Priority.ALWAYS);
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
buttonBox.getChildren().addAll(spacer, extraButton);
|
buttonBox.getChildren().addAll(spacer, extraButton);
|
||||||
|
@ -571,6 +571,26 @@ public class ChatView extends AnchorPane {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onAttachText(String textAttachment, String name) {
|
||||||
|
if (!allowAttachments)
|
||||||
|
return;
|
||||||
|
try {
|
||||||
|
byte[] filesAsBytes = textAttachment.getBytes("UTF8");
|
||||||
|
int size = filesAsBytes.length;
|
||||||
|
int maxMsgSize = Connection.getPermittedMessageSize();
|
||||||
|
int maxSizeInKB = maxMsgSize / 1024;
|
||||||
|
if (size > maxMsgSize) {
|
||||||
|
new Popup().warning(Res.get("support.attachmentTooLarge", (size / 1024), maxSizeInKB)).show();
|
||||||
|
} else {
|
||||||
|
tempAttachments.add(new Attachment(name, filesAsBytes));
|
||||||
|
inputTextArea.setText(inputTextArea.getText() + "\n[" + Res.get("support.attachment") + " " + name + "]");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.toString());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void onOpenAttachment(Attachment attachment) {
|
private void onOpenAttachment(Attachment attachment) {
|
||||||
if (!allowAttachments)
|
if (!allowAttachments)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -21,12 +21,14 @@ import bisq.desktop.components.AutoTooltipButton;
|
||||||
import bisq.desktop.main.MainView;
|
import bisq.desktop.main.MainView;
|
||||||
import bisq.desktop.main.shared.ChatView;
|
import bisq.desktop.main.shared.ChatView;
|
||||||
import bisq.desktop.util.CssTheme;
|
import bisq.desktop.util.CssTheme;
|
||||||
|
import bisq.desktop.util.DisplayUtils;
|
||||||
|
|
||||||
import bisq.core.locale.Res;
|
import bisq.core.locale.Res;
|
||||||
import bisq.core.support.dispute.Dispute;
|
import bisq.core.support.dispute.Dispute;
|
||||||
import bisq.core.support.dispute.DisputeList;
|
import bisq.core.support.dispute.DisputeList;
|
||||||
import bisq.core.support.dispute.DisputeManager;
|
import bisq.core.support.dispute.DisputeManager;
|
||||||
import bisq.core.support.dispute.DisputeSession;
|
import bisq.core.support.dispute.DisputeSession;
|
||||||
|
import bisq.core.support.messages.ChatMessage;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
import bisq.core.util.coin.CoinFormatter;
|
import bisq.core.util.coin.CoinFormatter;
|
||||||
|
|
||||||
|
@ -39,6 +41,8 @@ import javafx.stage.Window;
|
||||||
|
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.MenuButton;
|
||||||
|
import javafx.scene.control.MenuItem;
|
||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
@ -46,6 +50,9 @@ import javafx.scene.layout.StackPane;
|
||||||
|
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
public class DisputeChatPopup {
|
public class DisputeChatPopup {
|
||||||
|
@ -106,12 +113,19 @@ public class DisputeChatPopup {
|
||||||
} else {
|
} else {
|
||||||
if (disputeManager.isAgent(selectedDispute)) {
|
if (disputeManager.isAgent(selectedDispute)) {
|
||||||
Button closeDisputeButton = new AutoTooltipButton(Res.get("support.closeTicket"));
|
Button closeDisputeButton = new AutoTooltipButton(Res.get("support.closeTicket"));
|
||||||
|
closeDisputeButton.setDefaultButton(true);
|
||||||
closeDisputeButton.setOnAction(e -> chatCallback.onCloseDisputeFromChatWindow(selectedDispute));
|
closeDisputeButton.setOnAction(e -> chatCallback.onCloseDisputeFromChatWindow(selectedDispute));
|
||||||
chatView.display(concreteDisputeSession, closeDisputeButton, pane.widthProperty());
|
chatView.display(concreteDisputeSession, closeDisputeButton, pane.widthProperty());
|
||||||
} else {
|
} else {
|
||||||
Button sendLogsButton = new AutoTooltipButton(Res.get("support.sendLogFiles"));
|
MenuButton menuButton = new MenuButton(Res.get("support.moreButton"));
|
||||||
sendLogsButton.setOnAction(e -> chatCallback.onSendLogsFromChatWindow(selectedDispute));
|
MenuItem menuItem1 = new MenuItem(Res.get("support.uploadTraderChat"));
|
||||||
chatView.display(concreteDisputeSession, sendLogsButton, pane.widthProperty());
|
MenuItem menuItem2 = new MenuItem(Res.get("support.sendLogFiles"));
|
||||||
|
menuItem1.setOnAction(e -> doTextAttachment(chatView));
|
||||||
|
menuItem2.setOnAction(e -> chatCallback.onSendLogsFromChatWindow(selectedDispute));
|
||||||
|
menuButton.getItems().addAll(menuItem1, menuItem2);
|
||||||
|
menuButton.getStyleClass().add("jfx-button");
|
||||||
|
menuButton.setStyle("-fx-padding: 0 10 0 10;");
|
||||||
|
chatView.display(concreteDisputeSession, menuButton, pane.widthProperty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chatView.activate();
|
chatView.activate();
|
||||||
|
@ -163,4 +177,23 @@ public class DisputeChatPopup {
|
||||||
// and after a short moment in the correct position
|
// and after a short moment in the correct position
|
||||||
UserThread.execute(() -> chatPopupStage.setOpacity(1));
|
UserThread.execute(() -> chatPopupStage.setOpacity(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void doTextAttachment(ChatView chatView) {
|
||||||
|
disputeManager.findTrade(selectedDispute).ifPresent(t -> {
|
||||||
|
List<ChatMessage> chatMessages = t.getChatMessages();
|
||||||
|
if (chatMessages.size() > 0) {
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
chatMessages.forEach(i -> {
|
||||||
|
boolean isMyMsg = i.isSenderIsTrader();
|
||||||
|
String metaData = DisplayUtils.formatDateTime(new Date(i.getDate()));
|
||||||
|
if (!i.isSystemMessage())
|
||||||
|
metaData = (isMyMsg ? "Sent " : "Received ") + metaData
|
||||||
|
+ (isMyMsg ? "" : " from Trader");
|
||||||
|
stringBuilder.append(metaData).append("\n").append(i.getMessage()).append("\n\n");
|
||||||
|
});
|
||||||
|
String fileName = selectedDispute.getShortTradeId() + "_" + selectedDispute.getRoleStringForLogFile() + "_TraderChat.txt";
|
||||||
|
chatView.onAttachText(stringBuilder.toString(), fileName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue