mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
Merge pull request #6597 from jmacxx/support_ui_improvements
Support UI improvements
This commit is contained in:
commit
cf56ae316e
@ -94,12 +94,12 @@ public class DisputeMsgEvents {
|
||||
log.debug("We got a ChatMessage added. id={}, tradeId={}", dispute.getId(), dispute.getTradeId());
|
||||
c.next();
|
||||
if (c.wasAdded()) {
|
||||
c.getAddedSubList().forEach(chatMessage -> onChatMessage(chatMessage, dispute));
|
||||
c.getAddedSubList().forEach(this::onChatMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onChatMessage(ChatMessage chatMessage, Dispute dispute) {
|
||||
private void onChatMessage(ChatMessage chatMessage) {
|
||||
if (chatMessage.getSenderNodeAddress().equals(p2PService.getAddress())) {
|
||||
return;
|
||||
}
|
||||
@ -116,22 +116,5 @@ public class DisputeMsgEvents {
|
||||
log.error(e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// We check at every new message if it might be a message sent after the dispute had been closed. If that is the
|
||||
// case we revert the isClosed flag so that the UI can reopen the dispute and indicate that a new dispute
|
||||
// message arrived.
|
||||
Optional<ChatMessage> newestChatMessage = dispute.getChatMessages().stream().
|
||||
sorted(Comparator.comparingLong(ChatMessage::getDate).reversed()).findFirst();
|
||||
// If last message is not a result message we re-open as we might have received a new message from the
|
||||
// trader/mediator/arbitrator who has reopened the case
|
||||
if (dispute.isClosed() && newestChatMessage.isPresent() && !newestChatMessage.get().isResultMessage(dispute)) {
|
||||
log.info("Reopening dispute {} due to new chat message received {}", dispute.getTradeId(), newestChatMessage.get().getUid());
|
||||
dispute.reOpen();
|
||||
if (dispute.getSupportType() == SupportType.MEDIATION) {
|
||||
mediationManager.requestPersistence();
|
||||
} else if (dispute.getSupportType() == SupportType.REFUND) {
|
||||
refundManager.requestPersistence();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,15 +42,14 @@ import com.google.protobuf.ByteString;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
@ -82,7 +81,8 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
||||
NEW,
|
||||
OPEN,
|
||||
REOPENED,
|
||||
CLOSED;
|
||||
CLOSED,
|
||||
RESULT_PROPOSED;
|
||||
|
||||
public static Dispute.State fromProto(protobuf.Dispute.State state) {
|
||||
return ProtoUtil.enumFromProto(Dispute.State.class, state.name());
|
||||
@ -168,7 +168,7 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
||||
@Setter
|
||||
private transient boolean payoutDone = false;
|
||||
|
||||
private transient final BooleanProperty isClosedProperty = new SimpleBooleanProperty();
|
||||
private transient final StringProperty disputeStateProperty = new SimpleStringProperty();
|
||||
private transient final IntegerProperty badgeCountProperty = new SimpleIntegerProperty();
|
||||
|
||||
private transient FileTransferReceiver fileTransferSession = null;
|
||||
@ -239,6 +239,7 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
||||
|
||||
id = tradeId + "_" + traderId;
|
||||
uid = UUID.randomUUID().toString();
|
||||
setState(State.NEW);
|
||||
refreshAlertLevel(true);
|
||||
}
|
||||
|
||||
@ -413,7 +414,7 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
||||
|
||||
public void setState(Dispute.State disputeState) {
|
||||
this.disputeState = disputeState;
|
||||
this.isClosedProperty.set(disputeState == State.CLOSED);
|
||||
this.disputeStateProperty.set(disputeState.toString());
|
||||
}
|
||||
|
||||
public void setDisputeResult(DisputeResult disputeResult) {
|
||||
@ -438,10 +439,6 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
||||
return Utilities.getShortId(tradeId);
|
||||
}
|
||||
|
||||
public ReadOnlyBooleanProperty isClosedProperty() {
|
||||
return isClosedProperty;
|
||||
}
|
||||
|
||||
public ReadOnlyIntegerProperty getBadgeCountProperty() {
|
||||
return badgeCountProperty;
|
||||
}
|
||||
@ -470,6 +467,10 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
||||
return this.disputeState == State.CLOSED;
|
||||
}
|
||||
|
||||
public boolean isResultProposed() {
|
||||
return this.disputeState == State.RESULT_PROPOSED;
|
||||
}
|
||||
|
||||
public void refreshAlertLevel(boolean senderFlag) {
|
||||
// if the dispute is "new" that is 1 alert that has to be propagated upstream
|
||||
// or if there are unread messages that is 1 alert that has to be propagated upstream
|
||||
@ -559,7 +560,7 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
||||
",\n agentPubKeyRing=" + agentPubKeyRing +
|
||||
",\n isSupportTicket=" + isSupportTicket +
|
||||
",\n chatMessages=" + chatMessages +
|
||||
",\n isClosedProperty=" + isClosedProperty +
|
||||
",\n disputeStateProperty=" + disputeStateProperty +
|
||||
",\n disputeResultProperty=" + disputeResultProperty +
|
||||
",\n disputePayoutTxId='" + disputePayoutTxId + '\'' +
|
||||
",\n openingDate=" + openingDate +
|
||||
|
@ -99,7 +99,7 @@ public abstract class DisputeListService<T extends DisputeList<Dispute>> impleme
|
||||
public void cleanupDisputes(@Nullable Consumer<String> closedDisputeHandler) {
|
||||
disputeList.stream().forEach(dispute -> {
|
||||
String tradeId = dispute.getTradeId();
|
||||
if (dispute.isClosed() && closedDisputeHandler != null) {
|
||||
if (dispute.isResultProposed() && closedDisputeHandler != null) {
|
||||
closedDisputeHandler.accept(tradeId);
|
||||
}
|
||||
});
|
||||
|
@ -31,6 +31,7 @@ import bisq.core.offer.bisq_v1.OfferPayload;
|
||||
import bisq.core.provider.price.MarketPrice;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
import bisq.core.support.SupportManager;
|
||||
import bisq.core.support.dispute.mediation.MediationResultState;
|
||||
import bisq.core.support.dispute.messages.DisputeResultMessage;
|
||||
import bisq.core.support.dispute.messages.OpenNewDisputeMessage;
|
||||
import bisq.core.support.dispute.messages.PeerOpenedDisputeMessage;
|
||||
@ -254,6 +255,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||
@Override
|
||||
public void onUpdatedDataReceived() {
|
||||
tryApplyMessages();
|
||||
checkDisputesForUpdates();
|
||||
}
|
||||
});
|
||||
|
||||
@ -263,8 +265,9 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||
});
|
||||
|
||||
walletsSetup.numPeersProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (walletsSetup.hasSufficientPeersForBroadcast())
|
||||
if (walletsSetup.hasSufficientPeersForBroadcast()) {
|
||||
tryApplyMessages();
|
||||
}
|
||||
});
|
||||
|
||||
tryApplyMessages();
|
||||
@ -292,6 +295,46 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||
maybeClearSensitiveData();
|
||||
}
|
||||
|
||||
private void checkDisputesForUpdates() {
|
||||
List<Dispute> disputes = getDisputeList().getList();
|
||||
disputes.forEach(dispute -> {
|
||||
if (dispute.isResultProposed()) {
|
||||
// an open dispute where the mediator has proposed a result. has the trade moved on?
|
||||
// if so, dispute can close and the mediator needs to be informed so they can close their ticket.
|
||||
tradeManager.getTradeById(dispute.getTradeId()).ifPresentOrElse(
|
||||
t -> checkForMediatedTradePayout(t, dispute),
|
||||
() -> closedTradableManager.getTradableById(dispute.getTradeId()).ifPresent(
|
||||
t -> checkForMediatedTradePayout((Trade) t, dispute)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void checkForMediatedTradePayout(Trade trade, Dispute dispute) {
|
||||
if (trade.disputeStateProperty().get().isArbitrated() || trade.getTradePhase() == Trade.Phase.PAYOUT_PUBLISHED) {
|
||||
disputedTradeUpdate(trade.getDisputeState().toString(), dispute, true);
|
||||
} else {
|
||||
// user accepted/rejected mediation proposal (before lockup period has expired)
|
||||
trade.mediationResultStateProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue == MediationResultState.MEDIATION_RESULT_ACCEPTED ||
|
||||
newValue == MediationResultState.MEDIATION_RESULT_REJECTED) {
|
||||
disputedTradeUpdate(newValue.toString(), dispute, false);
|
||||
}
|
||||
});
|
||||
// user rejected mediation after lockup period: opening arbitration
|
||||
trade.disputeStateProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue.isArbitrated()) {
|
||||
disputedTradeUpdate(newValue.toString(), dispute, true);
|
||||
}
|
||||
});
|
||||
// trade paid out through mediation
|
||||
trade.statePhaseProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue == Trade.Phase.PAYOUT_PUBLISHED) {
|
||||
disputedTradeUpdate(newValue.toString(), dispute, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTrader(Dispute dispute) {
|
||||
return pubKeyRing.equals(dispute.getTraderPubKeyRing());
|
||||
}
|
||||
@ -650,8 +693,6 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||
chatMessage.setSystemMessage(true);
|
||||
dispute.addAndPersistChatMessage(chatMessage);
|
||||
|
||||
addPriceInfoMessage(dispute, 0);
|
||||
|
||||
disputeList.add(dispute);
|
||||
|
||||
// We mirrored dispute already!
|
||||
@ -719,6 +760,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||
}
|
||||
}
|
||||
);
|
||||
addPriceInfoMessage(dispute, 0);
|
||||
requestPersistence();
|
||||
}
|
||||
|
||||
@ -897,17 +939,23 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||
}
|
||||
}
|
||||
|
||||
public void addMediationReOpenedMessage(Dispute dispute, boolean senderIsTrader) {
|
||||
// when a mediated trade changes, send a system message informing the mediator, so they can maybe close their ticket.
|
||||
public void disputedTradeUpdate(String message, Dispute dispute, boolean close) {
|
||||
if (dispute.isClosed()) {
|
||||
return;
|
||||
}
|
||||
ChatMessage chatMessage = new ChatMessage(
|
||||
getSupportType(),
|
||||
dispute.getTradeId(),
|
||||
dispute.getTraderId(),
|
||||
senderIsTrader,
|
||||
Res.get("support.info.disputeReOpened"),
|
||||
true,
|
||||
Res.get("support.info.disputedTradeUpdate", message),
|
||||
p2PService.getAddress());
|
||||
chatMessage.setSystemMessage(false);
|
||||
dispute.addAndPersistChatMessage(chatMessage);
|
||||
this.sendChatMessage(chatMessage);
|
||||
chatMessage.setSystemMessage(true);
|
||||
this.sendChatMessage(chatMessage); // inform the mediator
|
||||
if (close) {
|
||||
dispute.setIsClosed(); // close the trader's ticket
|
||||
}
|
||||
requestPersistence();
|
||||
}
|
||||
|
||||
|
@ -288,6 +288,47 @@ public final class DisputeResult implements NetworkPayload {
|
||||
return payoutSuggestion.toString();
|
||||
}
|
||||
|
||||
public String getPayoutSuggestionCustomizedToBuyerOrSeller(boolean isBuyer) {
|
||||
// see github.com/bisq-network/proposals/issues/407
|
||||
if (isBuyer) {
|
||||
switch (payoutSuggestion) {
|
||||
case BUYER_GETS_TRADE_AMOUNT:
|
||||
return Res.get("disputeSummaryWindow.result.buyerGetsTradeAmount");
|
||||
case BUYER_GETS_TRADE_AMOUNT_MINUS_PENALTY:
|
||||
return Res.get("disputeSummaryWindow.result.buyerGetsTradeAmountMinusPenalty");
|
||||
case BUYER_GETS_TRADE_AMOUNT_PLUS_COMPENSATION:
|
||||
return Res.get("disputeSummaryWindow.result.buyerGetsTradeAmountPlusCompensation");
|
||||
case SELLER_GETS_TRADE_AMOUNT:
|
||||
return Res.get("disputeSummaryWindow.result.buyerGetsHisDeposit");
|
||||
case SELLER_GETS_TRADE_AMOUNT_MINUS_PENALTY:
|
||||
return Res.get("disputeSummaryWindow.result.buyerGetsHisDepositPlusPenaltyFromSeller");
|
||||
case SELLER_GETS_TRADE_AMOUNT_PLUS_COMPENSATION:
|
||||
return Res.get("disputeSummaryWindow.result.buyerGetsHisDepositMinusPenalty");
|
||||
case CUSTOM_PAYOUT:
|
||||
return Res.get("disputeSummaryWindow.result.customPayout");
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
switch (payoutSuggestion) {
|
||||
case SELLER_GETS_TRADE_AMOUNT:
|
||||
return Res.get("disputeSummaryWindow.result.sellerGetsTradeAmount");
|
||||
case SELLER_GETS_TRADE_AMOUNT_MINUS_PENALTY:
|
||||
return Res.get("disputeSummaryWindow.result.sellerGetsTradeAmountMinusPenalty");
|
||||
case SELLER_GETS_TRADE_AMOUNT_PLUS_COMPENSATION:
|
||||
return Res.get("disputeSummaryWindow.result.sellerGetsTradeAmountPlusCompensation");
|
||||
case BUYER_GETS_TRADE_AMOUNT:
|
||||
return Res.get("disputeSummaryWindow.result.sellerGetsHisDeposit");
|
||||
case BUYER_GETS_TRADE_AMOUNT_MINUS_PENALTY:
|
||||
return Res.get("disputeSummaryWindow.result.sellerGetsHisDepositPlusPenaltyFromBuyer");
|
||||
case BUYER_GETS_TRADE_AMOUNT_PLUS_COMPENSATION:
|
||||
return Res.get("disputeSummaryWindow.result.sellerGetsHisDepositMinusPenalty");
|
||||
case CUSTOM_PAYOUT:
|
||||
return Res.get("disputeSummaryWindow.result.customPayout");
|
||||
default:
|
||||
}
|
||||
}
|
||||
return Res.get("popup.headline.error");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
@ -141,8 +141,12 @@ public final class MediationManager extends DisputeManager<MediationDisputeList>
|
||||
|
||||
@Override
|
||||
public void cleanupDisputes() {
|
||||
// closes any trades/disputes which paid out while Bisq was not in use
|
||||
disputeListService.cleanupDisputes(tradeId -> tradeManager.getTradeById(tradeId).filter(trade -> trade.getPayoutTx() != null)
|
||||
.ifPresent(trade -> tradeManager.closeDisputedTrade(tradeId, Trade.DisputeState.MEDIATION_CLOSED)));
|
||||
.ifPresent(trade -> {
|
||||
tradeManager.closeDisputedTrade(tradeId, Trade.DisputeState.MEDIATION_CLOSED);
|
||||
findOwnDispute(tradeId).ifPresent(Dispute::setIsClosed);
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -201,7 +205,7 @@ public final class MediationManager extends DisputeManager<MediationDisputeList>
|
||||
} else {
|
||||
log.warn("We got a dispute mail msg what we have already stored. TradeId = " + chatMessage.getTradeId());
|
||||
}
|
||||
dispute.setIsClosed();
|
||||
dispute.setState(Dispute.State.RESULT_PROPOSED);
|
||||
|
||||
dispute.setDisputeResult(disputeResult);
|
||||
|
||||
@ -216,6 +220,7 @@ public final class MediationManager extends DisputeManager<MediationDisputeList>
|
||||
trade.setDisputeState(Trade.DisputeState.MEDIATION_CLOSED);
|
||||
|
||||
tradeManager.requestPersistence();
|
||||
checkForMediatedTradePayout(trade, dispute);
|
||||
}
|
||||
} else {
|
||||
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(tradeId);
|
||||
|
@ -158,7 +158,7 @@ public final class RefundManager extends DisputeManager<RefundDisputeList> {
|
||||
String roleContextMsg = Res.get("support.initialArbitratorMsg",
|
||||
DisputeAgentLookupMap.getMatrixLinkForAgent(getAgentNodeAddress(dispute).getFullAddress()));
|
||||
String link = "https://bisq.wiki/Dispute_resolution#Level_3:_Arbitration";
|
||||
return Res.get("support.initialInfo", role, roleContextMsg, role, link);
|
||||
return Res.get("support.initialInfoRefundAgent", role, roleContextMsg, role, link);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -977,8 +977,9 @@ portfolio.pending.mediationResult.popup.info=The mediator has suggested the foll
|
||||
You can accept or reject this suggested payout.\n\n\
|
||||
By accepting, you sign the proposed payout transaction. \
|
||||
Mediation is expected to be the optimal resolution for both traders. \
|
||||
If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed.\n\n\
|
||||
If one or both of you reject the suggestion, you will have to wait until {2} (block {3}) to reject mediation suggestion, \
|
||||
If your trading peer also accepts and signs, the payout will be completed, and the trade will be closed. \
|
||||
Please inform the mediator if the trade is not paid out in the next 48h.\n\n\
|
||||
If agreement is not possible, you will have to wait until {2} (block {3}) to Send to Arbitration,\
|
||||
which will open a second-round dispute with an arbitrator who will investigate the case again and do a payout based on their findings.\n\n\
|
||||
If the trade goes to arbitration the arbitrator will pay out the trade amount plus one peer's security deposit. \
|
||||
This means the total arbitration payout will be less than the mediation payout. \
|
||||
@ -986,12 +987,14 @@ portfolio.pending.mediationResult.popup.info=The mediator has suggested the foll
|
||||
one peer not responding, or disputing the mediator made a fair payout suggestion. \n\n\
|
||||
More details about the arbitration model: [HYPERLINK:https://bisq.wiki/Dispute_resolution#Level_3:_Arbitration]
|
||||
portfolio.pending.mediationResult.popup.selfAccepted.lockTimeOver=You have accepted the mediator''s suggested payout \
|
||||
but it seems that your trading peer has not accepted it.\n\n\
|
||||
but it seems that your trading peer has not yet accepted it. \
|
||||
Inform your mediator that you have accepted mediation if your peer has not accepted the mediation suggestion in 48h.\n\n\
|
||||
Once the lock time is over on {0} (block {1}), you can open a second-round dispute with an arbitrator who will \
|
||||
investigate the case again and do a payout based on their findings.\n\n\
|
||||
You can find more details about the arbitration model at:\
|
||||
You can find more details about the arbitration model at: \
|
||||
[HYPERLINK:https://bisq.wiki/Dispute_resolution#Level_3:_Arbitration]
|
||||
portfolio.pending.mediationResult.popup.openArbitration=Reject and request arbitration
|
||||
portfolio.pending.mediationResult.popup.reject=Reject
|
||||
portfolio.pending.mediationResult.popup.openArbitration=Send to arbitration
|
||||
portfolio.pending.mediationResult.popup.alreadyAccepted=You've already accepted
|
||||
|
||||
portfolio.pending.failedTrade.taker.missingTakerFeeTx=The taker fee transaction is missing.\n\n\
|
||||
@ -1210,7 +1213,7 @@ support.save=Save file to disk
|
||||
support.messages=Messages
|
||||
support.input.prompt=Enter message...
|
||||
support.send=Send
|
||||
support.addAttachments=Add attachments
|
||||
support.addAttachments=Attach Files
|
||||
support.closeTicket=Close ticket
|
||||
support.attachments=Attachments:
|
||||
support.savedInMailbox=Message saved in receiver's mailbox
|
||||
@ -1276,6 +1279,20 @@ support.initialInfo=Please enter a description of your problem in the text field
|
||||
\t● You need to cooperate with the {2} and provide the information they request to make your case.\n\
|
||||
\t● You accepted the rules outlined in the dispute document in the user agreement when you first started the application.\n\n\
|
||||
You can read more about the dispute process at: {3}
|
||||
support.initialInfoRefundAgent=Please describe why have you opened arbitration, or why do you think your peer did so. \
|
||||
Add as much information as possible to speed up dispute resolution time. Mediation and trading chats are not shared with the arbitrator.\n\n\
|
||||
Here is a check list for information you should provide:\n\
|
||||
\t● If you are the BTC buyer: Did you make the Fiat or Altcoin transfer? If so, did you click the 'payment started' \
|
||||
button in the application? Did you accept mediator's suggestion?\n\
|
||||
\t● If you are the BTC seller: Did you receive the Fiat or Altcoin payment? If so, did you click the 'payment received' \
|
||||
button in the application? Did you accept mediator's suggestion?\n\
|
||||
Please make yourself familiar with the basic rules for the dispute process:\n\
|
||||
\t● You need to respond to the {0}''s requests within 2 days.\n\
|
||||
\t● {1}\n\
|
||||
\t● The maximum period for a dispute is 14 days.\n\
|
||||
\t● You need to cooperate with the {2} and provide the information they request to make your case.\n\
|
||||
\t● You accepted the rules outlined in the dispute document in the user agreement when you first started the application.\n\n\
|
||||
You can read more about the dispute process at: {3}
|
||||
support.initialMediatorMsg=Mediators will generally reply to you within 24 hours.\n\
|
||||
\t If you have not had a reply after 48 hours please feel free to reach out to your mediator on Matrix.\n\
|
||||
\t Mediators usernames on Matrix are the same as their usernames within the Bisq app.\n\
|
||||
@ -1304,7 +1321,7 @@ support.warning.disputesWithInvalidDonationAddress=The delayed payout transactio
|
||||
support.warning.disputesWithInvalidDonationAddress.mediator=\n\nDo you still want to close the dispute?
|
||||
support.warning.disputesWithInvalidDonationAddress.refundAgent=\n\nYou must not do the payout.
|
||||
support.warning.traderCloseOwnDisputeWarning=Traders can only self-close their support tickets when the trade has been paid out.
|
||||
support.info.disputeReOpened=Dispute ticket has been re-opened.
|
||||
support.info.disputedTradeUpdate=Disputed trade update: {0}
|
||||
|
||||
####################################################################
|
||||
# Settings
|
||||
@ -2856,10 +2873,22 @@ disputeSummaryWindow.reason.WRONG_SENDER_ACCOUNT=Wrong sender account
|
||||
disputeSummaryWindow.reason.PEER_WAS_LATE=Peer was late
|
||||
# suppress inspection "UnusedProperty"
|
||||
disputeSummaryWindow.reason.TRADE_ALREADY_SETTLED=Trade already settled
|
||||
|
||||
disputeSummaryWindow.result.buyerGetsTradeAmount=(BTC buyer receives trade amount + his deposit)
|
||||
disputeSummaryWindow.result.buyerGetsTradeAmountMinusPenalty=(BTC buyer receives trade amount + his deposit - a penalty)
|
||||
disputeSummaryWindow.result.buyerGetsTradeAmountPlusCompensation=(BTC buyer receives trade amount + his deposit + compensation from seller)
|
||||
disputeSummaryWindow.result.buyerGetsHisDeposit=(BTC buyer receives his deposit)
|
||||
disputeSummaryWindow.result.buyerGetsHisDepositPlusPenaltyFromSeller=(BTC buyer receives his deposit + compensation from seller)
|
||||
disputeSummaryWindow.result.buyerGetsHisDepositMinusPenalty=(BTC buyer receives a penalty on his deposit)
|
||||
disputeSummaryWindow.result.sellerGetsTradeAmount=(BTC seller receives trade amount + his deposit)
|
||||
disputeSummaryWindow.result.sellerGetsTradeAmountMinusPenalty=(BTC seller receives trade amount + his deposit - a penalty)
|
||||
disputeSummaryWindow.result.sellerGetsTradeAmountPlusCompensation=(BTC seller receives trade amount + his deposit + compensation from buyer)
|
||||
disputeSummaryWindow.result.sellerGetsHisDeposit=(BTC seller receives his deposit)
|
||||
disputeSummaryWindow.result.sellerGetsHisDepositPlusPenaltyFromBuyer=(BTC seller receives his deposit + compensation from buyer)
|
||||
disputeSummaryWindow.result.sellerGetsHisDepositMinusPenalty=(BTC seller receives a penalty on his deposit)
|
||||
disputeSummaryWindow.result.customPayout=(a custom payout)
|
||||
disputeSummaryWindow.summaryNotes=Summary notes
|
||||
disputeSummaryWindow.addSummaryNotes=Add summary notes
|
||||
disputeSummaryWindow.close.button=Close ticket
|
||||
disputeSummaryWindow.close.button=Apply
|
||||
|
||||
# Do no change any line break or order of tokens as the structure is used for signature verification
|
||||
# suppress inspection "TrailingSpacesInProperty"
|
||||
@ -2879,7 +2908,11 @@ disputeSummaryWindow.close.msg=Ticket closed on {0}\n\
|
||||
disputeSummaryWindow.close.msgWithSig={0}{1}{2}{3}
|
||||
|
||||
disputeSummaryWindow.close.nextStepsForMediation=\nNext steps:\n\
|
||||
Open trade and accept or reject suggestion from mediator
|
||||
Go to Open trades to Accept the mediation proposal and wait for your peer's acceptance, if necessary.\n\
|
||||
Click Reject if you disagree and explain your reasons on the mediation ticket.\n\
|
||||
If the trade has not been paid out in the next 48h, please inform your mediator on this ticket.\n\
|
||||
Keep in mind that the arbitrator can only make the payout of the trade amount + 1 security deposit. The other security \
|
||||
deposit will be left unpaid.
|
||||
disputeSummaryWindow.close.nextStepsForRefundAgentArbitration=\nNext steps:\n\
|
||||
No further action is required from you. If the arbitrator decided in your favor, you'll see a "Refund from arbitration" transaction in Funds/Transactions
|
||||
disputeSummaryWindow.close.closePeer=You need to close also the trading peers ticket!
|
||||
@ -3318,6 +3351,7 @@ notification.trade.confirmed=Your trade has at least one blockchain confirmation
|
||||
notification.trade.paymentStarted=The BTC buyer has started the payment.
|
||||
notification.trade.selectTrade=Select trade
|
||||
notification.trade.peerOpenedDispute=Your trading peer has opened a {0}.
|
||||
notification.trade.disputeResolved=A proposed solution has been issued for the {0}.
|
||||
notification.trade.disputeClosed=The {0} has been closed.
|
||||
notification.walletUpdate.headline=Trading wallet update
|
||||
notification.walletUpdate.msg=Your trading wallet is sufficiently funded.\nAmount: {0}
|
||||
|
@ -163,6 +163,7 @@ public abstract class Overlay<T extends Overlay<T>> {
|
||||
protected boolean hideCloseButton;
|
||||
protected boolean isDisplayed;
|
||||
protected boolean disableActionButton;
|
||||
protected boolean disableTertiaryActionButton;
|
||||
|
||||
@Getter
|
||||
protected BooleanProperty isHiddenProperty = new SimpleBooleanProperty();
|
||||
@ -176,11 +177,11 @@ public abstract class Overlay<T extends Overlay<T>> {
|
||||
|
||||
protected Label headlineIcon, copyIcon, headLineLabel, messageLabel;
|
||||
protected String headLine, message, closeButtonText, actionButtonText,
|
||||
secondaryActionButtonText, dontShowAgainId, dontShowAgainText,
|
||||
secondaryActionButtonText, tertiaryActionButtonText, dontShowAgainId, dontShowAgainText,
|
||||
truncatedMessage;
|
||||
private ArrayList<String> messageHyperlinks;
|
||||
private String headlineStyle;
|
||||
protected Button actionButton, secondaryActionButton;
|
||||
protected Button actionButton, secondaryActionButton, tertiaryActionButton;
|
||||
private HBox buttonBox;
|
||||
protected AutoTooltipButton closeButton;
|
||||
|
||||
@ -189,6 +190,7 @@ public abstract class Overlay<T extends Overlay<T>> {
|
||||
protected Optional<Runnable> closeHandlerOptional = Optional.<Runnable>empty();
|
||||
protected Optional<Runnable> actionHandlerOptional = Optional.empty();
|
||||
protected Optional<Runnable> secondaryActionHandlerOptional = Optional.<Runnable>empty();
|
||||
protected Optional<Runnable> tertiaryActionHandlerOptional = Optional.<Runnable>empty();
|
||||
protected ChangeListener<Number> positionListener;
|
||||
|
||||
protected Timer centerTime;
|
||||
@ -301,6 +303,11 @@ public abstract class Overlay<T extends Overlay<T>> {
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T onTertiaryAction(Runnable actionHandlerOptional) {
|
||||
this.tertiaryActionHandlerOptional = Optional.of(actionHandlerOptional);
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T headLine(String headLine) {
|
||||
this.headLine = headLine;
|
||||
return cast();
|
||||
@ -433,6 +440,11 @@ public abstract class Overlay<T extends Overlay<T>> {
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T tertiaryActionButtonText(String text) {
|
||||
this.tertiaryActionButtonText = text;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T useShutDownButton() {
|
||||
this.actionButtonText = Res.get("shared.shutDown");
|
||||
this.actionHandlerOptional = Optional.ofNullable(BisqApp.getShutDownHandler());
|
||||
@ -489,6 +501,10 @@ public abstract class Overlay<T extends Overlay<T>> {
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T setTertiaryButtonDisabledState(boolean disableState) {
|
||||
this.disableTertiaryActionButton = disableState;
|
||||
return cast();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Protected
|
||||
@ -1006,6 +1022,17 @@ public abstract class Overlay<T extends Overlay<T>> {
|
||||
buttonBox.getChildren().add(secondaryActionButton);
|
||||
}
|
||||
|
||||
if (tertiaryActionButtonText != null && tertiaryActionHandlerOptional.isPresent()) {
|
||||
tertiaryActionButton = new AutoTooltipButton(tertiaryActionButtonText);
|
||||
tertiaryActionButton.setOnAction(event -> {
|
||||
hide();
|
||||
tertiaryActionHandlerOptional.ifPresent(Runnable::run);
|
||||
});
|
||||
|
||||
buttonBox.getChildren().add(tertiaryActionButton);
|
||||
tertiaryActionButton.setDisable(disableTertiaryActionButton);
|
||||
}
|
||||
|
||||
if (!hideCloseButton)
|
||||
buttonBox.getChildren().add(closeButton);
|
||||
} else if (!hideCloseButton) {
|
||||
|
@ -265,7 +265,7 @@ public class NotificationCenter {
|
||||
message = Res.get("notification.trade.peerOpenedDispute", disputeOrTicket);
|
||||
break;
|
||||
case MEDIATION_CLOSED:
|
||||
message = Res.get("notification.trade.disputeClosed", disputeOrTicket);
|
||||
message = Res.get("notification.trade.disputeResolved", disputeOrTicket);
|
||||
break;
|
||||
default:
|
||||
// if (DevEnv.isDevMode()) {
|
||||
|
@ -240,8 +240,9 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||
addPayoutAmountTextFields();
|
||||
addReasonControls();
|
||||
applyDisputeResultToUiControls();
|
||||
|
||||
boolean applyPeersDisputeResult = peersDisputeOptional.isPresent() && peersDisputeOptional.get().isClosed();
|
||||
boolean applyPeersDisputeResult = peersDisputeOptional.isPresent() && (
|
||||
peersDisputeOptional.get().getDisputeState() == Dispute.State.RESULT_PROPOSED ||
|
||||
peersDisputeOptional.get().getDisputeState() == Dispute.State.CLOSED);
|
||||
if (applyPeersDisputeResult) {
|
||||
// If the other peers dispute has been closed we apply the result to ourselves
|
||||
DisputeResult peersDisputeResult = peersDisputeOptional.get().getDisputeResultProperty().get();
|
||||
@ -697,7 +698,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||
}
|
||||
|
||||
if (peersDisputeOptional.isPresent() && peersDisputeOptional.get().isClosed()) {
|
||||
closeTicket(closeTicketButton); // all checks done already on peers ticket
|
||||
applyDisputeResult(closeTicketButton); // all checks done already on peers ticket
|
||||
} else {
|
||||
maybeCheckTransactions().thenAccept(continue1 -> {
|
||||
if (continue1) {
|
||||
@ -705,7 +706,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||
if (continue2) {
|
||||
maybeMakePayout().thenAccept(continue3 -> {
|
||||
if (continue3) {
|
||||
closeTicket(closeTicketButton);
|
||||
applyDisputeResult(closeTicketButton);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -964,17 +965,16 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||
return asyncStatus;
|
||||
}
|
||||
|
||||
private void closeTicket(Button closeTicketButton) {
|
||||
private void applyDisputeResult(Button closeTicketButton) {
|
||||
DisputeManager<? extends DisputeList<Dispute>> disputeManager = getDisputeManager(dispute);
|
||||
if (disputeManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isRefundAgent = disputeManager instanceof RefundManager;
|
||||
disputeResult.setLoserPublisher(false); // field no longer used per pazza / leo816
|
||||
disputeResult.setCloseDate(new Date());
|
||||
dispute.setDisputeResult(disputeResult);
|
||||
dispute.setIsClosed();
|
||||
dispute.setState(isRefundAgent ? Dispute.State.CLOSED : Dispute.State.RESULT_PROPOSED);
|
||||
DisputeResult.Reason reason = disputeResult.getReason();
|
||||
|
||||
summaryNotesTextArea.textProperty().unbindBidirectional(disputeResult.summaryNotesProperty());
|
||||
@ -992,8 +992,10 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||
Res.get("disputeSummaryWindow.reason." + reason.name()),
|
||||
disputeResult.getPayoutSuggestionText(),
|
||||
amount,
|
||||
formatter.formatCoinWithCode(disputeResult.getBuyerPayoutAmount()),
|
||||
formatter.formatCoinWithCode(disputeResult.getSellerPayoutAmount()),
|
||||
formatter.formatCoinWithCode(disputeResult.getBuyerPayoutAmount()) +
|
||||
(isRefundAgent ? "" : " " + disputeResult.getPayoutSuggestionCustomizedToBuyerOrSeller(true)),
|
||||
formatter.formatCoinWithCode(disputeResult.getSellerPayoutAmount()) +
|
||||
(isRefundAgent ? "" : " " + disputeResult.getPayoutSuggestionCustomizedToBuyerOrSeller(false)),
|
||||
disputeResult.summaryNotesProperty().get()
|
||||
);
|
||||
|
||||
@ -1013,12 +1015,14 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
||||
|
||||
disputeManager.sendDisputeResultMessage(disputeResult, dispute, summaryText);
|
||||
|
||||
if (peersDisputeOptional.isPresent() && !peersDisputeOptional.get().isClosed() && !DevEnv.isDevMode()) {
|
||||
UserThread.runAfter(() -> new Popup()
|
||||
.attention(Res.get("disputeSummaryWindow.close.closePeer"))
|
||||
.show(),
|
||||
200, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
peersDisputeOptional.ifPresent(peersDispute -> {
|
||||
if (!peersDispute.isResultProposed() && !peersDispute.isClosed()) {
|
||||
UserThread.runAfter(() -> new Popup()
|
||||
.attention(Res.get("disputeSummaryWindow.close.closePeer"))
|
||||
.show(),
|
||||
200, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
});
|
||||
|
||||
finalizeDisputeHandlerOptional.ifPresent(Runnable::run);
|
||||
|
||||
|
@ -644,35 +644,43 @@ public abstract class TradeStepView extends AnchorPane {
|
||||
.headLine(headLine)
|
||||
.instruction(message)
|
||||
.actionButtonText(actionButtonText)
|
||||
.onAction(() -> {
|
||||
model.dataModel.mediationManager.onAcceptMediationResult(trade,
|
||||
() -> {
|
||||
log.info("onAcceptMediationResult completed");
|
||||
acceptMediationResultPopup = null;
|
||||
},
|
||||
errorMessage -> {
|
||||
UserThread.execute(() -> {
|
||||
new Popup().error(errorMessage).show();
|
||||
if (acceptMediationResultPopup != null) {
|
||||
acceptMediationResultPopup.hide();
|
||||
acceptMediationResultPopup = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
.secondaryActionButtonText(Res.get("portfolio.pending.mediationResult.popup.openArbitration"))
|
||||
.onSecondaryAction(() -> {
|
||||
model.dataModel.mediationManager.rejectMediationResult(trade);
|
||||
model.dataModel.onOpenDispute();
|
||||
acceptMediationResultPopup = null;
|
||||
})
|
||||
.onClose(() -> {
|
||||
acceptMediationResultPopup = null;
|
||||
});
|
||||
|
||||
.onAction(this::acceptProposal)
|
||||
.secondaryActionButtonText(Res.get("portfolio.pending.mediationResult.popup.reject"))
|
||||
.onSecondaryAction(this::rejectProposal)
|
||||
.tertiaryActionButtonText(Res.get("portfolio.pending.mediationResult.popup.openArbitration"))
|
||||
.onTertiaryAction(this::startArbitration)
|
||||
.setTertiaryButtonDisabledState(remaining > 0)
|
||||
.onClose(() -> acceptMediationResultPopup = null);
|
||||
acceptMediationResultPopup.show();
|
||||
}
|
||||
|
||||
private void acceptProposal() {
|
||||
model.dataModel.mediationManager.onAcceptMediationResult(trade,
|
||||
() -> {
|
||||
log.info("onAcceptMediationResult completed");
|
||||
acceptMediationResultPopup = null;
|
||||
},
|
||||
errorMessage -> {
|
||||
UserThread.execute(() -> {
|
||||
new Popup().error(errorMessage).show();
|
||||
if (acceptMediationResultPopup != null) {
|
||||
acceptMediationResultPopup.hide();
|
||||
acceptMediationResultPopup = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void rejectProposal() {
|
||||
model.dataModel.mediationManager.rejectMediationResult(trade);
|
||||
acceptMediationResultPopup = null;
|
||||
}
|
||||
|
||||
private void startArbitration() {
|
||||
model.dataModel.onOpenDispute();
|
||||
acceptMediationResultPopup = null;
|
||||
}
|
||||
|
||||
protected String getCurrencyName(Trade trade) {
|
||||
return CurrencyUtil.getNameByCode(getCurrencyCode(trade));
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import bisq.desktop.components.AutoTooltipLabel;
|
||||
import bisq.desktop.components.BisqTextArea;
|
||||
import bisq.desktop.components.BusyAnimation;
|
||||
import bisq.desktop.components.TableGroupHeadline;
|
||||
import bisq.desktop.main.overlays.notifications.Notification;
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.util.DisplayUtils;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
@ -214,6 +215,10 @@ public class ChatView extends AnchorPane {
|
||||
|
||||
Button uploadButton = new AutoTooltipButton(Res.get("support.addAttachments"));
|
||||
uploadButton.setOnAction(e -> onRequestUpload());
|
||||
Button clipboardButton = new AutoTooltipButton(Res.get("shared.copyToClipboard"));
|
||||
clipboardButton.setOnAction(e -> copyChatMessagesToClipboard(clipboardButton));
|
||||
uploadButton.setStyle("-fx-pref-width: 66; -fx-padding: 3 3 3 3;");
|
||||
clipboardButton.setStyle("-fx-pref-width: 50; -fx-padding: 3 3 3 3;");
|
||||
|
||||
sendMsgInfoLabel = new AutoTooltipLabel();
|
||||
sendMsgInfoLabel.setVisible(false);
|
||||
@ -229,7 +234,7 @@ public class ChatView extends AnchorPane {
|
||||
HBox buttonBox = new HBox();
|
||||
buttonBox.setSpacing(10);
|
||||
if (allowAttachments)
|
||||
buttonBox.getChildren().addAll(sendButton, uploadButton, sendMsgBusyAnimation, sendMsgInfoLabel);
|
||||
buttonBox.getChildren().addAll(sendButton, uploadButton, clipboardButton, sendMsgBusyAnimation, sendMsgInfoLabel);
|
||||
else
|
||||
buttonBox.getChildren().addAll(sendButton, sendMsgBusyAnimation, sendMsgInfoLabel);
|
||||
|
||||
@ -591,6 +596,24 @@ public class ChatView extends AnchorPane {
|
||||
}
|
||||
}
|
||||
|
||||
private void copyChatMessagesToClipboard(Button sourceBtn) {
|
||||
optionalSupportSession.ifPresent(session -> {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
chatMessages.forEach(i -> {
|
||||
String metaData = DisplayUtils.formatDateTime(new Date(i.getDate()));
|
||||
metaData = metaData + (i.isSystemMessage() ? " (System message)" :
|
||||
(i.isSenderIsTrader() ? " (from Trader)" : " (from Agent)"));
|
||||
stringBuilder.append(metaData).append("\n").append(i.getMessage()).append("\n\n");
|
||||
});
|
||||
Utilities.copyToClipboard(stringBuilder.toString());
|
||||
new Notification()
|
||||
.notification(Res.get("shared.copiedToClipboard"))
|
||||
.hideCloseButton()
|
||||
.autoClose()
|
||||
.show();
|
||||
});
|
||||
}
|
||||
|
||||
private void onOpenAttachment(Attachment attachment) {
|
||||
if (!allowAttachments)
|
||||
return;
|
||||
|
@ -94,8 +94,8 @@ import javafx.geometry.Pos;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.property.ReadOnlyStringProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
|
||||
import javafx.collections.ListChangeListener;
|
||||
@ -181,7 +181,7 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
protected FilteredList<Dispute> filteredList;
|
||||
protected InputTextField filterTextField;
|
||||
private ChangeListener<String> filterTextFieldListener;
|
||||
protected AutoTooltipButton sigCheckButton, reOpenButton, closeButton, sendPrivateNotificationButton, reportButton, fullReportButton;
|
||||
protected AutoTooltipButton sigCheckButton, openOrCloseButton, sendPrivateNotificationButton, reportButton, fullReportButton;
|
||||
private final Map<String, ListChangeListener<ChatMessage>> disputeChatMessagesListeners = new HashMap<>();
|
||||
@Nullable
|
||||
private ListChangeListener<Dispute> disputesListener; // Only set in mediation cases
|
||||
@ -256,22 +256,17 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
alertIconLabel.setVisible(false);
|
||||
alertIconLabel.setManaged(false);
|
||||
|
||||
reOpenButton = new AutoTooltipButton(Res.get("support.reOpenButton.label"));
|
||||
reOpenButton.setDisable(true);
|
||||
reOpenButton.setVisible(false);
|
||||
reOpenButton.setManaged(false);
|
||||
HBox.setHgrow(reOpenButton, Priority.NEVER);
|
||||
reOpenButton.setOnAction(e -> {
|
||||
reOpenDisputeFromButton();
|
||||
});
|
||||
|
||||
closeButton = new AutoTooltipButton(Res.get("support.closeTicket"));
|
||||
closeButton.setDisable(true);
|
||||
closeButton.setVisible(false);
|
||||
closeButton.setManaged(false);
|
||||
HBox.setHgrow(closeButton, Priority.NEVER);
|
||||
closeButton.setOnAction(e -> {
|
||||
closeDisputeFromButton();
|
||||
openOrCloseButton = new AutoTooltipButton(Res.get("support.reOpenButton.label"));
|
||||
openOrCloseButton.setDisable(true);
|
||||
openOrCloseButton.setVisible(false);
|
||||
openOrCloseButton.setManaged(false);
|
||||
HBox.setHgrow(openOrCloseButton, Priority.NEVER);
|
||||
openOrCloseButton.setOnAction(e -> {
|
||||
if (openOrCloseButton.getText().equalsIgnoreCase(Res.get("support.closeTicket"))) {
|
||||
closeDisputeFromButton();
|
||||
} else {
|
||||
reOpenDisputeFromButton();
|
||||
}
|
||||
});
|
||||
|
||||
sendPrivateNotificationButton = new AutoTooltipButton(Res.get("support.sendNotificationButton.label"));
|
||||
@ -314,8 +309,7 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
filterTextField,
|
||||
alertIconLabel,
|
||||
spacer,
|
||||
reOpenButton,
|
||||
closeButton,
|
||||
openOrCloseButton,
|
||||
sendPrivateNotificationButton,
|
||||
reportButton,
|
||||
fullReportButton,
|
||||
@ -449,7 +443,8 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
|
||||
// For open filter we do not want to continue further as json data would cause a match
|
||||
if (filter.equalsIgnoreCase("open")) {
|
||||
return !dispute.isClosed() ? FilterResult.OPEN_DISPUTES : FilterResult.NO_MATCH;
|
||||
return !dispute.isClosed() || dispute.unreadMessageCount(senderFlag()) > 0 ?
|
||||
FilterResult.OPEN_DISPUTES : FilterResult.NO_MATCH;
|
||||
}
|
||||
|
||||
if (dispute.getTradeId().toLowerCase().contains(filter)) {
|
||||
@ -509,18 +504,14 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
}
|
||||
|
||||
// a derived version in the ClientView for users pops up an "Are you sure" box first.
|
||||
// this version includes the sending of an automatic message to the user, see addMediationReOpenedMessage
|
||||
protected void reOpenDisputeFromButton() {
|
||||
if (reOpenDispute()) {
|
||||
disputeManager.addMediationReOpenedMessage(selectedDispute, false);
|
||||
}
|
||||
reOpenDispute();
|
||||
}
|
||||
|
||||
// only applicable to traders
|
||||
// only allow them to close the dispute if the trade is paid out
|
||||
// traders -> only allow them to close the dispute if the trade is paid out
|
||||
// the reason for having this is that sometimes traders end up with closed disputes that are not "closed" @pazza
|
||||
protected void closeDisputeFromButton() {
|
||||
disputeManager.findTrade(selectedDispute).ifPresent(
|
||||
disputeManager.findTrade(selectedDispute).ifPresentOrElse(
|
||||
(trade) -> {
|
||||
if (trade.isFundsLockedIn()) {
|
||||
new Popup().warning(Res.get("support.warning.traderCloseOwnDisputeWarning")).show();
|
||||
@ -529,6 +520,11 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
disputeManager.requestPersistence();
|
||||
onSelectDispute(selectedDispute);
|
||||
}
|
||||
},
|
||||
() -> {
|
||||
selectedDispute.setIsClosed();
|
||||
disputeManager.requestPersistence();
|
||||
onSelectDispute(selectedDispute);
|
||||
});
|
||||
}
|
||||
|
||||
@ -542,7 +538,6 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
isNodeAddressOk(selectedDispute,
|
||||
!disputeManager.isTrader(selectedDispute))) {
|
||||
selectedDispute.reOpen();
|
||||
handleOnProcessDispute(selectedDispute);
|
||||
disputeManager.requestPersistence();
|
||||
onSelectDispute(selectedDispute);
|
||||
return true;
|
||||
@ -580,9 +575,15 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
selectedDispute = dispute;
|
||||
}
|
||||
|
||||
reOpenButton.setDisable(selectedDispute == null || !selectedDispute.isClosed());
|
||||
closeButton.setDisable(selectedDispute == null || selectedDispute.isClosed());
|
||||
sendPrivateNotificationButton.setDisable(selectedDispute == null);
|
||||
if (selectedDispute == null) {
|
||||
openOrCloseButton.setDisable(true);
|
||||
sendPrivateNotificationButton.setDisable(true);
|
||||
} else {
|
||||
openOrCloseButton.setDisable(false);
|
||||
sendPrivateNotificationButton.setDisable(false);
|
||||
openOrCloseButton.setText(selectedDispute.isClosed() ?
|
||||
Res.get("support.reOpenButton.label").toUpperCase() : Res.get("support.closeTicket").toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1140,7 +1141,8 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
private TableColumn<Dispute, Dispute> getDateColumn() {
|
||||
TableColumn<Dispute, Dispute> column = new AutoTooltipTableColumn<>(Res.get("shared.date")) {
|
||||
{
|
||||
setMinWidth(180);
|
||||
setMinWidth(100);
|
||||
setPrefWidth(150);
|
||||
}
|
||||
};
|
||||
column.setCellValueFactory((dispute) -> new ReadOnlyObjectWrapper<>(dispute.getValue()));
|
||||
@ -1166,7 +1168,8 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
private TableColumn<Dispute, Dispute> getTradeIdColumn() {
|
||||
TableColumn<Dispute, Dispute> column = new AutoTooltipTableColumn<>(Res.get("shared.tradeId")) {
|
||||
{
|
||||
setMinWidth(110);
|
||||
setMinWidth(50);
|
||||
setPrefWidth(100);
|
||||
}
|
||||
};
|
||||
column.setCellValueFactory((dispute) -> new ReadOnlyObjectWrapper<>(dispute.getValue()));
|
||||
@ -1303,7 +1306,7 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
private TableColumn<Dispute, Dispute> getMarketColumn() {
|
||||
TableColumn<Dispute, Dispute> column = new AutoTooltipTableColumn<>(Res.get("shared.market")) {
|
||||
{
|
||||
setMinWidth(80);
|
||||
setMinWidth(60);
|
||||
}
|
||||
};
|
||||
column.setCellValueFactory((dispute) -> new ReadOnlyObjectWrapper<>(dispute.getValue()));
|
||||
@ -1329,7 +1332,7 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
private TableColumn<Dispute, Dispute> getRoleColumn() {
|
||||
TableColumn<Dispute, Dispute> column = new AutoTooltipTableColumn<>(Res.get("support.role")) {
|
||||
{
|
||||
setMinWidth(130);
|
||||
setMinWidth(80);
|
||||
}
|
||||
};
|
||||
column.setCellValueFactory((dispute) -> new ReadOnlyObjectWrapper<>(dispute.getValue()));
|
||||
@ -1390,7 +1393,8 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
private TableColumn<Dispute, Dispute> getStateColumn() {
|
||||
TableColumn<Dispute, Dispute> column = new AutoTooltipTableColumn<>(Res.get("support.state")) {
|
||||
{
|
||||
setMinWidth(50);
|
||||
setMinWidth(75);
|
||||
setPrefWidth(100);
|
||||
}
|
||||
};
|
||||
column.getStyleClass().add("last-column");
|
||||
@ -1402,8 +1406,8 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
return new TableCell<>() {
|
||||
|
||||
|
||||
ReadOnlyBooleanProperty closedProperty;
|
||||
ChangeListener<Boolean> listener;
|
||||
ReadOnlyStringProperty closedProperty;
|
||||
ChangeListener<String> listener;
|
||||
|
||||
@Override
|
||||
public void updateItem(final Dispute item, boolean empty) {
|
||||
@ -1414,16 +1418,16 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
}
|
||||
|
||||
listener = (observable, oldValue, newValue) -> {
|
||||
setText(newValue ? Res.get("support.closed") : Res.get("support.open"));
|
||||
setText(newValue);
|
||||
if (getTableRow() != null)
|
||||
getTableRow().setOpacity(newValue && item.getBadgeCountProperty().get() == 0 ? 0.4 : 1);
|
||||
getTableRow().setOpacity(newValue.equalsIgnoreCase("CLOSED") && item.getBadgeCountProperty().get() == 0 ? 0.4 : 1);
|
||||
if (item.isClosed() && item == chatPopup.getSelectedDispute())
|
||||
chatPopup.closeChat(); // close the chat popup when the associated ticket is closed
|
||||
};
|
||||
closedProperty = item.isClosedProperty();
|
||||
closedProperty = item.getDisputeStateProperty();
|
||||
closedProperty.addListener(listener);
|
||||
boolean isClosed = item.isClosed();
|
||||
setText(isClosed ? Res.get("support.closed") : Res.get("support.open"));
|
||||
setText(closedProperty.get());
|
||||
if (getTableRow() != null)
|
||||
getTableRow().setOpacity(isClosed && item.getBadgeCountProperty().get() == 0 ? 0.4 : 1);
|
||||
} else {
|
||||
@ -1496,7 +1500,11 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements
|
||||
|
||||
@Override
|
||||
public void onCloseDisputeFromChatWindow(Dispute dispute) {
|
||||
handleOnProcessDispute(dispute);
|
||||
if (dispute.getDisputeState() == Dispute.State.NEW || dispute.getDisputeState() == Dispute.State.OPEN) {
|
||||
handleOnProcessDispute(dispute);
|
||||
} else {
|
||||
closeDisputeFromButton();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -257,7 +257,8 @@ public abstract class DisputeAgentView extends DisputeView implements MultipleHo
|
||||
private TableColumn<Dispute, Dispute> getAlertColumn() {
|
||||
TableColumn<Dispute, Dispute> column = new AutoTooltipTableColumn<>("Alert") {
|
||||
{
|
||||
setMinWidth(50);
|
||||
setMinWidth(20);
|
||||
setPrefWidth(20);
|
||||
}
|
||||
};
|
||||
column.getStyleClass().add("last-column");
|
||||
|
@ -85,8 +85,8 @@ public class MediatorView extends DisputeAgentView {
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
reOpenButton.setVisible(true);
|
||||
reOpenButton.setManaged(true);
|
||||
openOrCloseButton.setVisible(true);
|
||||
openOrCloseButton.setManaged(true);
|
||||
setupReOpenDisputeListener();
|
||||
}
|
||||
|
||||
|
@ -76,10 +76,8 @@ public class MediationClientView extends DisputeClientView {
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
reOpenButton.setVisible(true);
|
||||
reOpenButton.setManaged(true);
|
||||
closeButton.setVisible(true);
|
||||
closeButton.setManaged(true);
|
||||
openOrCloseButton.setVisible(true);
|
||||
openOrCloseButton.setManaged(true);
|
||||
setupReOpenDisputeListener();
|
||||
}
|
||||
|
||||
|
@ -909,6 +909,7 @@ message Dispute {
|
||||
OPEN = 2;
|
||||
REOPENED = 3;
|
||||
CLOSED = 4;
|
||||
RESULT_PROPOSED = 5;
|
||||
}
|
||||
string trade_id = 1;
|
||||
string id = 2;
|
||||
|
Loading…
Reference in New Issue
Block a user