diff --git a/build.gradle b/build.gradle index 81a315ad..d35c6bd0 100644 --- a/build.gradle +++ b/build.gradle @@ -610,7 +610,7 @@ configure(project(':desktop')) { apply plugin: 'com.github.johnrengelman.shadow' apply from: 'package/package.gradle' - version = '1.0.13-SNAPSHOT' + version = '1.0.14-SNAPSHOT' jar.manifest.attributes( "Implementation-Title": project.name, diff --git a/common/src/main/java/haveno/common/app/Version.java b/common/src/main/java/haveno/common/app/Version.java index 14e5a6c7..2b4b01b0 100644 --- a/common/src/main/java/haveno/common/app/Version.java +++ b/common/src/main/java/haveno/common/app/Version.java @@ -28,7 +28,7 @@ import static com.google.common.base.Preconditions.checkArgument; public class Version { // The application versions // We use semantic versioning with major, minor and patch - public static final String VERSION = "1.0.13"; + public static final String VERSION = "1.0.14"; /** * Holds a list of the tagged resource files for optimizing the getData requests. diff --git a/common/src/main/java/haveno/common/file/FileUtil.java b/common/src/main/java/haveno/common/file/FileUtil.java index 8bb6ae75..ca533cc0 100644 --- a/common/src/main/java/haveno/common/file/FileUtil.java +++ b/common/src/main/java/haveno/common/file/FileUtil.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; @@ -76,11 +77,11 @@ public class FileUtil { public static List getBackupFiles(File dir, String fileName) { File backupDir = new File(Paths.get(dir.getAbsolutePath(), BACKUP_DIR).toString()); - if (!backupDir.exists()) return null; + if (!backupDir.exists()) return new ArrayList(); String dirName = "backups_" + fileName; if (dirName.contains(".")) dirName = dirName.replace(".", "_"); File backupFileDir = new File(Paths.get(backupDir.getAbsolutePath(), dirName).toString()); - if (!backupFileDir.exists()) return null; + if (!backupFileDir.exists()) return new ArrayList(); File[] files = backupFileDir.listFiles(); return Arrays.asList(files); } diff --git a/core/src/main/java/haveno/core/support/dispute/Dispute.java b/core/src/main/java/haveno/core/support/dispute/Dispute.java index 268cb3e2..15595b88 100644 --- a/core/src/main/java/haveno/core/support/dispute/Dispute.java +++ b/core/src/main/java/haveno/core/support/dispute/Dispute.java @@ -467,7 +467,7 @@ public final class Dispute implements NetworkPayload, PersistablePayload { } public boolean isOpen() { - return this.disputeState == State.OPEN || this.disputeState == State.REOPENED; + return isNew() || this.disputeState == State.OPEN || this.disputeState == State.REOPENED; } public boolean isClosed() { diff --git a/core/src/main/java/haveno/core/support/dispute/DisputeManager.java b/core/src/main/java/haveno/core/support/dispute/DisputeManager.java index 6fca89f8..d6b24697 100644 --- a/core/src/main/java/haveno/core/support/dispute/DisputeManager.java +++ b/core/src/main/java/haveno/core/support/dispute/DisputeManager.java @@ -359,6 +359,13 @@ public abstract class DisputeManager> extends Sup return; } + // skip if payout is confirmed + if (trade.isPayoutConfirmed()) { + String errorMsg = "Cannot open dispute because payout is already confirmed for " + trade.getClass().getSimpleName() + " " + trade.getId(); + faultHandler.handleFault(errorMsg, new IllegalStateException(errorMsg)); + return; + } + synchronized (disputeList.getObservableList()) { if (disputeList.contains(dispute)) { String msg = "We got a dispute msg that we have already stored. TradeId = " + dispute.getTradeId() + ", DisputeId = " + dispute.getId(); @@ -368,116 +375,109 @@ public abstract class DisputeManager> extends Sup } Optional storedDisputeOptional = findDispute(dispute); - boolean reOpen = storedDisputeOptional.isPresent() && storedDisputeOptional.get().isClosed(); - if (!storedDisputeOptional.isPresent() || reOpen) { + boolean reOpen = storedDisputeOptional.isPresent(); - // add or re-open dispute - if (reOpen) { - dispute = storedDisputeOptional.get(); - } else { - disputeList.add(dispute); - } - - String disputeInfo = getDisputeInfo(dispute); - String sysMsg = dispute.isSupportTicket() ? - Res.get("support.youOpenedTicket", disputeInfo, Version.VERSION) : - Res.get("support.youOpenedDispute", disputeInfo, Version.VERSION); - - ChatMessage chatMessage = new ChatMessage( - getSupportType(), - dispute.getTradeId(), - keyRing.getPubKeyRing().hashCode(), - false, - Res.get("support.systemMsg", sysMsg), - p2PService.getAddress()); - chatMessage.setSystemMessage(true); - dispute.addAndPersistChatMessage(chatMessage); - - // export latest multisig hex - try { - trade.exportMultisigHex(); - } catch (Exception e) { - log.error("Failed to export multisig hex", e); - } - - // create dispute opened message - NodeAddress agentNodeAddress = getAgentNodeAddress(dispute); - DisputeOpenedMessage disputeOpenedMessage = new DisputeOpenedMessage(dispute, - p2PService.getAddress(), - UUID.randomUUID().toString(), - getSupportType(), - trade.getSelf().getUpdatedMultisigHex(), - trade.getArbitrator().getPaymentSentMessage()); - log.info("Send {} to peer {}. tradeId={}, openNewDisputeMessage.uid={}, " + - "chatMessage.uid={}", - disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress, - disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(), - chatMessage.getUid()); - recordPendingMessage(disputeOpenedMessage.getClass().getSimpleName()); - - // send dispute opened message - trade.setDisputeState(Trade.DisputeState.DISPUTE_REQUESTED); - mailboxMessageService.sendEncryptedMailboxMessage(agentNodeAddress, - dispute.getAgentPubKeyRing(), - disputeOpenedMessage, - new SendMailboxMessageListener() { - @Override - public void onArrived() { - log.info("{} arrived at peer {}. tradeId={}, openNewDisputeMessage.uid={}, " + - "chatMessage.uid={}", - disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress, - disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(), - chatMessage.getUid()); - clearPendingMessage(); - - // We use the chatMessage wrapped inside the openNewDisputeMessage for - // the state, as that is displayed to the user and we only persist that msg - chatMessage.setArrived(true); - trade.advanceDisputeState(Trade.DisputeState.DISPUTE_REQUESTED); - requestPersistence(); - resultHandler.handleResult(); - } - - @Override - public void onStoredInMailbox() { - log.info("{} stored in mailbox for peer {}. tradeId={}, openNewDisputeMessage.uid={}, " + - "chatMessage.uid={}", - disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress, - disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(), - chatMessage.getUid()); - clearPendingMessage(); - - // We use the chatMessage wrapped inside the openNewDisputeMessage for - // the state, as that is displayed to the user and we only persist that msg - chatMessage.setStoredInMailbox(true); - requestPersistence(); - resultHandler.handleResult(); - } - - @Override - public void onFault(String errorMessage) { - log.error("{} failed: Peer {}. tradeId={}, openNewDisputeMessage.uid={}, " + - "chatMessage.uid={}, errorMessage={}", - disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress, - disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(), - chatMessage.getUid(), errorMessage); - - clearPendingMessage(); - // We use the chatMessage wrapped inside the openNewDisputeMessage for - // the state, as that is displayed to the user and we only persist that msg - chatMessage.setSendMessageError(errorMessage); - trade.setDisputeState(Trade.DisputeState.NO_DISPUTE); - requestPersistence(); - faultHandler.handleFault("Sending dispute message failed: " + - errorMessage, new DisputeMessageDeliveryFailedException()); - } - }); + // add or re-open dispute + if (reOpen) { + dispute = storedDisputeOptional.get(); } else { - String msg = "We got a dispute already open for that trade and trading peer.\n" + - "TradeId = " + dispute.getTradeId(); - log.warn(msg); - faultHandler.handleFault(msg, new DisputeAlreadyOpenException()); + disputeList.add(dispute); } + + String disputeInfo = getDisputeInfo(dispute); + String sysMsg = dispute.isSupportTicket() ? + Res.get("support.youOpenedTicket", disputeInfo, Version.VERSION) : + Res.get("support.youOpenedDispute", disputeInfo, Version.VERSION); + + ChatMessage chatMessage = new ChatMessage( + getSupportType(), + dispute.getTradeId(), + keyRing.getPubKeyRing().hashCode(), + false, + Res.get("support.systemMsg", sysMsg), + p2PService.getAddress()); + chatMessage.setSystemMessage(true); + dispute.addAndPersistChatMessage(chatMessage); + + // export latest multisig hex + try { + trade.exportMultisigHex(); + } catch (Exception e) { + log.error("Failed to export multisig hex", e); + } + + // create dispute opened message + NodeAddress agentNodeAddress = getAgentNodeAddress(dispute); + DisputeOpenedMessage disputeOpenedMessage = new DisputeOpenedMessage(dispute, + p2PService.getAddress(), + UUID.randomUUID().toString(), + getSupportType(), + trade.getSelf().getUpdatedMultisigHex(), + trade.getArbitrator().getPaymentSentMessage()); + log.info("Send {} to peer {}. tradeId={}, openNewDisputeMessage.uid={}, " + + "chatMessage.uid={}", + disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress, + disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(), + chatMessage.getUid()); + recordPendingMessage(disputeOpenedMessage.getClass().getSimpleName()); + + // send dispute opened message + trade.setDisputeState(Trade.DisputeState.DISPUTE_REQUESTED); + mailboxMessageService.sendEncryptedMailboxMessage(agentNodeAddress, + dispute.getAgentPubKeyRing(), + disputeOpenedMessage, + new SendMailboxMessageListener() { + @Override + public void onArrived() { + log.info("{} arrived at peer {}. tradeId={}, openNewDisputeMessage.uid={}, " + + "chatMessage.uid={}", + disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress, + disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(), + chatMessage.getUid()); + clearPendingMessage(); + + // We use the chatMessage wrapped inside the openNewDisputeMessage for + // the state, as that is displayed to the user and we only persist that msg + chatMessage.setArrived(true); + trade.advanceDisputeState(Trade.DisputeState.DISPUTE_REQUESTED); + requestPersistence(); + resultHandler.handleResult(); + } + + @Override + public void onStoredInMailbox() { + log.info("{} stored in mailbox for peer {}. tradeId={}, openNewDisputeMessage.uid={}, " + + "chatMessage.uid={}", + disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress, + disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(), + chatMessage.getUid()); + clearPendingMessage(); + + // We use the chatMessage wrapped inside the openNewDisputeMessage for + // the state, as that is displayed to the user and we only persist that msg + chatMessage.setStoredInMailbox(true); + requestPersistence(); + resultHandler.handleResult(); + } + + @Override + public void onFault(String errorMessage) { + log.error("{} failed: Peer {}. tradeId={}, openNewDisputeMessage.uid={}, " + + "chatMessage.uid={}, errorMessage={}", + disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress, + disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(), + chatMessage.getUid(), errorMessage); + + clearPendingMessage(); + // We use the chatMessage wrapped inside the openNewDisputeMessage for + // the state, as that is displayed to the user and we only persist that msg + chatMessage.setSendMessageError(errorMessage); + trade.setDisputeState(Trade.DisputeState.NO_DISPUTE); + requestPersistence(); + faultHandler.handleFault("Sending dispute message failed: " + + errorMessage, new DisputeMessageDeliveryFailedException()); + } + }); } requestPersistence(); diff --git a/desktop/package/macosx/Info.plist b/desktop/package/macosx/Info.plist index 2d4618f7..31325683 100644 --- a/desktop/package/macosx/Info.plist +++ b/desktop/package/macosx/Info.plist @@ -5,10 +5,10 @@ CFBundleVersion - 1.0.13 + 1.0.14 CFBundleShortVersionString - 1.0.13 + 1.0.14 CFBundleExecutable Haveno diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java index db008e93..3c6ed097 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java @@ -308,7 +308,7 @@ public abstract class MutableOfferView> exten if (CurrencyUtil.isTraditionalCurrency(tradeCurrency.getCode())) { placeOfferButtonLabel = Res.get("createOffer.placeOfferButton", Res.get("shared.buy")); } else { - placeOfferButtonLabel = Res.get("createOffer.placeOfferButtonCrypto", Res.get("shared.buy"), tradeCurrency.getCode()); + placeOfferButtonLabel = Res.get("createOffer.placeOfferButtonCrypto", Res.get("shared.sell"), tradeCurrency.getCode()); } nextButton.setId("buy-button"); fundFromSavingsWalletButton.setId("buy-button"); @@ -317,7 +317,7 @@ public abstract class MutableOfferView> exten if (CurrencyUtil.isTraditionalCurrency(tradeCurrency.getCode())) { placeOfferButtonLabel = Res.get("createOffer.placeOfferButton", Res.get("shared.sell")); } else { - placeOfferButtonLabel = Res.get("createOffer.placeOfferButtonCrypto", Res.get("shared.sell"), tradeCurrency.getCode()); + placeOfferButtonLabel = Res.get("createOffer.placeOfferButtonCrypto", Res.get("shared.buy"), tradeCurrency.getCode()); } nextButton.setId("sell-button"); fundFromSavingsWalletButton.setId("sell-button"); @@ -707,10 +707,10 @@ public abstract class MutableOfferView> exten triggerPriceInputTextField.clear(); if (!CurrencyUtil.isTraditionalCurrency(newValue)) { if (model.isShownAsBuyOffer()) { - placeOfferButton.updateText(Res.get("createOffer.placeOfferButtonCrypto", Res.get("shared.buy"), + placeOfferButton.updateText(Res.get("createOffer.placeOfferButtonCrypto", Res.get("shared.sell"), model.getTradeCurrency().getCode())); } else { - placeOfferButton.updateText(Res.get("createOffer.placeOfferButtonCrypto", Res.get("shared.sell"), + placeOfferButton.updateText(Res.get("createOffer.placeOfferButtonCrypto", Res.get("shared.buy"), model.getTradeCurrency().getCode())); } } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookViewModel.java index f5fdd745..b0a0f7c1 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookViewModel.java @@ -402,9 +402,7 @@ abstract class OfferBookViewModel extends ActivatableViewModel { } public Optional getMarketBasedPrice(Offer offer) { - OfferDirection displayDirection = offer.isTraditionalOffer() ? direction : - direction.equals(OfferDirection.BUY) ? OfferDirection.SELL : OfferDirection.BUY; - return priceUtil.getMarketBasedPrice(offer, displayDirection); + return priceUtil.getMarketBasedPrice(offer, direction); } String formatMarketPriceMarginPct(Offer offer) { diff --git a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java index 1b0867ef..6f4e9635 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java @@ -306,12 +306,12 @@ public class TakeOfferView extends ActivatableViewAndModel