Skip to content

Commit

Permalink
Merge pull request #6597 from jmacxx/support_ui_improvements
Browse files Browse the repository at this point in the history
Support UI improvements
  • Loading branch information
ripcurlx authored Mar 27, 2023
2 parents 324f5ef + c404eb1 commit cf56ae3
Show file tree
Hide file tree
Showing 18 changed files with 329 additions and 147 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,12 @@ private void setDisputeListener(Dispute dispute) {
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;
}
Expand All @@ -116,22 +116,5 @@ private void onChatMessage(ChatMessage chatMessage, Dispute dispute) {
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();
}
}
}
}
23 changes: 12 additions & 11 deletions core/src/main/java/bisq/core/support/dispute/Dispute.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,14 @@

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;
Expand Down Expand Up @@ -82,7 +81,8 @@ public enum State {
NEW,
OPEN,
REOPENED,
CLOSED;
CLOSED,
RESULT_PROPOSED;

public static Dispute.State fromProto(protobuf.Dispute.State state) {
return ProtoUtil.enumFromProto(Dispute.State.class, state.name());
Expand Down Expand Up @@ -168,7 +168,7 @@ public static protobuf.Dispute.State toProtoMessage(Dispute.State state) {
@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;
Expand Down Expand Up @@ -239,6 +239,7 @@ public Dispute(long openingDate,

id = tradeId + "_" + traderId;
uid = UUID.randomUUID().toString();
setState(State.NEW);
refreshAlertLevel(true);
}

Expand Down Expand Up @@ -413,7 +414,7 @@ public void reOpen() {

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) {
Expand All @@ -438,10 +439,6 @@ public String getShortTradeId() {
return Utilities.getShortId(tradeId);
}

public ReadOnlyBooleanProperty isClosedProperty() {
return isClosedProperty;
}

public ReadOnlyIntegerProperty getBadgeCountProperty() {
return badgeCountProperty;
}
Expand Down Expand Up @@ -470,6 +467,10 @@ public boolean isClosed() {
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
Expand Down Expand Up @@ -559,7 +560,7 @@ public String toString() {
",\n agentPubKeyRing=" + agentPubKeyRing +
",\n isSupportTicket=" + isSupportTicket +
",\n chatMessages=" + chatMessages +
",\n isClosedProperty=" + isClosedProperty +
",\n disputeStateProperty=" + disputeStateProperty +
",\n disputeResultProperty=" + disputeResultProperty +
",\n disputePayoutTxId='" + disputePayoutTxId + '\'' +
",\n openingDate=" + openingDate +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ protected String getFileName() {
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);
}
});
Expand Down
66 changes: 57 additions & 9 deletions core/src/main/java/bisq/core/support/dispute/DisputeManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
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;
Expand Down Expand Up @@ -254,6 +255,7 @@ public void onAllServicesInitialized() {
@Override
public void onUpdatedDataReceived() {
tryApplyMessages();
checkDisputesForUpdates();
}
});

Expand All @@ -263,8 +265,9 @@ public void onUpdatedDataReceived() {
});

walletsSetup.numPeersProperty().addListener((observable, oldValue, newValue) -> {
if (walletsSetup.hasSufficientPeersForBroadcast())
if (walletsSetup.hasSufficientPeersForBroadcast()) {
tryApplyMessages();
}
});

tryApplyMessages();
Expand Down Expand Up @@ -292,6 +295,46 @@ public void onUpdatedDataReceived() {
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());
}
Expand Down Expand Up @@ -650,8 +693,6 @@ private void doSendPeerOpenedDisputeMessage(Dispute disputeFromOpener,
chatMessage.setSystemMessage(true);
dispute.addAndPersistChatMessage(chatMessage);

addPriceInfoMessage(dispute, 0);

disputeList.add(dispute);

// We mirrored dispute already!
Expand Down Expand Up @@ -719,6 +760,7 @@ public void onFault(String errorMessage) {
}
}
);
addPriceInfoMessage(dispute, 0);
requestPersistence();
}

Expand Down Expand Up @@ -897,17 +939,23 @@ private void addMediationResultMessage(Dispute dispute) {
}
}

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();
}

Expand Down
41 changes: 41 additions & 0 deletions core/src/main/java/bisq/core/support/dispute/DisputeResult.java
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,47 @@ public String getPayoutSuggestionText() {
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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,12 @@ protected AckMessageSourceType getAckMessageSourceType() {

@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
Expand Down Expand Up @@ -201,7 +205,7 @@ public void onDisputeResultMessage(DisputeResultMessage disputeResultMessage) {
} 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);

Expand All @@ -216,6 +220,7 @@ public void onDisputeResultMessage(DisputeResultMessage disputeResultMessage) {
trade.setDisputeState(Trade.DisputeState.MEDIATION_CLOSED);

tradeManager.requestPersistence();
checkForMediatedTradePayout(trade, dispute);
}
} else {
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(tradeId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ protected String getDisputeInfo(Dispute dispute) {
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
Expand Down
Loading

0 comments on commit cf56ae3

Please sign in to comment.