Skip to content

Commit 164d0fb

Browse files
Merge pull request #3273 from HenrikJannsen/add-new-mediator-selection-version
Add new mediator selection version
2 parents 4b48f66 + 9cd22f8 commit 164d0fb

File tree

21 files changed

+299
-155
lines changed

21 files changed

+299
-155
lines changed

apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/open_trades/message_box/MyProtocolLogMessageBox.java

+5-32
Original file line numberDiff line numberDiff line change
@@ -19,51 +19,24 @@
1919

2020
import bisq.chat.ChatChannel;
2121
import bisq.chat.ChatMessage;
22-
import bisq.desktop.components.controls.BisqMenuItem;
2322
import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem;
2423
import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListController;
25-
import javafx.geometry.Pos;
26-
import javafx.scene.layout.HBox;
27-
import org.fxmisc.easybind.EasyBind;
28-
import org.fxmisc.easybind.Subscription;
24+
import bisq.desktop.main.content.chat.message_container.list.message_box.MessageDeliveryStatusBox;
2925

3026
public class MyProtocolLogMessageBox extends PeerProtocolLogMessageBox {
31-
private final Subscription shouldShowTryAgainPin, messageDeliveryStatusNodePin;
27+
private final MessageDeliveryStatusBox messageDeliveryStatusBox;
3228

3329
public MyProtocolLogMessageBox(ChatMessageListItem<? extends ChatMessage, ? extends ChatChannel<? extends ChatMessage>> item,
3430
ChatMessagesListController controller) {
3531
super(item);
3632

37-
BisqMenuItem tryAgainMenuItem = item.getTryAgainMenuItem();
38-
HBox deliveryStateHBox = new HBox();
39-
deliveryStateHBox.setAlignment(Pos.CENTER);
40-
HBox messageStatusHbox = new HBox(5, tryAgainMenuItem, deliveryStateHBox);
41-
messageStatusHbox.setAlignment(Pos.CENTER);
33+
messageDeliveryStatusBox = new MessageDeliveryStatusBox(item, controller);
4234

43-
messageDeliveryStatusNodePin = EasyBind.subscribe(item.getMessageDeliveryStatusNode(), node -> {
44-
deliveryStateHBox.setManaged(node != null);
45-
deliveryStateHBox.setVisible(node != null);
46-
if (node != null) {
47-
deliveryStateHBox.getChildren().setAll(node);
48-
}
49-
});
50-
51-
shouldShowTryAgainPin = EasyBind.subscribe(item.getShouldShowTryAgain(), showTryAgain -> {
52-
tryAgainMenuItem.setVisible(showTryAgain);
53-
tryAgainMenuItem.setManaged(showTryAgain);
54-
if (showTryAgain) {
55-
tryAgainMenuItem.setOnAction(e -> controller.onResendMessage(item.getMessageId()));
56-
} else {
57-
tryAgainMenuItem.setOnAction(null);
58-
}
59-
});
60-
61-
dateTimeHBox.getChildren().add(0, messageStatusHbox);
35+
dateTimeHBox.getChildren().add(0, messageDeliveryStatusBox);
6236
}
6337

6438
@Override
6539
public void dispose() {
66-
shouldShowTryAgainPin.unsubscribe();
67-
messageDeliveryStatusNodePin.unsubscribe();
40+
messageDeliveryStatusBox.dispose();
6841
}
6942
}

apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/take_offer/review/TakeOfferReviewController.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,9 @@ public void takeOffer(Runnable onCancelHandler) {
171171
onCancelHandler.run();
172172
return;
173173
}
174-
Optional<UserProfile> mediator = mediationRequestService.selectMediator(bisqEasyOffer.getMakersUserProfileId(), takerIdentity.getId());
174+
Optional<UserProfile> mediator = mediationRequestService.selectMediator(bisqEasyOffer.getMakersUserProfileId(),
175+
takerIdentity.getId(),
176+
bisqEasyOffer.getId());
175177
if (!DevMode.isDevMode() && mediator.isEmpty()) {
176178
new Popup().warning(Res.get("bisqEasy.takeOffer.noMediatorAvailable.warning"))
177179
.closeButtonText(Res.get("action.cancel"))
@@ -204,11 +206,11 @@ private void doTakeOffer(BisqEasyOffer bisqEasyOffer, UserIdentity takerIdentity
204206
log.info("Selected mediator for trade {}: {}", bisqEasyTrade.getShortId(), mediator.map(UserProfile::getUserName).orElse("N/A"));
205207
model.setBisqEasyTrade(bisqEasyTrade);
206208
errorMessagePin = bisqEasyTrade.errorMessageObservable().addObserver(errorMessage -> {
207-
if (errorMessage != null) {
208-
UIThread.run(() -> new Popup().error(Res.get("bisqEasy.openTrades.failed.popup",
209-
errorMessage,
210-
StringUtils.truncate(bisqEasyTrade.getErrorStackTrace(), 500)))
211-
.show());
209+
if (errorMessage != null) {
210+
UIThread.run(() -> new Popup().error(Res.get("bisqEasy.openTrades.failed.popup",
211+
errorMessage,
212+
StringUtils.truncate(bisqEasyTrade.getErrorStackTrace(), 500)))
213+
.show());
212214
}
213215
}
214216
);

apps/desktop/desktop/src/main/java/bisq/desktop/main/content/bisq_easy/trade_wizard/review/TradeWizardReviewController.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,9 @@ public void takeOffer() {
423423
// If taker is banned we don't need to show them a popup
424424
return;
425425
}
426-
Optional<UserProfile> mediator = mediationRequestService.selectMediator(bisqEasyOffer.getMakersUserProfileId(), takerIdentity.getId());
426+
Optional<UserProfile> mediator = mediationRequestService.selectMediator(bisqEasyOffer.getMakersUserProfileId(),
427+
takerIdentity.getId(),
428+
bisqEasyOffer.getId());
427429
if (!DevMode.isDevMode() && mediator.isEmpty()) {
428430
new Popup().warning(Res.get("bisqEasy.takeOffer.noMediatorAvailable.warning"))
429431
.closeButtonText(Res.get("action.cancel"))

apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessageListItem.java

+39-39
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@
2929
import bisq.chat.Citation;
3030
import bisq.chat.bisq_easy.BisqEasyOfferMessage;
3131
import bisq.chat.bisq_easy.offerbook.BisqEasyOfferbookMessage;
32+
import bisq.chat.bisq_easy.open_trades.BisqEasyOpenTradeMessage;
3233
import bisq.chat.priv.PrivateChatMessage;
3334
import bisq.chat.pub.PublicChatChannel;
3435
import bisq.chat.reactions.ChatMessageReaction;
3536
import bisq.chat.reactions.Reaction;
3637
import bisq.common.currency.Market;
3738
import bisq.common.data.Pair;
39+
import bisq.common.data.Triple;
3840
import bisq.common.locale.LanguageRepository;
3941
import bisq.common.observable.Observable;
4042
import bisq.common.observable.Pin;
@@ -50,6 +52,7 @@
5052
import bisq.i18n.Res;
5153
import bisq.network.NetworkService;
5254
import bisq.network.identity.NetworkId;
55+
import bisq.network.p2p.services.confidential.ack.AckRequestingMessage;
5356
import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus;
5457
import bisq.network.p2p.services.confidential.resend.ResendMessageService;
5558
import bisq.offer.Direction;
@@ -73,8 +76,6 @@
7376
import javafx.beans.property.BooleanProperty;
7477
import javafx.beans.property.SimpleBooleanProperty;
7578
import javafx.beans.property.SimpleObjectProperty;
76-
import javafx.scene.Node;
77-
import javafx.scene.control.Label;
7879
import javafx.scene.image.ImageView;
7980
import lombok.EqualsAndHashCode;
8081
import lombok.Getter;
@@ -108,17 +109,14 @@ public final class ChatMessageListItem<M extends ChatMessage, C extends ChatChan
108109
private final ReputationScore reputationScore;
109110
private final ReputationScoreDisplay reputationScoreDisplay = new ReputationScoreDisplay();
110111
private final boolean offerAlreadyTaken;
111-
@Nullable
112-
private String messageId;
113112
private final MarketPriceService marketPriceService;
114113
private final UserIdentityService userIdentityService;
115114
private final BooleanProperty showHighlighted = new SimpleBooleanProperty();
116115

117116
// Delivery status
118117
private final Set<Pin> mapPins = new HashSet<>();
119118
private final Set<Pin> statusPins = new HashSet<>();
120-
private final BooleanProperty shouldShowTryAgain = new SimpleBooleanProperty();
121-
private final SimpleObjectProperty<Node> messageDeliveryStatusNode = new SimpleObjectProperty<>();
119+
private final SimpleObjectProperty<Map<String, Triple<MessageDeliveryStatus, String, Boolean>>> messageDeliveryStatusByPeerProfileId = new SimpleObjectProperty<>();
122120
private final Optional<ResendMessageService> resendMessageService;
123121
private ImageView successfulDeliveryIcon, connectingDeliveryIcon, pendingDeliveryIcon, addedToMailboxIcon, failedDeliveryIcon;
124122
private BisqMenuItem tryAgainMenuItem;
@@ -349,9 +347,30 @@ private void initializeDeliveryStatusIcons() {
349347
private void addSubscriptionToMessageDeliveryStatus(NetworkService networkService) {
350348
mapPins.add(networkService.getMessageDeliveryStatusByMessageId().addObserver(new HashMapObserver<>() {
351349
@Override
352-
public void put(String messageId, Observable<MessageDeliveryStatus> value) {
353-
if (messageId.equals(chatMessage.getId())) {
354-
updateMessageStatus(messageId, value);
350+
public void put(String ackRequestingMessageId, Observable<MessageDeliveryStatus> value) {
351+
if (chatMessage instanceof AckRequestingMessage ackRequestingMessage) {
352+
String messageId = ackRequestingMessageId;
353+
String chatMessageId = ackRequestingMessage.getAckRequestingMessageId();
354+
String peersProfileId = null;
355+
String separator = BisqEasyOpenTradeMessage.ACK_REQUESTING_MESSAGE_ID_SEPARATOR;
356+
if (chatMessage instanceof BisqEasyOpenTradeMessage bisqEasyOpenTradeMessage) {
357+
// In case of a bisqEasyOpenTradeMessage we use the message id and receiver id separated with a '_'.
358+
// This allows us to handle the ACK messages separately to know when the message was received by
359+
// both the peer and the mediator (in case of mediation).
360+
if (messageId.contains(separator)) {
361+
String[] parts = messageId.split(separator);
362+
messageId = parts[0];
363+
peersProfileId = parts[1];
364+
}
365+
if (chatMessageId.contains(separator)) {
366+
String[] parts = chatMessageId.split(separator);
367+
chatMessageId = parts[0];
368+
}
369+
}
370+
371+
if (messageId.equals(chatMessageId)) {
372+
updateMessageStatus(ackRequestingMessageId, value, peersProfileId);
373+
}
355374
}
356375
}
357376

@@ -370,39 +389,20 @@ public void clear() {
370389
}));
371390
}
372391

373-
private void updateMessageStatus(String messageId, Observable<MessageDeliveryStatus> value) {
392+
private void updateMessageStatus(String ackRequestingMessageId,
393+
Observable<MessageDeliveryStatus> value,
394+
@Nullable String peersProfileId) {
374395
// Delay to avoid ConcurrentModificationException
375396
UIThread.runOnNextRenderFrame(() -> statusPins.add(value.addObserver(status -> UIThread.run(() -> {
376-
ChatMessageListItem.this.messageId = messageId;
377-
boolean shouldShowTryAgain = false;
378-
if (status != null) {
379-
Label statusLabel = new Label();
380-
statusLabel.setTooltip(new BisqTooltip(Res.get("chat.message.deliveryState." + status.name())));
381-
switch (status) {
382-
// Successful delivery
383-
case ACK_RECEIVED:
384-
case MAILBOX_MSG_RECEIVED:
385-
statusLabel.setGraphic(successfulDeliveryIcon);
386-
break;
387-
// Pending delivery
388-
case CONNECTING:
389-
statusLabel.setGraphic(connectingDeliveryIcon);
390-
break;
391-
case SENT:
392-
case TRY_ADD_TO_MAILBOX:
393-
statusLabel.setGraphic(pendingDeliveryIcon);
394-
break;
395-
case ADDED_TO_MAILBOX:
396-
statusLabel.setGraphic(addedToMailboxIcon);
397-
break;
398-
case FAILED:
399-
statusLabel.setGraphic(failedDeliveryIcon);
400-
shouldShowTryAgain = resendMessageService.map(service -> service.canManuallyResendMessage(messageId)).orElse(false);
401-
break;
402-
}
403-
messageDeliveryStatusNode.set(statusLabel);
397+
Map<String, Triple<MessageDeliveryStatus, String, Boolean>> map = messageDeliveryStatusByPeerProfileId.get();
398+
if (map == null) {
399+
map = new HashMap<>();
404400
}
405-
this.shouldShowTryAgain.set(shouldShowTryAgain);
401+
boolean canManuallyResendMessage = status == MessageDeliveryStatus.FAILED &&
402+
resendMessageService.map(service -> service.canManuallyResendMessage(ackRequestingMessageId)).orElse(false);
403+
map.put(peersProfileId, new Triple<>(status, ackRequestingMessageId, canManuallyResendMessage));
404+
messageDeliveryStatusByPeerProfileId.set(null); // trigger update by setting it to null
405+
messageDeliveryStatusByPeerProfileId.set(map);
406406
}))));
407407
}
408408

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* This file is part of Bisq.
3+
*
4+
* Bisq is free software: you can redistribute it and/or modify it
5+
* under the terms of the GNU Affero General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or (at
7+
* your option) any later version.
8+
*
9+
* Bisq is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
12+
* License for more details.
13+
*
14+
* You should have received a copy of the GNU Affero General Public License
15+
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package bisq.desktop.main.content.chat.message_container.list.message_box;
19+
20+
import bisq.chat.ChatChannel;
21+
import bisq.chat.ChatMessage;
22+
import bisq.common.data.Triple;
23+
import bisq.desktop.common.utils.ImageUtil;
24+
import bisq.desktop.components.controls.BisqMenuItem;
25+
import bisq.desktop.components.controls.BisqTooltip;
26+
import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem;
27+
import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListController;
28+
import bisq.i18n.Res;
29+
import bisq.network.p2p.services.confidential.ack.MessageDeliveryStatus;
30+
import javafx.geometry.Pos;
31+
import javafx.scene.control.Label;
32+
import javafx.scene.layout.HBox;
33+
import org.fxmisc.easybind.EasyBind;
34+
import org.fxmisc.easybind.Subscription;
35+
36+
import java.util.HashMap;
37+
import java.util.Map;
38+
39+
public class MessageDeliveryStatusBox extends HBox {
40+
private final Subscription messageDeliveryStatusNodePin;
41+
private final Map<String, Triple<BisqMenuItem, Label, BisqTooltip>> deliveryStateTripleByProfileId = new HashMap<>();
42+
43+
public MessageDeliveryStatusBox(ChatMessageListItem<? extends ChatMessage, ? extends ChatChannel<? extends ChatMessage>> item,
44+
ChatMessagesListController controller) {
45+
super(7.5);
46+
setAlignment(Pos.CENTER);
47+
48+
messageDeliveryStatusNodePin = EasyBind.subscribe(item.getMessageDeliveryStatusByPeerProfileId(), map -> {
49+
setManaged(map != null);
50+
setVisible(map != null);
51+
if (map == null) {
52+
return;
53+
}
54+
map.forEach((peersProfileId, triple) -> {
55+
MessageDeliveryStatus status = triple.getFirst();
56+
String ackRequestingMessageId = triple.getSecond();
57+
boolean canManuallyResendMessage = triple.getThird();
58+
59+
Triple<BisqMenuItem, Label, BisqTooltip> deliveryStateTriple = deliveryStateTripleByProfileId.get(peersProfileId);
60+
BisqMenuItem tryAgainMenuItem;
61+
Label icon;
62+
BisqTooltip tooltip;
63+
if (deliveryStateTriple == null) {
64+
tryAgainMenuItem = new BisqMenuItem("try-again-grey", "try-again-white");
65+
tryAgainMenuItem.useIconOnly(22);
66+
tryAgainMenuItem.setTooltip(new BisqTooltip(Res.get("chat.message.resendMessage")));
67+
68+
icon = new Label();
69+
tooltip = new BisqTooltip();
70+
icon.setTooltip(tooltip);
71+
72+
HBox hBox = new HBox(1, tryAgainMenuItem, icon);
73+
hBox.setAlignment(Pos.CENTER);
74+
getChildren().add(hBox);
75+
76+
deliveryStateTripleByProfileId.put(peersProfileId, new Triple<>(tryAgainMenuItem, icon, tooltip));
77+
} else {
78+
tryAgainMenuItem = deliveryStateTriple.getFirst();
79+
icon = deliveryStateTriple.getSecond();
80+
tooltip = deliveryStateTriple.getThird();
81+
}
82+
83+
tryAgainMenuItem.setVisible(canManuallyResendMessage);
84+
tryAgainMenuItem.setManaged(canManuallyResendMessage);
85+
if (canManuallyResendMessage) {
86+
tryAgainMenuItem.setOnAction(e -> controller.onResendMessage(ackRequestingMessageId));
87+
} else {
88+
tryAgainMenuItem.setOnAction(null);
89+
}
90+
91+
92+
String deliveryState = Res.get("chat.message.deliveryState." + status.name());
93+
if (map.size() > 1) {
94+
String userName = controller.getUserName(peersProfileId);
95+
tooltip.setText(Res.get("chat.message.deliveryState.multiplePeers", userName, deliveryState));
96+
} else {
97+
tooltip.setText(deliveryState);
98+
}
99+
100+
switch (status) {
101+
// Successful delivery
102+
case ACK_RECEIVED:
103+
case MAILBOX_MSG_RECEIVED:
104+
icon.setGraphic(ImageUtil.getImageViewById("received-check-grey"));
105+
break;
106+
// Pending delivery
107+
case CONNECTING:
108+
icon.setGraphic(ImageUtil.getImageViewById("connecting-grey"));
109+
break;
110+
case SENT:
111+
case TRY_ADD_TO_MAILBOX:
112+
icon.setGraphic(ImageUtil.getImageViewById("sent-message-grey"));
113+
break;
114+
case ADDED_TO_MAILBOX:
115+
icon.setGraphic(ImageUtil.getImageViewById("mailbox-grey"));
116+
break;
117+
case FAILED:
118+
icon.setGraphic(ImageUtil.getImageViewById("undelivered-message-yellow"));
119+
break;
120+
}
121+
});
122+
});
123+
}
124+
125+
public void dispose() {
126+
messageDeliveryStatusNodePin.unsubscribe();
127+
deliveryStateTripleByProfileId.values().forEach(triple ->
128+
triple.getFirst().setOnAction(null));
129+
}
130+
}

0 commit comments

Comments
 (0)