mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-08-03 04:06:23 -04:00
support multithreading in api and protocols
close trade wallets while unused for scalability verify txs do not use unlock height increase trade init timeout to 60s
This commit is contained in:
parent
fdddc87477
commit
bb95b4b1d6
82 changed files with 2786 additions and 2338 deletions
|
@ -736,8 +736,10 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||
});
|
||||
|
||||
model.getUpdatedDataReceived().addListener((observable, oldValue, newValue) -> {
|
||||
p2PNetworkIcon.setOpacity(1);
|
||||
p2pNetworkProgressBar.setProgress(0);
|
||||
UserThread.execute(() -> {
|
||||
p2PNetworkIcon.setOpacity(1);
|
||||
p2pNetworkProgressBar.setProgress(0);
|
||||
});
|
||||
});
|
||||
|
||||
p2pNetworkProgressBar = new JFXProgressBar(-1);
|
||||
|
|
|
@ -240,7 +240,7 @@ public class LockedView extends ActivatableView<VBox, Void> {
|
|||
|
||||
private Optional<Tradable> getTradable(LockedListItem item) {
|
||||
String offerId = item.getAddressEntry().getOfferId();
|
||||
Optional<Trade> tradeOptional = tradeManager.getTradeById(offerId);
|
||||
Optional<Trade> tradeOptional = tradeManager.getOpenTrade(offerId);
|
||||
if (tradeOptional.isPresent()) {
|
||||
return Optional.of(tradeOptional.get());
|
||||
} else if (openOfferManager.getOpenOfferById(offerId).isPresent()) {
|
||||
|
|
|
@ -239,7 +239,7 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
|||
|
||||
private Optional<Tradable> getTradable(ReservedListItem item) {
|
||||
String offerId = item.getAddressEntry().getOfferId();
|
||||
Optional<Trade> tradeOptional = tradeManager.getTradeById(offerId);
|
||||
Optional<Trade> tradeOptional = tradeManager.getOpenTrade(offerId);
|
||||
if (tradeOptional.isPresent()) {
|
||||
return Optional.of(tradeOptional.get());
|
||||
} else if (openOfferManager.getOpenOfferById(offerId).isPresent()) {
|
||||
|
|
|
@ -32,9 +32,7 @@ import javafx.collections.ObservableList;
|
|||
import java.util.Optional;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
|
||||
import monero.daemon.model.MoneroTx;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
|
||||
|
@ -82,14 +80,14 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
|||
|
||||
private boolean isMakerDepositTx(String txId) {
|
||||
return Optional.ofNullable(trade.getMakerDepositTx())
|
||||
.map(MoneroTxWallet::getHash)
|
||||
.map(MoneroTx::getHash)
|
||||
.map(hash -> hash.equals(txId))
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
private boolean isTakerDepositTx(String txId) {
|
||||
return Optional.ofNullable(trade.getTakerDepositTx())
|
||||
.map(MoneroTxWallet::getHash)
|
||||
.map(MoneroTx::getHash)
|
||||
.map(hash -> hash.equals(txId))
|
||||
.orElse(false);
|
||||
}
|
||||
|
|
|
@ -382,17 +382,19 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
|
|||
}
|
||||
|
||||
private void updateChartData() {
|
||||
seriesBuy.getData().clear();
|
||||
seriesSell.getData().clear();
|
||||
areaChart.getData().clear();
|
||||
UserThread.execute(() -> {
|
||||
seriesBuy.getData().clear();
|
||||
seriesSell.getData().clear();
|
||||
areaChart.getData().clear();
|
||||
|
||||
boolean isCrypto = CurrencyUtil.isCryptoCurrency(model.getCurrencyCode());
|
||||
boolean isCrypto = CurrencyUtil.isCryptoCurrency(model.getCurrencyCode());
|
||||
|
||||
// crypto: left-sell, right-buy. fiat: left-buy, right-sell
|
||||
seriesBuy.getData().addAll(filterOutliersBuy(model.getBuyData(), isCrypto));
|
||||
seriesSell.getData().addAll(filterOutliersSell(model.getSellData(), isCrypto));
|
||||
// crypto: left-sell, right-buy. fiat: left-buy, right-sell
|
||||
seriesBuy.getData().addAll(filterOutliersBuy(model.getBuyData(), isCrypto));
|
||||
seriesSell.getData().addAll(filterOutliersSell(model.getSellData(), isCrypto));
|
||||
|
||||
areaChart.getData().addAll(List.of(seriesBuy, seriesSell));
|
||||
areaChart.getData().addAll(List.of(seriesBuy, seriesSell));
|
||||
});
|
||||
}
|
||||
|
||||
List<XYChart.Data<Number, Number>> filterOutliersBuy(List<XYChart.Data<Number, Number>> buy, boolean isCrypto) {
|
||||
|
|
|
@ -62,7 +62,7 @@ import bisq.core.util.FormattingUtils;
|
|||
import bisq.core.util.coin.CoinFormatter;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.app.DevEnv;
|
||||
import bisq.common.config.Config;
|
||||
import bisq.common.util.Tuple3;
|
||||
|
@ -305,7 +305,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||
GridPane.setMargin(nrOfOffersLabel, new Insets(10, 0, 0, 0));
|
||||
root.getChildren().add(nrOfOffersLabel);
|
||||
|
||||
offerListListener = c -> nrOfOffersLabel.setText(Res.get("offerbook.nrOffers", model.getOfferList().size()));
|
||||
offerListListener = c -> UserThread.execute(() -> nrOfOffersLabel.setText(Res.get("offerbook.nrOffers", model.getOfferList().size())));
|
||||
|
||||
// Fixes incorrect ordering of Available offers:
|
||||
// https://github.com/bisq-network/bisq-desktop/issues/588
|
||||
|
|
|
@ -51,7 +51,7 @@ import bisq.network.p2p.P2PService;
|
|||
import bisq.network.p2p.network.CloseConnectionReason;
|
||||
import bisq.network.p2p.network.Connection;
|
||||
import bisq.network.p2p.network.ConnectionListener;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.app.DevEnv;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
@ -524,23 +524,25 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
}
|
||||
|
||||
private void updateSpinnerInfo() {
|
||||
if (!showPayFundsScreenDisplayed.get() ||
|
||||
offerWarning.get() != null ||
|
||||
errorMessage.get() != null ||
|
||||
showTransactionPublishedScreen.get()) {
|
||||
spinnerInfoText.set("");
|
||||
} else if (dataModel.getIsBtcWalletFunded().get()) {
|
||||
spinnerInfoText.set("");
|
||||
/* if (dataModel.isFeeFromFundingTxSufficient.get()) {
|
||||
UserThread.execute(() -> {
|
||||
if (!showPayFundsScreenDisplayed.get() ||
|
||||
offerWarning.get() != null ||
|
||||
errorMessage.get() != null ||
|
||||
showTransactionPublishedScreen.get()) {
|
||||
spinnerInfoText.set("");
|
||||
} else if (dataModel.getIsBtcWalletFunded().get()) {
|
||||
spinnerInfoText.set("");
|
||||
/* if (dataModel.isFeeFromFundingTxSufficient.get()) {
|
||||
spinnerInfoText.set("");
|
||||
} else {
|
||||
spinnerInfoText.set("Check if funding tx miner fee is sufficient...");
|
||||
}*/
|
||||
} else {
|
||||
spinnerInfoText.set("Check if funding tx miner fee is sufficient...");
|
||||
}*/
|
||||
} else {
|
||||
spinnerInfoText.set(Res.get("shared.waitingForFunds"));
|
||||
}
|
||||
spinnerInfoText.set(Res.get("shared.waitingForFunds"));
|
||||
}
|
||||
|
||||
isWaitingForFunds.set(!spinnerInfoText.get().isEmpty());
|
||||
isWaitingForFunds.set(!spinnerInfoText.get().isEmpty());
|
||||
});
|
||||
}
|
||||
|
||||
private void addListeners() {
|
||||
|
|
|
@ -515,47 +515,49 @@ public abstract class Overlay<T extends Overlay<T>> {
|
|||
if (owner != null) {
|
||||
Scene rootScene = owner.getScene();
|
||||
if (rootScene != null) {
|
||||
Scene scene = new Scene(getRootContainer());
|
||||
scene.getStylesheets().setAll(rootScene.getStylesheets());
|
||||
scene.setFill(Color.TRANSPARENT);
|
||||
UserThread.execute(() -> {
|
||||
Scene scene = new Scene(getRootContainer());
|
||||
scene.getStylesheets().setAll(rootScene.getStylesheets());
|
||||
scene.setFill(Color.TRANSPARENT);
|
||||
|
||||
setupKeyHandler(scene);
|
||||
setupKeyHandler(scene);
|
||||
|
||||
stage = new Stage();
|
||||
stage.setScene(scene);
|
||||
Window window = rootScene.getWindow();
|
||||
setModality();
|
||||
stage.initStyle(StageStyle.TRANSPARENT);
|
||||
stage.setOnCloseRequest(event -> {
|
||||
event.consume();
|
||||
doClose();
|
||||
stage = new Stage();
|
||||
stage.setScene(scene);
|
||||
Window window = rootScene.getWindow();
|
||||
setModality();
|
||||
stage.initStyle(StageStyle.TRANSPARENT);
|
||||
stage.setOnCloseRequest(event -> {
|
||||
event.consume();
|
||||
doClose();
|
||||
});
|
||||
stage.sizeToScene();
|
||||
stage.show();
|
||||
|
||||
layout();
|
||||
|
||||
addEffectToBackground();
|
||||
|
||||
// On Linux the owner stage does not move the child stage as it does on Mac
|
||||
// So we need to apply centerPopup. Further with fast movements the handler loses
|
||||
// the latest position, with a delay it fixes that.
|
||||
// Also on Mac sometimes the popups are positioned outside of the main app, so keep it for all OS
|
||||
positionListener = (observable, oldValue, newValue) -> {
|
||||
if (stage != null) {
|
||||
layout();
|
||||
if (centerTime != null)
|
||||
centerTime.stop();
|
||||
|
||||
centerTime = UserThread.runAfter(this::layout, 3);
|
||||
}
|
||||
};
|
||||
window.xProperty().addListener(positionListener);
|
||||
window.yProperty().addListener(positionListener);
|
||||
window.widthProperty().addListener(positionListener);
|
||||
|
||||
animateDisplay();
|
||||
isDisplayed = true;
|
||||
});
|
||||
stage.sizeToScene();
|
||||
stage.show();
|
||||
|
||||
layout();
|
||||
|
||||
addEffectToBackground();
|
||||
|
||||
// On Linux the owner stage does not move the child stage as it does on Mac
|
||||
// So we need to apply centerPopup. Further with fast movements the handler loses
|
||||
// the latest position, with a delay it fixes that.
|
||||
// Also on Mac sometimes the popups are positioned outside of the main app, so keep it for all OS
|
||||
positionListener = (observable, oldValue, newValue) -> {
|
||||
if (stage != null) {
|
||||
layout();
|
||||
if (centerTime != null)
|
||||
centerTime.stop();
|
||||
|
||||
centerTime = UserThread.runAfter(this::layout, 3);
|
||||
}
|
||||
};
|
||||
window.xProperty().addListener(positionListener);
|
||||
window.yProperty().addListener(positionListener);
|
||||
window.widthProperty().addListener(positionListener);
|
||||
|
||||
animateDisplay();
|
||||
isDisplayed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,9 @@ public class Notification extends Overlay<Notification> {
|
|||
if (autoClose && autoCloseTimer == null)
|
||||
autoCloseTimer = UserThread.runAfter(this::doClose, 6);
|
||||
|
||||
stage.addEventHandler(MouseEvent.MOUSE_PRESSED, (event) -> doClose());
|
||||
UserThread.execute(() -> {
|
||||
stage.addEventHandler(MouseEvent.MOUSE_PRESSED, (event) -> doClose());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -54,7 +54,7 @@ import bisq.core.trade.protocol.SellerProtocol;
|
|||
import bisq.core.user.Preferences;
|
||||
|
||||
import bisq.network.p2p.P2PService;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.crypto.PubKeyRing;
|
||||
import bisq.common.crypto.PubKeyRingProvider;
|
||||
import bisq.common.handlers.ErrorMessageHandler;
|
||||
|
@ -87,8 +87,7 @@ import javax.annotation.Nullable;
|
|||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
||||
|
||||
import monero.daemon.model.MoneroTx;
|
||||
import monero.wallet.MoneroWallet;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
|
@ -360,54 +359,56 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
}
|
||||
|
||||
private void doSelectItem(@Nullable PendingTradesListItem item) {
|
||||
if (selectedTrade != null)
|
||||
selectedTrade.stateProperty().removeListener(tradeStateChangeListener);
|
||||
UserThread.execute(() -> {
|
||||
if (selectedTrade != null)
|
||||
selectedTrade.stateProperty().removeListener(tradeStateChangeListener);
|
||||
|
||||
if (item != null) {
|
||||
selectedTrade = item.getTrade();
|
||||
if (selectedTrade == null) {
|
||||
log.error("selectedTrade is null");
|
||||
return;
|
||||
}
|
||||
|
||||
MoneroTxWallet makerDepositTx = selectedTrade.getMakerDepositTx();
|
||||
MoneroTxWallet takerDepositTx = selectedTrade.getTakerDepositTx();
|
||||
String tradeId = selectedTrade.getId();
|
||||
tradeStateChangeListener = (observable, oldValue, newValue) -> {
|
||||
if (makerDepositTx != null && takerDepositTx != null) { // TODO (woodser): this treats separate deposit ids as one unit, being both available or unavailable
|
||||
makerTxId.set(makerDepositTx.getHash());
|
||||
takerTxId.set(takerDepositTx.getHash());
|
||||
notificationCenter.setSelectedTradeId(tradeId);
|
||||
selectedTrade.stateProperty().removeListener(tradeStateChangeListener);
|
||||
} else {
|
||||
makerTxId.set("");
|
||||
takerTxId.set("");
|
||||
if (item != null) {
|
||||
selectedTrade = item.getTrade();
|
||||
if (selectedTrade == null) {
|
||||
log.error("selectedTrade is null");
|
||||
return;
|
||||
}
|
||||
};
|
||||
selectedTrade.stateProperty().addListener(tradeStateChangeListener);
|
||||
|
||||
Offer offer = selectedTrade.getOffer();
|
||||
if (offer == null) {
|
||||
log.error("offer is null");
|
||||
return;
|
||||
}
|
||||
MoneroTx makerDepositTx = selectedTrade.getMakerDepositTx();
|
||||
MoneroTx takerDepositTx = selectedTrade.getTakerDepositTx();
|
||||
String tradeId = selectedTrade.getId();
|
||||
tradeStateChangeListener = (observable, oldValue, newValue) -> {
|
||||
if (makerDepositTx != null && takerDepositTx != null) { // TODO (woodser): this treats separate deposit ids as one unit, being both available or unavailable
|
||||
makerTxId.set(makerDepositTx.getHash());
|
||||
takerTxId.set(takerDepositTx.getHash());
|
||||
notificationCenter.setSelectedTradeId(tradeId);
|
||||
selectedTrade.stateProperty().removeListener(tradeStateChangeListener);
|
||||
} else {
|
||||
makerTxId.set("");
|
||||
takerTxId.set("");
|
||||
}
|
||||
};
|
||||
selectedTrade.stateProperty().addListener(tradeStateChangeListener);
|
||||
|
||||
isMaker = tradeManager.isMyOffer(offer);
|
||||
if (makerDepositTx != null && takerDepositTx != null) {
|
||||
makerTxId.set(makerDepositTx.getHash());
|
||||
takerTxId.set(takerDepositTx.getHash());
|
||||
Offer offer = selectedTrade.getOffer();
|
||||
if (offer == null) {
|
||||
log.error("offer is null");
|
||||
return;
|
||||
}
|
||||
|
||||
isMaker = tradeManager.isMyOffer(offer);
|
||||
if (makerDepositTx != null && takerDepositTx != null) {
|
||||
makerTxId.set(makerDepositTx.getHash());
|
||||
takerTxId.set(takerDepositTx.getHash());
|
||||
} else {
|
||||
makerTxId.set("");
|
||||
takerTxId.set("");
|
||||
}
|
||||
notificationCenter.setSelectedTradeId(tradeId);
|
||||
} else {
|
||||
selectedTrade = null;
|
||||
makerTxId.set("");
|
||||
takerTxId.set("");
|
||||
notificationCenter.setSelectedTradeId(null);
|
||||
}
|
||||
notificationCenter.setSelectedTradeId(tradeId);
|
||||
} else {
|
||||
selectedTrade = null;
|
||||
makerTxId.set("");
|
||||
takerTxId.set("");
|
||||
notificationCenter.setSelectedTradeId(null);
|
||||
}
|
||||
selectedItemProperty.set(item);
|
||||
selectedItemProperty.set(item);
|
||||
});
|
||||
}
|
||||
|
||||
private void tryOpenDispute(boolean isSupportTicket) {
|
||||
|
@ -458,8 +459,9 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
byte[] payoutTxSerialized = null;
|
||||
String payoutTxHashAsString = null;
|
||||
MoneroTxWallet payoutTx = trade.getPayoutTx();
|
||||
MoneroWallet multisigWallet = xmrWalletService.getMultisigWallet(trade.getId());
|
||||
MoneroWallet multisigWallet = xmrWalletService.getMultisigWallet(trade.getId());
|
||||
String updatedMultisigHex = multisigWallet.getMultisigHex();
|
||||
xmrWalletService.closeMultisigWallet(trade.getId()); // close multisig wallet
|
||||
if (payoutTx != null) {
|
||||
// payoutTxSerialized = payoutTx.bitcoinSerialize(); // TODO (woodser): no need to pass serialized txs for xmr
|
||||
// payoutTxHashAsString = payoutTx.getHashAsString();
|
||||
|
|
|
@ -363,7 +363,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
|||
}
|
||||
|
||||
private void updateMoveTradeToFailedColumnState() {
|
||||
moveTradeToFailedColumn.setVisible(model.dataModel.list.stream().anyMatch(item -> isMaybeInvalidTrade(item.getTrade())));
|
||||
UserThread.execute(() -> moveTradeToFailedColumn.setVisible(model.dataModel.list.stream().anyMatch(item -> isMaybeInvalidTrade(item.getTrade()))));
|
||||
}
|
||||
|
||||
private boolean isMaybeInvalidTrade(Trade trade) {
|
||||
|
|
|
@ -137,7 +137,8 @@ public class BuyerStep2View extends TradeStepView {
|
|||
showPopup();
|
||||
} else if (state.ordinal() <= Trade.State.BUYER_SEND_FAILED_FIAT_PAYMENT_INITIATED_MSG.ordinal()) {
|
||||
if (!trade.hasFailed()) {
|
||||
switch (state) {
|
||||
UserThread.execute(() -> {
|
||||
switch (state) {
|
||||
case BUYER_CONFIRMED_IN_UI_FIAT_PAYMENT_INITIATED:
|
||||
case BUYER_SENT_FIAT_PAYMENT_INITIATED_MSG:
|
||||
busyAnimation.play();
|
||||
|
@ -169,7 +170,8 @@ public class BuyerStep2View extends TradeStepView {
|
|||
busyAnimation.stop();
|
||||
statusLabel.setText(Res.get("shared.sendingConfirmationAgain"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log.warn("Trade contains error message {}", trade.getErrorMessage());
|
||||
statusLabel.setText("");
|
||||
|
|
|
@ -471,43 +471,45 @@ public class ChatView extends AnchorPane {
|
|||
}
|
||||
|
||||
private void updateMsgState(ChatMessage message) {
|
||||
boolean visible;
|
||||
AwesomeIcon icon = null;
|
||||
String text = null;
|
||||
statusIcon.getStyleClass().add("status-icon");
|
||||
statusInfoLabel.getStyleClass().add("status-icon");
|
||||
statusHBox.setOpacity(1);
|
||||
log.debug("updateMsgState msg-{}, ack={}, arrived={}", message.getMessage(),
|
||||
message.acknowledgedProperty().get(), message.arrivedProperty().get());
|
||||
if (message.acknowledgedProperty().get()) {
|
||||
visible = true;
|
||||
icon = AwesomeIcon.OK_SIGN;
|
||||
text = Res.get("support.acknowledged");
|
||||
} else if (message.ackErrorProperty().get() != null) {
|
||||
visible = true;
|
||||
icon = AwesomeIcon.EXCLAMATION_SIGN;
|
||||
text = Res.get("support.error", message.ackErrorProperty().get());
|
||||
statusIcon.getStyleClass().add("error-text");
|
||||
statusInfoLabel.getStyleClass().add("error-text");
|
||||
} else if (message.arrivedProperty().get()) {
|
||||
visible = true;
|
||||
icon = AwesomeIcon.OK;
|
||||
text = Res.get("support.arrived");
|
||||
} else if (message.storedInMailboxProperty().get()) {
|
||||
visible = true;
|
||||
icon = AwesomeIcon.ENVELOPE;
|
||||
text = Res.get("support.savedInMailbox");
|
||||
} else {
|
||||
visible = false;
|
||||
log.debug("updateMsgState called but no msg state available. message={}", message);
|
||||
}
|
||||
UserThread.execute(() -> {
|
||||
boolean visible;
|
||||
AwesomeIcon icon = null;
|
||||
String text = null;
|
||||
statusIcon.getStyleClass().add("status-icon");
|
||||
statusInfoLabel.getStyleClass().add("status-icon");
|
||||
statusHBox.setOpacity(1);
|
||||
log.debug("updateMsgState msg-{}, ack={}, arrived={}", message.getMessage(),
|
||||
message.acknowledgedProperty().get(), message.arrivedProperty().get());
|
||||
if (message.acknowledgedProperty().get()) {
|
||||
visible = true;
|
||||
icon = AwesomeIcon.OK_SIGN;
|
||||
text = Res.get("support.acknowledged");
|
||||
} else if (message.ackErrorProperty().get() != null) {
|
||||
visible = true;
|
||||
icon = AwesomeIcon.EXCLAMATION_SIGN;
|
||||
text = Res.get("support.error", message.ackErrorProperty().get());
|
||||
statusIcon.getStyleClass().add("error-text");
|
||||
statusInfoLabel.getStyleClass().add("error-text");
|
||||
} else if (message.arrivedProperty().get()) {
|
||||
visible = true;
|
||||
icon = AwesomeIcon.OK;
|
||||
text = Res.get("support.arrived");
|
||||
} else if (message.storedInMailboxProperty().get()) {
|
||||
visible = true;
|
||||
icon = AwesomeIcon.ENVELOPE;
|
||||
text = Res.get("support.savedInMailbox");
|
||||
} else {
|
||||
visible = false;
|
||||
log.debug("updateMsgState called but no msg state available. message={}", message);
|
||||
}
|
||||
|
||||
statusHBox.setVisible(visible);
|
||||
if (visible) {
|
||||
AwesomeDude.setIcon(statusIcon, icon, "14");
|
||||
statusIcon.setTooltip(new Tooltip(text));
|
||||
statusInfoLabel.setText(text);
|
||||
}
|
||||
statusHBox.setVisible(visible);
|
||||
if (visible) {
|
||||
AwesomeDude.setIcon(statusIcon, icon, "14");
|
||||
statusIcon.setTooltip(new Tooltip(text));
|
||||
statusInfoLabel.setText(text);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -85,76 +85,78 @@ public class DisputeChatPopup {
|
|||
}
|
||||
|
||||
public void openChat(Dispute selectedDispute, DisputeSession concreteDisputeSession, String counterpartyName) {
|
||||
closeChat();
|
||||
this.selectedDispute = selectedDispute;
|
||||
selectedDispute.getChatMessages().forEach(m -> m.setWasDisplayed(true));
|
||||
disputeManager.requestPersistence();
|
||||
|
||||
ChatView chatView = new ChatView(disputeManager, formatter, counterpartyName);
|
||||
chatView.setAllowAttachments(true);
|
||||
chatView.setDisplayHeader(false);
|
||||
chatView.initialize();
|
||||
|
||||
AnchorPane pane = new AnchorPane(chatView);
|
||||
pane.setPrefSize(760, 500);
|
||||
AnchorPane.setLeftAnchor(chatView, 10d);
|
||||
AnchorPane.setRightAnchor(chatView, 10d);
|
||||
AnchorPane.setTopAnchor(chatView, -20d);
|
||||
AnchorPane.setBottomAnchor(chatView, 10d);
|
||||
pane.getStyleClass().add("dispute-chat-border");
|
||||
Button closeDisputeButton = null;
|
||||
if (!selectedDispute.isClosed() && !disputeManager.isTrader(selectedDispute)) {
|
||||
closeDisputeButton = new AutoTooltipButton(Res.get("support.closeTicket"));
|
||||
closeDisputeButton.setOnAction(e -> chatCallback.onCloseDisputeFromChatWindow(selectedDispute));
|
||||
}
|
||||
chatView.display(concreteDisputeSession, closeDisputeButton, pane.widthProperty());
|
||||
chatView.activate();
|
||||
chatView.scrollToBottom();
|
||||
chatPopupStage = new Stage();
|
||||
chatPopupStage.setTitle(Res.get("disputeChat.chatWindowTitle", selectedDispute.getShortTradeId())
|
||||
+ " " + selectedDispute.getRoleString());
|
||||
StackPane owner = MainView.getRootContainer();
|
||||
Scene rootScene = owner.getScene();
|
||||
chatPopupStage.initOwner(rootScene.getWindow());
|
||||
chatPopupStage.initModality(Modality.NONE);
|
||||
chatPopupStage.initStyle(StageStyle.DECORATED);
|
||||
chatPopupStage.setOnHiding(event -> {
|
||||
chatView.deactivate();
|
||||
// at close we set all as displayed. While open we ignore updates of the numNewMsg in the list icon.
|
||||
UserThread.execute(() -> {
|
||||
closeChat();
|
||||
this.selectedDispute = selectedDispute;
|
||||
selectedDispute.getChatMessages().forEach(m -> m.setWasDisplayed(true));
|
||||
disputeManager.requestPersistence();
|
||||
chatPopupStage = null;
|
||||
});
|
||||
|
||||
Scene scene = new Scene(pane);
|
||||
CssTheme.loadSceneStyles(scene, preferences.getCssTheme(), false);
|
||||
scene.addEventHandler(KeyEvent.KEY_RELEASED, ev -> {
|
||||
if (ev.getCode() == KeyCode.ESCAPE) {
|
||||
ev.consume();
|
||||
chatPopupStage.hide();
|
||||
ChatView chatView = new ChatView(disputeManager, formatter, counterpartyName);
|
||||
chatView.setAllowAttachments(true);
|
||||
chatView.setDisplayHeader(false);
|
||||
chatView.initialize();
|
||||
|
||||
AnchorPane pane = new AnchorPane(chatView);
|
||||
pane.setPrefSize(760, 500);
|
||||
AnchorPane.setLeftAnchor(chatView, 10d);
|
||||
AnchorPane.setRightAnchor(chatView, 10d);
|
||||
AnchorPane.setTopAnchor(chatView, -20d);
|
||||
AnchorPane.setBottomAnchor(chatView, 10d);
|
||||
pane.getStyleClass().add("dispute-chat-border");
|
||||
Button closeDisputeButton = null;
|
||||
if (!selectedDispute.isClosed() && !disputeManager.isTrader(selectedDispute)) {
|
||||
closeDisputeButton = new AutoTooltipButton(Res.get("support.closeTicket"));
|
||||
closeDisputeButton.setOnAction(e -> chatCallback.onCloseDisputeFromChatWindow(selectedDispute));
|
||||
}
|
||||
chatView.display(concreteDisputeSession, closeDisputeButton, pane.widthProperty());
|
||||
chatView.activate();
|
||||
chatView.scrollToBottom();
|
||||
chatPopupStage = new Stage();
|
||||
chatPopupStage.setTitle(Res.get("disputeChat.chatWindowTitle", selectedDispute.getShortTradeId())
|
||||
+ " " + selectedDispute.getRoleString());
|
||||
StackPane owner = MainView.getRootContainer();
|
||||
Scene rootScene = owner.getScene();
|
||||
chatPopupStage.initOwner(rootScene.getWindow());
|
||||
chatPopupStage.initModality(Modality.NONE);
|
||||
chatPopupStage.initStyle(StageStyle.DECORATED);
|
||||
chatPopupStage.setOnHiding(event -> {
|
||||
chatView.deactivate();
|
||||
// at close we set all as displayed. While open we ignore updates of the numNewMsg in the list icon.
|
||||
selectedDispute.getChatMessages().forEach(m -> m.setWasDisplayed(true));
|
||||
disputeManager.requestPersistence();
|
||||
chatPopupStage = null;
|
||||
});
|
||||
|
||||
Scene scene = new Scene(pane);
|
||||
CssTheme.loadSceneStyles(scene, preferences.getCssTheme(), false);
|
||||
scene.addEventHandler(KeyEvent.KEY_RELEASED, ev -> {
|
||||
if (ev.getCode() == KeyCode.ESCAPE) {
|
||||
ev.consume();
|
||||
chatPopupStage.hide();
|
||||
}
|
||||
});
|
||||
chatPopupStage.setScene(scene);
|
||||
chatPopupStage.setOpacity(0);
|
||||
chatPopupStage.show();
|
||||
|
||||
xPositionListener = (observable, oldValue, newValue) -> chatPopupStageXPosition = (double) newValue;
|
||||
chatPopupStage.xProperty().addListener(xPositionListener);
|
||||
yPositionListener = (observable, oldValue, newValue) -> chatPopupStageYPosition = (double) newValue;
|
||||
chatPopupStage.yProperty().addListener(yPositionListener);
|
||||
|
||||
if (chatPopupStageXPosition == -1) {
|
||||
Window rootSceneWindow = rootScene.getWindow();
|
||||
double titleBarHeight = rootSceneWindow.getHeight() - rootScene.getHeight();
|
||||
chatPopupStage.setX(Math.round(rootSceneWindow.getX() + (owner.getWidth() - chatPopupStage.getWidth() / 4 * 3)));
|
||||
chatPopupStage.setY(Math.round(rootSceneWindow.getY() + titleBarHeight + (owner.getHeight() - chatPopupStage.getHeight() / 4 * 3)));
|
||||
} else {
|
||||
chatPopupStage.setX(chatPopupStageXPosition);
|
||||
chatPopupStage.setY(chatPopupStageYPosition);
|
||||
}
|
||||
|
||||
// Delay display to next render frame to avoid that the popup is first quickly displayed in default position
|
||||
// and after a short moment in the correct position
|
||||
UserThread.execute(() -> chatPopupStage.setOpacity(1));
|
||||
});
|
||||
chatPopupStage.setScene(scene);
|
||||
chatPopupStage.setOpacity(0);
|
||||
chatPopupStage.show();
|
||||
|
||||
xPositionListener = (observable, oldValue, newValue) -> chatPopupStageXPosition = (double) newValue;
|
||||
chatPopupStage.xProperty().addListener(xPositionListener);
|
||||
yPositionListener = (observable, oldValue, newValue) -> chatPopupStageYPosition = (double) newValue;
|
||||
chatPopupStage.yProperty().addListener(yPositionListener);
|
||||
|
||||
if (chatPopupStageXPosition == -1) {
|
||||
Window rootSceneWindow = rootScene.getWindow();
|
||||
double titleBarHeight = rootSceneWindow.getHeight() - rootScene.getHeight();
|
||||
chatPopupStage.setX(Math.round(rootSceneWindow.getX() + (owner.getWidth() - chatPopupStage.getWidth() / 4 * 3)));
|
||||
chatPopupStage.setY(Math.round(rootSceneWindow.getY() + titleBarHeight + (owner.getHeight() - chatPopupStage.getHeight() / 4 * 3)));
|
||||
} else {
|
||||
chatPopupStage.setX(chatPopupStageXPosition);
|
||||
chatPopupStage.setY(chatPopupStageYPosition);
|
||||
}
|
||||
|
||||
// Delay display to next render frame to avoid that the popup is first quickly displayed in default position
|
||||
// and after a short moment in the correct position
|
||||
UserThread.execute(() -> chatPopupStage.setOpacity(1));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1131,7 +1131,7 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
|
|||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
Optional<Trade> tradeOptional = tradeManager.getTradeById(item.getTradeId());
|
||||
Optional<Trade> tradeOptional = tradeManager.getOpenTrade(item.getTradeId());
|
||||
if (tradeOptional.isPresent()) {
|
||||
field = new HyperlinkWithIcon(item.getShortTradeId());
|
||||
field.setMouseTransparent(false);
|
||||
|
@ -1349,31 +1349,33 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
|
|||
@Override
|
||||
public void updateItem(final Dispute item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null && !empty) {
|
||||
if (closedProperty != null) {
|
||||
closedProperty.removeListener(listener);
|
||||
}
|
||||
UserThread.execute(() -> {
|
||||
if (item != null && !empty) {
|
||||
if (closedProperty != null) {
|
||||
closedProperty.removeListener(listener);
|
||||
}
|
||||
|
||||
listener = (observable, oldValue, newValue) -> {
|
||||
setText(newValue ? Res.get("support.closed") : Res.get("support.open"));
|
||||
listener = (observable, oldValue, newValue) -> {
|
||||
setText(newValue ? Res.get("support.closed") : Res.get("support.open"));
|
||||
if (getTableRow() != null)
|
||||
getTableRow().setOpacity(newValue && 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.addListener(listener);
|
||||
boolean isClosed = item.isClosed();
|
||||
setText(isClosed ? Res.get("support.closed") : Res.get("support.open"));
|
||||
if (getTableRow() != null)
|
||||
getTableRow().setOpacity(newValue && 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.addListener(listener);
|
||||
boolean isClosed = item.isClosed();
|
||||
setText(isClosed ? Res.get("support.closed") : Res.get("support.open"));
|
||||
if (getTableRow() != null)
|
||||
getTableRow().setOpacity(isClosed && item.getBadgeCountProperty().get() == 0 ? 0.4 : 1);
|
||||
} else {
|
||||
if (closedProperty != null) {
|
||||
closedProperty.removeListener(listener);
|
||||
closedProperty = null;
|
||||
getTableRow().setOpacity(isClosed && item.getBadgeCountProperty().get() == 0 ? 0.4 : 1);
|
||||
} else {
|
||||
if (closedProperty != null) {
|
||||
closedProperty.removeListener(listener);
|
||||
closedProperty = null;
|
||||
}
|
||||
setText("");
|
||||
}
|
||||
setText("");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1389,27 +1391,29 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
|
|||
}
|
||||
|
||||
private void updateChatMessageCount(Dispute dispute, JFXBadge chatBadge) {
|
||||
if (chatBadge == null)
|
||||
return;
|
||||
// when the chat popup is active, we do not display new message count indicator for that item
|
||||
if (chatPopup.isChatShown() && selectedDispute != null && dispute.getId().equals(selectedDispute.getId())) {
|
||||
chatBadge.setText("");
|
||||
chatBadge.setEnabled(false);
|
||||
chatBadge.refreshBadge();
|
||||
// have to UserThread.execute or the new message will be sent to peer as "read"
|
||||
UserThread.execute(() -> dispute.setChatMessagesSeen(senderFlag()));
|
||||
return;
|
||||
}
|
||||
UserThread.execute(() -> {
|
||||
if (chatBadge == null)
|
||||
return;
|
||||
// when the chat popup is active, we do not display new message count indicator for that item
|
||||
if (chatPopup.isChatShown() && selectedDispute != null && dispute.getId().equals(selectedDispute.getId())) {
|
||||
chatBadge.setText("");
|
||||
chatBadge.setEnabled(false);
|
||||
chatBadge.refreshBadge();
|
||||
// have to UserThread.execute or the new message will be sent to peer as "read"
|
||||
UserThread.execute(() -> dispute.setChatMessagesSeen(senderFlag()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (dispute.unreadMessageCount(senderFlag()) > 0) {
|
||||
chatBadge.setText(String.valueOf(dispute.unreadMessageCount(senderFlag())));
|
||||
chatBadge.setEnabled(true);
|
||||
} else {
|
||||
chatBadge.setText("");
|
||||
chatBadge.setEnabled(false);
|
||||
}
|
||||
chatBadge.refreshBadge();
|
||||
dispute.refreshAlertLevel(senderFlag());
|
||||
if (dispute.unreadMessageCount(senderFlag()) > 0) {
|
||||
chatBadge.setText(String.valueOf(dispute.unreadMessageCount(senderFlag())));
|
||||
chatBadge.setEnabled(true);
|
||||
} else {
|
||||
chatBadge.setText("");
|
||||
chatBadge.setEnabled(false);
|
||||
}
|
||||
chatBadge.refreshBadge();
|
||||
dispute.refreshAlertLevel(senderFlag());
|
||||
});
|
||||
}
|
||||
|
||||
private String getCounterpartyName() {
|
||||
|
|
|
@ -92,12 +92,12 @@ public class Transitions {
|
|||
public void fadeOutAndRemove(Node node, int duration, EventHandler<ActionEvent> handler) {
|
||||
FadeTransition fade = fadeOut(node, getDuration(duration));
|
||||
fade.setInterpolator(Interpolator.EASE_IN);
|
||||
fade.setOnFinished(actionEvent -> {
|
||||
fade.setOnFinished(actionEvent -> UserThread.execute(() -> {
|
||||
((Pane) (node.getParent())).getChildren().remove(node);
|
||||
//Profiler.printMsgWithTime("fadeOutAndRemove");
|
||||
if (handler != null)
|
||||
handler.handle(actionEvent);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
// Blur
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue